๐ ๋ฐฐ๋ฌ ์ถ์ Live Activity
๋ง๋ค๊ธฐ
ActivityKit์ ํ์ฉํด ๋ฐฐ๋ฌ ํํฉ์ ์ ๊ธ ํ๋ฉด๊ณผ Dynamic Island์์ ์ค์๊ฐ์ผ๋ก ํ์ธํ ์ ์๋ Live Activity๋ฅผ ๊ตฌํํฉ๋๋ค.
๐ Live Activity๋?
Live Activity๋ ์งํ ์ค์ธ ์์ ์ ์ค์๊ฐ ์ํ๋ฅผ ์ ๊ธ ํ๋ฉด๊ณผ Dynamic Island์ ํ์ํฉ๋๋ค. ์์ ฏ๊ณผ ๋ฌ๋ฆฌ ์๊ฐ ์ ํ(์ต๋ 8์๊ฐ)์ด ์๊ณ , ์ค์๊ฐ ์ ๋ฐ์ดํธ๊ฐ ๊ฐ๋ฅํฉ๋๋ค.
โ
์ข์ ์ฌ๋ก
๋ฐฐ๋ฌ/ํ์ ์ถ์ , ์คํฌ์ธ ๊ฒฝ๊ธฐ, ํ์ด๋จธ, ์์
์ฌ์, ํญ๊ณตํธ ์ ๋ณด
โ ๋์ ์ฌ๋ก
๋ ์จ(โ Widget), ์บ๋ฆฐ๋ ์๋ฆผ(โ Notification), ์ฃผ๊ฐ(โ Widget)
๐๏ธ ActivityAttributes ์ ์
import ActivityKit struct DeliveryAttributes: ActivityAttributes { // Static: ๋ณ๊ฒฝ ๋ถ๊ฐ let orderNumber: String let storeName: String // ContentState: ์ค์๊ฐ ์ ๋ฐ์ดํธ struct ContentState: Codable, Hashable { let status: DeliveryStatus let estimatedArrival: Date let driverName: String? } } enum DeliveryStatus: String, Codable { case preparing // ์ค๋น ์ค case pickedUp // ํฝ์ ์๋ฃ case nearby // ๊ทผ์ฒ ๋์ฐฉ case delivered // ๋ฐฐ๋ฌ ์๋ฃ }
๐๏ธ Dynamic Island ๋ ์ด์์
Dynamic Island๋ 3๊ฐ์ง ์ํ๊ฐ ์์ต๋๋ค: Compact, Minimal, Expanded
struct DeliveryLiveActivity: Widget { var body: some WidgetConfiguration { ActivityConfiguration(for: DeliveryAttributes.self) { context in // ์ ๊ธ ํ๋ฉด LockScreenView(context: context) } dynamicIsland: { context in DynamicIsland { // Expanded DynamicIslandExpandedRegion(.leading) { Image(systemName: "person.circle") } DynamicIslandExpandedRegion(.trailing) { Image(systemName: context.state.status.symbol) } DynamicIslandExpandedRegion(.center) { Text(context.attributes.storeName) } DynamicIslandExpandedRegion(.bottom) { ProgressView(value: context.state.progress) } } compactLeading: { Image(systemName: "bicycle") } compactTrailing: { Text(context.state.estimatedArrival, style: .timer) } minimal: { Image(systemName: "bicycle") } } } }
๐ฑ LockScreen View ๊ตฌํ
struct LockScreenView: View { let context: ActivityViewContext<DeliveryAttributes> var body: some View { HStack(spacing: 12) { // ์ํ ์์ด์ฝ Image(systemName: context.state.status.symbol) .font(.title2) .foregroundStyle(.blue) VStack(alignment: .leading, spacing: 4) { Text(context.attributes.storeName) .font(.headline) Text(context.state.status.description) .font(.caption) .foregroundStyle(.secondary) // ๋์ฐฉ ์์ ์๊ฐ Text(context.state.estimatedArrival, style: .relative) .font(.caption2) .foregroundStyle(.blue) } Spacer() // ์งํ๋ฅ ProgressView(value: context.state.progress) .progressViewStyle(.circular) } .padding(16) } }
โก Activity ์๋ช ์ฃผ๊ธฐ
class DeliveryActivityManager { private var currentActivity: Activity<DeliveryAttributes>? // 1. Activity ์์ func startActivity(orderNumber: String, storeName: String) async throws { let attributes = DeliveryAttributes( orderNumber: orderNumber, storeName: storeName ) let initialState = DeliveryAttributes.ContentState( status: .preparing, estimatedArrival: Date().addingTimeInterval(1800), driverName: nil ) currentActivity = try Activity.request( attributes: attributes, content: ActivityContent(state: initialState, staleDate: nil), pushType: .token // Push Notification ์ง์ ) } // 2. ์ํ ์ ๋ฐ์ดํธ func updateStatus(_ status: DeliveryStatus, driverName: String? = nil) async { let newState = DeliveryAttributes.ContentState( status: status, estimatedArrival: Date().addingTimeInterval(600), driverName: driverName ) await currentActivity?.update( ActivityContent(state: newState, staleDate: nil) ) } // 3. Activity ์ข ๋ฃ func endActivity() async { let finalState = DeliveryAttributes.ContentState( status: .delivered, estimatedArrival: Date(), driverName: nil ) await currentActivity?.end( ActivityContent(state: finalState, staleDate: nil), dismissalPolicy: .after(Date().addingTimeInterval(3600)) // 1์๊ฐ ํ ์๋ ์ ๊ฑฐ ) } }
โ๏ธ Info.plist ์ค์
Live Activity๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด Info.plist์ ๊ถํ์ ์ถ๊ฐํด์ผ ํฉ๋๋ค:
<key>NSSupportsLiveActivities</key> <true/>
๐ Push Notification ์ฐ๋ (์ ํ)
์๋ฒ์์ ์๊ฒฉ์ผ๋ก ์ ๋ฐ์ดํธํ๋ ค๋ฉด Push Token์ ์ฌ์ฉํฉ๋๋ค:
// Push Token ๊ฐ์ ธ์ค๊ธฐ for await pushToken in Activity<DeliveryAttributes>.pushToStartTokenUpdates { // ์๋ฒ๋ก Push Token ์ ์ก await sendTokenToServer(pushToken) }
โ ๏ธ ์ฃผ์์ฌํญ
โฑ๏ธ ์๊ฐ ์ ํ
์ต๋ 8์๊ฐ ๋์๋ง ํ์๋ฉ๋๋ค. ๊ทธ ์ดํ์๋ ์๋์ผ๋ก ์ ๊ฑฐ๋ฉ๋๋ค.
๐ ๋ฐฐํฐ๋ฆฌ ์ํฅ
๋๋ฌด ์์ฃผ ์
๋ฐ์ดํธํ๋ฉด ๋ฐฐํฐ๋ฆฌ ์๋ชจ๊ฐ ํฝ๋๋ค. ์ต์ 5์ด ๊ฐ๊ฒฉ์ ๊ถ์ฅํฉ๋๋ค.
๐ฑ ๊ธฐ๊ธฐ ์ ํ
Dynamic Island๋ iPhone 14 Pro ์ด์์์๋ง ํ์๋ฉ๋๋ค. ๋ค๋ฅธ ๊ธฐ๊ธฐ์์๋ ์ ๊ธ ํ๋ฉด์๋ง ํ์๋ฉ๋๋ค.
๐จ UI ์ ์ฝ
ํญ ์ ์ค์ฒ, ์ ๋๋ฉ์ด์
, ๋น๋์ค๋ ์ง์๋์ง ์์ต๋๋ค. Button, Toggle, TextField๋ ์ฌ์ฉํ ์ ์์ต๋๋ค.
์ฑ๋ฆฐ์ง ์๋ฃ!
Dynamic Island์ ์ ๊ธ ํ๋ฉด์์ ๋ฐฐ๋ฌ ํํฉ์ ์ค์๊ฐ์ผ๋ก ํ์ธํ ์ ์๋ Live Activity๋ฅผ ์์ฑํ์ต๋๋ค!