前提
現在開発中のiOSアプリは幅広いユーザー層を考慮し、現時点ではiOS12もサポート対象となっています。その環境ではSwiftUIを使用することはできませんが、UIKitのビューの開発効率を上げるために、次のコードのようにUIViewRepresentable
を用いてXcode Previewsでビューをプレビューできるようにしています。
import UIKit
class SampleLabel: UILabel {
override init(frame: CGRect) {
super.init(frame: frame)
construct()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
construct()
}
private func construct() {
text = "Sample"
}
}
#if canImport(SwiftUI) && DEBUG
import SwiftUI
@available (iOS 13.0, *)
struct SampleLabelWrapper: UIViewRepresentable {
func makeUIView(context: Context) -> some UIView {
SampleLabelWrapper()
}
func updateUIView(_ uiView: UIViewType, context: Context) {}
}
@available (iOS 13.0, *)
struct SampleLabel_Previews: PreviewProvider {
static var previews: some View {
SampleLabelWrapper()
}
}
#endif
Xcode14のXcode Previewsで発生したエラー
今回、開発環境をXcode14にアップデートしたところ、このようなコードでのXcode Previewsで次のエラーが発生するようになり、プレビューができなくなってしまいました。
error: '__designTimeString(_:fallback:)' is only available in iOS 13.0 or newer
text = __designTimeString("#4947.[1].[2].[0].[0]", fallback: "Sample")
このエラーが起きている箇所は、text = "Sample"
の部分です。StringやIntといったプリミティブな型の定数が書かれているところ全般に__designTime~
という関数が差し込まれているようです。これがプレビューのために非公開APIで、iOS13以降でしか使用できないためにエラーとなっていそうです。
しかし、なぜUIKitのビューにプレビューのためのコードが差し込まれてしまうのでしょうか...
(参考) XcodePreviews の仕組み
回避策
この問題が回避できないと開発効率に大きく影響するため、回避策を探りました。結論としては以下の2つです。
- (当たり前ですが)開発中だけMinimum Deployment TargetをiOS13以降にする
- UIKitのビューのコードと、PreviewProviderのコードを別のファイルに記述する
(検証時のコードはこちら 👉 https://github.com/temoki/Xcode14PreviewsBug)
後者について、次のように別のファイルに記述することでこのエラーは発生しなくなります。
import UIKit
class SampleLabel: UILabel {
override init(frame: CGRect) {
super.init(frame: frame)
construct()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
construct()
}
private func construct() {
text = "Sample"
}
}
#if canImport(SwiftUI) && DEBUG
import SwiftUI
@available (iOS 13.0, *)
struct SampleLabelWrapper: UIViewRepresentable {
func makeUIView(context: Context) -> some UIView {
SampleLabelWrapper()
}
func updateUIView(_ uiView: UIViewType, context: Context) {}
}
@available (iOS 13.0, *)
struct SampleLabel_Previews: PreviewProvider {
static var previews: some View {
SampleLabelWrapper()
}
}
#endif
おそらくですが__designTime~
を差し込む条件が、PreviewProvider
の記述のあるコード全体になっているのではにでしょうか。そのためUIKitのViewのコードにも差し込まれているのではないかと思います。
本件はAppleにフィードバック済みですので、Xcode14.1あたりで修正されてくれることを祈ります。