포스트

Dex (9)

WidgetKit으로 홈 화면에 포켓몬 위젯 표시하기

Widget 만들기

앱을 실행하지 않아도 정보를 확인할 수 있게 하려면 Widget이 필요하다. 이번 글에서는 WidgetKit을 사용해 포켓몬 정보를 아래 사진처럼 홈 화면에서 바로 볼 수 있도록 위젯을 만들어본다.

Image

이것도 역시 전에 공부를 했던 적이 있기에 WidgetKit 지난글을 참고하면 좋을듯하다.

위젯 추가는 이렇게

Image

Image

Image

이때 check는 모두 하지 않는걸로…

Image

늘 그렇듯 Activate

이전에도 이렇게 캡쳐를 했었지만 리마인드할때는 역시 다시한번 캡쳐하는게 좋긴하다.

위의 과정을 거치면 우리를 맞이해주는 귀여운 위젯이 나타난다.

Image

이렇게 기본적인 위젯 생성이 끝났다.

Widget의 핵심 구조

이전에 한번 다뤄봤기에, 이번엔 간단하게 정리.

SwiftUI 기반의 Widget은 다음과 같은 핵심 구성 요소로 이루어져 있다:

구성요소설명
TimelineProvider위젯이 언제, 어떤 데이터를 표시할지 결정함. 업데이트 주기, 데이터 제공 등의 역할 수행
TimelineEntry실제 위젯에 표시할 데이터 모델. date는 필수 속성이며, 사용자 정의 속성 추가 가능
EntryView위젯의 실제 UI 구성 요소. SwiftUI View 형태로 작성
Widget위젯을 선언하고 시스템에 등록함. 어떤 provider, view, supported size를 사용할지 정의
WidgetBundle복수 개의 위젯을 한 프로젝트 내에서 묶을 때 사용
PreviewProvider위젯의 다양한 상태를 Xcode에서 시각적으로 테스트할 수 있도록 지원

TimelineProvider의 주요 함수

함수 이름설명
placeholder(in:)위젯이 로드되기 전 잠깐 표시할 자리 표시용 콘텐츠를 반환. 위젯 갤러리 등에서 사용됨
getSnapshot(in:completion:)위젯의 미리보기에서 사용될 콘텐츠를 반환. 빠른 응답이 중요하며 placeholder와 유사한 데이터 사용
getTimeline(in:completion:)위젯이 실제 표시할 데이터를 시간 순서대로 반환. 여러 개의 TimelineEntry를 담아 업데이트 시점을 지정

Widget 다듬기

데이터 모델링

1
2
3
4
5
6
struct SimpleEntry: TimelineEntry {
    let date: Date
    let name: String
    let types: [String]
    let sprite: Image
}

이렇게 우리가 필요한 데이터들을 모델링 해준다.


코드 수정하기

이제 Provider 부분을 수정해야한다.

모델링을 손봤기 때문에 에러가 발생.

1
2
3
func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: .now, name: "bulbasaur", types: ["grass", "poison"], sprite: Image(.bulbasaur))
    }

이런식으로 나머지들도 고쳐주면 된다. (그래봤자 Provider 함수부분만 고쳐주면 끝)


Target 추가하기 (Asset, CoreData, PokemonExt, Persistence)

이렇게 코드를 작성하고 보니 이미지가 없다고 에러가 발생

Image

이렇게 Widget을 추가하게되면 Asset도 생기는데, 아무것도 없기 때문.

그러면 또 이미지를 드래그해서 추가해야하나? 그건 아니다.

Target을 추가해주면된다. 현재 이미지 에셋이 Dex 앱 하나에만 적용이 되어있는데, 위젯에서도 사용하기위해 Target을 추가 해주기만 하면 된다.

Image

현재는 Dex 앱 하나에만 타겟이 설정된 상태. +를 눌러 추가해주자.

ImageImage

이렇게 Target에 Wigdet도 추가가 된걸 알 수 있다.

나머지들도 동일한 방법으로 추가해준다. (사진은 생략)

이렇게 하면 이제 Widget에서도 사용이 가능해진다.

Placeholder 만들기

일종의 MockData라고 보면된다.

Preview에서 사용할 데이터이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct SimpleEntry: TimelineEntry {
    let date: Date
    let name: String
    let types: [String]
    let sprite: Image
    
    static var placeholder: SimpleEntry {
        SimpleEntry(
            date: .now,
            name: "bulbasaur",
            types: ["grass", "poison"],
            sprite: Image(.bulbasaur)
        )
    }
    
    static var placeholder2: SimpleEntry {
        SimpleEntry(
            date: .now,
            name: "mew",
            types: ["psychic"],
            sprite: Image(.mew)
        )
    }
}

이렇게 2개를 만들어 주었다.

Placeholder 적용하기

위에서 Provider 쪽에 코드를 작성했던것을 placeholder를 사용하여 바꿔주도록 하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func placeholder(in context: Context) -> SimpleEntry {
    SimpleEntry.placeholder // chagned
}
func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
    let entry = SimpleEntry.placeholder // chagned
    completion(entry)
}

func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
    // 생략
    let currentDate = Date()
    for hourOffset in 0 ..< 5 {
        //생략
        let entry = SimpleEntry.placeholder // changed
        entries.append(entry)
    }
    //생략
}

이런식으로 바꿔준다.

getSnapshot, getTimeline 부분도 동일하게 수정하자.

이후 Preview에도 적용을 한다.

1
2
3
4
5
6
#Preview(as: .systemSmall) {
    DexWidget()
} timeline: {
    SimpleEntry.placeholder
    SimpleEntry.placeholder2
}

Preview에서 2개를 만들었기에 이렇게 2개를 선택할수 있다.

Image

ImageImage

다음 글에서는 이 데이터를 바탕으로 위젯 디자인을 해볼 예정이다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.