티스토리 뷰
내용
- 최소버전과 관련하여 위젯이 등장하지 않던 문제 원인 파악 및 해결
- iOS deployment target 15.0 으로 설정된 상태에서 잠금 화면 위젯(16.0+) 대응해보기
🚨 문제 상황 인식
메인 앱 타겟의 최소 버전은 15.0 이었습니다. 16.0 버전의 아이폰에서 앱은 설치할 수 있지만, 위젯을 설치할 수 없었습니다.
그 이유는 widget extension 의 최소 버전이 16.2 로 설정되어 있었기 때문이었습니다.
왜 16.2 였었나?
PreviewProvider 프로토콜을 채택하여 preview 하는 struct 에서 Live Activity 의 미리보기를 만들 수 있는 메서드가 있는데 해당 메서드의 지원 버전이 16.2+ 이기 때문이었습니다.
previewContext(_:isStale:viewKind:) | Apple Developer Documentation
widget extension 을 만들 때 Live Activity 를 포함하는 옵션을 선택하게 되었고 자연스레 widget extension target 의 deployment target 이 16.2 로 설정되었습니다.
추가적으로, 아래 링크에서는 현재 iOS 사용 현황에 대해서 살펴볼 수 있습니다. 현재 iOS 15 미만의 기기들은 전체에서 8% 정도 됩니다.(iOS 16 은 72%, iOS 15 는 20%)
App Store - 지원 - Apple Developer
✅ 문제 해결
현재 서비스는 메인 앱이 최소 버전 15.0, 홈 위젯은 14.0+, 잠금 화면 위젯은 16.0+, 추가적으로 프로젝트 내에는 Live Acitivty 지원하는 위젯(16.1+)이 있지만 아직 서비스를 제공하지 않습니다.
그렇기 때문에 운영체제의 버전별로 분기처리를 해주지 않으면 다음과 같이 에러가 등장합니다.
대표적으로 잠금 화면 위젯을 지원하기 위한 widget family 의 case 로 accessoryCircular 가 있습니다. 해당 case 를 사용하기 위해서 16.0 이상에서만 가능합니다.
- 다음과 같이 코드에 분기처리를 해주겠습니다.
struct QRCodeWidget: Widget {
// ✅ 지원하는 WidgetFamily 대응.
private let supportedFamilies: [WidgetFamily] = {
if #available(iOSApplicationExtension 16.0, *) {
return [.systemSmall, .accessoryCircular]
} else {
return [.systemSmall]
}
}()
let kind: String = "QRCodeWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: QRCodeProvider()) { entry in
QRCodeEntryView(entry: entry)
}
.configurationDisplayName("QR Code 위젯")
.description("QR Code 를 인식할 수 있도록 카메라로 빠르게 접근합니다.")
// ✅ supportedFamilies([.systemSall, .accessoryCircular]) 에서 분기처리하기 위해서 수정.
.supportedFamilies(supportedFamilies)
}
}
// 출처: https://stackoverflow.com/questions/72819402/lockscreen-swift-widget-only-available-to-ios-16-users
struct QRCodeWidget_Previews: PreviewProvider {
static var previews: some View {
QRCodeEntryView(entry: QRCodeEntry(date: Date()))
.previewContext(WidgetPreviewContext(family: .systemSmall))
// ✅ 지원하는 WidgetFamily 대응.
if #available(iOSApplicationExtension 16.0, *) {
QRCodeEntryView(entry: QRCodeEntry(date: Date()))
.previewContext(WidgetPreviewContext(family: .accessoryCircular))
}
}
}
iOS 15.0 시뮬레이터에서 정상 실행되었습니다.
🚨 문제 상황 인식 - WidgetBundel 에서 분기처리
iOS 16.0 시뮬레이터에서는 실행 시 다음과 같이 Fatal error: Unavailable
가 등장했습니다.
다음과 같이 분기처리를 하는데도 다음과 같이 에러가 등장했습니다.
Closure containing control flow statement cannot be used with result builder 'WidgetBundleBuilder’
WidgetBundleBuilder 인 body 에서 분기 처리는 할 수 없다는 에러였습니다.
✅ 문제 해결
WidgetBundle 프로토콜을 살펴보면 아래와 같이 구성되어 있습니다.
public protocol WidgetBundle {
associatedtype Body : Widget
/// Creates a widget bundle using the bundle's body as its content.
init()
/// Declares the group of widgets that an app supports.
///
/// The order that the widgets appear in this property determines the order
/// they are shown to the user when adding a widget. The following example
/// shows how to use a widget bundle builder to define a body showing
/// a game status widget first and a character detail widget second:
///
/// @main
/// struct GameWidgets: WidgetBundle {
/// var body: some Widget {
/// GameStatusWidget()
/// CharacterDetailWidget()
/// }
/// }
///
@WidgetBundleBuilder var body: Self.Body { get }
}
Body 는 Widget 자료형을 가지기 때문에 분기 처리 과정의 결과로 Widget 을 반환해주어야 합니다.
그래서 직접 WidgetBundleBuilder.buildBock()
를 사용해서 하나의 단일 Widget 을 반환하였습니다.
(참고로 10개까지 한번에 Widget 으로 반환해줄 수 있습니다.)
import WidgetKit
import SwiftUI
@main
struct WidgetsBundle: WidgetBundle {
var body: some Widget {
// ✅ OpenAppLockScreenWidget 이 잠금화면 위젯이기 때문에 다음과 같이 분기처리.
if #available(iOSApplicationExtension 16.0, *) {
return WidgetBundleBuilder.buildBlock(MyCardWidget(), OpenAppLockScreenWidget(), QRCodeWidget())
} else {
return WidgetBundleBuilder.buildBlock(MyCardWidget(), QRCodeWidget())
}
}
}
// 출처: [https://developer.apple.com/forums/thread/711441](https://developer.apple.com/forums/thread/711441)
결과
iOS 15.0, 16.0 에서 정상작동을 확인할 수 있습니다.
관련 이슈들 출처:
What is the proper way of adding A… | Apple Developer Forums
'iOS' 카테고리의 다른 글
iOS) Google Analytics 적용 (2) | 2023.05.18 |
---|---|
iOS) WidgetKit 에게 timeline 변경 알리기 (0) | 2023.05.12 |
iOS) Widget view 의 이미지 맥시멈 사이즈 (0) | 2023.05.09 |
iOS) FlexLayout 과 PinLayout 을 사용하여 UI 구현 (2) | 2023.03.25 |
iOS) SnapKit 활용(Y축 중심을 기준 / 너비 대비 높이) (0) | 2023.03.08 |
- Widget
- Notification
- Protocol
- OpenSourceLibrary
- MVVM
- YPImagePicker
- watchOS
- CloneCoding
- SwiftUI
- Objective-C
- WidgetKit
- github
- Swift
- containerBackground
- RxCocoa
- 2022 KAKAO TECH INTERNSHIP
- WWDC22
- MOYA
- configurable widget
- UserDefaults
- Firebase
- urlsession
- projectsetting
- APNS
- async/await
- 서버통신
- rxswift
- Algorithm
- IOS
- WWDC
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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