티스토리 뷰
아래 개발자 문서를 참고해서 timeline 이 변경되었을 때 WidgetKit 에 알리는 방법에 대해 알아보겠습니다.
Keeping a widget up to date | Apple Developer Documentation
그리고 다음의 목표를 구현해보겠습니다.
- 회원탈퇴 or 로그아웃할 때는 위젯을 엠티뷰로 업데이트하고,
- 위젯이 표현하는 명함이 삭제될 때는 위젯을 대표명함(첫 번째 명함)으로 업데이트해보겠습니다.
Inform WidgetKit when a timeline changes
무언가 widget 의 현재 timeline 에 영향을 미칠 때, 앱이 WidgetKit 에게 새로운 timeline 을 요청할 수 있습니다.
예를 들어, 게임 위젯 예제에서 팀원이 캐릭터에게 치유 물약을 주었다는 푸시 알림을 앱이 수신하면 앱은 WidgetKit 에 timeline 을 다시 로드하고, widget 의 컨텐츠를 업데이트할 수 있습니다.
또한, 위젯에서 사용자가 계정에 로그아웃 한 경우 모든 위젯을 다시 로드할 수 있습니다.
// 특정 유형의 widget 을 로드하기 위햇 앱은 WidgetCenter 를 다음과 같이 사용할 수 있음.
// kind 는 위젯의 WidgetConfiguration 을 생성하는데 사용한 문자열과 동일함.
WidgetCenter.shared.reloadTimelines(ofKind: "com.mygame.character-detail")
// 앱에서 WidgetBundle 을 사용하여 여러 위젯을 지원한다면, 다음과 같이 모든 위젯의 timeline 을 다시 로드할 수 있음.
WidgetCenter.shared.reloadAllTimelines()
또한, 위젯이 user-configurable properties 가 있는 경우 WidgetCenter 를 사용해서 적절한 설정이 있는지 확인하여 불필요한 reload 를 피할 수 있습니다.
예를 들어, 치료 물략을 받는 캐릭터에 대한 푸시 알림을 받으면 timeline 을 다시 로드하기 전에 위젯이 해당 캐릭터를 표시하는지 확인할 수 있습니다.
이후에 widget 의 컨텐츠를 업데이트할 수 있습니다.
다음 코드에서 getCurrentConfigurations(_:)
를 호출해서 user-configured widget 의 목록을 검색합니다. 그런 다음 결과인 WidgetInfo 객체를 반복하여 치유 물약을 받은 캐릭터로 구성된 intent 객체를 찾습니다. 찾게되면 앱은 해당 widget’s kind 에 대해 reloadTimelines(ofKind:)
메서드를 호출합니다.
WidgetCenter.shared.getCurrentConfigurations { result in
guard case .success(let widgets) = result else { return }
// Iterate over the WidgetInfo elements to find one that matches
// the character from the push notification.
if let widget = widgets.first(
where: { widget in
let intent = widget.configuration as? SelectCharacterIntent
return intent?.character == characterThatReceivedHealingPotion
}
) {
WidgetCenter.shared.reloadTimelines(ofKind: widget.kind)
}
}
적용해보기
- 해당 명함이 삭제되었다 or 회원탈퇴 or 로그아웃했을 때 WidgetKit 에게 알려 위젯을 업데이트해보겠습니다.
- 회원탈퇴 or 로그아웃할 때는 위젯을 엠티뷰로 업데이트하고,
- 위젯이 표현하는 명함이 삭제될 때는 위젯을 대표명함(첫 번째 명함)으로 업데이트해보겠습니다.
해당 동작이 이루어질 때마다 WidgetCenter.shared.reloadTimelines(ofKind: "MyCardWidget")
메서드를 호출하여 위젯의 timeline 을 다시 로드하면 됩니다.
예를 들어, 다음과 같이 로그아웃 시에 호출하면 됩니다.
func setLogoutClicked() {
// MyCardWidget timeline reload.
WidgetCenter.shared.reloadTimelines(ofKind: "MyCardWidget")
// 액세스 토큰 삭제.
UserDefaults.standard.removeObject(forKey: "accessToken")
}
그렇다면 timeline 은 어떻게 구성하면 될까요?
- 현재로부터 5초뒤의 timeline entry 를 한 개 만들고, TimelineReloadPolicy 를
atEnd
로 설정해서 5초 뒤에 또 다시 timeline 을 생성하도록 하겠습니다.
func getTimeline(for configuration: MyCardIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
var entries: [MyCardEntry] = []
let currentDate = Date()
let entryDate = Calendar.current.date(byAdding: .second, value: 5, to: currentDate) ?? Date()
if let card = configuration.myCard {
// ✅ 서버통신
cardListFetchWithAPI { result in
switch result {
case .success(let response):
if let data = response?.data {
// ✅ 해당 위젯의 명함(configuration.myCard.identifier)이 현재 로그인된 계정의 명함 목록(cardDataModel.cardUUID)에 있는가?
if data.contains(where: { cardDataModel in
return cardDataModel.cardUUID == card.identifier
}) {
// ✅ 명함이 있음.
let entry = MyCardEntry(date: entryDate,
widgetCard: WidgetCard(cardUUID: card.identifier ?? "",
title: card.displayString,
userName: card.userName ?? "",
// fetchImage: 이미지 다운로드 메서드.
backgroundImage: fetchImage(card.cardImage ?? "")))
entries = [entry]
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
} else {
if data.isEmpty {
// ✅ 대표 명함이 없음. -> 엠티뷰
let entry = MyCardEntry(date: entryDate, widgetCard: nil)
entries = [entry]
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
} else {
// ✅ 해당 명함이 삭제되어 없음. -> 대표 명함(첫 번째 명함)을 보여주도록 함
let entry = MyCardEntry(date: entryDate,
widgetCard: WidgetCard(cardUUID: data[0].cardUUID,
title: data[0].cardName,
userName: data[0].userName,
backgroundImage: fetchImage(data[0].cardImage)))
entries = [entry]
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
}
case .failure(let error):
print(error)
// ✅ 회원 탈퇴, 로그아웃
// Widget 에서 widgetCard 를 옵셔널 바이딩하여 nil 이면 엠티뷰가 설정되도록 분기처리하였습니다.
let entry = MyCardEntry(date: entryDate, widgetCard: nil)
entries = [entry]
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
} else {
// ✅ Configuration 로 부터 card 받지 못함.(=로그인 전 위젯 생성 시)
let entry = MyCardEntry(date: entryDate, widgetCard: nil)
entries = [entry]
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
결과
- 로그아웃 시에 엠티뷰로 위젯 업데이트(회원탈퇴도 동일한 결과)
- 명함 삭제 시에 대표 명함으로 위젯 업데이트(직장명함1 삭제. 덕질명함이 대표명함)
'iOS' 카테고리의 다른 글
iOS) Widget 의 기본값 설정하기(IntentHandler, getSnapshot) (0) | 2023.05.18 |
---|---|
iOS) Google Analytics 적용 (2) | 2023.05.18 |
iOS) Lock Screen Widget(잠금 화면 위젯)의 최소 버전 대응하기 (0) | 2023.05.10 |
iOS) Widget view 의 이미지 맥시멈 사이즈 (0) | 2023.05.09 |
iOS) FlexLayout 과 PinLayout 을 사용하여 UI 구현 (2) | 2023.03.25 |
- rxswift
- MVVM
- urlsession
- 2022 KAKAO TECH INTERNSHIP
- MOYA
- 서버통신
- WidgetKit
- YPImagePicker
- SwiftUI
- projectsetting
- Algorithm
- WWDC
- IOS
- Notification
- CloneCoding
- RxCocoa
- Swift
- async/await
- UserDefaults
- OpenSourceLibrary
- configurable widget
- containerBackground
- Objective-C
- Protocol
- Widget
- APNS
- watchOS
- github
- Firebase
- WWDC22
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- Total
- Today
- Yesterday