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

요구 사항


설정

다음 단계에서는 Live Activities를 빠르게 설정하는 방법을 안내합니다. 자세한 내용 및 디자인 사용자 지정에 대해서는 Apple의 Live Activities 개발자 문서를 참조하세요.

1. Widget Extension 추가

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

Xcode에서 앱에 새 Widget Extension 타겟을 추가합니다.

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

Live Activity를 위한 Widget Extension 옵션.

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

Live Activity를 위한 Widget Extension 옵션.

2. Info.plist 업데이트

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

Info에 Supports Live Activities 키를 추가하고 값을 Boolean YES로 설정합니다

프로그래밍 방식으로 설정하는 경우 다음과 같아야 합니다:
info.plist
<key>NSSupportsLiveActivities</key>
<true/>
Live Activities를 업데이트할 때 Apple이 업데이트의 긴급성을 결정하는 데 사용하는 “priority”를 설정할 수 있는 옵션이 있습니다. Apple에는 높은 우선 순위 플래그를 너무 자주 사용하는 요청을 조절하는 내부 임계값이 있습니다.Live Activities 사용 사례가 더 자주 높은 우선 순위 업데이트에 의존하는 경우 Apple의 개발자 문서에 따라 Info.plist에 NSSupportsLiveActivitiesFrequentUpdates 키를 Boolean 유형으로 YES로 설정하여 추가할 수 있습니다. Live Activity가 푸시 예산을 초과하면 사용자에게 대화 상자가 표시되며, Live Activity를 계속 허용하면 원활한 사용자 경험을 위해 예산이 자동으로 증가합니다.

3. SDK 추가

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

Widget Extension 타겟에 OneSignalFramework를 추가합니다

4. 위젯 속성 및 UI 정의

your-nameLiveActivity.swift 파일(예: OneSignalWidgetLiveActivity.swift)을 열어 구조체의 속성을 정의하고 위젯 UI를 변경합니다.
  • your-nameAttributes는 Live Activity의 정적 콘텐츠를 설명합니다.
  • ContentState는 Live Activity의 동적 콘텐츠를 설명합니다.
예시를 따르는 경우 아래 코드를 OneSignalWidgetLiveActivity.swift 파일에 복사하여 붙여넣습니다.
your-nameLiveActivity.swift
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. 메인 타겟 멤버십 허용

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

메인 타겟 멤버십을 허용합니다

6. AppDelegate에 설정 메서드 추가

OneSignal SDK 초기화 후 AppDelegate에서 OneSignal.LiveActivities.setup을 호출합니다. OneSignalWidgetAttributes를 Live Activity 속성 구조체의 이름으로 바꿉니다.
AppDelegate
// 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 비동기 시퀀스를 사용하여 업데이트를 관리하고 보고합니다.
앱에서 다음 시퀀스 중 하나를 직접 소비하는 경우 OneSignal Live Activity 동작을 방해할 수 있습니다:
  • activityStateUpdates
  • pushTokenUpdates
  • pushToStartTokenUpdates
  • activityUpdates

Live Activity 시작

기기에서 Live Activity를 시작하는 두 가지 옵션이 있습니다:
  • Push-to-start
  • Trigger-in-app
Push To Start API 요청을 보냅니다. 모든 이름과 ID가 위젯 구성과 정확히 일치하는지 확인합니다(매개변수는 대소문자를 구분함). 누락되거나 잘못 추가된 것이 있으면 위젯을 실행하려고 할 때 문제가 발생할 수 있습니다.위의 예시에서 작동하는 요청 예시는 다음과 같습니다.바꾸기:
  • YOUR_APP_ID를 OneSignal 앱 ID로 바꿉니다.
  • YOUR_APP_API_KEY를 OneSignal API 키로 바꿉니다.
  • OneSignalWidgetAttributes를 Widget Attributes 구조체의 이름으로 바꿉니다.
    curl
    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가 표시됩니다.

잠금 화면의 Live Activity

push-to-start로 Live Activity를 성공적으로 시작했습니다!사용자는 계속 업데이트를 받으려면 “허용”을 선택해야 합니다.

Live Activity 업데이트

Live Activity 업데이트 API를 사용하여 활성 위젯을 업데이트합니다. 활동을 시작할 때 사용한 activity_id와 일치시킵니다. 이 예시 요청은 정의한 push-to-start라는 activity_id가 있으므로 push-to-start 위젯을 업데이트합니다. click-to-start 위젯을 업데이트하려면 요청 경로를 push-to-start 대신 click-to-start를 사용하도록 업데이트합니다.
curl
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"
}'

Live Activity가 업데이트되었습니다

Live Activity를 성공적으로 업데이트했습니다!Live Activity 업데이트에 대한 자세한 내용은 Live Activity 업데이트 API를 확인하세요.

Live Activity 종료

동일한 Live Activity 업데이트 API를 사용하여 "event": "end"를 설정하여 Live Activity를 종료할 수 있습니다.
curl
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() 메서드를 사용합니다.
  • 사용자가 Live Activity를 수동으로 스와이프하여 제거합니다.
  • 사용자가 iOS 설정에서 Live Activities에 대한 권한을 취소합니다.

Live Activity가 종료되었습니다

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

모범 사례 및 권장 사항

디자인 고려 사항

  • Apple의 Live Activities 휴먼 인터페이스 가이드라인을 따르세요.
    • 중요한 정보를 우선순위로 지정하여 빠르게 이해하기 쉽게 만듭니다.
    • Dynamic Island에 주의를 끄는 요소를 앱에 추가하지 마세요.
    • 여백을 사용하고 요소 간 간격을 유지합니다.
    • 배경에 굵은 색상을 사용합니다. 라이트 및 다크 모드 모두에 대해 디자인합니다.

기능

대체 메시지 설정

  • 사용자가 라이브 활동을 시작한 후 업데이트를 받을 수 없는 특정 경우, 앱을 열면 새로고침되어 계속 진행되어야 합니다
    • 이를 고려하기 위해 사용자에게 첫 번째 업데이트를 보낸 것을 알고 있는 시점 이후 미래의 날짜와 시간으로 stale date를 설정하고, 업데이트를 받지 못한 사용자에게는 대체 메시지가 표시됩니다.
    • 위젯 UI에서 이 “stale” 상태를 수신 대기하여 대체 메시지를 표시할 수 있습니다:
    swift
      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
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")
수명 주기 변경 사항을 추적합니다:
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으로 문의하세요. 최신 플랜 세부 정보는 여기를 참조하세요.

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

애플리케이션이 이미 일련의 푸시 알림을 보낼 수 있으며, 설계된 Live Activity가 이러한 푸시 알림의 필요성을 대체합니다. 예를 들어 푸시를 통해 점수 업데이트를 보내는 경우 Live Activity를 통해 이를 대체할 수 있습니다. 사용자가 너무 많은 메시지를 받지 않도록 하려면 사용자가 Live Activity를 선택할 때 데이터 태그를 추가하는 것이 좋습니다. 이 데이터 태그를 추가하면 동일하거나 유사한 콘텐츠를 포함할 수 있는 푸시 메시지에서 이 데이터 태그가 있는 사용자를 제외할 수 있습니다. 데이터 태그세그먼트에 대해 자세히 알아보세요.

문제 해결

수신자 없음

Live Activity를 시작하거나 업데이트하려고 할 때 사용자를 찾으려면 활동 유형, 위젯 및 cURL 요청이 모두 일치하는 값을 갖도록 해야 합니다.
  1. 요청의 경로 매개변수를 확인하여 올바르게 형식화된 요청을 서버에 보내고 있는지 확인합니다. 앱 ID는 OneSignal.Initialize 메서드에 사용된 앱 ID와 일치해야 하며 활동 유형은 Live Activity 파일에서 정의한 유형과 일치해야 합니다.
  2. Push To Start API 요청의 본문에 다음 매개변수가 있어야 합니다:
  • event: "start"
  • event_updates: 활동 유형 아래의 구조체에서 정의하고 위젯에서 사용하는 동적 데이터입니다. 요청, 유형 및 위젯 간에 문자 대소문자 및 변수가 모두 일치하는지 확인합니다.
  • event_attributes: 정적 데이터는 Event Updates와 동일한 논리를 따르며 사용 중인 모든 변수를 포함해야 하며 라이브 활동 및 요청의 모든 부분에서 일치해야 합니다
  • activity_id: 위젯에 ID를 할당하며 사용자의 기기에서 실행된 후 활동을 업데이트하는 데 사용됩니다.
  • name: Live Activity 이름.
  • contents: 푸시 전송에 필요한 메시지 콘텐츠.
  • headings: 푸시 전송에 필요한 메시지 제목.
  • included_segments와 같은 타겟팅 매개변수. 사용 가능한 옵션.

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

  1. 요청이 올바르게 형식화되었는지 확인합니다. 위젯에서 사용되는 필드가 생략되면 활동이 예상대로 실행되거나 업데이트되지 않을 수 있습니다.
  2. API 요청에서 설정하는 priority 수준을 결정합니다. 이를 10(최고 우선 순위)으로 설정하는 경우 5로 낮추고 다시 테스트하세요. Apple은 자체 내부 속도 제한에 따라 너무 자주 전송되는 요청을 조절합니다.
사용 사례가 더 자주 업데이트에 의존하는 경우 Apple의 개발자 문서에 따라 Info.plist에 NSSupportsLiveActivitiesFrequentUpdates 키를 Boolean 유형으로 YES로 설정하여 추가합니다. Live Activity가 푸시 예산을 초과하면 사용자에게 대화 상자가 표시되며, Live Activity를 계속 허용하면 원활한 사용자 경험을 위해 예산이 자동으로 증가합니다.
도움이 필요하신가요?지원 팀과 채팅하거나 support@onesignal.com으로 이메일을 보내주세요.다음을 포함해 주세요:
  • 발생한 문제의 세부 정보 및 재현 단계(가능한 경우)
  • OneSignal 앱 ID
  • External ID 또는 Subscription ID(해당하는 경우)
  • OneSignal 대시보드에서 테스트한 메시지의 URL(해당하는 경우)
  • 관련 로그 또는 오류 메시지
기꺼이 도와드리겠습니다!