- Published on
π± iOS - WidgetKit
- Authors
- Name
- μ΄μ°½μ€
WidgetKit
WWDCμ iOS ννΈμμ κ½€ ν¬κ² μκ°λ WidgetKitμ΄ μμμ£ ..
Widgetμ΄ μ΄μ μ λλ©μ΄μ μ κ°κ² λμμ΅λλ€.
λ€λ₯Έ νλ«νΌμΌλ‘μ νμ₯μ λ€μ΄κ΅¬μ.
κ·Έλ°λ° μ κ·Έ μ μ λ΄μ©λ μμ§ λͺ»λ΄€κ±°λ μ..
κ·Έλμ μ§κΈ μμν©λλ€.
WidgetKit!
νμ§λ§ μμνμλ§μ ν° μ₯λ²½μ λ§λ¬μ΅λλ€.
Widgetμ μ€μ§ SwiftUI λ‘λ§ λ§λ€ μ μμ΅λλ€β¦
κ·Έλλ μμ νλ©΄μ΄λκΉβ¦ ν λ² ν΄λ΄μΌκ² μ΄μ..
How WidgetKit Works
μ νμ μμ ―μ λν΄μ κ΅μ₯ν μ νμ μΈ μμλΌλ μκ°μ κ°μ§κ³ μλ κ² κ°μ΅λλ€.
μ¬μ©μλ ν νλ©΄μ ν루μ 90ν μ λ λ°©λ¬Ένμ§λ§, κ·Έ μκ°μ κ΅μ₯ν μ λ€λ κ²μ κ°μ‘°νκ³ μλ€μ.
κ·Έλμ Widgetμλ λ‘λ© μΈλμΌμ΄ν°κ° λκ³ μλ μν©μ΄ μμ΄μΌ νλ€ κ³ ν©λλ€.
WidgetKitμ λ°±κ·ΈλΌμ΄λ μ΅μ€ν μ μ λλ€.
μκ°μ λ°λΌ νλ©΄λ€μ ν¨ν€μ§νμ¬ νμΌλ‘ μ λ¬ν΄μ€λ€κ³ νλλ°μ.. μ΄λ κ² ν¨μΌλ‘μ¨ μ±μ μ€ννκ³ , λ°μ΄ν°λ₯Ό λΆλ¬μ€κ³ , νλ©΄μ λμ°λ μΌλ ¨μ κ³Όμ λ€μ μ€ν΅ν©λλ€.
μ λ§ μμ ―μΌλ‘ νμλ νλ©΄μ λν μ΄λ‘ κ°μ Έμ€κΈ°λ§ νλ κ²μ΄μ£ ..
κ·Έλμ μμ ―μ κ°λ°νλλ° μμ΄ ν΅μ¬μ μΈ ν€μλλ βνμλΌμΈβ μ λλ€.
μμ ―μ νμλΌμΈμ λ°λΌ λμν©λλ€.
μμ ―μ νμλΌμΈμ 미리 μ€μΌμ€λ§λ λλ‘ μ λ°μ΄νΈ λ μλ μκ³ , μ±μμ μμ²νμ¬ μ λ°μ΄νΈ λ μλ μμ΅λλ€.
μ¬κΈ° λ¬λ ₯ μ±μ μμ ―μ΄ μλλ°μ, μ΄ κ²½μ°μλ λ¬λ ₯μ λ±λ‘λ μΌμ λ€μ λ°λΌ μ€μΌμ€λ§μ ν μ μμ΅λλ€.
κ° μΌμ μ΄ λ€κ°μ€κ±°λ λλ μμ μ μμ ―μ μ λ°μ΄νΈν μ μλ κ²μ΄μ£ .
κ·Έλ°λ° μ¬μ©μκ° μ±μ μ΄κ³ , μΌμ μ μμ νλ€λ©΄μ?
μ΄λ΄ λλ μ±μμ μ§μ μμ²νμ¬ μμ ―μ μ λ°μ΄νΈ μν¬ μ μλ κ²μ΄μ£ . (μ ννλ νμλΌμΈμ μ λ°μ΄νΈνλ κ²μ λλ€.)
Widget Definition
μ€μ λ‘ μμ ―μ λ§λ€κΈ° μ μ, μμ ―μ μ μν΄μΌν©λλ€.
μμ ―μ λ€ κ°μ§λ‘ μ μν μ μμ΅λλ€: kind , configuration , supportedFamilies , placeholder λ‘μ!
Kind
μ°μ kind μ λλ€.
μμ ―μ νλμ μ’ λ₯λ§ μμ§ μμ΅λλ€.
κ°μ μ¬μ΄μ¦λΌλ μ¬λ¬κ°μ μ 보λ₯Ό μμ½νμ¬ λ³΄μ¬μ£Όκ±°λ, νλμ μ 보λ₯Ό μ‘°κΈ λ μμΈνκ² λ³΄μ¬μ€ μλ μμ£ .
Configuration
μ΄λ κ² λΆλ₯λ κ° μ’ λ₯(kind)μ μμ ―λ€μ μ΄λ€ Configuration μ μ§μνλ μ§μ λν μ 보λ λ΄κ³ μμ΅λλ€.
StaticConfiguration κ³Ό IntentConfiguration , μ΄λ κ² λ κ°μ§ Configurationμ΄ μλλ°μ, μμ£Ό κ°λ¨ν κ°λ μ λλ€!
StaticConfiguration μ μ¬μ€ Configurationμ΄ μλ κ²λλ€.
νλμ Configurationλ§μ μ¬μ©νκΈ° μν΄ μ΄λ κ² μ΄λ¦μ΄ λΆμ κ² κ°μμ.
λ³λμ μ€μ μ΄ νμ μλ, μ€μ§ νλμ κ³ μ λ μ’ λ₯μ λ°μ΄ν°λ§μ 보μ¬μ€ λ μ¬μ©λ©λλ€.
λ°λλ‘ IntentConfiguration μ μ€μ μ ν΅ν΄ μμ ―μ΄ λ³΄μ¬μ£Όλ νλ©΄μ΄ λ¬λΌμ§ λ μ¬μ©λ©λλ€.
μμλ‘λ βμ¬λ¬ μ’ λ₯μ 리μ€νΈ μ€μμ μ΄λ€ μ’ λ₯λ₯Ό μμ ―μΌλ‘ 보μ¬μ€ μ§ μ νμ΄ νμν λβ κ° μκ² μ£ ?
Supported Families
SupportedFamilies λ μ§μνλ μμ ―μ ν¬κΈ° μ λλ€.
κΈ°λ³ΈμΌλ‘λ μΈ μ’ λ₯μ ν¬κΈ°λ₯Ό λͺ¨λ μ§μνλ κ²μΌλ‘ λμ΄μμ§λ§, κ°λ°μκ° μ νν μ μμ΅λλ€.
Placeholder
Placeholder λ κ°λ¨ν©λλ€.
μ΄λ€ λ°μ΄ν°λ λ€μ΄κ°μ§ μμ, μμ ―μ΄ μ΄λ€ ννμΈμ§λ§ νννλ λΉμ΄μλ Viewλ₯Ό μ 곡ν΄μ£Όλ©΄ λ©λλ€.
StatelessUI
μμ ―μ λ€μμ λ€ κ°μ§ μμΉμ μ§μΌμΌν©λλ€.
μ¬μ€ μ νμ΄ μ νν΄λκ³ μκΈ° λλ¬Έμ μ΄κΈΈ μλ μμ§λ§μ?
- μμ ―μ μμ λ³λμ μ±μ΄ μλλ€. μ±μ μ§μ μν€κΈ° μν λ¨νΈμ μΈ μ 보λ₯Ό μ 곡ν λΏμ΄λ€.
- μμ ―μ μ€ν¬λ‘€μ μ§μνμ§ μλλ€.
- μμ ―μλ μμ§μ΄λ μ¬μ§μ΄λ λΉλμ€κ° λ€μ΄κ° μ μλ€.
- μμ ―μλ μ€μ§ ν μ μ€μ²λ§μ΄ μ ν¨νλ€.
λ€ κ·Όλ° WWDC2023μμ μ μ νλ€ μ€ "μμ§μ΄λ μ¬μ§μ΄λ λΉλμ€κ° λ€μ΄κ° μ μλ€"λ μ΄λμ λ νμ©μ΄ λμ΅λλ€.
UIμ μ λλ©μ΄μ μ μΆκ°ν μ μκ² μ λ°μ΄νΈ λμκ±°λ μ
μμ ―μ μ€μ§ μ±μ νΉμ μ§μ μ λλ¬νκΈ° μν μμ»·μ μ 곡ν λΏμ΄κ³ , μ΄κ²μ URL API λ₯Ό νμ©ν΄ ꡬνλλ€κ³ νλ€μ!
Views
μμ ―μλ μΈ κ°μ§ Viewκ° νμν©λλ€.
첫 λ²μ§Έλ placeholder λ‘ μ΄λ―Έ λ€λ£¬ λ΄μ©μ΄κ΅¬μ..
λ λ²μ§Έλ‘ snapshot μ΄λΌλ νλ©΄μ΄ νμν©λλ€.
Widget Galleryμ κ°μ κ³³μμ λΉ λ₯΄κ² λ°μ΄ν°λ₯Ό λΆλ¬μ μ¬μ©ν μ μλ λ¨μΌμ±μ νλ©΄μ λλ€.
λ§μ§λ§μΌλ‘λ timeline μ λλ€.
μ€μ λ‘ μμ ―μ ν νλ©΄μ λμΈ λ μ¬μ©λλ νλ©΄μ λλ€.
λ³΄ν΅ snapshotκ³Ό timelineμ 첫 νλ©΄μ λμΌν νλ©΄μΌλ‘ ꡬμ±λλκ² μ’λ€κ³ νλ€μ.
νμλΌμΈμ Light Modeμ Dark ModeμΌ λμ νλ©΄μ λμμ μ 곡νμ¬ μμ€ν μ΄ μμμ μ‘°μ ν μ μλλ‘ λμ΄ μκ³ , ν λ²μ λ©°μΉ λμμ Viewλ₯Ό μ 곡νλ κ²μ΄ μ’λ€κ³ ν©λλ€.
Reload
νμ§λ§ μμ ―λ μ’ λ μμ£Ό μ λ°μ΄νΈ λμ΄μΌ ν λκ° μκ² μ£ ?
μ΄λ΄ λλ reload λΌλ κ°λ μ μ¬μ©ν©λλ€.
Reloadκ° μ€νλλ©΄ νλ©΄μ λ°°μΉλ λͺ¨λ μμ ― μ λν΄μ μμ€ν μ΄ μλ‘μ΄ timelineμ μ 곡 νλλ‘ μμ²ν©λλ€.
μ μ½λλ‘ ν λ² μ΄ν΄λ΄ μλ€.
public protocol TimelineProvider {
associatedType Entry: TimelineEntry
typealias Context = TimelineProviderContext
func snapshot(with context: Self.Context,
completion: @escaping (Self.Entry) -> ())
func timeline(with context: Self.Context,
completion: @escaping (Timeline<Self.Entry>) -> ())
}
μ½λλ‘ λ΄λ μ λͺ¨λ₯΄κ² κΈ΄ νλ°μ.. π€
Entry
λ λ³΄ν΅ Date
λ°μ΄ν°κ° λ€μ΄κ°λ€κ³ νꡬμ..
Context
μλ environment μ 보μ Entry
λ₯Ό μμ²ν μμ€ν
μ λν μ λ³΄κ° λ€μ΄μ¨λ€κ³ ν©λλ€.
snapshot
κ³Ό timeline
μ λ§ κ·Έλλ‘ μ΄μ μ μ΄ν΄λ³΄μλ snapshotκ³Ό timeline λ°μ΄ν°λ₯Ό μ 곡λ°κΈ° μν ν¨μμ
λλ€.
snapshot
μλ λ¨μΌ λ°μ΄ν°κ°, timeline
μλ μ¬λ¬ κ°μ λ°μ΄ν°κ° λ€μ΄κ°κ² μ£ ?
public struct Provider: TimelineProvider {
public func snapshot(with context: Context,
completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date())
completion(entry)
}
public func timeline(with context: Context,
completion: @escaping (Timeline<Entry>) -> ()) {
let entry = SimpleEntry(date: Date())
let timeline = Timeline(entries: [entry, entry], policy: .atEnd)
completion(timeline)
}
}
TimelineProvider
λ₯Ό μ±ννλ©΄ μ΄λ° ννμ λͺ¨μ΅μΌκ²λλ€.
μ¬κΈ°μ μ£Όλͺ©ν μ μ policy: .atEnd
λΆλΆμ
λλ€.
μ¬κΈ°μ policy
λ reloadκ° λλ μμ μ μ νλ policyλΌκ³ λ³Ό μ μλλ°μ,
atEnd λ‘ μ 곡ν timelineμ΄ λλ λ reloadλ₯Ό ν μ μκ³ , after(date: Date)
λ‘ μΌμ μκ°μ΄ λμ λ reloadνκ±°λ never
λ‘ μμ²νμ§ μμ μ μμ΅λλ€.
κ·Έλμ reloadλ λ체 μΈμ λλλ?
μμ°μ μΌλ‘ λ°μνλ reloadλ μλμ κ·μΉμ λ°λΌ μ΄λ£¨μ΄μ§λλ€.
- ReloadPolicyλ₯Ό λ°λΌ λκ±°λ
- λ μμ£Ό νλ©΄μ λ±μ₯νλ μμ ―μ λ λ§μ reloadλ₯Ό μ 곡νκ±°λ
- κΈ°κΈ° νκ²½μ΄ λ°λ λ μμ€ν μ΄ κ°μ λ‘ reloadν©λλ€.
μ±μ΄ μμ²νλ reloadλ Background Notification μ΄ μκ±°λ, μ¬μ©μκ° λ°μ΄ν°λ₯Ό λ³κ²½ νμ λ μ΄λ£¨μ΄μ§λλ€.
λ κ²½μ° λͺ¨λ WidgetCenter
μ reloadTimelines(ofKind:)
λ₯Ό μ¬μ©νλ©΄ λλ€κ³ νλ€μ.
reloadTimelines(ofKind:)
reloadAllTimelines
getCurrentConfigurations(completion:)
WidgetKitμ μ΄λ κ² μΈκ°μ§ APIλ₯Ό μ 곡νκ³ μλ κ²μ μμλκ³ λμ΄κ°μλ€!
κ·Έλ°λ° μμ ―μ μ¬μ©ν λ, λ€νΈμν¬ ν΅μ μ ν΅ν΄ μλ‘μ΄ λ°μ΄ν°λ₯Ό λ°μμμΌν λλ μκ² μ£ ?
κ·Έλ΄ λλ background URLSessions
λ₯Ό μ¬μ©ν©λλ€.
νμ§λ§! μμ ―μ μ λμ λ μ€μκ° λ°μ΄ν°λ₯Ό λ°μνκΈ° μν κΈ°λ₯μ΄ μλκΈ° λλ¬Έμ κ·Έ λΉλλ₯Ό μ μ€μ ν΄λ¬λΌκ³ ν©λλ€.
κ·Έλμ μ 리ν΄λ³΄λ©΄.. μμ ―μ Reloadλ Background Networking μ ν΅ν΄ μ΄λ£¨μ΄μ§κ±°λ, Timeline μ μν΄ μ΄λ£¨μ΄μ§κ±°λ μ±μ μμ² μ λ°λΌ μ΄λ£¨μ΄μ§ μ μκ² μ΅λλ€.
Personalization & Intelligence
μμμ μμ ―μ μ¬λ¬ μ€μ μ λ°λΌ λ°μ΄ν°λ₯Ό λ€λ₯΄κ² 보μ¬μ€ μ μλ€κ³ νμμ΅λλ€.
Personalization
μ΄ κΈ°λ₯μ μ¬μ©νλ €λ©΄ App Intent λΌλ κ²μ μ¬μ©ν΄μΌ ν©λλ€.
Intent Frameworkλ μ¬λ¬κ°μ νλΌλ―Έν°λ‘ μ΄λ£¨μ΄μ Έ μμ΅λλ€.
κ° νλΌλ―Έν°λ μ μ μκ² λ¬»λ μΌμ’ μ μ§λ¬Έμ΄λΌκ³ μκ°νλ©΄ λ©λλ€.
μλ₯Ό λ€μ΄ μ£Όμ μ±μ κ²½μ° μ μ κ° μμ ―μ ν°μΉνμ¬ μ΄λ€ μ£Όμμ νμν κ²μΈμ§λ₯Ό μ νν μ μκ² μ£ .
μ΄ λ, App Intentλ₯Ό μ¬μ©ν΄ μ μ κ° μ±μμ μ§μ ν΄λ μ£Όμμ 리μ€νΈλ₯Ό μμ ―μΌλ‘ λ°μμ¬ μ μμ΅λλ€.
λ§μ½ μ μ κ° μνλ μ΅μ μ΄ λ¦¬μ€νΈμ μλ€λ©΄?
Intentsλ dynamic options capability κΈ°λ₯μ μ 곡ν©λλ€.
μ μ κ° μ’ λͺ©μ κ²μνλ©΄, μμ€ν μ΄ μ£Όμ Intents Extensionμ μ€ννμ¬ λ¦¬μ€νΈλ₯Ό μλ‘κ² λ°μμ¬ μ μμ΅λλ€.
μ΄ λΆλΆμ λ°λ‘ λ 곡λΆκ° νμν΄λ³΄μ΄λ€μβ¦
SiriKitκ³Ό κ΄λ ¨λ λ΄μ©μ΄λΌκ³ ν©λλ€..
Intentλ₯Ό μ¬μ©νλ©΄ μ΄μ μ μ½λλ λ€μκ³Ό κ°μ΄ λ°κΏμ£Όμ΄μΌ ν©λλ€.
@main
public struct SampleWidget: Widget {
private let kind: String = "SampleWidget"
public var body: some WidgetConfiguration {
IntentConfiguration(kind: kind,
intent: ConfigurationIntent.self
provider: Provider(),
placeholder: PlaceholderView()) { entry in
SampleWidgetEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
}
}
StaticConfiguration λμ IntentConfiguration μ μ¬μ©ν΄μ£Όκ³ (μ.. μ΄λμ μ΄λ¦μ΄ IntentCongiruationμ΄μꡬλβ¦)
public struct Provider: IntentTimelineProvider {
public func timeline(for configuration: ConfigurationIntent, with context: Context,
completion: @escaping (Timeline<Entry>) -> ()) {
let entry = SimpleEntry(date: Date(), configuration: configuration)
// generate a timeline based on the values of the Intent
completion(timeline)
}
}
TimelineProvider
λ IntentTimelineProvider
λ‘ μ
λ°μ΄νΈν΄μ μ 곡ν΄μ£Όμ΄μΌ ν©λλ€.
IntentHandlerλ₯Ό μ¬μ©ν λμ μ£Όμμ !
DynamicIntentλ₯Ό ꡬμ±νκ³ , IntentHandler
λ₯Ό ꡬνν λ ν€λ§¨ κ³³μ΄ μμλλ°μ..
DynamicSummonerSelectionIntentHandler
νλ‘ν μ½μ μ€μνλ € νμ λμ
λλ€.
ν΄λΉ νλ‘ν μ½μ μ±ννκ³ , stub
λ₯Ό μΆκ°νλ©΄ XCodeκ° λ κ°μ§ λ©μλλ₯Ό μΆκ°ν΄μ€λλ€.
func provideSummonerOptionsCollection(for intent: DynamicSummonerSelectionIntent, with completion: @escaping (INObjectCollection<Summoner>?, Error?) -> Void) {
//
}
func provideSummonerOptionsCollection(for intent: DynamicSummonerSelectionIntent) async throws -> INObjectCollection<Summoner> {
//
}
μ λ μ²μμ β@escaping ν΄λ‘μ λ°©μκ³Ό async/awaitμ λ κ°μ§ λ°©μμ λͺ¨λ μ 곡ν΄μ€μΌνλꡬλ!β λΌκ³ μκ°νκ³ μμ μ νμ΅λλ€.
κ·Έλ¬λ©΄ μ΄λ° μλ¬κ° λ°κ²λλ€..
Method with Objective-C selector conflicts with method with the same Objective-C selector.
λ체 μ΄λμ Conflictκ° λλκ±°μ§? λΌλ μλ¬Έμ΄ λ€μμ§λ§.. μμΈμ κ°λ¨νμ΅λλ€.
Handlerμ λ€μ΄κ°μ λ΄μ©μ μ μ΄ν΄λ³΄λ©΄.. λ νλ‘ν μ½μ΄ λμλλ Obj-C ν¨μκ° κ°μ΅λλ€..
κ·Έλμ κ°μ ν¨μλΌλ μλ¬λ₯Ό λ΄κ³ μλ κ²μ΄μ£ ..
κ·Έλ λ€λ©΄ ν΄κ²°λ²μβ¦?
λ ν¨μ μ€ νλλ§ κ³¨λΌμ μ¬μ©ν΄μ£Όμλ©΄ λ©λλ€β¦ π₯²
Intelligence
μ κ·Έλ°λ° μ νμ΄ μ 곡νλ κΈ°λ³Έ μμ ― μ€ βμ€λ§νΈ μ€νβ μ΄λΌλ λμ΄ μμ£ ?
iOSκ° μ μ μ ν¨ν΄κ³Ό νκ²½μ λΆμνμ¬ μν©μ κ°μ₯ λ§λ μμ ―μ μ ννμ¬ λ³΄μ¬μ£Όλ κΈ°λ₯μ λλ€.
μ΄λ»κ² λ΄ μμ ―μ΄ μ€λ§νΈ μ€νμ λ° μ μκ² νλλ? νλ©΄ λκ°μ§ λ°©λ²μ΄ μμ΅λλ€.
1. Shortcuts Donate
μ μ κ° μ± λ΄μμ νλμ ν΄μ Shortcutsλ‘ donateλ₯Ό νκ² λλ©΄, κ°μ INIntentλ₯Ό μ¬μ©νλ μμ ―μ΄ μ€λ§νΈ μ€νμ μλ‘ μ¬ μ μκ² λ©λλ€.
2. TimelineEntryRelevance API
μ΄λ€ μκ°μ΄ λ€κ°μ€κ³ , λ΄ μ±μ entryκ° κ°μ₯ μ€μν μμ μ΄λΌκ³ μκ°λλ©΄, μμ€ν
μκ² score
μ duration
μ μ 곡νμ¬ μ€λ§νΈ μ€νμ μλ‘ μμΉνκ² μ λν μ μμ΅λλ€.
Relevanceλ TimelineEntryRelevance(score:)
λ‘ μ¬μ©ν μ μμ΅λλ€.
μ¬κΈ°μ score
λΌλ νλΌλ―Έν°κ° μ€μνλ°, μ ν¬κ° μνλ μ΄λ€ μμλ₯Ό λ£μ΄μ£Όλ©΄ λ©λλ€.
μ΄ μμμ λ²μλ μ ν΄μ Έμμ§ μμΌλ©°, μ ν¬κ° μ λ¬ν κ° μμλ€κ³Όμ μλμ μΈ μ°¨μ΄μ λ°λΌμ μμμ μ°μ μμκ° μ ν΄μ§λλ€.