> ## Documentation Index
> Fetch the complete documentation index at: https://documentation.onesignal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Live Activities 개발자 설정

> OneSignal을 사용하여 iOS 앱에서 Live Activities를 설정하여 잠금 화면과 Dynamic Island에 실시간 업데이트를 표시합니다.

Live Activities를 사용하면 iOS 앱이 잠금 화면 및 Dynamic Island에 직접 실시간 업데이트를 표시할 수 있습니다. 배송 추적, 스포츠 점수 또는 시간에 민감한 트랜잭션 업데이트에 이상적이며 앱을 열지 않고도 사용자에게 정보를 제공합니다.

<Note> Android에는 [Android 라이브 알림](./android-live-notifications)이라는 유사한 기능이 있습니다. </Note>

## 요구 사항

* 네이티브 iOS(Swift/Objective-C)를 사용하는 경우 [iOS SDK 설정](./ios-sdk-setup)을 따르세요.
  * 래퍼 SDK(React Native, Flutter, Unity 등)를 사용하는 경우 [모바일 SDK 설정](./mobile-sdk-setup)을 따른 다음 [크로스 플랫폼 Live Activity SDK 설정](./cross-platform-live-activity-setup)을 따르세요.
* **push-to-start** 지원을 위한 OneSignal iOS SDK 버전 5.2.0+ ([릴리스 노트 참조](https://github.com/OneSignal/OneSignal-iOS-SDK/releases/tag/5.2.0)).
* **클릭 추적** 및 **확인된 전달**을 위한 OneSignal iOS SDK 버전 5.2.15+
* iOS 16.1+ 및 iPadOS 17+
* [.p8 APNs 키](./ios-p8-token-based-connection-to-apns)를 사용합니다. Apple은 Live Activities에서 p12 인증서를 지원하지 않습니다.
* Xcode 14 이상

***

## 설정

다음 단계에서는 Live Activities를 빠르게 설정하는 방법을 안내합니다. 자세한 내용 및 디자인 사용자 지정에 대해서는 [Apple의 Live Activities 개발자 문서](https://developer.apple.com/design/human-interface-guidelines/live-activities)를 참조하세요.

### 1. Widget Extension 추가

Xcode에서 **파일 > 새로 만들기 > 타겟... > Widget Extension**으로 이동합니다.

<Frame caption="Xcode에서 앱에 새 Widget Extension 타겟을 추가합니다.">
  <img src="https://mintcdn.com/onesignal/FXJz6yFfOqztaEND/images/live-activities/live-activity-widget-extension.png?fit=max&auto=format&n=FXJz6yFfOqztaEND&q=85&s=60a52d6484203c4d6af21bc3a7da5e62" width="2166" height="1488" data-path="images/live-activities/live-activity-widget-extension.png" />
</Frame>

선택하고 **다음**을 누릅니다.

이름(예: `OneSignalWidget`)을 제공하고 **Include Live Activity**가 선택되어 있는지 확인하여 Widget Extension을 구성합니다. 그런 다음 **완료**를 클릭합니다.

<Frame caption="Live Activity를 위한 Widget Extension 옵션.">
  <img src="https://mintcdn.com/onesignal/yt4lRKoquAlWvRvF/images/live-activities/OneSignalWidget.png?fit=max&auto=format&n=yt4lRKoquAlWvRvF&q=85&s=47412314963f6fbd58fd2c7d3a529322" width="2166" height="1488" data-path="images/live-activities/OneSignalWidget.png" />
</Frame>

스킴을 활성화하라는 메시지가 표시되면 **활성화하지 않음**을 클릭합니다.

<Frame caption="스킴 활성화 프롬프트 — 활성화하지 않음을 선택합니다.">
  <img src="https://mintcdn.com/onesignal/yt4lRKoquAlWvRvF/images/live-activities/la-scheme-activation.png?fit=max&auto=format&n=yt4lRKoquAlWvRvF&q=85&s=d3a2207ff2b5988c63c2abd631a72541" width="2166" height="1488" data-path="images/live-activities/la-scheme-activation.png" />
</Frame>

### 2. `Info.plist` 업데이트

메인 타겟의 `Info.plist`에서 `Supports Live Activities` 키를 **Boolean**으로 추가하고 `YES`로 설정합니다.

<Frame caption="Info에 Supports Live Activities 키를 추가하고 값을 Boolean YES로 설정합니다">
  <img src="https://mintcdn.com/onesignal/yt4lRKoquAlWvRvF/images/live-activities/info-la-setting.png?fit=max&auto=format&n=yt4lRKoquAlWvRvF&q=85&s=e839231401651b71d0ae8d48614ec130" width="2166" height="1488" data-path="images/live-activities/info-la-setting.png" />
</Frame>

프로그래밍 방식으로 설정하는 경우 다음과 같아야 합니다:

```xml info.plist theme={null}
<key>NSSupportsLiveActivities</key>
<true/>
```

<Note>
  Live Activities를 업데이트할 때 Apple이 업데이트의 긴급성을 결정하는 데 사용하는 "priority"를 설정할 수 있는 옵션이 있습니다. Apple에는 높은 우선 순위 플래그를 너무 자주 사용하는 요청을 조절하는 내부 임계값이 있습니다.

  Live Activities 사용 사례가 더 자주 높은 우선 순위 업데이트에 의존하는 경우 [Apple의 개발자 문서](https://developer.apple.com/documentation/activitykit/starting-and-updating-live-activities-with-activitykit-push-notifications#Determine-the-update-frequency)에 따라 Info.plist에 `NSSupportsLiveActivitiesFrequentUpdates` 키를 Boolean 유형으로 YES로 설정하여 추가할 수 있습니다. Live Activity가 푸시 예산을 초과하면 사용자에게 대화 상자가 표시되며, Live Activity를 계속 허용하면 원활한 사용자 경험을 위해 예산이 자동으로 증가합니다.
</Note>

### 3. SDK 추가

<Tabs>
  <Tab title="Package Manager">
    <Note>
      래퍼 SDK(예: **Flutter**)에서 SPM이 활성화된 경우 이 단계를 건너뛸 수 있습니다. OneSignal 패키지는 이미 해결되어 모든 타겟에서 사용할 수 있습니다.
    </Note>

    Widget Extension 타겟에서 **일반 > 프레임워크, 라이브러리 및 임베디드 콘텐츠** 아래에 `OneSignalFramework`를 추가합니다:

    <Frame caption="Widget Extension 타겟에 OneSignalFramework를 추가합니다">
      <img src="https://mintcdn.com/onesignal/yt4lRKoquAlWvRvF/images/live-activities/add-one-signal-framework.png?fit=max&auto=format&n=yt4lRKoquAlWvRvF&q=85&s=386781616958e5a80a5cfec5ee143dcb" width="2392" height="1488" data-path="images/live-activities/add-one-signal-framework.png" />
    </Frame>
  </Tab>

  <Tab title="Cocoapods">
    프로젝트의 타겟 목록에서 위젯 확장 타겟의 이름을 찾습니다. 예시 이름은 `OneSignalWidgetExtension`입니다.

    <Frame caption="위젯 확장 타겟의 이름을 찾습니다">
      <img src="https://mintcdn.com/onesignal/yt4lRKoquAlWvRvF/images/live-activities/cocoapods-la-extension-name.png?fit=max&auto=format&n=yt4lRKoquAlWvRvF&q=85&s=850a91387539587802306a66771435eb" width="2166" height="1488" data-path="images/live-activities/cocoapods-la-extension-name.png" />
    </Frame>

    `Podfile`을 열고 다음 코드를 추가합니다. `OneSignalWidgetExtension`을 위젯 확장 타겟의 이름으로 바꿉니다.

    <CodeGroup>
      ```ruby Podfile theme={null}
      target 'OneSignalWidgetExtension' do
        use_frameworks!
        pod 'OneSignal/OneSignal', '>= 5.0.0', '< 6.0'
      end
      ```

      ```ruby Full Podfile example theme={null}
      # Uncomment the next line to define a global platform for your project
      # platform :ios, '9.0'

      target 'Demo Momenta' do
        # Comment the next line if you don't want to use dynamic frameworks
        use_frameworks!
        pod 'OneSignal/OneSignal', '>= 5.0.0', '< 6.0'
        pod 'OneSignal/OneSignalInAppMessages', '>= 5.0.0', '< 6.0'
      end

      target 'OneSignalNotificationServiceExtension' do
        use_frameworks!
        pod 'OneSignal/OneSignal', '>= 5.0.0', '< 6.0'
      end

      target 'OneSignalWidgetExtension' do
        use_frameworks!
        pod 'OneSignal/OneSignal', '>= 5.0.0', '< 6.0'
      end
      ```
    </CodeGroup>

    Xcode를 닫고 `pod repo update && pod install`을 실행하여 `OneSignalLiveActivities` pod를 설치합니다.

    이 작업을 수행한 후에도 "No such module 'OneSignalLiveActivities'" 오류가 계속 표시되면 **"Widget Extension 타겟" > 일반 > 프레임워크 및 라이브러리 > + 아이콘**으로 이동하여 종속성을 수동으로 추가할 수 있습니다. `OneSignalLiveActivities` 프레임워크를 선택합니다:

    <Frame caption="종속성을 수동으로 추가합니다">
      <img src="https://mintcdn.com/onesignal/YOTSrtBSoqdrJ37A/images/docs/427d54ef84e97edaf0a7c3b158b48b3bec7305fb1764db9dd4ef7c340d77bc6c-image.png?fit=max&auto=format&n=YOTSrtBSoqdrJ37A&q=85&s=f7ba0aa4234ef69ebd7820ef5e50a18b" width="1273" height="881" data-path="images/docs/427d54ef84e97edaf0a7c3b158b48b3bec7305fb1764db9dd4ef7c340d77bc6c-image.png" />
    </Frame>
  </Tab>
</Tabs>

### 4. 위젯 속성 및 UI 정의

`your-nameLiveActivity.swift` 파일(예: `OneSignalWidgetLiveActivity.swift`)을 열어 구조체의 속성을 정의하고 위젯 UI를 변경합니다.

* `your-nameAttributes`는 Live Activity의 정적 콘텐츠를 설명합니다.
* `ContentState`는 Live Activity의 동적 콘텐츠를 설명합니다.

예시를 따르는 경우 아래 코드를 `OneSignalWidgetLiveActivity.swift` 파일에 복사하여 붙여넣습니다.

```swift your-nameLiveActivity.swift theme={null}
import ActivityKit
import WidgetKit
import SwiftUI
// Import the OneSignalLiveActivities module
// If you get an error about the module not being found, return to step 3 and ensure you have added the OneSignalLiveActivities pod correctly.
import OneSignalLiveActivities

// Update to inherit from OneSignalLiveActivityAttributes
// This will be used in your API requests to start a Live Activity
struct OneSignalWidgetAttributes: OneSignalLiveActivityAttributes  {
    // Update to inherit from OneSignalLiveActivityContentState
    public struct ContentState: OneSignalLiveActivityContentState {
        // Dynamic stateful properties about your activity go here!
        var emoji: String
        // Add a reference to OneSignalLiveActivityContentStateData?
        var onesignal: OneSignalLiveActivityContentStateData?
    }
    // Fixed non-changing properties about your activity go here!
    var name: String
    // Add a reference to OneSignalLiveActivityAttributeData
    var onesignal: OneSignalLiveActivityAttributeData
}

struct OneSignalWidgetLiveActivity: Widget {
    var body: some WidgetConfiguration {
      // Update to use `for: the-name-of-your-attributes-struct`
      // This will be used in your API requests to start a Live Activity
        ActivityConfiguration(for: OneSignalWidgetAttributes.self) { context in
            // Lock screen/banner UI goes here
            // Update to show the attributes sent via the payload
            VStack {
                Text("Hello \(context.attributes.name) \(context.state.emoji)")
            }
            .activityBackgroundTint(Color.cyan)
            .activitySystemActionForegroundColor(Color.black)

        } dynamicIsland: { context in
            DynamicIsland {
                // Expanded UI goes here.  Compose the expanded UI through
                // various regions, like leading/trailing/center/bottom
                DynamicIslandExpandedRegion(.leading) {
                    Text("Leading")
                }
                DynamicIslandExpandedRegion(.trailing) {
                    Text("Trailing")
                }
                DynamicIslandExpandedRegion(.bottom) {
                    Text("Bottom \(context.state.emoji)")
                    // more content
                }
            } compactLeading: {
                Text("L")
            } compactTrailing: {
                Text("T \(context.state.emoji)")
            } minimal: {
                Text(context.state.emoji)
            }
            .widgetURL(URL(string: "http://www.apple.com"))
            .keylineTint(Color.red)
        }
    }
}
```

### 5. 메인 타겟 멤버십 허용

<Note>
  이 단계는 사용자 정의 속성(예: `OneSignalWidgetAttributes`)을 사용할 때만 필요합니다. `DefaultLiveActivityAttributes`를 사용하는 경우 이 단계를 건너뛰세요 — 해당 타입은 OneSignal SDK에서 제공되며 메인 앱 타겟에서 이미 접근 가능합니다.
</Note>

`your-nameLiveActivity.swift` 파일의 **Target Membership** 목록에 메인 앱 타겟을 추가합니다. 이는 메인 앱 타겟이 설정 호출(6단계)에서 사용자 정의 속성 구조체를 참조할 수 있도록 하기 위해 필요합니다.

Xcode에서 화면 오른쪽의 인스펙터 패널을 엽니다. **Target Membership** 내에서 **+** 버튼을 클릭하고 `ContentView` 및 OneSignal 초기화 코드가 포함된 메인 앱 타겟을 선택합니다.

<Frame caption="메인 타겟 멤버십을 허용합니다">
  <img src="https://mintcdn.com/onesignal/FXJz6yFfOqztaEND/images/live-activities/target-membership.png?fit=max&auto=format&n=FXJz6yFfOqztaEND&q=85&s=3315253623dfec6c13a56ff30a156772" width="2392" height="1488" data-path="images/live-activities/target-membership.png" />
</Frame>

### 6. AppDelegate에 설정 메서드 추가

OneSignal SDK 초기화 후 `AppDelegate`에서 `OneSignal.LiveActivities.setup`을 호출합니다.

`OneSignalWidgetAttributes`를 Live Activity 속성 구조체의 이름으로 바꿉니다.

```swift AppDelegate theme={null}
// Import the OneSignalLiveActivities module
import OneSignalLiveActivities

// This should be added after initializing the OneSignal SDK
if #available(iOS 16.1, *) {
	OneSignal.LiveActivities.setup(OneSignalWidgetAttributes.self)
  // If you have multiple Live Activities, you can add them here with the setup method
  // OneSignal.LiveActivities.setup(LiveActivityWidgetAttributes-2.self)
}
```

이는 ActivityKit 비동기 시퀀스를 사용하여 업데이트를 관리하고 보고합니다.

<Warning>
  앱에서 다음 시퀀스 중 하나를 직접 소비하는 경우 OneSignal Live Activity 동작을 방해할 수 있습니다:

  * activityStateUpdates
  * pushTokenUpdates
  * pushToStartTokenUpdates
  * activityUpdates
</Warning>

***

## Live Activity 시작

기기에서 Live Activity를 시작하는 두 가지 옵션이 있습니다:

<Tabs>
  <Tab title="Push-to-start">
    [Push To Start API 요청](/reference/start-live-activity)을 보냅니다. 모든 이름과 ID가 위젯 구성과 정확히 일치하는지 확인합니다(매개변수는 대소문자를 구분함). 누락되거나 잘못 추가된 것이 있으면 위젯을 실행하려고 할 때 문제가 발생할 수 있습니다.

    위의 예시에서 작동하는 요청 예시는 다음과 같습니다.

    바꾸기:

    * `YOUR_APP_ID`를 OneSignal 앱 ID로 바꿉니다.
    * `YOUR_APP_API_KEY`를 OneSignal API 키로 바꿉니다.
    * `OneSignalWidgetAttributes`를 Widget Attributes 구조체의 이름으로 바꿉니다.

      ```curl curl theme={null}
      curl --location 'https://api.onesignal.com/apps/YOUR_APP_ID/activities/activity/OneSignalWidgetAttributes' \
      --header 'Authorization: key YOUR_APP_API_KEY' \
      --header 'Content-Type: application/json' \
      --data '{
          "event": "start",
          "activity_id": "push-to-start",
          "included_segments": [
              "Subscribed Users"
          ],
          "event_attributes": {
              "name": "World"
          },
          "event_updates": {
              "emoji":"🤩"
          },
          "name": "Live Activities Test",
          "contents": {
              "en": "A push started this Live Activity"
          },
          "headings": {
            "en": "Live Activity Started"
          }
        }'
      ```

    제공된 예시 코드를 따르는 경우 기기의 잠금 화면에 Live Activity가 표시됩니다.

    <Frame caption="잠금 화면의 Live Activity">
      <img src="https://mintcdn.com/onesignal/FXJz6yFfOqztaEND/images/live-activities/live-activity-lock-screen.jpg?fit=max&auto=format&n=FXJz6yFfOqztaEND&q=85&s=8abc098ce8772b1c457e5f1effc8c14e" width="900" height="385" data-path="images/live-activities/live-activity-lock-screen.jpg" />
    </Frame>

    <Check>
      push-to-start로 Live Activity를 성공적으로 시작했습니다!

      사용자는 계속 업데이트를 받으려면 "허용"을 선택해야 합니다.
    </Check>
  </Tab>

  <Tab title="Trigger-in-app">
    사용자가 앱과 상호 작용하는 동안 Live Activity를 트리거하도록 할 수 있습니다.

    예를 들어 사용자에게 활성 이벤트가 진행 중일 때(주문을 했거나, 게임이 진행 중이거나, 이벤트가 곧 시작될 예정 등) 앱을 열면 Live Activity를 자동으로 표시할 수 있습니다.

    이 예시에서는 버튼을 사용하여 Live Activity를 수동으로 시작합니다.

    ```swift theme={null}
    import SwiftUI
    import ActivityKit
    import OneSignalFramework
    import OneSignalLiveActivities

    struct ContentView: View {
      @StateObject private var viewModel = LiveActivityViewModel()

      var body: some View {
        VStack {
          Button("Start Live Activity") {
            viewModel.startLiveActivity()
          }
        }
      }
    }

    class LiveActivityViewModel: ObservableObject {

      func startLiveActivity() {
        let osAttributes = OneSignalLiveActivityAttributeData.create(activityId: "click-to-start")
        let attributes = OneSignalWidgetAttributes(name: "OneSignal", onesignal: osAttributes)
        let contentState = OneSignalWidgetAttributes.ContentState(emoji:"🤩", onesignal: nil)
        do {
            let activity = try Activity<OneSignalWidgetAttributes>.request(
                attributes: attributes,
                contentState: contentState,
                pushType: .token)
        } catch {
            print(error.localizedDescription)
        }
      }
    }
    ```

    버튼을 클릭하면 Live Activity가 실행됩니다.
  </Tab>
</Tabs>

***

## Live Activity 클릭 추적

OneSignal의 클릭 추적을 구현하여 사용자가 Live Activities와 Dynamic Islands를 탭할 때를 추적합니다. 이를 통해 참여도를 측정하고 선택적으로 사용자를 앱의 특정 콘텐츠로 딥 링크할 수 있습니다.

### 1단계: 위젯에 클릭 추적 추가

Live Activity 위젯에서 클릭을 추적하려는 UI 컴포넌트에 `.onesignalWidgetURL()` 수정자를 추가합니다:

```swift theme={null}
import OneSignalLiveActivities

struct ExampleAppFirstWidget: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(for: ExampleAppFirstWidgetAttributes.self) { context in
            VStack {
                Spacer()
                Text("Sample Text " + context.attributes.title).font(.headline)
                // Other UI code
            }
            .onesignalWidgetURL(URL(string: "myapp://settings"), context: context)
            // Pass nil if you don't need deep linking: .onesignalWidgetURL(nil, context: context)
        } dynamicIsland: { context in
            DynamicIsland {
                DynamicIslandExpandedRegion(.leading) {
                    Text("Leading")
                }
                // Other Dynamic Island regions
            } compactLeading: {
                Text("L")
            } compactTrailing: {
                Text("T")
            } minimal: {
                Text("Min")
            }
            .onesignalWidgetURL(URL(string: "myapp://settings"), context: context)
            // Optional: Track Dynamic Island clicks separately
        }
    }
}
```

**중요 고려 사항:**

* 딥 링크용 URL을 전달하거나 내비게이션 없이 클릭 추적만 원하는 경우 `nil`을 전달할 수 있습니다
* `.onesignalWidgetURL()`을 사용하는 경우 뷰 계층 구조에 Apple의 `.widgetURL()` 수정자를 **포함할 수 없습니다**
* 두 클릭을 모두 추적하려면 메인 Live Activity 뷰와 Dynamic Island 모두에 수정자를 적용합니다

### 2단계: 앱에서 URL 처리

클릭을 추적하고 사용자를 적절히 라우팅하기 위해 앱에 URL 처리를 추가합니다:

```swift theme={null}
import OneSignalLiveActivities

// AppDelegate 예시:
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    let originalURL = OneSignal.LiveActivities.trackClickAndReturnOriginal(url)

    // 원본 URL을 처리하여 사용자 내비게이트
    if let url = originalURL {
        // 커스텀 URL 라우팅 로직
        return handleDeepLink(url)
    }
    return false
}

// SceneDelegate 예시:
func scene(_ scene: UIScene, openURLContexts URLContexts: Set) {
    guard let url = URLContexts.first?.url else { return }

    let originalURL = OneSignal.LiveActivities.trackClickAndReturnOriginal(url)

    if let url = originalURL {
        // 커스텀 URL 라우팅 로직
        handleDeepLink(url)
    }
}

// SwiftUI 예시:
.onOpenURL { url in
    let originalURL = OneSignal.LiveActivities.trackClickAndReturnOriginal(url)

    if let url = originalURL {
        // 커스텀 URL 라우팅 로직
        handleDeepLink(url)
    }
}
```

`trackClickAndReturnOriginal()` 메서드는 OneSignal로 클릭을 자동으로 추적하고 앱이 처리할 위젯에서 지정한 원본 URL을 반환합니다.

***

## Live Activity 업데이트

[Live Activity 업데이트 API](/reference/update-live-activity-api)를 사용하여 활성 위젯을 업데이트합니다.

활동을 시작할 때 사용한 `activity_id`와 일치시킵니다.

이 예시 요청은 정의한 `push-to-start`라는 `activity_id`가 있으므로 push-to-start 위젯을 업데이트합니다.
click-to-start 위젯을 업데이트하려면 요청 경로를 `push-to-start` 대신 `click-to-start`를 사용하도록 업데이트합니다.

```curl curl theme={null}
curl --location 'https://api.onesignal.com/apps/YOUR_APP_ID/live_activities/push-to-start/notifications' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Authorization: key YOUR_API_KEY' \
--data '{
    "event": "update",
    "event_updates": {
        "emoji": "😎"
    },
    "contents": {
        "en": "A push updated this Live Activity"
    },
    "name": "Live Activity Updated"
}'
```

<Frame caption="Live Activity가 업데이트되었습니다">
  <img src="https://mintcdn.com/onesignal/FXJz6yFfOqztaEND/images/live-activities/live-activity-updated.jpg?fit=max&auto=format&n=FXJz6yFfOqztaEND&q=85&s=729ab3b33c297c15e52617ebd065b293" width="895" height="152" data-path="images/live-activities/live-activity-updated.jpg" />
</Frame>

<Check>
  Live Activity를 성공적으로 업데이트했습니다!

  Live Activity 업데이트에 대한 자세한 내용은 [Live Activity 업데이트 API](/reference/update-live-activity-api)를 확인하세요.
</Check>

***

## Live Activity 종료

동일한 [Live Activity 업데이트 API](/reference/update-live-activity-api)를 사용하여 `"event": "end"`를 설정하여 Live Activity를 종료할 수 있습니다.

```curl curl theme={null}
curl --location 'https://api.onesignal.com/apps/YOUR_APP_ID/live_activities/my_activity_id/notifications' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Authorization: key YOUR_API_KEY' \
--data '{
    "event": "end",
    "event_updates": {
        "emoji": "👋"
    },
    "contents": {
        "en": "A push ended this Live Activity"
    },
    "name": "Live Activity Ended"
}'
```

Live Activity가 종료될 수 있는 다른 방법:

* SDK [`exit()` 메서드](./mobile-sdk-reference#exit)를 사용합니다.
* 사용자가 Live Activity를 수동으로 스와이프하여 제거합니다.
* 사용자가 iOS 설정에서 Live Activities에 대한 권한을 취소합니다.

<Frame caption="Live Activity가 종료되었습니다">
  <img src="https://mintcdn.com/onesignal/FXJz6yFfOqztaEND/images/live-activities/live-activity-ended.jpg?fit=max&auto=format&n=FXJz6yFfOqztaEND&q=85&s=734a62b67cb2fe989203087a22945448" width="1218" height="200" data-path="images/live-activities/live-activity-ended.jpg" />
</Frame>

<Check>
  Live Activity를 성공적으로 종료하고 예시를 완료했습니다!
</Check>

***

## 모범 사례 및 권장 사항

### 디자인 고려 사항

* Apple의 [Live Activities 휴먼 인터페이스 가이드라인](https://developer.apple.com/design/human-interface-guidelines/live-activities)을 따르세요.
  * 중요한 정보를 우선순위로 지정하여 빠르게 이해하기 쉽게 만듭니다.
  * Dynamic Island에 주의를 끄는 요소를 앱에 추가하지 마세요.
  * 여백을 사용하고 요소 간 간격을 유지합니다.
  * 배경에 굵은 색상을 사용합니다. 라이트 및 다크 모드 모두에 대해 디자인합니다.

### 기능

* Apple은 각 [Live Activity 프레젠테이션](https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities#Review-Live-Activity-presentations)을 지원해야 합니다.
  * [컴팩트](https://developer.apple.com/design/human-interface-guidelines/live-activities#Compact-presentation)
  * [최소](https://developer.apple.com/design/human-interface-guidelines/live-activities#Minimal-presentation)
  * [확장](https://developer.apple.com/design/human-interface-guidelines/live-activities#Expanded-presentation)
  * [잠금 화면](https://developer.apple.com/design/human-interface-guidelines/live-activities#The-Lock-Screen)
* [딥 링크](https://developer.apple.com/design/human-interface-guidelines/live-activities#Offering-interactivity)를 테스트합니다.
* Apple의 [Live Activities로 라이브 데이터 표시](https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities) 가이드를 검토합니다.
* Live Activity에 민감한 정보를 표시하지 마세요.

### 대체 메시지 설정

Live Activity가 시작된 후 사용자가 업데이트를 받을 수 없는 경우, 앱을 열면 활동이 새로고침됩니다. 첫 번째 업데이트를 보낸 것으로 예상되는 시점 이후의 미래 날짜와 시간으로 [stale date](/reference/start-live-activity#body-stale-date)를 설정합니다. 업데이트를 받지 못한 사용자에게는 대신 대체 메시지가 표시됩니다.

위젯 UI에서 이 "stale" 상태를 수신 대기하여 대체 메시지를 표시할 수 있습니다:

<Frame caption="Live Activity가 stale 상태가 될 때 표시되는 대체 메시지.">
  <img src="https://mintcdn.com/onesignal/CKWnlQN-faoVTpm2/images/live-activities/live-activity-refresh.png?fit=max&auto=format&n=CKWnlQN-faoVTpm2&q=85&s=180ef1cc07313ef04361fbd45e2fec24" alt="대체 메시지를 표시하는 Live Activity 위젯" width="509" height="181" data-path="images/live-activities/live-activity-refresh.png" />
</Frame>

```swift theme={null}
struct ptsLiveActivity: Widget {
    var body: some WidgetConfiguration {
        ActivityConfiguration(for: ptsAttributes.self) { context in
            //This will flip to true after the stale date
            let isStale = context.isStale
            if !isStale{
                VStack {
                    Text("\(context.attributes.name) \(context.state.emoji)")
                        .activityBackgroundTint(Color.cyan)
                        .activitySystemActionForegroundColor(Color.black)
                }
            }  else {
            //If the message is stale, we request the user clicks the widget to open the app
                VStack {
                    Text("Something went wrong, please click to refresh")
                }
            }
            // ... Rest of the widget UI
        }
    }
}
```

***

## FAQ

### 높은 우선 순위 업데이트에 대한 예산은 얼마입니까?

Apple은 높은 우선 순위(`priority: 10`) 업데이트에 대한 고정된 제한을 제공하지 않지만 동적 시스템 수준 예산을 시행합니다. 짧은 기간 동안 너무 많은 높은 우선 순위 업데이트를 보내면 업데이트가 지연되거나 삭제되는 조절이 발생할 수 있습니다.

조절 위험을 줄이려면:

* 우선 순위 수준을 혼합하여 사용합니다: Apple은 균형을 위해 `priority: 5`(표준) 및 `priority: 10`(높음)을 모두 사용할 것을 권장합니다.
* `priority: 10`은 시간에 민감하거나 중요한 업데이트(예: 주문 상태 변경, 게임 점수)에만 예약합니다.

사용 사례에 빈번한 업데이트가 필요한 경우:

* 앱의 `Info.plist` 파일에 `NSSupportsLiveActivitiesFrequentUpdates` 키를 Boolean `YES`로 설정하여 추가합니다.
* 이 예산이 초과되면 iOS가 사용자에게 추가 업데이트를 허용하라는 메시지를 표시할 수 있습니다. 사용자가 동의하면 Apple이 자동으로 허용된 업데이트 제한을 확장하여 원활한 경험을 유지합니다.

자세한 내용은 [Apple의 개발자 문서](https://developer.apple.com/documentation/activitykit/starting-and-updating-live-activities-with-activitykit-push-notifications#Determine-the-update-frequency)를 참조하세요.

### 메인 앱에서 Live Activity 업데이트를 읽을 수 있습니까?

예. 디버깅 또는 UI 동기화를 위해 업데이트를 관찰할 수 있습니다:

```swift theme={null}
Task {
    for await content in activity.contentUpdates {
        print("LA activity id: \(activity.id), content update: \(content.state)")
    }
}
// Example output:
// LA activity id: EFE6D54D-F7E1-45EF-9514-4C2598F8DED2, content update: ContentState(message: "My message from payload")
```

수명 주기 변경 사항을 추적합니다:

```swift theme={null}
Task {
    for await state in activity.activityStateUpdates {
        print("LA state update: \(state)")
        //If you wanted to do something based on the state, you would use this:
      	if state != ActivityState.active {
            //Do something here
        }
    }
}

// Example output 1 - LA ended, but still visible
// LA state update: active

/* State can be equal to 4 possible values on the ActivityState enum
active, dismissed, ended, and stale
*/
```

### API가 구독자 제한을 초과했다는 오류 메시지와 함께 400을 반환했습니다. 어떻게 해야 합니까?

푸시 구독자 수가 플랜의 푸시 구독자보다 많은 경우 다음 플랜으로 계정을 업그레이드하거나 `support@onesignal.com`으로 문의하세요. 최신 플랜 세부 정보는 [여기를 참조하세요](https://onesignal.com/pricing).

### 푸시 및 Live Activities를 모두 보내는 것을 피하려면 어떻게 해야 합니까?

애플리케이션이 이미 일련의 푸시 알림을 보낼 수 있으며, 설계된 Live Activity가 이러한 푸시 알림의 필요성을 대체합니다. 예를 들어 푸시를 통해 점수 업데이트를 보내는 경우 Live Activity를 통해 이를 대체할 수 있습니다.

사용자가 너무 많은 메시지를 받지 않도록 하려면 사용자가 Live Activity를 선택할 때 데이터 태그를 추가하는 것이 좋습니다. 이 데이터 태그를 추가하면 동일하거나 유사한 콘텐츠를 포함할 수 있는 푸시 메시지에서 이 데이터 태그가 있는 사용자를 제외할 수 있습니다. [데이터 태그](./add-user-data-tags) 및 [세그먼트](./segmentation)에 대해 자세히 알아보세요.

***

## 문제 해결

### 수신자 없음

Live Activity를 시작하거나 업데이트하려고 할 때 사용자를 찾으려면 활동 유형, 위젯 및 cURL 요청이 모두 일치하는 값을 갖도록 해야 합니다.

1. 요청의 경로 매개변수를 확인하여 올바르게 형식화된 요청을 서버에 보내고 있는지 확인합니다. 앱 ID는 `OneSignal.Initialize` 메서드에 사용된 앱 ID와 일치해야 하며 활동 유형은 Live Activity 파일에서 정의한 유형과 일치해야 합니다.

2. [Push To Start API 요청](/reference/start-live-activity)의 본문에 다음 매개변수가 있어야 합니다:

* `event`: `"start"`
* `event_updates`: 활동 유형 아래의 구조체에서 정의하고 위젯에서 사용하는 동적 데이터입니다. 요청, 유형 및 위젯 간에 문자 대소문자 및 변수가 모두 일치하는지 확인합니다.
* `event_attributes`: 정적 데이터는 Event Updates와 동일한 논리를 따르며 사용 중인 모든 변수를 포함해야 하며 라이브 활동 및 요청의 모든 부분에서 일치해야 합니다
* `activity_id`: 위젯에 ID를 할당하며 사용자의 기기에서 실행된 후 활동을 업데이트하는 데 사용됩니다.
* `name`: Live Activity 이름.
* `contents`: 푸시 전송에 필요한 메시지 콘텐츠.
* `headings`: 푸시 전송에 필요한 메시지 제목.
* `included_segments`와 같은 타겟팅 매개변수. [사용 가능한 옵션](/reference/create-message).

### 활동이 전송되었지만 수신되지 않음

1. 요청이 올바르게 형식화되었는지 확인합니다. 위젯에서 사용되는 필드가 생략되면 활동이 예상대로 실행되거나 업데이트되지 않을 수 있습니다.

2. API 요청에서 설정하는 `priority` 수준을 결정합니다. 이를 `10`(최고 우선 순위)으로 설정하는 경우 `5`로 낮추고 다시 테스트하세요. Apple은 자체 내부 속도 제한에 따라 너무 자주 전송되는 요청을 조절합니다.

사용 사례가 더 자주 업데이트에 의존하는 경우 [Apple의 개발자 문서](https://developer.apple.com/documentation/activitykit/starting-and-updating-live-activities-with-activitykit-push-notifications#Determine-the-update-frequency)에 따라 Info.plist에 `NSSupportsLiveActivitiesFrequentUpdates` 키를 Boolean 유형으로 YES로 설정하여 추가합니다. Live Activity가 푸시 예산을 초과하면 사용자에게 대화 상자가 표시되며, Live Activity를 계속 허용하면 원활한 사용자 경험을 위해 예산이 자동으로 증가합니다.

<Info>
  도움이 필요하신가요?

  지원 팀과 채팅하거나 `support@onesignal.com`으로 이메일을 보내주세요.

  다음을 포함해 주세요:

  * 발생한 문제의 세부 정보 및 재현 단계(가능한 경우)
  * OneSignal 앱 ID
  * External ID 또는 Subscription ID(해당하는 경우)
  * OneSignal 대시보드에서 테스트한 메시지의 URL(해당하는 경우)
  * 관련 [로그 또는 오류 메시지](/docs/ko/capturing-a-debug-log)

  기꺼이 도와드리겠습니다!
</Info>

***
