티스토리 뷰
이번 WWDC 2023 영상 중 위젯 구조에 대해 짧지만 상세히 다룬 세션(Bring widgets to life, 8분 ~ 10분)이 있었습니다.
평소 샘플 코드 구조를 흉내내며 위젯을 구현했었던 과거가 부끄러울 정도로... 제가 이해했던 것과 달라서 공유차 영상 내용을 정리합니다.
위젯을 구현하기 위해선, 먼저 Xcode에서 Widget Extension을 정의해야 합니다.
이 Widget Extention은 시스템이 확인하여 독립적인 프로세스로 동작(run)시킵니다.

위젯은 TimelineProvider를 정의합니다.
이 TimelineProvider는 일련의 Entries를 리턴하고, 각각의 Entry는 실제 위젯의 모델이 됩니다.
위젯이 Visible해지면, 시스템이 Widget Extension Process를 실행(launch)시키고 위젯의 TimelineProvider에게 Entries를 요청합니다.

요청된 Entries는 View Builder에게 공급됩니다.
(여기서 View Builder란 Widget Configuration의 일부분, 즉 우리가 코드로 작성한 위젯의 뷰 부분을 의미합니다).

View Builder는 공급받은 Entries를 사용해 일련의 Views를 생성합니다.
생성한 Views의 표현(Representation)은 디스크에 아카이빙합니다.

특정 Entry를 표시할 시간이 되면, 시스템은 디스크에 아카이브 해뒀던 위젯의 표현을 프로세스에서 디코딩하고 렌더링합니다.
이게 의미하는 건, 우리가 작성한 위젯의 View Code가 아카이빙을 하는 동안에만 실행된다는 걸 의미합니다.
즉, View의 표현은 시스템 프로세스에 의해서 렌더링 된다는 의미입니다.

다만 데이터가 정적이지 않다면 Entries를 업데이트하고 싶을 때가 있습니다.
그럴 땐 아래와 같이, 위젯에서 보여줄 데이터를 업데이트 할 때마다reloadTimelines(ofKind:) 메소드를 호출하면 됩니다.
이 메소드를 호출하면 지금까지 설명했던 과정이 반복됩니다.
Entries를 재생성하고, 새로운 Views를 복사해서 디스크에 아카이빙합니다.

마지막으로 위젯 아키텍처에서 핵심사항 3가지가 있습니다.
먼저 첫 번째인 'Widgets are rendered in a separate process' 입니다.
이말은, 위젯이 보이는 순간에는 우리가 작성해둔 코드가 실행 중이 아니라는 의미입니다.
보이는 순간보다 더 앞선 시간에 이미 View Builder가 실행됐고, 생성된 뷰는 디스크에 아카이빙 됩니다.
사용자에게 보이는 위젯은 이미 생성된 뷰가 렌더링된 것일 뿐입니다.
두 번째는 'Changes are driven by timeline entries' 입니다.
위젯 콘텐츠에 대한 변화들은 우리가 TimelineEntry를 업데이트 함으로써 가능하다는 의미입니다.
마지막 세 번째는 'Reloads from interactions are guranteed'입니다.
이는 이번 iOS 17부터 위젯이 interactive 해졌기에 추가된 내용입니다.
iOS 17부터 위젯은 Button, Toggle을 사용하여 사용자 인터렉션을 줄 수 있습니다.
보통 위젯에 대한 업데이트는 best effort basis로 완료되는데, 인터렉션에 의한 리로드는 항상 발생이 보장됨을 의미합니다.

테스트
여기까지는 WWDC 영상을 쭉 따라간 것에 불과하고, 실제로 코드로 동작을 확인해보겠습니다.
사실 영상을 보면서 궁금했던 부분은, 우리가 보는 위젯의 뷰는 이미 디스크에 아카이빙 됐고 특정 시간대가 되면 디코딩되어서 렌더링된다는 부분입니다.
즉, 위젯을 구성하기 위해 작성된 뷰 코드는... (제 망상이었지만) 특정 Entry의 시간이 될 때마다 실행되면서 뷰를 구현하는 것이 아닌, TimelineProvider에 의해서 Entry가 공급되는 순간에 다 실행되어 뷰를 만드는 것입니다.
그리고 그 뷰들은 디스크에 아카이빙 되어 특정 시간이 되면 시스템 프로세스에 의해 디코딩 및 렌더링되는 것이죠.
테스트를 통해 풀고자 하는 의문은 이렇습니다.
"우리가 위젯을 구현하기 위해 작성한 View 코드는 특정 Entry 시간마다 실행될까? 아니면 TimelineProvider가 Entry를 공급할 때만 실행될까?"
제 망상(특정 Entry 시간 때마다 뷰 코드가 실행될 거야!)이 틀림을 증명하고, 영상 내용(위젯뷰는 디스크에 아카이빙되고 이때 뷰코드가 실행됨. 그리고 특정 Entry 시간 때마다 시스템 프로세스가 디코딩 및 렌더링)이 진짜임을 증명하는 게 목적입니다.
이를 확인하기 위해 샘플 프로젝트를 만들었습니다.
(애플이 프로젝트 생성시 제공해주는 코드를 베이스로 만들었습니다)
먼저 위젯을 구성하는 코드입니다.
공급받은 Entry의 date, count를 그려주는데, 여기에 추가로 GroupUserDefaults.shared.count를 그려줍니다.

GroupUserDefaults는 UserDefaults를 래핑한 클래스입니다.
App Group을 이용해 앱과 Widget Extension간 데이터를 공유를 도와줍니다.

이제 위젯을 그리는 데에 필요한 Entry를 공급하는 TimelineProvider입니다.
각 Entry는 1분의 간격을 가지고, (중요) 현재 앱과 공유하는 count 값으로 생성됩니다.
3개 정도 만들고, Timeline policy는 .atEnd로 설정하여 3분이 지나면 Entry를 다시 요청하도록 작성했습니다.

이제 최초로 위젯을 홈화면에 추가하면 아래와 같이 값이 0으로 보일 것입니다.

이후 앱을 열어 버튼을 눌러 UserDefaults count를 마구 증가시켜줍니다.


이제 특정 Entry로 위젯이 렌더링될 때마다 관찰하면 됩니다.
지금은 Entry 간격이 1분이니, 1분마다 업데이트되는 Time을 보며 밑의 값을 관찰하면 되겠습니다.
Time이 바뀐다는 것은 곧 다른 Entry를 의미합니다.
만약 Entry가 공급될 때마다 뷰가 렌더링된다면, Count from en... 값이 0 일때, Direct count는 11이어야 합니다.
하지만 Entry가 공급될 때 뷰가 전부 렌더링 되어 디스크에 아카이빙 되었다면, 두 값은 항상 동일해야 합니다.



결과를 보면 Count from en.. 값과 Direct count 값은 Entry가 바뀌어도 0으로 유지되다가, 같이 11로 업데이트 되었습니다.
이는 위의 영상에서 설명한 것과 동일하게, Entry가 공급될 때 뷰가 렌더링 되어 디스크에 아카이빙되었다가 특정한 때에 맞춰 디코딩 후 렌더링되는 것을 의미합니다.
즉, 반복하자면 우리가 작성한 위젯 View 코드는 디스크에 아카이빙되는 그 순간에만 실행된다는 의미입니다.
결론
iOS 위젯은 일반적으로 기대하는 것과는 조금 다르게 동작합니다.
보통은 사용자가 보는 위젯 뷰가 업데이트 될 때마다 뷰 코드가 실행되고, 새로 그려진다고 생각합니다.
하지만 실제론, 사용자가 보는 위젯 뷰는 이미 디스크에 아카이빙된 걸 그려주는 것에 불과합니다.
다시말해 위젯 View 코드는 위젯뷰가 그려질 때마다 실행되는 것이 아닌, 디스크에 아카이빙 될 때 실행됩니다.
특히 다이나믹한 데이터를 기반으로 위젯을 구현할 때는 이러한 점을 고려하여, Timeline Entry를 중심으로 위젯 리로드 로직을 생각해야합니다.
What's next?
사실 위젯 인터렉션이 궁금해서 wwdc 영상을 본 거라... 이후 위젯 인터렉션도 자세히 봐야겠습니다.
참고자료
https://developer.apple.com/videos/play/wwdc2023/10028/
https://developer.apple.com/documentation/widgetkit/keeping-a-widget-up-to-date
https://developer.apple.com/documentation/widgetkit/timelineprovider
'iOS' 카테고리의 다른 글
NSFetchedResultsControllerDelegate > controller(_:didChangeContentWith:) 사용시 놓칠 수 있는 주의점 (0) | 2022.05.03 |
---|---|
[iOS 구글 로그인] GoogleSignIn -iOS 6.2.0 소식 (0) | 2022.03.21 |
GoogleAPIClientForREST 라이브러리 추가 후 디버거가 동작하지 않는 문제 (0) | 2022.03.21 |
암기빵 개발자 홈페이지 (0) | 2022.01.12 |
[iOS 구글 로그인] GoogleSignIn-iOS 6.0.0 로그인 플로우 변경점과 해결방법 (0) | 2021.12.05 |
- Total
- Today
- Yesterday
- docker image
- ES6
- 왕국타이머
- 소프트웨어마에스트로11기
- tensorflow
- docker
- docker-compose
- 평면모니터
- React Native
- 간단정리
- 앱출시
- 사이드 프로젝트
- 코코아터치
- 개인정보처리방침
- 게이밍모니터
- 출석하냥
- 암기빵
- 도커
- Swift
- dockerfile
- 제발태그그만
- 데일리
- docker tag
- 카카오인턴십
- 도커 이미지
- 144hz모니터
- iOS #코코아터치
- QHD모니터
- ios
- 한성모니터
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |