본문 바로가기

iOS/학습정리

SwiftUI) Widget - (4) TimelineProvider

이번 글은 iOS 14부터 추가된 Widget과 관련된 시리즈 글 중 네 번째 글입니다.
SwiftUI) Widget - (1) Meet Widget
SwiftUI) Widget - (2) How Widget Works
SwiftUI) Widget - (3) Timeline
같은 시리즈의 다른 글도 읽어보시길 추천드립니다. 글에 오류가 있거나 궁금한 점이 있으시면 언제든 댓글 남겨주세요. 감사합니다:)

TimelineProvider

위젯의 화면을 언제 갱신할지 WidgetKit에 알려주는 타입

protocol TimelineProvider

WidgetKit은 provider에게 timeline을 요청하는 경우가 많습니다. Timeline은 TimelineEntry를 따르는 객체의 배열입니다. 각각의 timelineEntry는 date와 위젯을 나타내는데 필요한 추가적인 프로퍼티를 가지고 있습니다.

struct CharacterDetailEntry: TimelineEntry {
    var date: Date
    var healthLevel: Double
}

WidgetKit은 둘 중 하나의 방식으로 timelineEntry를 요청합니다.

  1. 위젯의 현재 상태를 나타내는 즉각적인 하나의 스냅샷
  2. 현재 순간에 대한 것과 가능하다면 위젯의 상태가 변할 미래의 여러 date를 포함하는 엔트리 배열

WidgetKit은 사용자가 위젯을 추가할 때와 같이 일시적인 상황에서 위젯을 보여줄 때 스냅샷을 요청합니다. WidgetKit은 엔트리를 어떻게 사용할지에 대한 세부사항을 포함한 context를 파라미터로 제공합니다. context는 위젯 갤러리에 표시될 프리뷰인지 여부와 위젯의 크기에 대한 WidgetFamily를 포함합니다.

context.isPreview가 true이면 위젯은 위젯 갤러리에 보이고 있으므로 provider로 부터 빨리 응답을 받아야 합니다. 스냅샷을 생성할 때 필요한 정보를 얻을 수 없거나, 로드하는데 오랜 시간이 걸린다면, 샘플 데이터를 대신 사용하도록 합니다. 예를 들어, 캐릭터의 건강 수치를 정하는데에 서버로부터 데이터 패칭이 필요하다면, 위젯은 75%의 건강 수치를 보여줄 수 있습니다.

struct CharacterDetailProvider: TimelineProvider {
    func getSnapshot(in context: Context, completion: @escaping (Entry) -> Void) {
        let date = Date()
        let entry: CharacterDetailEntry

        if context.isPreview && !hasHealthLevel {
            entry = CharacterDetailEntry(date: date, healthLevel: 0.75)
        } else {
            entry = CharacterDetailEntry(date: date, healthLevel: currentHealthLevel)
        }
        completion(entry)
    }
}

WidgetKit은 사용자가 위젯을 추가한 후에 타임라인을 요청합니다. 위젯을 갱신하기 위해서는 위젯 extension을 활성화하여야 하는데, 위젯 extension이 항상 작동하는 것은 아니기 때문에 WidgetKit은 언제 위젯을 갱신할지 알아야합니다. Provider가 생성한 타임라인은 위젯을 언제 갱신할지 WidgetKit에 알려줍니다.

Determining a Refresh Policy

타임라인을 생성할 때, 프로바이더는 WidgetKit이 새로운 타임라인을 언제 요청할지 컨트롤할 refresh 정책을 구체화합니다. 참고: Timeline

예를 들어, 드래곤이 2.5시간 마다 나타나서 게임 캐릭터와 싸웁니다. 이 전투의 결과가 캐릭터의 건강 수치를 변화시키기 때문에, provider는 전투 이후에 새로운 타임라인을 요청할 수 있습니다.

// Request a timeline refresh after 2.5 hours.
let date = Calendar.current.date(byAdding: .minute, value: 150, to: Date())
let timeline = Timeline(entries: entries, policy: .after(date))
completion(timeline)

Refreshing Widgets Efficiently

각각의 위젯은 매일 제한된 갱신 요청을 받습니다. 위젯이 갱신 요청을 얼마나 받을지에 영향을 미치는 요소는 다양합니다. 해당 앱이 foreground와 background 중 어디에서 작동중인지, 위젯이 스크린에 얼마나 자주 보여지는지, 앱이 관여하는 활동이 무엇인지 등이 있습니다.

Xcode에서 위젯을 디버깅할 때 WidgetKit이 갱신 요청에 제한을 두는 것은 아닙니다.
따라서 위젯이 올바르게 작동하도록 앱과 Xcode디버거 밖에서의 위젯 행동을 테스트 해야합니다.

위젯 Refresh 최적화

• 위젯에서 필요한 데이터를 앱이 미리 준비하도록 합니다.
  해당 데이터를 저장하기 위하여 공유 그룹 컨테이너를 사용합니다.

• 공유 데이터를 최신으로 유지하기 위해 앱의 백그라운드 처리 시간을 활용합니다.
  Updating Your App with Background App Refresh

• 위젯에서 표시할 정보가 변경되었을 때에만 refreshTimeline(ofKind:)를 호출한다.

앱이 foreground에 있을 때, 활성화된 미디어 세션이 있거나, 표준 로케이션 서비스를 사용하고 있는 경우 refresh 되는 것이 위젯의 limit에 포함되지는 않습니다.

'iOS > 학습정리' 카테고리의 다른 글

SwiftUI) Widget - (3) Timeline  (0) 2021.02.28
iOS) Intents  (0) 2021.02.28
SwiftUI) Widget - (2) How Widget Works  (2) 2021.02.28
SwiftUI) Widget - (1) Meet Widget  (0) 2021.02.28
iOS) UIResponder와 Responder Chain  (0) 2021.02.10