[ Widget ] 내 맘대로 뚝딱, 폰 화면에서 앱 위젯 구성하기
WidgetKit의 존재 목적은 무엇인가?
WidgetKit은 단순히 Widget 기능만 관리하는 것이 아니라, watch 앱 화면 디스플레이를 구성하는 Complication, 폰 잠금화면에서 해제했을 때 나오는 제어 센터, 등을 관리하는 apple controls를 활용하려고 할 때 WidgetKit 라이브러리를 사용해야한다.
앱의 콘텐츠를 노출할 수 있는 범위
- 홈, 잠금화면: Timeline 기반
- Live Activity: Push / ActivityKit
- 애플 와치 컴플리케이션 : Timeline + ClockKit
그치만 여기서 다뤄 볼 주제는 홈 화면에서 사용자의 편의를 위해 위젯을 추가해 사용해 보는 부분에 대해 언급해보려고 한다.
폰 화면의 바탕 배경을 지그시 누르면, 편집 화면이 뜨면서 왼쪽 상단에 위젯을 추가할 수 있는 메뉴가 뜬다. 앱을 만들 때 위젯 구성을 추가한 앱이라면 위젯 검색을 통해 위젯을 추가할 수 있다.
위젯 사용방식
systemSmall, systemMedium 이라는 위젯 사이즈를 구성하면 사이즈별로 위젯의 구성방식을 달리 구성할 수 있다.
기본 small 에서는 사용자가 버튼을 누를 때 1씩 증가하는 카운트 기능이 원형 링으로 보이는 구조다. 사실 이 기능은 Code Chris (2023년)에서 본 영상이었는데, 뭔가 이 기능으로는 기본적이고 시시하다는 생각을 했다. 그치만, 위젯을 구성하기에 뼈대를 잡고 갈 때 좋은 영상인 것 같다.
초기 내가 추가한 기능 - 위젯 구성 옵션
1. 사용자가 원하는 목표치만큼 숫자를 구성할 수 있다.
2. 원하는 색의 원형 링을 선택할 수 있다.
업데이트
1. 맘에 들지 않았던 부분: @AppStorage 로 저장했기에 나중에 다시 위젯을 봐도 지속적으로 카운트 된 숫자가 유지되고 있었다.
➡ 사용자가 목표치를 달성했다면 더이상 숫자를 카운트 할 수 없다. 다시 0으로 리셋이 가능하다.
2. 목표가 달성되면 목표 완성이라는 글씨가 뜨고 더 이상 카운트 버튼을 누를 수 없게 했다.
그럼 이러한 기능을 만들기 위해 건드려야 했던 부분들은 어떤 것들이 있을까? 활성화 할 거냐는 문구가 뜨고 Activate 버튼을 누르면 된다. Scheme은 앱을 빌드하고 디버깅하는 방법을 정의하는 설정 모음이다.
Xcode에서 앱화면을 만들고 구성하는 프로젝트와 별도로 Widget을 구성하는 것은 Widget extension 기능을 추가해 줘야 한다.
Xcode 상단 윈도우의 File > Target > Widget Extension 이라고 검색해준다. 파일명도 커스텀하게 생성해주면
그 전에 더 설정해줘야 할 것이 있는데 기본적으로 생성한 프로젝트와 widget extension 의 타겟에 + capability 추가로 app groups 라는 것을 동일하게 추가해 줘야한다.
App Groups란?
여러 앱이나 앱 확장 프로그램 간에 컨테이너를 공유할 수 있게 해주는 iOS의 핵심 기능
- 앱과 위젯 간에 데이터 공유
- 공유 UserDefaults 사용
- 공유 파일 시스템 액세스
- 앱 확장 프로그램과의 통신
그럼 위젯을 구성하는 기본 파일들이 뜨는데,
- Bundle: 여러 위젯을 만든다면 하나의 확장 프로그램으로 묶는 역할을 한다. 이는 Widget Extension 추가시 자동으로 생성되는 파일이다.
@main
struct widgetextensionBundle: WidgetBundle {
var body: some Widget {
widgetextension()
}
}
- App Intents
- 위젯의 기본 구조를 정의하는 Widget 구조체(struct)
- 위젯 기본 정보 정의
Widget 프로토콜
kind: widget identifier(고유 식별자)
AppIntentConfiguration: 위젯 설정을 정의하는 구조체
supportedFamilies: 위젯이 지원하는 크기(small, medium, large 등)
- 위젯 기본 정보 정의
struct widgetextension: Widget {
let kind: String = "widgetextension"
var body: some WidgetConfiguration {
AppIntentConfiguration(
kind: kind,
intent: StreakConfigIntent.self,
provider: Provider()) { entry in
widgetextensionEntryView(entry: entry)
}
.configurationDisplayName("Streak Widget")
.description("Track the number increasing in real-time")
.supportedFamilies([.systemSmall, .systemMedium])
}
}
- (AppIntent)TimelineProvider: 위젯에 데이터를 제공하고 관리하는 역할을 함.
DataService