티스토리 뷰

728x90

 

최근 LiveActivity를 적용해보면서 App Extension 경험과 

Tuist에 녹여내는 과정중에 삽질을 많이 하게 돼 복습도 할겸 블로깅을 해보려고 합니다!

 


겪었던 문제 

 

1. Tuist를 통해 Widget Extension을 추가해야 함

2. Widget Extension에서도 Asset을 따로 만들어야 함

 

일반적으로는 App Extension의 경우 target membership을 아래와 같이 체크하면 해결 되는데,

 

tuist를 적용하게 되면 아래와 같은 방법으로 추가해줘야 합니다.

 

먼저 Project.swift 파일을 열기위해 아래 명령어를 터미널에 입력해주시고

tuist edit

 

Project 객체에 target을 추가해줍니다.

// Project.swift

let project = Project(
    name: appName,
    targets: [
        // App target
        .target(
            name: appName,
            destinations: .iOS,
            product: .app,
            
            // etc..
            
            dependencies: [
              // ... 
              .target(name: "WidgetExtension") // 1. 의존성 추가
            ]
        ),
        // 2
        // Extension target
        .target(
            name: "WidgetExtension",
            destinations: .iOS,
            product: .appExtension, // extension
            bundleId: bundleId + ".WidgetExtension",
            deploymentTargets: deploymentTarget,
            infoPlist: .extendingDefault(with: [
                "CFBundleDisplayName": "$(PRODUCT_NAME)",
                "UIUserInterfaceStyle": "Light",
                "NSExtension": [
                    "NSExtensionPointIdentifier": "com.apple.widgetkit-extension"
                ],
            ]),
            sources: "WidgetExtension/Sources/**",
            resources: "WidgetExtension/Resources/**",
            settings: configureSettings(with: .appExtension)
        ),
    ],
    additionalFiles: ["README.md"],
    resourceSynthesizers: [
        .assets(),
        .fonts()
    ]
)

 

1. app target의 dependencies에 WidgetExtension의 의존성을 추가해줍니다

2. target을 하나 추가하여 appExtension을 추가해줍니다.

 

새롭게 만들어진 target의 경우 infoPlist, sources, resources 등 다시 만들어줘야 하는데요. 

App target에서 했던것 처럼 똑같이 추가를 해주면 됩니다.

 

 


 

+ 추가

공통 모듈 생성

 

추가된 Widget Extension의 경우도 Asset을 따로 만들어줘야 하는데요.

저는 공통으로 사용되는 Asset이나 코드가 있는 경우 공통으로 관리되는 모듈을 하나 더 만드는 방법으로 구현했습니다.

 

다시 Project.swift를 열어주시고

let project = Project(
    name: appName,
    targets: [
        .target(
            name: appName,
            destinations: .iOS,
            product: .app,
            bundleId: bundleId,
            deploymentTargets: deploymentTarget,
            infoPlist: .extendingDefault(with: infoPlist),
            sources: ["App/Sources/**"],
            resources: ["App/Resources/**"],
            dependencies: [
                .external(name: "RxSwift"),
                .external(name: "RxCocoa"),
                .external(name: "Alamofire"),
                .external(name: "CoreXLSX"),
                .external(name: "SwiftyJSON"),
                .external(name: "SkeletonView"),
                .target(name: "Common"),             // 의존성 추가
                .target(name: "WidgetExtension"),    // 의존성 추가
            ],
            settings: configureSettings(with: .app)
        ),
        .target(
            name: "WidgetExtension",
            destinations: .iOS,
            product: .appExtension,
            bundleId: bundleId + ".WidgetExtension",
            deploymentTargets: deploymentTarget,
            infoPlist: .extendingDefault(with: [
                "CFBundleDisplayName": "$(PRODUCT_NAME)",
                "NSExtension": [
                    "NSExtensionPointIdentifier": "com.apple.widgetkit-extension"
                ],
            ]),
            sources: "WidgetExtension/Sources/**",
            dependencies: [
                .target(name: "Common")           // 의존성 추가
            ],
            settings: configureSettings(with: .appExtension)
        ),
        .target(
            name: "Common", // 공통으로 사용해야할 코드가 있을 경우 여기에 코드 분리
            destinations: .iOS,
            product: .framework,
            productName: "Common",
            bundleId: bundleId + ".Common",
            deploymentTargets: deploymentTarget,
            sources: "Common/Sources/**"
        ),
    ]
)

 

위와 같이 'Common' 이라는 모듈을 만들었습니다.

폴더 구조는 아래와 같아요.

 

폴더구조

 

 

Usage

공통으로 사용하는 framework (Common)

// Common.swift

import ActivityKit

public struct WidgetExtensionAttributes: ActivityAttributes {
    public struct ContentState: Codable, Hashable {
        // Dynamic stateful properties about your activity go here!
        public var busNumber: Int
        public var currentBusStop: String
        public var stopLeft: Int
        public var totalStop: Int
        
        public init(busNumber: Int, currentBusStop: String, stopLeft: Int, totalStop: Int) {
            self.busNumber = busNumber
            self.currentBusStop = currentBusStop
            self.stopLeft = stopLeft
            self.totalStop = totalStop
        }
    }

    // Fixed non-changing properties about your activity go here!
    public var currentBusStopInfo: String
    
    public init(currentBusStopInfo: String) {
        self.currentBusStopInfo = currentBusStopInfo
    }
}

 

NOTE


struct, 변수들을 public 및 초기화(init)시켜줘야 다른 파일에서도 import해서 사용할 수 있습니다.

 

public을 붙이는 이유는 접근제어자 를 참고해주세요~!

 

 

사용방법은 아래와 같습니다. 

 

저는 WidgetExtensionAttributes를 App, WidgetExtension 두 군데에서 다 사용하기 위해 Common 모듈에 따로 추가했습니다.

 

[App] LiveActivitiyManager

 

 

 

[WidgetExtension] WidgetExtensionLiveActivity

 

 

이외에도 Color, Font 등 각 모듈에서 공통으로 사용하는게 있다면 한곳에서 관리하고 가져오는 방법으로 구현하도록 했습니다.

 

물론 이 방법이 답은 아니라고 생각합니다.

만약 다른 모듈에서 Color가 하나만 필요할 경우에도 Common 모듈을 import 해야할까? 라는 의문이 생기게 되네요.

이런 경우는 따로 import 하지않고 해당 모듈에서만 필요한 Color를 사용한다던지 할것 같습니다. 

이 부분은 좀더 익숙해져보고 다른분들에게 조언을 한번 구해보고 수정해보도록 하겠습니다. :) 

 


오늘은 LiveActivity를 적용해보면서 아래의 방법들에 대해 배워갈 수 있었던것 같습니다.

1. Widget Extension을 적용하는 방법

2. tuist와 같이 사용하는 방법,

3. 공통으로 사용하는 경우 하나의 framework로 관리하는 방법

 

하나씩 tuist 기능을 알아가면서 일단은 정리하고 있지만 추후 새롭게 알게 되는게 있으면 수정하도록 하겠습니다.

 

 

 

참고

1. 

https://github.dev/Saik0s/BackgroundExperiments/blob/8ef3291d77f31a0aa95c3b8bda59a1bbcd86d6d7/Project.swift#L65

728x90