사내 React Native(이하 RN)으로 개발된 앱에서 위젯 개발 feature가 생겼다. 나는 내심 iOS Native 개발을 해보고 싶던 터라 1순위로 해당 feature를 개발을 희망했고, 다른 개발자분들도 1순위로 원해서 사다리 타기를 통해 해당 feature를 내가 개발할 수 있게 되었다.
원했던 것이지만 RN의 Native Module도 한 번 만들어 본 적 없던 나에게 큰 챌린지가 될 것 같았고 꼭 해내고 싶었다.
해당 포스트는 사내 앱 위젯 개발 경험을 예시로, 간단한 React Native에서의 iOS위젯 개발을 소개하는 글입니다.
1. 설계
2. 구현
으로 나누어 포스팅합니다.
Native Module
을 Objective-C로 개발하여 RCTBridgeModule을 통해 React Native에서 호출
할 수 있다. (iOS Native Modules RN 공식문서)UserDefaults
라는 저장소가 있는데, 이것을 통해 앱 어디에서나 데이터 CRUD가 가능하다.UserDefaults를 CRUD하는 Native Module을 작성하여 React Native에서 iOS의 Native단에 존재하는 UserDefaults 저장소에 CRUD
한다.UserDefaults
는 어디서나 쓸 수 있기 때문에 해당 저장소의 값을 위젯에서 사용하여 data를 그려주면 될 것이다.어떻게 위젯의 값을 refresh 할 것인가?
위 질문에 나에게 던져졌다. 나에게 떠오른 3가지의 방법
TimelineProvider
를 통해 언제 위젯을 업데이트하고 싶은지 제공할 수 있다.WidgetKit
에서 TimelineProvider
의 method를 호출해 새로운 Timeline을 요구하고 이 때 method에서 받아오는 데이터로 위젯을 render한다WidgetKit
은 다음과 같은 생애주기를 가진다getSnapshot request
를 통해 snapshot 데이터를 보여준다timeline request
를 통해 실제 데이터를 보여준다Timeline
을 만들 때 각각 TimelineReloadPolicy
를 지정해주어야한다.(Apple 공식문서)자세한 설명은 Apple 공식문서에 그림과 함께 나와 있어요 !
.atEnd
: 모든 timeline의 요청이 끝나고 요청 (default)
.after(Date)
: 첫 after policy timeline 요청으로부터 next timeline 요청까지의 date를 정할 수 있음, 이후의 timeline은 버려짐.
.never
: 요청 안함 (static한 위젯에 필요할 것 같음)
우리는 RN에서 상태값이 변할 때마다 위젯의 새로운 Timeline을 만들고 싶어! 시간을 예측할 수 없어!
그래서 해당 방법은 탈락 ❌
위젯에서 Observable을 구독해도 의미가 없다라는 글을 읽었다 😳
그리고 굳이 2개의 객체를 같은 동작을 위해 만들면 유지보수 측면에서도 나중에 다른 개발자가 Observable은 왜 필요하지?라고 생각할 것 같아서 탈락 ❌
Apple 공식문서 발췌
Changes in your app’s state may affect a widget’s timeline. When this happens, you can tell WidgetKit to reload the timeline for either a specific kind of widget or all widgets. For example, your app might register for push notifications based on the widgets the user has configured. When your app receives a push notification that changes the state for one or more of your widgets, requesting a reload of their timelines updates their display.
If you only need to reload a certain kind of widget, you can request a reload for only that kind. For example, in response to a push notification about a change in a game’s status, you could request a reload for only the game status widgets:
WidgetCenter.shared.reloadTimelines(ofKind: "com.mygame.gamestatus")
To request a reload for all of your widgets:
WidgetCenter.shared.reloadAllTimelines()
앱의 상태가 변경되면 위젯의 Timeline에 영향을 줄 수 있습니다. 이 경우 WidgetKit에 특정 종류의 위젯 또는 모든 위젯에 대한 Timeline을 다시 로드하도록 지시할 수 있습니다. 예를 들어 사용자가 구성한 위젯에 따라 앱이 푸시 알림을 등록할 수 있습니다. 앱이 하나 이상의 위젯 상태를 변경하는 푸시 알림을 수신하면 위젯 timeline을 다시 로드하도록 요청하면 위젯의 값이 업데이트됩니다.
특정 종류의 위젯만 다시 로드해야 하는 경우 해당 종류에 대해서만 다시 로드를 요청할 수 있습니다. 예를 들어 게임 상태 변경에 대한 푸시 알림에 응답하여 게임 상태 위젯에 대해서만 다시 로드를 요청할 수 있습니다:
WidgetCenter.shared.reloadTimelines(ofKind: "com.mygame.gamestatus")
모든 위젯에 대해 다시 로드를 요청하는 방법:
WidgetCenter.shared.reloadAllTimelines()
우선 우리 앱의 위젯은 하나 뿐이라 reloadAllTimelines()
를 RN Native Module에서 UserDefaults를 Write 할 때 함께 호출하여 값이 변하면 새로운 Timeline을 가져올 수 있도록 하였다!