Live Activity는 iPhone 잠금 화면과 Dynamic Island에 실시간 정보를 표시하는 다이나믹 위젯입니다: 배송 상태, 스포츠 점수, 타이머, 음악 재생 등. WidgetKit과 함께 ActivityKit(Swift, iOS 16.1+)을 통해 구현됩니다. Live Activity는 로컬 또는 푸시 알림을 통해 업데이트되며, 여러 상태를 지원하고 이벤트 종료 후 시스템에 의해 종료됩니다. 자세한 내용은 ActivityKit Documentation을 참조하세요.
주요 내용
Live Activity는 WidgetKit의 확장으로, iPhone 잠금 화면과 Dynamic Island에 동적 콘텐츠를 표시합니다. 시스템 타이머로 업데이트되는 정적 위젯과 달리, Live Activity는 앱에 의해 시작되며 제한된 시간 동안 유지됩니다 — 활성 Activity는 최대 8시간, 완료 후 최대 4시간입니다. 사용자는 기기를 잠금 해제하지 않고도 최신 데이터를 볼 수 있습니다: 배송 주문 상태, 택시의 목적지까지 거리, 경기 결과 또는 운동 진행 상황.
ActivityKit은 iOS 16.1에 도입된 Live Activities 작업을 위한 프레임워크입니다. Activity 요청, 업데이트 및 종료를 위한 API를 제공합니다. Activity는 콘텐츠와 상태를 가진 객체입니다. 콘텐츠(ActivityContent)는 정적 데이터를 정의하고, 상태(ActivityState)는 시간에 따라 변화하는 동적 데이터를 정의합니다. 시스템 자체가 새 상태를 렌더링할 시기를 결정합니다 — 이는 배터리를 절약하고 부드러운 애니메이션을 보장합니다.
각 Live Activity는 ActivityID로 고유하게 식별되며 하나의 프로세스(만든 앱)에 바인딩됩니다. 시스템은 메모리가 부족하거나 배터리 잔량이 적을 때 Activity를 종료할 수 있습니다. 개발자는 ActivityKit 델리게이트를 통해 강제 종료 알림을 받고 복원을 위해 마지막 상태를 저장할 수 있습니다.
Live Activity 아키텍처는 두 프레임워크로 구축됩니다: WidgetKit은 SwiftUI 뷰 렌더링을 담당하고, ActivityKit은 Activity 라이프사이클을 담당합니다. 개발자는 LiveActivityConfiguration을 지원하는 WidgetBundle을 생성합니다. 각 구성은 데이터 유형(Attributes 및 State)과 시스템이 잠금 화면 및 Dynamic Island에서 렌더링하는 SwiftUI 뷰를 정의합니다. 데이터는 ActivityAttributes를 통해 전달됩니다 — 콘텐츠용 let 필드와 상태용 var 필드가 있는 구조체입니다.
| 구성 요소 | 목적 | API |
|---|---|---|
| Attributes | 전체 Activity의 정적 데이터 | let name: String, let icon: String |
| ContentState | 업데이트 시 변경되는 동적 상태 | var progress: Double, var status: Status |
| Activity | Activity 요청 및 관리 객체 | Activity.request(attributes:content:) |
| PushToken | 서버 푸시 업데이트용 토큰 | activity.pushToken publisher |
| ActivityUI | 잠금 화면 및 Dynamic Island용 SwiftUI 뷰 | LockScreenView, ExpandedView, CompactView |
Activity 라이프사이클은 세 단계로 구성됩니다: 활성(시스템이 표시 및 업데이트), 최종(Activity 종료됨但 UI는 4시간 동안 표시됨), 제거됨(시스템이 UI 제거). 앱은 언제든지 Activity를 종료할 수 있습니다. 시스템도 Activity를 강제로 종료합니다 — 예를 들어, 기기 재시작 또는 8시간 제한 만료 시.
import ActivityKit
struct DeliveryAttributes: ActivityAttributes {
public struct ContentState: Codable & Hashable {
var status: DeliveryStatus
var estimatedMinutes: Int
}
var orderNumber: String
var restaurantName: String
}
WidgetBundle은 @main 매크로를 통해 Live Activity를 등록합니다: 위젯은 LiveActivityConfiguration을 포함한 구성 목록을 반환합니다. 이 구성을 기반으로 시스템은 예상 데이터 유형과 다양한 상태(Dynamic Island용 컴팩트, 최소, 확장)에서 UI를 렌더링하는 방법을 알게 됩니다.
Activity 만들기는 ActivityKit에 요청하는 것으로 시작됩니다. 앱이 초기 데이터와 함께 Activity.request(attributes:content:pushType:)를 호출합니다. 시스템은 기기에서 Dynamic Island를 사용할 수 있는지 확인하고 Activity 객체를 반환합니다. 활성 Activity 제한을 초과하면 요청이 실패할 수 있습니다 — 일반적으로 동시에 5개를 넘지 않습니다. 성공적인 요청 후, 시스템은 잠금 화면에 위젯을 표시하고 가능한 경우 Dynamic Island에도 표시합니다.
let attributes = DeliveryAttributes(
orderNumber: "A-1234",
restaurantName: "Pizza House"
)
let initialState = DeliveryAttributes.ContentState(
status: DeliveryStatus.preparing,
estimatedMinutes: 30
)
do {
let activity = try await Activity<DeliveryAttributes>.request(
attributes: attributes,
content: ActivityContent(state: initialState, staleDate: nil),
pushType: .token
)
print("Activity started: \(activity.id)")
} catch {
print("Failed: \(error)")
}
Live Activity용 SwiftUI 뷰는 WidgetKit의 LockScreenView, ExpandedView 및 CompactView 구조체를 사용합니다. 뷰는 현재 속성 및 상태와 함께 컨텍스트(ActivityViewContext)를 받습니다. 시스템에서 새 상태가 도착하면 뷰가 자동으로 업데이트됩니다. Live Activity는 제한된 SwiftUI 구성 요소만 지원합니다 — Text, Image, HStack, VStack 및 몇 가지 수정자.
각 Live Activity 뷰는 가볍고 빨라야 합니다 — 시스템은 무거운 뷰 렌더링을 거부합니다. 렌더링 시간 제한은 프레임당 30ms입니다. 애니메이션은 상태 간 시스템 전환으로 제한됩니다 — Live Activity에서는 사용자 정의 애니메이션을 사용할 수 없습니다. 렌더링은 잠금 화면 응답성을 우선시하는 WidgetKit 백그라운드 프로세스에서 수행됩니다.
로컬 업데이트 — 앱이 activity.update(using:)을 호출하여 Activity 상태를 업데이트합니다. 이 메서드는 새 ContentState와 업데이트 시 알림을 표시하기 위한 선택적 AlertConfiguration을 허용합니다. 로컬 업데이트는 빠릅니다 — 시스템이 다음 렌더링 주기에서 뷰를 다시 그리고, 일반적으로 1~2초 이내입니다. 빈번한 업데이트(타이머, 스톱워치)에는 로컬 모드를 사용하세요 — 푸시보다 안정적이고 빠릅니다.
let updatedState = DeliveryAttributes.ContentState(
status: DeliveryStatus.outForDelivery,
estimatedMinutes: 10
)
await activity.update(
ActivityContent<DeliveryAttributes.ContentState>(
state: updatedState,
staleDate: Date().addingTimeInterval(60)
),
alertConfiguration: AlertConfiguration(
title: "Order out for delivery",
body: "Arriving in 10 min"
)
)
푸시 업데이트 — 서버가 JSON 형식의 새 상태와 함께 ActivityKit 푸시 알림을 보냅니다. 이를 위해 앱은 activity.pushToken 게시자로부터 pushToken을 받아 서버에 전달합니다. 서버는 새 ContentState가 포함된 페이로드로 APNs에 POST 요청을 보냅니다. 시스템이 푸시를 수신하고, 상태를 디코딩하며, 앱의 참여 없이 Live Activity를 업데이트합니다 — 이를 통해 앱이 종료된 경우에도 Activity를 업데이트할 수 있습니다.
푸시 업데이트는 드문 업데이트(5~10분마다 배송 상태)에 더 효율적입니다 — 앱이 네트워크 연결을 유지할 필요가 없습니다. 1~2초마다 빈번한 업데이트에는 로컬 메서드를 사용하세요. 서버 페이로드에는 ContentState의 변경 가능한 필드만 포함됩니다 — 정적 Attributes 데이터는 Activity 생성 시에만 전송됩니다.
Dynamic Island는 iPhone 14 Pro, 15 Pro 이상의 하드웨어-소프트웨어 영역으로 Live Activity 콘텐츠에 맞게 조정됩니다. 시스템은 자동으로 Activity를 세 가지 모드로 표시합니다: 컴팩트(컷아웃 왼쪽의 아이콘 + 짧은 텍스트), 최소(아이콘만), 확장(정보가 있는 300pt 직사각형). 개발자는 이러한 모드를 직접 제어하지 않습니다 — 시스템이 우선순위와 사용 가능한 공간에 따라 모드를 선택합니다.
struct DeliveryLiveActivity: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: DeliveryAttributes.self) { context in
LockScreenView(context: context)
} dynamicIsland: { context in
DynamicIsland {
DynamicIslandExpandedContent {
ExpandedView(context: context)
}
} compactLeading: {
CompactLeadingView(context: context)
} compactTrailing: {
CompactTrailingView(context: context)
} minimal: {
MinimalView(context: context)
}
}
}
}
Dynamic Island는 대화형을 지원합니다 — 사용자가 영역을 탭하면 앱으로 들어가거나 확장 뷰가 열립니다. 컴팩트 모드를 탭하면 확장 모드로 전환되고, 왼쪽으로 스와이프하면 Activity가 최소 모드로 전환됩니다. 확장 모드의 버튼(일시 중지/취소)은 SwiftUI의 Link를 통해 처리됩니다 — 시스템이 딥 링크 URL로 앱을 실행하고 처리는 UIApplicationDelegate에서 수행됩니다.
Dynamic Island 제한 사항: 확장 모드의 너비는 최대 300pt, 컴팩트 텍스트는 최대 30자입니다. 색상과 글꼴은 시스템 테마와 일치합니다 — 사용자 정의는 제한됩니다. 모드 간 전환 애니메이션은 시스템 정의이며 사용자 정의할 수 없습니다. 여러 앱이 활성 Activity를 가지고 있는 경우, Dynamic Island는 시작 시간과 콘텐츠 유형에 따라 우선순위를 두고 표시합니다.
자주 묻는 질문
일반 WidgetKit 위젯은 정적 정보를 표시하고 최소 15~30분 간격으로 시스템 타이머로 업데이트됩니다. Live Activity는 잠금 화면과 Dynamic Island에서 실시간으로 동적 데이터를 표시합니다. Live Activity는 앱에 의해 시작되며 최대 8시간 동안 유지되고 이벤트 종료 후 시스템에 의해 종료됩니다. 영구적으로 존재하는 위젯과 다릅니다.
Live Activity는 두 가지 업데이트 모드를 지원합니다: 로컬 — 앱이 ActivityKit API를 통해 언제든지 상태를 업데이트, 그리고 푸시 업데이트 — 서버가 ActivityKit 푸시 알림을 보내면 시스템이 이를 위젯의 새 상태로 변환합니다. 배터리 잔량이 낮으면 시스템이 충전기가 연결될 때까지 Live Activity를 정지할 수 있습니다.
Live Activities는 iOS 16.1 이상의 iPhone에서 사용할 수 있습니다. iPhone 14 Pro 이상에서는 Live Activity가 Dynamic Island에도 표시됩니다. iPad에서는 Live Activity가 잠금 화면에서만 지원됩니다 — iPad에는 Dynamic Island가 없습니다. Apple Watch는 Live Activities를 직접 지원하지 않지만 완료 알림을 표시할 수 있습니다.
시스템은 활성 Live Activities 수를 제한합니다 — 일반적으로 모든 앱에서 동시에 5개를 넘지 않습니다. 제한을 초과하여 시작하려고 하면 ActivityKit이 오류를 반환합니다. 각 Live Activity는 여러 상태를 가질 수 있습니다 — 예: 대기 중, 이동 중, 음식 주문 배송 완료. 종료 후 Activity는 UI에 4시간 더 유지됩니다.
네, 앱은 알림 전송 권한을 요청해야 합니다 — Live Activity는 시스템 알림 채널을 사용합니다. 사용자는 설정에서 특정 앱의 Live Activity를 비활성화할 수 있습니다. 첫 실행 시 ActivityKit이 권한을 요청하는 대화상자를 표시합니다. 권한이 없으면 Activity 요청이 실패합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.