Skip to content

Commit

Permalink
Update 240728_Live_Activity,_App_Extension,_Tuist,_Widget.md
Browse files Browse the repository at this point in the history
  • Loading branch information
leeari95 committed Jul 28, 2024
1 parent 8e1c8bb commit f4c1689
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 3 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# TIL
> Today I Learned
์ด ์›นํŽ˜์ด์ง€๋Š” [Swift Docc](https://www.swift.org/documentation/docc)๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋งŒ๋“  ๋ฌธ์„œ ํŽ˜์ด์ง€์ž…๋‹ˆ๋‹ค.
์ด ์›นํŽ˜์ด์ง€๋Š” [Swift DocC](https://www.swift.org/documentation/docc)๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋งŒ๋“  ๋ฌธ์„œ ํŽ˜์ด์ง€์ž…๋‹ˆ๋‹ค.

* https://leeari95.github.io/TIL/documentation/arinote/

Expand All @@ -11,6 +11,6 @@

* `Swift`: ์Šคํฌ๋ฆฝํŠธ ์ž‘์„ฑ
* `Swift Package Manager`: ์˜์กด์„ฑ ๊ด€๋ฆฌ ๋ฐ ๋นŒ๋“œ ์‹œ์Šคํ…œ
* `Swift Docc`: ๋ฌธ์„œ ์ƒ์„ฑ ๋ฐ ๊ด€๋ฆฌ
* `Swift DocC`: ๋ฌธ์„œ ์ƒ์„ฑ ๋ฐ ๊ด€๋ฆฌ
* `Markdown`: ๋ฌธ์„œ ๋‚ด์šฉ ์ž‘์„ฑ
* `GitHub Actions`: ์›นํŽ˜์ด์ง€ ๋ฐฐํฌ ์ž๋™ํ™”
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ $ swift package --allow-writing-to-directory {์ €์žฅ์œ„์น˜} \
3. docc-plugin์œผ๋กœ Documentation์„ ์›นํŽ˜์ด์ง€๋กœ ๋ณ€ํ™˜
4. Github Pages ๋ฐฐํฌ

> workflow: https://github.com/leeari95/TIL/blob/main/.github/workflows/update-markdown-files.yml
> workflow: [https://github.com/leeari95/TIL/blob/main/.github/workflows/update-markdown-files.yml](https://github.com/leeari95/TIL/blob/main/.github/workflows/update-markdown-files.yml)
---

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
# 240726 Live Activity, App Extension, Tuist, Widget


๊ธฐ์กด UIKit ํ”„๋กœ์ ํŠธ์— Live Activity๋ฅผ ์ถ”๊ฐ€ํ•ด๋ณด์ž!

7์›” 26์ผ (๊ธˆ)


# ํ•™์Šต๋‚ด์šฉ


- Tuist์— App Extension ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ๋ฒ•
- Live Activity ์ดˆ๊ธฐ ๊ตฌํ˜„


# ๊ณ ๋ฏผํ•œ ์  / ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

## Tuist๋กœ ๊ด€๋ฆฌํ•˜๋Š” ํ”„๋กœ์ ํŠธ์— ์œ„์ ฏ์„ ์ถ”๊ฐ€ํ•˜๊ธฐ ์œ„ํ•œ Extension ์„ค์ •

1. ๊ธฐ์กด ํ”„๋กœ์ ํŠธ ํƒ€๊ฒŸ์— ์•„๋ž˜์™€ ๊ฐ™์ด ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.

```swift
let targets: [Target] = [
// ๊ธฐ์กด ํ”„๋กœ์ ํŠธ ํƒ€๊ฒŸ...
.target(
name: "LiveActivity",
destinations: [.iPhone, .iPad],
product: .appExtension,
bundleId: "$(PRODUCT_BUNDLE_IDENTIFIER).WidgetExtension",
deploymentTargets: .iOS("15.0"),
infoPlist: .file(path: "LiveActivity/Info.plist"),
sources: [
"LiveActivity/Sources/**"
],
resources: .resources(["LiveActivity/Resources/**"],
dependencies: [],
settings: .settings(
configurations: [
.debug(name: .debug, xcconfig: .relativeToRoot("Project/Configurations/LiveActivity.xcconfig")),
.release(name: .release, xcconfig: .relativeToRoot("Project/Configurations/LiveActivity.xcconfig"))
],
defaultSettings: .none
)
)
]
```

2. ํ”„๋กœ์ ํŠธ Dependencies์— ์ƒˆ๋กœ ๋งŒ๋“  App Extension ํƒ€๊ฒŸ์„ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.

```swift
let dependencies: [TargetDependency] = [
// ....
.target(name: "LiveActivity")
]
```


## Live Activity ์ดˆ๊ธฐ ์„ค์ • ๊ตฌํ˜„ํ•ด๋ณด๊ธฐ

Tuist์—์„œ๋Š” ํƒ€๊ฒŸ์„ ์ถ”๊ฐ€ํ•œ๋‹ค๊ณ  ํ•ด์„œ ์ดˆ๊ธฐ .swift ํŒŒ์ผ์„ ์ƒ์„ฑํ•ด์ฃผ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, Xcode์—์„œ ์ง์ ‘ ํƒ€๊ฒŸ์„ ์ถ”๊ฐ€ํ•ด์„œ ํ•ด๋‹น ํŒŒ์ผ์„ ํ™œ์šฉํ•˜์˜€๋‹ค.

![](https://github.com/user-attachments/assets/74a8ccdd-7c4c-491f-8ccd-4936f870668f)

![](https://github.com/user-attachments/assets/c8b95428-e917-42cd-8ad4-478ee06dff83)
![](https://github.com/user-attachments/assets/d3bb67af-39d5-4aca-a581-e71fbc8bb67c)

> Include Configuration App Intent: App Intent ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ์•ฑ์˜ ๋™์ž‘๊ณผ ์ฝ˜ํ…์ธ ๋ฅผ Siri, Spotlight, Widget, Control ๋“ฑ์„ ํฌํ•จํ•œ ์—ฌ๋Ÿฌ ํ”Œ๋žซํผ์˜ ์‹œ์Šคํ…œ ํ™˜๊ฒฝ๊ณผ ๊ธด๋ฐ€ํ•˜๊ฒŒ ํ†ตํ•ฉํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. Apple Intelligence์™€ ํ–ฅ์ƒ๋œ App Intent๋ฅผ ํ†ตํ•ด Siri๋Š” ์‚ฌ๋žŒ๋“ค์ด ์•ฑ์˜ ๊ธฐ๋Šฅ์„ ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์•ฑ์˜ ๋™์ž‘์„ ์ œ์•ˆํ•˜๊ณ  ์•ฑ ๋‚ด์—์„œ ๊ทธ๋ฆฌ๊ณ  ์•ฑ ์ „๋ฐ˜์—์„œ ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.


๊ธฐ๋ณธ ๊ตฌํ˜„์€ ์œ„์ ฏ๊นŒ์ง€ ๊ตฌํ˜„๋˜์–ด์žˆ์ง€๋งŒ, ๋‚˜๋Š” Live Activity๋งŒ์„ ๊ตฌํ˜„ํ• ๊ฑฐ๋ผ ๊ตฌํ˜„๋œ ์œ„์ ฏ ์ฝ”๋“œ๋Š” ๋ชจ๋‘ ์ œ๊ฑฐํ•ด์ฃผ๊ณ  Live Activity๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ์•„๋ž˜์™€ ๊ฐ™์ด ํ•„์š”ํ•œ ์ฝ”๋“œ๋งŒ ๋‚จ๊ฒจ๋‘์—ˆ๋‹ค.

```swift
import ActivityKit
import SwiftUI
import WidgetKit

// Target Membership for this file: UIKit App + Widget Exension
@available(iOS 16.2, *)
struct Attributes: ActivityAttributes {
public struct ContentState: Codable, Hashable {
// Dynamic stateful properties about your activity go here!
var emoji: String
}

// Fixed non-changing properties about your activity go here!
var name: String
}
```

์—ฌ๊ธฐ์„œ Attributes๋Š” UIKit์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ์•ฑ ํƒ€๊ฒŸ์—์„œ ์‚ฌ์šฉํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— Target membership์„ ๋‘˜๋‹ค ์ถ”๊ฐ€ํ•ด์ฃผ์—ˆ๋‹ค.
์ฃผ๋กœ Live Activity๋ฅผ ํ™œ์„ฑํ™”/๋น„ํ™œ์„ฑํ™”, ๊ทธ๋ฆฌ๊ณ  ์—…๋ฐ์ดํŠธ ํ•ด์ค„ ๋•Œ ์‚ฌ์šฉ๋˜๋Š” ํƒ€์ž…์ด๋‹ค.

```swift
import SwiftUI
import WidgetKit

// Target Membership for this file: Widget Exension
@available(iOS 16.2, *)
@main
struct Bundle: WidgetBundle {
var body: some Widget {
LiveActivity()
}
}

@available(iOS 16.2, *)
struct LiveActivity: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: LiveStreamNotificationAttributes.self) { context in
// Lock screen/banner UI goes here
VStack {
Text("Hello \(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)
}
}
}
```

์œ„ ์ฝ”๋“œ๋Š” ์œ„์ ฏ์˜ UI๋ฅผ ๊ทธ๋ ค์ฃผ๋Š” ์ฝ”๋“œ์ด๋‹ค. Xcode์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•ด์ฃผ๋Š” ์ƒ˜ํ”Œ ์ฝ”๋“œ๋‹ค.

๊ทธ๋ฆฌ๊ณ  UIKit ์•ฑ ๋‚ด์—์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ฃผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

```swift
// ๋ผ์ด๋ธŒ ์•กํ‹ฐ๋น„ํ‹ฐ ํ™œ์„ฑํ™”ํ•˜๊ธฐ
let attributes = Attributes(name: "test")
let contentState = Attributes.ContentState(emoji: "๐Ÿท")

do {
let activity = try ActivityKit.Activity<Attributes>.request(
attributes: attributes,
content: .init(state: contentState, staleDate: nil),
pushType: nil
)
print(activity)
} catch {
print("start Activity From App: \(error)")
}
```

```swift
// ๋ผ์ด๋ธŒ ์•กํ‹ฐ๋น„ํ‹ฐ ์—…๋ฐ์ดํŠธ
Task {
let updateContentState = Attributes.ContentState(emoji: state.emoji)
for activity in ActivityKit.Activity<Attributes>.activities {
await activity.update(.init(state: updateContentState, staleDate: nil))
}
}
```

```swift
// ๋ผ์ด๋ธŒ ์•กํ‹ฐ๋น„ํ‹ฐ ๋น„ํ™œ์„ฑํ™”
Task {
for activity in ActivityKit.Activity<Attributes>.activities {
await activity.end(nil, dismissalPolicy: .immediate)
}
}
```

# Trouble shooting


## Live Acticity UI๊ฐ€ ๊ทธ๋ ค์ง€์ง€ ์•Š๋Š” ๋ฌธ์ œ

### ๋ฌธ์ œ

๊ธฐ์กด UIKit ์•ฑ์ด ์„ค์น˜๋˜์–ด์žˆ๋Š” ์ƒํ™ฉ์—์„œ Live Activity ์ดˆ๊ธฐ ์„ค์ •์„ ๊ตฌํ˜„ํ•˜๊ณ , ๋นŒ๋“œ๋ฅผ ๋Œ๋ ธ๋Š”๋ฐ, ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ์™€ ๋””๋ฐ”์ด์Šค ๋ชจ๋‘ Live Activity๋ฅผ ํ™œ์„ฑํ™”ํ•˜๊ณ , ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๋‹ค์ด๋‚˜๋ฏน ์•„์ผ๋žœ๋“œ๋ฅผ ํด๋ฆญํ•˜๋ฉด ์•ฑ์œผ๋กœ ์ง„์ž…์€ ๋˜์„œ ํ™œ์„ฑํ™”๋Š” ๋œ ๋“ฏ ํ•˜๋‚˜, UI๊ฐ€ ์ „ํ˜€ ๋ณด์—ฌ์ง€์ง€ ์•Š๋Š” ๋ฌธ์ œ์˜€๋‹ค.

### ํ•ด๊ฒฐ

๋ช‡์‹œ๊ฐ„ ์‚ฝ์งˆํ–ˆ๋Š”๋ฐ ๋„์ €ํžˆ ์ด์œ ๋ฅผ ์•Œ ์ˆ˜๊ฐ€ ์—†์—ˆ๋‹ค.
ํŒ€์›๋ถ„๋“ค์—๊ฒŒ ๊ณต์œ ๋“œ๋ ธ๋Š”๋ฐ, ๊ฐ™์€ ์‚ฝ์งˆ์„ ํ–ˆ๋˜ ๋‚ด์šฉ์„ ์•Œ๊ฒŒ๋˜์—ˆ๋‹ค.
ํ•ด๊ฒฐ๋ฐฉ๋ฒ•์€ ๋„ˆ๋ฌด ๊ฐ„๋‹จํ–ˆ๋‹ค.

1. ๊ธฐ์กด ์•ฑ์„ ์ œ๊ฑฐํ•œ๋‹ค.
2. ๋นŒ๋“œ๋ฅผ ๋‹ค์‹œ ๋Œ๋ ค์„œ ์•ฑ์„ ์žฌ์„ค์น˜ํ•œ๋‹ค.

์•ฑ ์ œ๊ฑฐํ•ด๋„ ๋‚˜ํƒ€๋‚˜์ง€ ์•Š์œผ๋ฉด ์•ฑ ์ œ๊ฑฐํ›„ ๋””๋ฐ”์ด์Šค๋ฅผ ์žฌ์‹œ์ž‘ํ•˜์—ฌ ์•ฑ์„ ๋‹ค์‹œ ์„ค์น˜ํ•˜๋ฉด ๋œ๋‹ค๊ณ  ํ•œ๋‹ค.

์œ„ ๋ฐฉ๋ฒ•์œผ๋กœ ๋„ˆ๋ฌด๋‚˜๋„ ์†์‰ฝ๊ฒŒ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค... Xcode ๋‚ด์—์„œ ๋ญ”๊ฐ€ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•œ ๋ฌธ์ œ์ธ๊ฑธ๊นŒ...?


---


# ์ฐธ๊ณ  ๋งํฌ

- [https://developer.apple.com/documentation/widgetkit/making-a-configurable-widget](https://developer.apple.com/documentation/widgetkit/making-a-configurable-widget)
- [https://github.com/tuist/tuist/blob/main/fixtures/ios_app_with_extensions/Project.swift](https://github.com/tuist/tuist/blob/main/fixtures/ios_app_with_extensions/Project.swift)
- [https://developer.apple.com/documentation/AppIntents](https://developer.apple.com/documentation/AppIntents)
- [https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities](https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities)
- [https://developer.apple.com/documentation/activitykit/starting-and-updating-live-activities-with-activitykit-push-notifications](https://developer.apple.com/documentation/activitykit/starting-and-updating-live-activities-with-activitykit-push-notifications)

0 comments on commit f4c1689

Please sign in to comment.