๐ŸŒ™

[Swift] D-Day ์œ„์ ฏ ๋งŒ๋“ค๊ธฐ (1)

yeggrrr๐Ÿผ 2025. 11. 28. 13:25
728x90

 

 

 

iOS์—์„œ '์œ„์ ฏ'์ด๋ผ๋Š” ๊ฐœ๋…์€ ์ƒ๊ฐ๋ณด๋‹ค ์˜ค๋ž˜๋๋‹ค.
๋‹ค๋งŒ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋…ธ์ถœ๋˜๋Š” ๋ชจ์Šต๊ณผ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋‹ค๋ฃจ๋Š” ๊ธฐ์ˆ  ์Šคํƒ์€ ํฌ๊ฒŒ ๋‘ ๋ฒˆ ๋ฐ”๋€Œ์—ˆ๋‹ค.

์ด๋ฒˆ ๊ธ€์—์„œ๋Š” ์œ„์ ฏ์˜ ํ๋ฆ„์„ ๊ฐ„๋‹จํžˆ ์งš๊ณ , ์™œ ์ง€๊ธˆ์€ SwiftUI ๊ธฐ๋ฐ˜์˜ WidgetKit์œผ๋กœ ๊ฐœ๋ฐœํ•ด์•ผ ํ•˜๋Š”์ง€๋ฅผ ์ •๋ฆฌํ•œ ๋’ค,
์˜ˆ์ œ๋กœ D-Day ์œ„์ ฏ์„ ๊ตฌํ˜„ํ•˜๋Š” ์ˆœ์„œ๋ฅผ ๋‹จ๊ณ„๋ณ„๋กœ ์‚ดํŽด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

 

 

 

iOS ์œ„์ ฏ์˜ ๋ณ€ํ™”์™€ WidgetKit์˜ ๋“ฑ์žฅ

1. Today Extension ์‹œ์ ˆ(iOS 8 ~ iOS 13)
์ดˆ๊ธฐ ์œ„์ ฏ์€ Notification Center์˜ Today View์— ์กด์žฌํ–ˆ๋‹ค.
์ด ๋•Œ์˜ ์œ„์ ฏ์€
- Today Extension ์ด๋ผ๋Š” ๋ณ„๋„์˜ ํƒ€๊นƒ์œผ๋กœ ๊ตฌํ˜„
- UIKit + AutoLayout + Storyboard ๊ธฐ๋ฐ˜
- ์•ฑ๊ณผ์˜ ์—ฐ๋™๋„ ์ง€๊ธˆ๋ณด๋‹ค ์ œ์•ฝ์ด ๋งŽ๊ณ , UI ์ž์œ ๋„๋Š” ์žˆ์—ˆ์ง€๋งŒ ์‹œ์Šคํ…œ ํ†ตํ•ฉ ๊ฒฝํ—˜์€ ์ œํ•œ์ 

๋ฌด์—‡๋ณด๋‹ค, ํ™ˆ ํ™”๋ฉด์— ์˜ฌ๋ ค๋†“๋Š” ํ˜•ํƒœ๊ฐ€ ์•„๋‹ˆ๋ผ '์•Œ๋ฆผ์„ผํ„ฐ ํ•œ ์ผ '์— ์žˆ๋Š” ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ ์ •๋„์˜€๋‹ค.

 

2. WidgetKit๋„์ž…(iOS 14~)
iOS 14๋ถ€ํ„ฐ ์• ํ”Œ์€ ์œ„์ ฏ์„ ํ™ˆ ํ™”๋ฉด์˜ First-class๋กœ ๋Œ์–ด์˜ฌ๋ ธ๋‹ค.
- ํ™ˆ ํ™”๋ฉด ์–ด๋””๋“  ๋ฐฐ์น˜ ๊ฐ€๋Šฅ
- ๋‹ค์–‘ํ•œ ์‚ฌ์ด์ฆˆ(Small / Medium / Large, ์ดํ›„ iOS 15+, 16+์—์„œ ๋” ํ™•์žฅ)

์ด ์‹œ์ ์—์„œ ์œ„์ ฏ ๊ตฌํ˜„ ๋ฐฉ์‹๋„ ์™„์ „ํžˆ ๋ฐ”๋€Œ๊ฒŒ ๋๋‹ค.
- UIKit ๊ธฐ๋ฐ˜ Today Extension → SwiftUI ๊ธฐ๋ฐ˜ WidgetKit
- UI๋Š” ์ „๋ถ€ SwiftUI View๋กœ๋งŒ ์ž‘์„ฑ
- ์—…๋ฐ์ดํŠธ๋Š” ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜์ด ์•„๋‹ˆ๋ผ Timeline ๊ธฐ๋ฐ˜์˜ '์Šค๋ƒ…์ƒท ๋ Œ๋”๋ง' ๊ตฌ์กฐ

์ด์ œ iOS์—์„œ '์œ„์ ฏ์„ ๋งŒ๋“ ๋‹ค'๋Š” WidgetKit + SwiftUI๋กœ ๊ฐœ๋ฐœํ•œ๋‹ค๋Š” ์˜๋ฏธ๋‹ค.

 


 

WidgetKit์˜ ์„ค๊ณ„ ์ฒ ํ•™: ์™œ SwiftUI๋งŒ ํ—ˆ์šฉํ• ๊นŒ?

WidgetKit์˜ ํ•ต์‹ฌ์€ ์•„๋ž˜ ๋‘ ๊ฐ€์ง€๋กœ ์š”์•ฝ๋œ๋‹ค.

1. ์ •์ ์ด๊ณ  ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ UI
์œ„์ ฏ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ํ™”๋ฉด์ด๋ผ๊ธฐ ๋ณด๋‹ค,
์‹œ์Šคํ…œ์ด ์ •ํ•œ ๊ฐ„๊ฒฉ๊ณผ ๊ทœ์น™์— ๋”ฐ๋ผ ๋…ธ์ถœ๋˜๋Š” '์ •๋ณด ์นด๋“œ'์— ๊ฐ€๊น๋‹ค.
๋”ฐ๋ผ์„œ ์•ฑ์ฒ˜๋Ÿผ ๋ณต์žกํ•œ ๋ทฐ ๊ณ„์ธต, ์• ๋‹ˆ๋ฉ”์ด์…˜, ์ œ์Šค์ฒ˜๊ฐ€ ์ฃผ์ธ๊ณต์ด ์•„๋‹ˆ๋‹ค.

2. Timeline ๊ธฐ๋ฐ˜ ๋ Œ๋”๋ง
WidgetKit์€ '์–ธ์ œ, ์–ด๋–ค ๋ฐ์ดํ„ฐ๋กœ ์œ„์ ฏ์„ ์—…๋ฐ์ดํŠธํ• ์ง€'๋ฅผ ๋ฏธ๋ฆฌ ์ •ํ•œ ํƒ€์ž„๋ผ์ธ(Timeline)์„ ๋„˜๊ฒจ๋ฐ›๊ณ ,
๊ทธ ์‹œ์ ๋งˆ๋‹ค SwiftUI View๋ฅผ ๋ Œ๋”๋งํ•˜์—ฌ ํ™”๋ฉด์— ๊ทธ๋ ค์ค€๋‹ค.


์ด๋•Œ SwiftUI๋Š”
- ์„ ์–ธํ˜• UI์ด๊ธฐ ๋•Œ๋ฌธ์— ํ˜„์žฌ ์ƒํƒœ → ๋ทฐ๋กœ ๋งคํ•‘ํ•˜๊ธฐ ์ข‹๊ณ 
- ์žฌ๋ Œ๋”๋ง ์‹œ ์‹œ์Šคํ…œ์ด ๋น„์šฉ์„ ์˜ˆ์ธกํ•˜๊ธฐ ์‰ฝ๊ณ 
- ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ๊ฐ€ ํ†ต์ œ๋˜๊ธฐ ๋•Œ๋ฌธ์—

WidgetKit์˜ '์Šค๋ƒ…์ƒท ๊ธฐ๋ฐ˜ ๋ Œ๋”๋ง' ๋ชจ๋ธ๊ณผ ์ž˜ ๋งž๋Š”๋‹ค.
๋ฐ˜๋Œ€๋กœ UIKit์€ ๋ผ์ดํ”„์‚ฌ์ดํด๊ณผ ์ƒํƒœ๊ฐ€ ๋ณต์žกํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์œ„ ๊ตฌ์กฐ์™€ ๋งž์ง€ ์•Š์•„ ์œ„์ ฏ์—์„œ๋Š” ์•„์˜ˆ ์‚ฌ์šฉ์ด ๊ธˆ์ง€๋œ๋‹ค.

 


D-Day ์œ„์ ฏ์„ ์˜ˆ์ œ๋กœ ์•„ํ‚คํ…์ฒ˜์žก๊ธฐ

๊ฐ„๋‹จํ•˜๊ฒŒ ๋งŒ๋“ค์–ด ๋ณผ ์œ„์ ฏ์˜ ๊ธฐ๋Šฅ
โ‘  ์‚ฌ์šฉ์ž๊ฐ€ ์•ฑ์—์„œ '๊ธฐ๋…์ผ ์ œ๋ชฉ + ๋‚ ์งœ'๋ฅผ ์„ค์ •
โ‘ก ํ™ˆ ํ™”๋ฉด์—์„œ
    - D-3, D-day, D+2์™€ ๊ฐ™์ด ๋‚จ์€/์ง€๋‚œ ๋‚ ์งœ ํ‘œ์‹œ
    - ๊ธฐ๋…์ผ ๋‚ ์งœ, ์ œ๋ชฉ ํ•จ๊ป˜ ๋…ธ์ถœ
โ‘ข ์ž์ •์ด ์ง€๋‚  ๋•Œ๋งˆ๋‹ค ์ž๋™์œผ๋กœ D-Day ์ˆซ์ž๊ฐ€ ๋ฐ”๋€œ
โ‘ฃ ์œ„์ ฏ์„ ํƒญํ•˜๋ฉด ์•ฑ์˜ D-Day ์„ค์ • ํ™”๋ฉด์œผ๋กœ ์ด๋™

๊ฐœ๋ฐœ ์ˆœ์„œ
โ‘  ๊ธฐ๋ณธ ํ”„๋กœ์ ํŠธ + Widget Extension ์ƒ์„ฑ
โ‘ก ๊ณต์šฉ ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ(App Group + UserDefaults) ์„ค๊ณ„
โ‘ข D-Day ๋„๋ฉ”์ธ ๋ชจ๋ธ ๋ฐ ๋‚ ์งœ ๊ณ„์‚ฐ ๋กœ์ง ๊ตฌํ˜„
โ‘ฃ TimelineProvider / Entry ๊ตฌํ˜„
โ‘ค SwiftUI๋กœ ์œ„์ ฏ UI ๊ตฌ์„ฑ
โ‘ฅ ์•ฑ์—์„œ ์„ค์ • ๋ณ€๊ฒฝ ์‹œ ์œ„์ ฏ ๊ฐฑ์‹  ์—ฐ๋™(WidgetCenter)
โ‘ฆ ์‹ค๊ธฐ๊ธฐ/์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ์—์„œ ์œ„์ ฏ ์ถ”๊ฐ€ ๋ฐ ๋™์ž‘ ๊ฒ€์ฆ



1๋‹จ๊ณ„ – Widget Extension ์ƒ์„ฑ

1. Xcode์—์„œ
- File > New > Target…
- iOS ํƒญ์—์„œ Widget Extension ์„ ํƒ

2. ์ด๋ฆ„ ์˜ˆ: DDayWidget
3. SwiftUI + WidgetKit ๊ธฐ๋ฐ˜ ํ…œํ”Œ๋ฆฟ์ด ์ƒ์„ฑ๋œ๋‹ค.

์ƒ์„ฑ ํ›„ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ:
- DDayWidget.swift (์œ„์ ฏ ์ •์˜)
- DDayWidgetExtension ํƒ€๊นƒ
- ๊ธฐ๋ณธ Provider / Entry / View ์ฝ”๋“œ

(์—ฌ๊ธฐ์„œ ๊ธฐ์กด ๊ฐœ๋ฐœํ•˜๋˜ ํ”„๋กœ์ ํŠธ๊ฐ€ UIKit ๊ธฐ๋ฐ˜์ด๋ผ๋„ ์ƒ๊ด€์—†์Œ! ํƒ€๊ฒŸ๋งŒ ์ถ”๊ฐ€ํ•˜๋ฉด ๋จ)

(์ถ”๊ฐ€ํ•˜๋ฉด ์ด๋ ‡๊ฒŒ ์ž๋™์œผ๋กœ ์˜ˆ์‹œ ํŒŒ์ผ๊ณผ ํ•จ๊ป˜ ์ƒ์„ฑ๋จ)

2๋‹จ๊ณ„ - App Group + ๊ณต์œ  ์ €์žฅ์†Œ ์„ค๊ณ„

์•ฑ๊ณผ ์œ„์ ฏ์€ ์„œ๋กœ ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค์ด๊ธฐ ๋•Œ๋ฌธ์—, ๋ฐ์ดํ„ฐ๋ฅผ ์ง์ ‘ ๊ณต์œ ํ•  ์ˆ˜ ์—†๋‹ค.
์• ํ”Œ์€ ์ด๋ฅผ ์œ„ํ•ด App Groups๋ผ๋Š” ๊ณต์œ  ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ๋‹ค.


App Group ์„ค์ •

1. ์•ฑ ํƒ€๊ฒŸ → Signing & Capabilities → + Capability → App Groups ์ถ”๊ฐ€
2. Widget ํƒ€๊นƒ์—๋„ ๋™์ผํ•˜๊ฒŒ App Groups ์ถ”๊ฐ€
3. ex) group.com.yegrcompany.ddaywidget ์ƒ์„ฑ ํ›„, ๋‘ ํƒ€๊นƒ ๋ชจ๋‘ ์ฒดํฌ


๊ณต์œ  UserDefaults ๋ž˜ํผ

enum SharedStore {
    static let suiteName = "group.com.yegrcompany.ddaywidget"

    static var defaults: UserDefaults? {
        UserDefaults(suiteName: suiteName)
    }
}

struct DDayConfig: Codable {
    let title: String
    let targetDate: Date
}

extension SharedStore {
    private static let key = "dday_config"

    static func save(config: DDayConfig) {
        guard let data = try? JSONEncoder().encode(config) else { return }
        defaults?.set(data, forKey: key)
    }

    static func loadConfig() -> DDayConfig? {
        guard
            let data = defaults?.data(forKey: key),
            let config = try? JSONDecoder().decode(DDayConfig.self, from: data)
        else { return nil }

        return config
    }
}

- ์•ฑ์—์„œ๋Š” D-Day ์„ค์ • ํ™”๋ฉด์—์„œ save(config:) ํ˜ธ์ถœ
- ์œ„์ ฏ์—์„œ๋Š” loadConfig()๋กœ ์ฝ์–ด์„œ ํ™”๋ฉด์— ๋ฐ˜์˜

 

3๋‹จ๊ณ„ - D-Day ๋‚ ์งœ ๊ณ„์‚ฐ ๋กœ์ง

์œ„์ ฏ์˜ ํ•ต์‹ฌ ๋„๋ฉ”์ธ์€ ๊ฒฐ๊ตญ '์˜ค๋Š˜๊ณผ ๋ชฉํ‘œ์ผ์˜ ์ผ(day) ๋‹จ์œ„ ์ฐจ์ด'์ด๋‹ค.

- ๋‚ ์งœ ์ฐจ์ด ๊ณ„์‚ฐ ๋กœ์ง

struct DDayInfo {
    let config: DDayConfig
    let dayDiff: Int   // ์˜ค๋Š˜ ๊ธฐ์ค€ ๋‚ ์งœ ์ฐจ์ด
}

func calculateDDay(target: Date, now: Date = Date()) -> Int {
    let calendar = Calendar.current
    let startOfToday = calendar.startOfDay(for: now)
    let startOfTarget = calendar.startOfDay(for: target)

    // (์˜ค๋Š˜ → ํƒ€๊ฒŸ) ๊ธฐ์ค€ ์ฐจ์ด
    let diff = calendar.dateComponents([.day], from: startOfToday, to: startOfTarget).day ?? 0
    return diff
}

func formattedDDayString(dayDiff: Int) -> String {
    switch dayDiff {
    case 0:
        return "D-Day"
    case let d where d > 0:
        return "D-\(d)"
    default:
        return "D+\(abs(dayDiff))"
    }
}

 

  • diff > 0 → ํƒ€๊ฒŸ์ด ๋ฏธ๋ž˜ → D-3
  • diff == 0 → ์˜ค๋Š˜์ด ๋ฐ”๋กœ ๊ทธ ๋‚  → D-Day
  • diff < 0 → ํƒ€๊ฒŸ์ด ๊ณผ๊ฑฐ → D+2

 

4๋‹จ๊ณ„ - TimelineProvider & Entry ๊ตฌํ˜„

WidgetKit์€ ํƒ€์ž„๋ผ์ธ์— ๋”ฐ๋ผ ์œ„์ ฏ์„ ๊ฐฑ์‹ ํ•œ๋‹ค.
์—ฌ๊ธฐ์„œ '์ž์ •๋งˆ๋‹ค ๋‹ค์‹œ ๊ณ„์‚ฐํ•ด์„œ ๊ทธ๋ ค๋‹ฌ๋ผ'๋Š” ํƒ€์ž„๋ผ์ธ์„ ๋„˜๊ฒจ์ฃผ๋ฉด ๋œ๋‹ค.

- Entry ์ •์˜

import WidgetKit
import SwiftUI

struct DDayEntry: TimelineEntry {
    let date: Date            // ์œ„์ ฏ ์ƒํƒœ ๊ธฐ์ค€ ์‹œ๊ฐ„
    let config: DDayConfig?   // ์ €์žฅ๋œ ์„ค์ • (์—†์„ ์ˆ˜๋„ ์žˆ์Œ)
    let dayDiff: Int          // ๊ณ„์‚ฐ๋œ ๋‚ ์งœ ์ฐจ์ด
}

 


- Provider ๊ตฌํ˜„

struct DDayProvider: TimelineProvider {
    func placeholder(in context: Context) -> DDayEntry {
        DDayEntry(
            date: Date(),
            config: DDayConfig(title: "๊ธฐ๋…์ผ", targetDate: Date()),
            dayDiff: 0
        )
    }

    func getSnapshot(in context: Context, completion: @escaping (DDayEntry) -> Void) {
        let config = SharedStore.loadConfig()
        let target = config?.targetDate ?? Date()
        let diff = calculateDDay(target: target)

        let entry = DDayEntry(date: Date(), config: config, dayDiff: diff)
        completion(entry)
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<DDayEntry>) -> Void) {
        let now = Date()
        let config = SharedStore.loadConfig()
        let target = config?.targetDate ?? now
        let diff = calculateDDay(target: target, now: now)

        let entry = DDayEntry(date: now, config: config, dayDiff: diff)

        // ๋‹ค์Œ ์ž์ • ์‹œ๊ฐ„ ๊ณ„์‚ฐ
        let calendar = Calendar.current
        let nextMidnight = calendar.nextDate(
            after: now,
            matching: DateComponents(hour: 0, minute: 0, second: 0),
            matchingPolicy: .nextTime
        )

        let timeline: Timeline<DDayEntry>
        if let next = nextMidnight {
            timeline = Timeline(entries: [entry], policy: .after(next))
        } 
        else {
            // ๊ณ„์‚ฐ ์‹คํŒจ ์‹œ ์ผ๋‹จ ๊ณ ์ •
            timeline = Timeline(entries: [entry], policy: .never)
        }

        completion(timeline)
    }
}


์—ฌ๊ธฐ์„œ ํฌ์ธํŠธ๋Š”
- ์œ„์ ฏ์ด '์‹ค์‹œ๊ฐ„'์œผ๋กœ ๋Œ์•„๊ฐ€๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, 
   '์ง€๊ธˆ ์ƒํƒœ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” Entry๋ฅผ ํ•˜๋‚˜ ๋„˜๊ฒจ์ฃผ๊ณ , ๋‹ค์Œ์— ์–ธ์ œ ๋‹ค์‹œ ๋ถˆ๋Ÿฌ์ค„์ง€(timeline policy)๋ฅผ ์•Œ๋ ค์ฃผ๋Š” ๊ตฌ์กฐ'๋ผ๋Š” ๊ฒƒ
- D-Day์˜ ๊ฒฝ์šฐ ๋‚ ์งœ ๋‹จ์œ„๋กœ๋งŒ ๋ณ€ํ•˜๋ฉด ๋˜๋ฏ€๋กœ, ์ž์ •๋งˆ๋‹ค ๋‹ค์‹œ ํ˜ธ์ถœ๋˜๋„๋ก ํƒ€์ž„๋ผ์ธ์„ ๊ตฌ์„ฑํ•œ๋‹ค๋Š” ์ 



5๋‹จ๊ณ„ - SwiftUI๋กœ ์œ„์ ฏ UI ๊ตฌ์„ฑ

์ด์ œ Entry์— ๋‹ด๊ธด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ SwiftUI๋กœ UI๋ฅผ ๊ทธ๋ฆฐ๋‹ค.

struct DDayWidgetView: View {
    var entry: DDayProvider.Entry

    var body: some View {
        ZStack {
            LinearGradient(
                gradient: Gradient(colors: [.blue.opacity(0.8), .purple]),
                startPoint: .topLeading,
                endPoint: .bottomTrailing
            )

            VStack(alignment: .leading, spacing: 4) {
                Text(entry.config?.title ?? "D-Day ๋ฏธ์„ค์ •")
                    .font(.headline)
                    .foregroundColor(.white)

                Text(formattedDDayString(dayDiff: entry.dayDiff))
                    .font(.system(size: 32, weight: .bold))
                    .foregroundColor(.white)

                if let target = entry.config?.targetDate {
                    Text(target, style: .date)
                        .font(.caption)
                        .foregroundColor(.white.opacity(0.9))
                }
            }
            .padding()
        }
    }
}

 

์œ„์ ฏ ์ •์˜

@main
struct DDayWidget: Widget {
    let kind: String = "DDayWidget"

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: DDayProvider()) { entry in
            DDayWidgetView(entry: entry)
        }
        .configurationDisplayName("D-Day ์œ„์ ฏ")
        .description("์ค‘์š”ํ•œ ๋‚ ์„ ํ™ˆ ํ™”๋ฉด์—์„œ ๋ฐ”๋กœ ํ™•์ธํ•˜์„ธ์š”.")
        .supportedFamilies([.systemSmall, .systemMedium])
    }
}

 

 

 

6๋‹จ๊ณ„ - ์•ฑ๊ณผ ์œ„์ ฏ์˜ ์—ฐ๋™(WidgetCenter)

์•ฑ์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ๊ธฐ๋…์ผ์„ ๋ฐ”๊ฟจ๋‹ค๋ฉด, ์œ„์ ฏ๋„ ๊ทธ ์‚ฌ์‹ค์„ ์•Œ์•„์•ผ ํ•œ๋‹ค.

1. ์•ฑ์—์„œ ์ƒˆ๋กœ์šด D-Day ์„ค์ • ์ €์žฅ
2. App Group(UserDefaults)์— DDayConfig ์ €์žฅ
3. WidgetCenter๋ฅผ ํ†ตํ•ด ์œ„์ ฏ ํƒ€์ž„๋ผ์ธ ๋ฆฌ๋กœ๋“œ ์š”์ฒญ

์•ฑ์—์„œ ์—ฐ๋™

import WidgetKit

func updateDDay(title: String, date: Date) {
    let config = DDayConfig(title: title, targetDate: date)
    SharedStore.save(config: config)

    // ์œ„์ ฏ ํƒ€์ž„๋ผ์ธ ์ƒˆ๋กœ๊ณ ์นจ ์š”์ฒญ
    WidgetCenter.shared.reloadAllTimelines()
}

์ด๊ฒŒ ์œ„์ ฏ ์ž…์žฅ์—์„œ๋Š” ๋‹ค์Œ ํƒ€์ž„๋ผ์ธ ์š”์ฒญ ์‹œ์ ์— ์ตœ์‹  ์„ค์ •์„ ์ฝ์–ด๊ฐ€๊ฒŒ ๋˜๋Š” ๊ตฌ์กฐ๋‹ค.



7๋‹จ๊ณ„ - ์‹ค์ œ ๊ธฐ๊ธฐ์—์„œ ์œ„์ ฏ ํ…Œ์ŠคํŠธ

1. ์•ฑ + ์œ„์ ฏ ํƒ€๊นƒ์ด ๋ชจ๋‘ ํฌํ•จ๋œ ์Šคํ‚ด์œผ๋กœ ๋นŒ๋“œ
2. ์‹ค๊ธฐ๊ธฐ ๋˜๋Š” ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ ํ™ˆ ํ™”๋ฉด์—์„œ:
    - ํ™ˆ ํ™”๋ฉด ๊ธธ๊ฒŒ ํ„ฐ์น˜ → '์œ„์ ฏ ์ถ”๊ฐ€'
    - DDayWidget ๊ฒ€์ƒ‰ ํ›„ ์ถ”๊ฐ€
3. ์•ฑ์—์„œ D-Day ์„ค์ • ๋ณ€๊ฒฝ → ํ™ˆ ํ™”๋ฉด์˜ ์œ„์ ฏ์ด ์ •์ƒ์ ์œผ๋กœ ๊ฐฑ์‹ ๋˜๋Š”์ง€ ํ™•์ธ

์ž์ • ๋ณ€ํ™” ํ…Œ์ŠคํŠธ๋Š” ์‹œ๊ฐ„์„ ๋ฐ”๊พธ๊ฑฐ๋‚˜, ์ž„์‹œ๋กœ timeline policy๋ฅผ ์งง๊ฒŒ ์„ค์ •ํ•ด์„œ ๊ฒ€์ฆํ•˜๊ณ ,
์‹ค์ œ ๋ฐฐํฌ ์ „์—๋Š” ์ž์ • ๊ธฐ์ค€์œผ๋กœ ๋˜๋Œ๋ฆฌ๋Š” ์‹์œผ๋กœ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.




 



์ด๋ฒˆ ๊ธ€์—์„œ๋Š” ์•„๋ž˜ ํ๋ฆ„์œผ๋กœ iOS ์œ„์ ฏ ๊ฐœ๋ฐœ์„ ์ •๋ฆฌํ•ด๋ดค๋‹ค.

- ์™œ ์œ„์ ฏ์€ ์ด์ œ SwiftUI๋กœ๋งŒ ๊ฐœ๋ฐœ์ด ๋˜๋Š”๊ฐ€?
    - WidgetKit ๋„์ž… ๋ฐฐ๊ฒฝ๊ณผ Today Extension๊ณผ์˜ ์ฐจ์ด
- D-Day ์œ„์ ฏ์„ ์˜ˆ์ œ๋กœ ์œ„์ ฏ ๊ตฌ์กฐ ์„ค๊ณ„
    - App Group ๊ธฐ๋ฐ˜ ๋ฐ์ดํ„ฐ ๊ณต์œ 
    - ๋‚ ์งœ ๊ณ„์‚ฐ ๋„๋ฉ”์ธ ๋กœ์ง
    - TimelineProvider / Entry / SwiftUI View
- ์•ฑ๊ณผ ์œ„์ ฏ ์—ฐ๋™๊นŒ์ง€ ํฌํ•จํ•œ end-to-end ๊ฐœ๋ฐœ ์ˆœ์„œ

๋‹ค์Œ์—๋Š”
- ์—ฌ๋Ÿฌ ๊ฐœ์˜ D-Day๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋‹ค์ค‘ ์œ„์ ฏ
- Intent ๊ธฐ๋ฐ˜์œผ๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ์œ„์ ฏ๋งˆ๋‹ค ๋‹ค๋ฅธ ๊ธฐ๋…์ผ์„ ์ง€์ •ํ•˜๋Š” ๊ตฌ์กฐ
- ์ž ๊ธˆ ํ™”๋ฉด ์œ„์ ฏ / StandBy ๋ชจ๋“œ ๋Œ€์‘
- HealthKit ๊ฑธ์Œ ์ˆ˜ ์œ„์ ฏ

์™€ ๊ฐ™์€ ์ฃผ์ œ๋กœ ์ •๋ฆฌํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

728x90