Application Extension with Tuist

sanghoon Ahn·2024년 5월 7일
2

Daily Issue

목록 보기
10/10
post-thumbnail

안녕하세요 szzang입니다!

이번에는 기능을 추가하다 겪은 어려움과, 해결했던 과정을 가져와 보았습니다 😃

흔하지 않은 기능이지만.. 누군가는 어려움을 겪고 있을 수 있기에
제가 해결했던 방법과 그 과정에서 알게된 내용을 공유하려 합니다 !

누군가에겐 도움이 되기를 바라며 시작합니다 🙏🏻


무엇이 문제입니까?

어느날 날아든 기능 요구사항

“App Push Message에 첨부한 사진이 나올 수 있게 해주세요”

Push Message를 사용하는 플랫폼이라면 충분히 해당 요구사항을 해결해야 하는 순간이 올 것 같습니다.

참고) 원티드는 Braze라는 CRM Tool을 사용하여 Push Message를 구현하였습니다. 🙂

Push Message는 구현해보았지만, 이미지를 추가해보는건 처음이라 무작정 Braze의 가이드를 찾았고,

먼저 Apple의 가이드 대로 Project에 Extension을 추가하라는 가이드를 확인했습니다.

여기서부터 살짝 두려움이 생겼습니다.

일반적인 Project 환경에서 Extension을 추가하는것은 크게 어렵지 않습니다.

버튼 몇번 딸깍하면 되는 ..

그러나 지금 상황은 Tuist로 구성된 Project 환경, Sheme에 따른 다른 Bundle ID, match를 통한 인증서 관리 등 훨씬 고려해야 하는 부분이 많았기 때문입니다.

에라 모르겠다 우선 가이드대로 들이받아봅니다.

Extension 먼저 추가하자! 구글에 검색을 때려봅니다.

Project 꼬이면… revert 하면 되겠지..

“Tuist App Extension” 만 구글에 검색해도 많은 참고 자료들이 나옵니다.


오 좋은데? 바로 시도해봐야지

sample을 참고하여 Project.swift 파일을 수정해보고 Project를 generate를 해봅니다.

(Project.swift 파일의 코드는 각자의 환경에 따라 매우 다를 수 있습니다. 참고시 주의 바랍니다 ⚠️)

let project = Project(
    name: "App",
    targets: [
        .target(
            name: "App",
            destinations: .iOS,
            product: .app,
            bundleId: "io.tuist.App",
            infoPlist: "Info.plist",
            sources: ["Sources/**"],
            dependencies: [
                .target(name: "NotificationServiceExtension"),
            ]
        ),
        .target(
            name: "NotificationServiceExtension",
            destinations: .iOS,
            product: .appExtension,
            bundleId: "io.tuist.App.NotificationServiceExtension",
            infoPlist: .extendingDefault(with: [
                "CFBundleDisplayName": "$(PRODUCT_NAME)",
                "NSExtension": [
                    "NSExtensionPointIdentifier": "com.apple.usernotifications.service",
                    "NSExtensionPrincipalClass": "$(PRODUCT_MODULE_NAME).NotificationService",
                ],
            ]),
            sources: [
	            .glob(.relativeToRoot("NotificationServiceExtension/**"))
            ],
            dependencies: []
        )
    ]
)

한번에 되면 글 안썼겠죠?

The target NotificationServiceExtension has the following invalid source files globs:
The directory "xxx/xxx/NotificationServiceExtension" defined in the glob pattern "xxx/xxx/NotificationServiceExtension/**" does not exist.

라는 에러가 나옵니다.

파일이 없다는 내용이니 우선 폴더를 만들면 되겠지?

여기서 잠깐 .. Extension은 추가하게되면 파일을 자동으로 생성해줍니다.
그러니까 순서가 잘못됐군요.

현재까지 작업한 순서는 아래와 같은데,

  • Tuist 수정 → Extension 추가*

아래와 같이 변경하면 폴더를 직접 만들지 않고 경로만 잘 적어주면 되겠군요!

  • Extesnion 추가 → Tuist 수정*

다시 처음부터!


Xcode에서 NotificationService Extension 추가 !


Product Name 작성하고 ! Finish !

파일추가 된거 확인했고 !

다시 Tuist를 수정하면 성공합니다 :)

자 이제 첫걸음 뗐습니다 하하

저는 Braze 측 요구사항에 맞게 NotificationService.swift 파일을 수정했습니다.

여러분도 이제 요구사항에 맞게 NotificationService.swift를 수정하시면 됩니다!

자 이제 제가 겪었던 문제들에 하나씩 맞딱 드릴텐데요, 하나씩 짚어보겠습니다.

.xcconfig

먼저 대부분의 제품은 개발/릴리즈 환경(schme)이 분리되어 있을 테고, 각각의 환경에 맞게 xcconfig, BundleID, 인증서를 관리하고 있을테죠 🥲

개발 환경

  • dev xcconfig / dev BundleID/ dev 인증서 등등

릴리즈 환경

  • xcconfig / BundleID / 인증서 등등

그러면 Notification Service Extension은 어떻게 해야할까요?

개발 / 라이브 모두 같이 쓸 수 있게 하나만? 아니면 각각 환경에 맞게 구성해야하나?

맞는 방법인진 모르겠으나, 저는 각 환경에 맞게 구성하였습니다.

(Tuist라서 변수를 사용할 수 있어 생각보다 손쉽게 해결했습니다.)

NotificationServiceExtension Target을 만들 때 사용하는 setting은 개발/라이브 config를 모두 가질 수 있도록 했습니다.

Target(
    name: "NotificationServiceExtension",
    destinations: .iOS,
    product: .appExtension,
    bundleId: "$(BUNDLE_ID)",
    infoPlist: .extendingDefault(with: [
        "CFBundleDisplayName": "$(PRODUCT_NAME)",
        "NSExtension": [
            "NSExtensionPointIdentifier": "com.apple.usernotifications.service",
            "NSExtensionPrincipalClass": "$(PRODUCT_MODULE_NAME).NotificationService",
        ],
    ]),
    sources: [
      .glob(.relativeToRoot("NotificationServiceExtension/**"))
    ],
    dependencies: [],
    settings: .settings(
	    configurations: [
          .debug(
              name: "Dev",
              xcconfig: .relativeToRoot("xxx/xxx/NotificationService/Dev.xcconfig")
          ),
          .release(
              name: "Live",
              xcconfig: .relativeToRoot("xxx/xxx/NotificationService/Live.xcconfig")
          )
      ],
      defaultSettings: .none
    )
)

settings를 보시면 debug/release 환경에서 사용할 xcconfig 값을 직접 지정했는데요,

저는 Xcode의 현재 xcconfig를 추출하여 필요한 내용 일부만 수정하는 방법을 사용하고 있습니다.

https://github.com/dempseyatgithub/BuildSettingExtractor

(해당 Application을 사용하면 현재 사용중인 프로젝트의 xcconfig를 간단하게 추출 할 수 있습니다 🙂)

테스트

처음에 시도했던 Tuist 설정 > Notification Service Extension을 추가한 뒤에

시뮬레이터에서 확인을 해보기 위해 직접 빌드해서 테스트를 해보았으나.. 😱

이미지가 나오지 않는겁니다 ㅜㅜ

왜 나오지 않을까 .. console 메세지를 확인해보니 Notification Service Extension 이 포함되지 않았다는 에러 메세지가 나왔습니다.

(스크린샷이 남아있는게 없네요 흑흑)

빌드도 잘되는데 왜 포함되지 않았다는걸까? 🤔

의문을 가지고 직접 테스트 Xcode Project를 생성 해보고, Extesnion을 추가하고 패키지를 확인해보니

아뿔싸, .appex 파일이 추가되어야 하는구나..

개발 했던 환경의 패키지에서는 해당 파일이 없었습니다 😭

💡 패키지 정보 확인 방법
Xcode Preference > Derived Data > CoreSimulator(시뮬레이터)/CoreDevice > 앱이 설치된 DeviceID > data > Containers > Bundle > Application > 설치된 앱 ID > 앱이름.app > 우클릭 + 패키지 내용 보기

그래서 처음에 언급했던것 처럼 Extension을 먼저 추가하고,

Tuist설정을 나중에 하니 .appex 파일이 정상적으로 추가되어 있었습니다 !! 🤘🏻

그렇게 다시한번 테스트를 진행하고, 이미지가 나오는것을 확인했습니다.

추가로 시뮬레이터에서는 이미지 롱클릭시 확대가 되지 않습니다 !!

이미지 확대는 실기기에서 확인해주세용 😀

Debug Breakpoint

마지막으로 개발하면서 알게된 내용이 하나 더 있습니다.

Extension 코드인 NotificaionService.swift 파일에 Breakpoint를 설정하고 App을 실행하게 되면

Breakpoint가 점선으로 변경되고, Push를 보내도 break가 걸리지 않습니다.

이럴때는 scheme을 NotificationService Extension으로 변경하고 실행해야 Breakpoint가 정상적으로 작동합니다!
자세한 내용은 링크를 참고해주세요!


오늘도 두서없는 맨땅에 헤딩글이 되어버렸습니다.

항상 잘 써보려고 하는데 막상 쓰다 보면 엉망이 되는것 같네요.

그래도 App Extension을 추가할 때 어떤것들을 확인해야되는지

시야가 조금 더 넓어진것 같아 뿌듯합니다.

질문이 있다면 댓글이나 오픈카톡방을 통해 남겨주세요 🤓

오늘도 읽어주셔서 감사합니다 🙇🏻‍♂️




참고자료

profile
hello, iOS

0개의 댓글