Skip to content

iOS_WidgetKit

NamKiBeom edited this page Dec 15, 2020 · 2 revisions

widgetKit์„ ์จ์„œ widget์„ ๋งŒ๋“ค์–ด๋ณด์ž!

๊ฐ„๋žตํ•œ ์„ค๋ช…

์ž.. ์—ฌ๋Ÿฌ๋ถ„ widget ์ข€ ๋งŒ๋“ค์–ด ๋ณผ๊นŒํ•˜๋Š”๋ฐ, ์ด๊ฑฐ ๋งŒ๋“ค์ž๊ณ  SwiftUI ๋ฐฐ์šฐ๊ธฐ๊ฐ€ ์ฐธ ๊ทธ๋ ‡์ฃ ? ์‚ฌ์‹ค ์ƒ UI ์ชฝ ๊ด€๋ จํ•ด์„œ๋งŒ SwiftUI๋ฅผ ์•Œ์•„์•ผํ•˜์ง€ ๋‹ค๋ฅธ ๋ถ€๋ถ„์—์„  ๋”ฑํžˆ ๋ชฐ๋ผ๋„ ๋˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋ช…ํ™•ํ•œ ์‚ฌ์‹ค์€ ์•„๋‹ˆ์ง€๋งŒ ใ…Žใ…Ž ๐Ÿ˜

widget์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ ํ•„์ˆ˜์ ์œผ๋กœ ์•Œ์•„์•ผํ•  ๊ฒƒ๋“ค์„ ์•Œ๋ ค๋“œ๋ฆด๊ฒŒ์š”

  1. Provider
  2. TimelineEntry
  3. EntryView

์ง„์งœ ๊ฐ„๋žตํ•˜๊ฒŒ ์ด๋ ‡๊ฒŒ 3๊ฐ€์ง€ ๊ตฌ์กฐ์ฒด๊ฐ€ ์žˆ๋Š”๋ฐ, ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๊ฒŒ ์„ค๋ช…์„ ๋“œ๋ฆฌ์ž๋ฉด,

Provider๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๊ณตํ•˜๋Š” ๊ตฌ์กฐ์ฒด, TimelineEntry๋Š” ๋ฐ์ดํ„ฐ ์ •์˜, EntryView๋Š” ๊ฐ€๊ณต๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” View

์ด๋ ‡๊ฒŒ ์ƒ๊ฐํ•˜์‹œ๋ฉด ์Œ.. ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์šฐ์‹ค ๊ฒƒ ๊ฐ™์•„์š”

Provider

์ฒซ ๋ฒˆ์งธ๋กœ Provider์ž…๋‹ˆ๋‹ค. ์œ„์— ์„ค๋ช…ํ–ˆ๋“ฏ์ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๊ณตํ•ด์ฃผ๋Š” ๊ตฌ์กฐ์ฒด๋ผ๊ณ  ๋ง์”€์„ ๋“œ๋ ธ๋Š”๋ฐ, ์ฒ˜์Œ์— ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜์‹œ๋ฉด ๋งจ ์œ„์— ์•„๋ž˜์™€ ๊ฐ™์ด 3๊ฐ€์ง€์˜ ํ•จ์ˆ˜๊ฐ€ ์ƒ์„ฑ๋˜์–ด ์žˆ์„ ๊ฒ๋‹ˆ๋‹ค.

struct Provider: TimelineProvider {
    func placeholder(in context: Context) -> TimelineEntry {
    
    }
    
    func getSnapshot(in context: Context, completion: @escaping (TimelineEntry) -> ()) {
    
    }
    
    func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
    
    }
}
  • placeholder : ์œ„์ ฏ์ด ์ฒ˜์Œ ๋‚˜์˜ฌ ๋•Œ, ์œ„์ ฏ์ด ๋Œ€์ถฉ ์ด๋ ‡๊ฒŒ ์ƒ๊ฒผ๊ตฌ๋‚˜~ ๋ผ๋Š”๊ฑธ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•œ ๋ฉ”์„œ๋“œ
  • getSnapshot : ์œ„์ ฏ์„ ์ถ”๊ฐ€ํ•  ๋•Œ, ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋กœ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•œ ๋ฉ”์„œ๋“œ
  • getTimeline : ์„ค์ •ํ•œ ์‹œ๊ฐ„ ์ฃผ๊ธฐ๋งˆ๋‹ค ๊ฐ€๊ณต๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ทฐ์— ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•œ ๋ฉ”์„œ๋“œ

์ด๊ฒŒ ์ œ๊ฐ€ ์ดํ•ดํ•œ๋Œ€๋กœ ์„ค๋ช…์„ ์ ์–ด๋ดค๋Š”๋ฐ ํ˜น์‹œ๋ผ๋„ ์ž˜๋ชป๋œ ๋ถ€๋ถ„์ด ์žˆ๋‹ค๋ฉด, ์กฐ์–ธ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค ใ…Žใ…Ž ์œ„์— ์„ค๋ช…๋Œ€๋กœ, placeholder์™€ getSnapshot์€ defaultํ•œ ๊ฐ’์„ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•œ ๋ฉ”์„œ๋“œ๋ผ๊ณ  ์ƒ๊ฐํ•˜์‹œ๋ฉด ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋ง ๊ทธ๋Œ€๋กœ ์•ฝ๊ฐ„ ์Œ.. ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋Š๋‚Œ??

getTimeline์€ widget์„ ๋ช‡ ๋ถ„, ๋˜๋Š” ๋ช‡ ์‹œ๊ฐ„ ๋‹จ์œ„๋กœ ์—…๋ฐ์ดํŠธ ํ•  ๊ฒƒ์ธ์ง€์— ๋Œ€ํ•ด ์„ค์ •ํ•  ์ˆ˜ ์žˆ๊ณ , ์–ด๋–ค ๋ฐ์ดํ„ฐ๋กœ ์—…๋ฐ์ดํŠธํ•  ๊ฒƒ์ธ์ง€๋„ ์„ค์ •ํ•ด์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ € ๊ฐ™์€ ๊ฒฝ์šฐ์—”, ์—ฌ๊ธฐ์„œ URLSession์„ ์‚ฌ์šฉํ•˜์—ฌ API๋ฅผ ํ˜ธ์ถœํ–ˆ์Šต๋‹ˆ๋‹ค. ํ˜ธ์ถœํ•ด์„œ ์–ป์€ response๋กœ JSON์„ ํŒŒ์‹ฑํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ timelineEntry ๋ผ๋Š” ๊ตฌ์กฐ์ฒด์— ๋‹ด์•„์„œ completion์œผ๋กœ ๋„˜๊ฒจ์ค๋‹ˆ๋‹ค. ๋Œ€๋žต์ ์ธ ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™๊ณ , Timeline์ด๋ผ๋Š” ๊ตฌ์กฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด์„œ completion์— ๋„˜๊ฒจ์ฃผ๋Š” ๋ชจ์Šต์ž…๋‹ˆ๋‹ค.

์ด ๋ถ€๋ถ„์—์„œ ์ดํ•ด๊ฐ€ ์ž˜ ์•ˆ๋ ์ˆ˜๋„ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค๋งŒ.. UserInfoEntry๊ฐ€ ์œ„์—์„œ ๋ง์”€๋“œ๋ฆฐ timelineEntry๋ฅผ ์ฑ„ํƒํ•œ ๊ตฌ์กฐ์ฒด๋ผ๊ณ  ์ƒ๊ฐํ•˜์‹œ๋ฉด ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

let info = try? JSONDecoder().decode(Response<UserData>.self, from: data)
                                            
let entry = UserInfoEntry(date: date, data: info?.data)
                                            
completion(Timeline(entries: [entry], policy: .after(nextUpdate)))

TimelineEntry

์ž! ์—ฌ๋Ÿฌ๋ถ„ ์ด์ œ TimelineEntry์ž…๋‹ˆ๋‹ค. ์‚ฌ์‹ค์ƒ ์ด๊ฑด ๊ฐ„๋‹จํ•œ ๊ตฌ์กฐ์ฒด์ธ๋ฐ์š”.. ์Œ.. ๋ชจ๋ธ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜์‹œ๋ฉด ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ดˆ๊ธฐ์— ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜์‹œ๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌ์กฐ์ฒด๊ฐ€ ๋งŒ๋“ค์–ด์ ธ ์žˆ์„ ๊ฒ๋‹ˆ๋‹ค.

struct ์—ฌ๋Ÿฌ๋ถ„์˜ํ”„๋กœ์ ํŠธ์ด๋ฆ„entry: TimelineEntry {
    let date: Date
}

date๋Š” ์•„๋งˆ ์—…๋ฐ์ดํŠธ ์‹œ์ ์„ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด์„œ? ํ•„์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์œ„์— ๋ง์”€๋“œ๋ ธ๋“ฏ์ด getTimeline ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ, ์—…๋ฐ์ดํŠธ ์‹œ์ ์„ ์ •ํ•ด์ค€๋‹ค๊ณ  ํ–ˆ์ฃ ? ๊ทธ๋ž˜์„œ ํ•„์š”ํ•œ ์ •๋ณด๋ผ๊ณ  ์ƒ๊ฐ์ด ๋“ญ๋‹ˆ๋‹ค. ์ด์ œ ์ด๊ฑธ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•ด์„œ ์ปค์Šคํ…€ํ•˜๋ฉด ๋˜๋Š”๋ฐ, ์ƒ๊ฐ๋ณด๋‹ค ์ •๋ง ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ƒฅ ์ €๊ธฐ๋‹ค๊ฐ€ ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ ํƒ€์ž…์œผ๋กœ ํ”„๋กœํผํ‹ฐ๋งŒ ์ถ”๊ฐ€์‹œ์ผœ์ฃผ๋ฉด ๋์ž…๋‹ˆ๋‹ค. ๐Ÿ‘

struct ์—ฌ๋Ÿฌ๋ถ„์˜ํ”„๋กœ์ ํŠธ์ด๋ฆ„entry: TimelineEntry {
    let date: Date
    let ์—ฌ๋ ค๋ถ„์ด์›ํ•˜๋Š”๋ฐ์ดํ„ฐ: ์—ฌ๋Ÿฌ๋ถ„์ด์›ํ•˜๋Š” ๋ฐ์ดํ„ฐํƒ€์ž…
}

์ง„์งœ ๊ฐ„๋‹จํ•˜์ฃ ? ์ด๋ ‡๊ฒŒ ์„ค์ •์„ ํ•ด๋†“๊ณ , provider์—์„œ ์ถ”๊ฐ€๋œ ํ”„๋กœํผํ‹ฐ์— ๋งž๊ฒŒ ์ฝ”๋“œ๋ฅผ ์กฐ๊ธˆ์”ฉ๋งŒ ๋ฐ”๊ฟ”์ฃผ๋ฉด ๋์ž…๋‹ˆ๋‹ค.

EntryView

๋งˆ์ง€๋ง‰์œผ๋กœ EntryView์ž…๋‹ˆ๋‹ค. ์œ„์—์„œ ๋ฐ์ดํ„ฐํƒ€์ž…์„ ์ •์˜ํ•ด์ฃผ๊ณ , ๊ทธ๋Ÿฐ๋‹ค์Œ ๊ฐ€๊ณต๋„ ํ•ด์ฃผ๊ณ .. ๊ทธ ๋‹ค์Œ์€ ์ด์ œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ทฐ์— ๋ณด์—ฌ์ค˜์•ผ๊ฒ ์ฃ ? ์ด๊ฒƒ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ฒ˜์Œ์— widget ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋ฉด, ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. SwiftUI๋ฅผ ์จ์•ผํ•œ๋‹ค๋Š” ๊ฑฑ์ •๋•Œ๋ฌธ์— ๊ณ„์† ๋ฏธ๋ค˜๋Š”๋ฐ, ์ด๋ ‡๊ฒŒ ์นœ์ ˆํ•˜๊ฒŒ ๋‹ค ํ‹€์„ ๋งŒ๋“ค์–ด์ค„๊ฑฐ๋ผ๊ณค ์ƒ๊ฐ๋„ ๋ชปํ–ˆ์–ด์š” ๐Ÿ˜ ์•„๋ž˜์™€ ๊ฐ™์ด ์ƒ์„ฑ๋˜์–ด ์žˆ์„ ๊ฒ๋‹ˆ๋‹ค.

struct Project04WidgetEntryView: View {
    var entry: Provider.Entry

    var body: some View {
    }
}

์ง„์งœ ๋‹ค์™”์–ด์š” ์—ฌ๋Ÿฌ๋ถ„ ์•„๊นŒ getTimeline ๋ฉ”์„œ๋“œ์—์„œ completion์œผ๋กœ Timeline ๊ตฌ์กฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด์„œ entry๋ฅผ ๋„˜๊ฒจ์คฌ์ฃ ? ์ด๊ฒŒ ๋‹ค ์ €๊ธฐ ์„ ์–ธ๋˜์–ด์žˆ๋Š” entry๋ผ๋Š” ๋ณ€์ˆ˜์— ๋“ค์–ด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ € ๋ณ€์ˆ˜ ์•ˆ์— ๋“ค์–ด๊ฐ€์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ทฐ์—๋‹ค ๋ฟŒ๋ ค์ฃผ๊ธฐ๋งŒํ•˜๋ฉด?? ๋์ž…๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ๋ถ„ ๋ฌผ๋ก  ๋ทฐ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๋ถ€๋ถ„์—์„œ๋Š” SwiftUI์— ๋Œ€ํ•œ ํ•™์Šต์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค๋งŒ.. ์—ฌ๊ธฐ์„œ๋Š” ๋‹ค๋ฃจ์ง€ ์•Š๊ฒ ์Šต๋‹ˆ๋‹ค.

์š”์•ฝ(FLOW)

ํ๋ฆ„์— ๋Œ€ํ•ด ์š”์•ฝ์„ ํ•ด๋“œ๋ฆฌ์ž๋ฉด, ์ด์™€๊ฐ™์€ ํ๋ฆ„์ด๋ผ๊ณ  ๋ณด์‹œ๋ฉด ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๊ฟ€ํŒ

๋ฉ”์ธํ”„๋กœ์ ํŠธ์—์„œ ์ •์˜ํ•ด๋†“์€ class ์‚ฌ์šฉํ•˜๊ธฐ!!

์„œ๋ฒ„์—์„œ ๋ฐ›์•„์˜ฌ ๋ฐ์ดํ„ฐ ํƒ€์ž…์ด๋‚˜ ๋„คํŠธ์›Œํฌ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ๊ฐ์ฒด๋ฅผ ์ด๋ฏธ ๋ฉ”์ธํ”„๋กœ์ ํŠธ์— ๋งŒ๋“ค์–ด๋†จ๋Š”๋ฐ, widget์—์„œ๋Š” ๋‹ค๋ฅธ ํ”„๋กœ์ ํŠธ๋‹ˆ๊นŒ ๋‹ค์‹œ ํŒŒ์ผ์„ ์ƒ์„ฑํ•ด์•ผํ•˜๋‚˜.. ๋ผ๋Š” ์ƒ๊ฐ์„ ์ €๋„ ์ฒ˜์Œ์— ํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ!! ๊ทธ๋ƒฅ ๊ฐ„๋‹จํ•˜๊ฒŒ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋”๋ผ๊ตฌ์š”.. ใ…Žใ…Ž ์•„์ฃผ ์ข‹์Šต๋‹ˆ๋‹ค.

๊ทธ๋ƒฅ ์ด๋ ‡๊ฒŒ ์žฌ์‚ฌ์šฉํ•˜๊ธธ ์›ํ•˜๋Š” ํŒŒ์ผ์— ๊ฐ€์„œ target Membership์— ์ฒดํฌ๋งŒ ํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•˜์ฃ ?

UserDefaults widget์—์„œ ๊ณต์œ ํ•˜๋Š” ๋ฐฉ๋ฒ•

๋‹น์—ฐํžˆ ๊ฐ™์€ ์•ฑ์ด๋‹ˆ๊นŒ UserDefaults๊ฐ€ ๊ณต์œ ๋˜์ง€ ์•Š์„๊นŒ?? ๋ผ๋Š” ํฌ๋ง์ฐฌ ์ƒ๊ฐ์„ ํ–ˆ์—ˆ๋Š”๋ฐ, ์—ญ์‹œ ์•„๋‹ˆ์˜€์–ด์š”.. ํ•˜์ง€๋งŒ, ์ฐพ์•„๋ณด๋‹ˆ ๋ฐฉ๋ฒ•์ด ์žˆ๋”๋ผ๊ตฌ์š”! ์œ ๋ ˆ์นด!!

์ด๋Ÿฐ ์‹์œผ๋กœ ๊ทธ๋ฃน์„ ์ •์˜ํ•ด์„œ ์ถ”๊ฐ€์‹œ์ผœ์ค€๋‹ค๋ฉด, ๊ทธ๋ฃน๋ผ๋ฆฌ UserDefault๋ฅผ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฃน์ด๋‹ˆ๊นŒ ๋ฉ”์ธํ”„๋กœ์ ํŠธ์™€ widgetExtension ๋‘˜ ๋ชจ๋‘ ๊ทธ๋ฃน์„ ๋™์ผํ•œ ์ด๋ฆ„์œผ๋กœ ์„ค์ •ํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ๋์ด ์•„๋‹™๋‹ˆ๋‹ค. ๊ทธ๋ฃน์—์„œ ๊ณต์œ ํ•˜๋Š” UserDefaults๋ฅผ static ๋ณ€์ˆ˜๋กœ ์„ ์–ธํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

extension UserDefaults {
    static var shared: UserDefaults {
        let appGroupId = "group.namkibeom.project04"
        return UserDefaults(suiteName: appGroupId)!
    }
}

์•„๊นŒ ์„ค์ •ํ•ด์คฌ๋˜ ๊ทธ๋ฃน์˜ ์ด๋ฆ„์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•ด์„œ UserDefaults๋ฅผ ์ƒ์„ฑํ•ด์ฃผ๊ณ , shared๋ฅผ standard ๋Œ€์‹  ์‚ฌ์šฉํ•˜๋ฉด ์ €์žฅ๋œ ์ •๋ณด๊ฐ€ ๊ณต์œ ๋ฉ๋‹ˆ๋‹ค.

Clone this wiki locally