はじめに
App Store Connectへのアップロードが4/24からXcode16が必須になります。
https://developer.apple.com/jp/news/upcoming-requirements/?id=02212025a
弊社が提供しているwithアプリでは、Xcode16への移行が完了し、一部トラブルはあったものの現在は落ち着いた状況となりましたので、
これからXcode16への移行をされる方や、Xcode16移行は完了したが対応内容を見直したい方向けに社内で対応した内容のまとめを共有させていただきます。
対応方針
大まかな対応方針としては、Xcode16アップデートで新たに発生したwarningについては後回しにしないように定めました。
基本的にXcode16でビルドが通るようにしておけば開発面では困ることはないものの、やはり期限を決めずに置いてしまうと消化するまで時間がかかってしまったり、CIなどで検知している場合はノイズになりやすいためです。
withではすでに対応が終えたからこそ言えるのですが、今回のXcodeアップデートに伴って発生するwarningはそれほど大変でもないと思いますので、もしこれからXcode16へ移行される方は同じようにwarning対応も一緒にやることをお勧めします。
具体的な方針
対応方針のところで書いたように、warningはなるべく一緒に対応することを念頭にいれましたが、
Xcode16にアップデートした直後は既存の型に後からプロトコル準拠を追加する箇所で多く発生するでしょう。
その際に、警告にフォーカスをあてると、@retroactive
を付与するサジェストが出るかと思います。
@retroactive
というのは、
https://github.com/swiftlang/swift-evolution/blob/main/proposals/0364-retroactive-conformance-warning.md
で提案された機能で、既存の型に後からプロトコル準拠を追加することを宣言するための識別子です。
@retroactive
を付与すると、warningは抑制されるようになります。
しかし、@retroactive
はただの識別子にすぎず、モジュール間で思わぬ衝突が発生するリスクは伴ったままです。
そのため根本的対応の先送りにしかならないため、極力@retroactive
を利用しない方針としました。
弊社が対応を始めたのは、Xcode16必須化のアナウンスがされる前で、スケジュールに余裕があったためこの方針を取りました。
しかしこれから対応される方は4/24までに対応を終えないとアップデートができないため、@retroactive
の利用も検討すると良いかもしれません。
以下、withで発生したwarning箇所とその対応になります。
対応内容
引数に@MainActor
が追加された箇所の対応
WKNavigationDelegate.webView(_:decidePolicyFor:decisionHandler:)
や
WKUIDelegate.webView(_:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:)
など、
クロージャー引数に@MainActor
ラベルが付与されました。
Xcode上のwarningメッセージからだと原因はわかりにくいですが、Appleのドキュメントの定義が変わっているため、
きちんとメソッドを揃えるよう対応しました。
https://developer.apple.com/documentation/webkit/wknavigationdelegate/webview(_:decidepolicyfor:decisionhandler:)-2ni62
func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {}
// completionHandlerに@MainActorが追加する
func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping @MainActor () -> Void) {}
JSONDecoderを継承したクラスに@unchecked Sendable
を適合する
withではAPI通信にJSONを使っているのですが、レスポンスのパース処理に失敗した場合のデバッグをしやすくするよう、JSONDecoderを継承した独自のクラスを使っています。
しかしJSONDecoder側は@unchecked Sendable
のprotocolに準拠していたものの、JSONDecoderを継承した独自クラスのほうでは@unchecked Sendable
の準拠をしていませんでした。
てっきりJSONDecoderを継承したクラスであれば、JSONDecoderの準拠しているSendable
にも自動で継承されるかと思っていたのですが、どうやらそうでないらしいです。
モジュール外の定義の型に対してExtensionで別のプロトコルへの準拠をさせないようにする
Extensionを使って型を別のprotocolへ適合する処理を入れたい場合があるかと思います。
そのような場合にXcode16からは、型定義とextensionする箇所でモジュールが異なる場合、
warningが発生するようになりました。
なぜwarningがでるようになったかについては
https://maiyama4.hatenablog.com/entry/2024/07/01/154134
の記事が大変参考になりました。
withではエラーを複数待ち受ける処理があり、その際はエラーオブジェクトを配列にいれ、配列の中身がエラー型であれば配列自体をエラーに適合する、という処理が書かれていました。
struct MyError: Error {
...
}
extension Array: Error where Iterator.Element == MyError {}
かなり乱暴なやり方ではありますが、一応ちゃんと動作はします。
しかし、こうしたArrayをErrorに独自で適合することが今回warning発生する原因となりました。
この例に関しては、Arrayを拡張するのではなく、MyError配列を内包するエラー適合したstructを作る形で修正しました。
struct MyErrors: Error {
let errors: [MyError]
}
struct MyError: Error {
...
}
そのほか細かい変更箇所
- Sign In with Appleのエラー型の追加
-
.matchedExcludedCredential
,.credentialImport
,.credentialExport
の3つが新たにエラー型に追加されました
-
-
DispatchQueue.main.async
でself
を参照している箇所への対応- Sendable周りの影響により
DispatchQueue.main.async
内でself
を使用する箇所にwarningがでるようになりました
- Sendable周りの影響により
重大な問題
ここまでの対応で、Xcodeアップデートに伴って発生したwarningはなくなりましたが、重大な問題が2点ありました。
UITableView、UICollectionViewでクラッシュする
UITableView、UICollectionViewでのセルの生成処理でクラッシュが発生するようになりました。
クラッシュの発生条件はcellForRowAt
、cellForItemAt
内で2回以上セルをdequeue
した際に発生したようです。
実際に使われないセルまでdequeue
するのはパフォーマンス面でも無駄ですし、普通に考えれば避けるべき実装ではありますが、
withではこうしたよくないコードがありました。
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
// 2回目のdequeueReusableCellが呼ばれた際にクラッシュする
let cell2 = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath)
return cell
}
このクラッシュに関しては、UITableView
、UICollectionView
といった利用頻度が高いViewではありながら、
セルの出しわけ条件が厳しければ厳しいほど顕在化しにくい特性があります。
そのため、現在のソースコードの状態だけでなく、今後追加されるソースコードにも意識が向くよう気をつけていきたい問題です。
UIApplication.shared.open(url)が機能しなくなる
URLを開く処理には、UIApplication.openURL(_:)とUIApplication.open(_:options:completionHandler:)
の2種類があり、前者は現在deprecatedになっています。
しかし、withでは前者のUIApplication.openURL(_:)
を使用している箇所が残っていました。
Xcode16以降では、iOS18以上でUIApplication.openURL(_:)
を呼び出しても反応しない動作になりました。
https://developer.apple.com/documentation/uikit/uiapplication/openurl(_:) にも
Calling this method has no effect. Use the open(_:options:completionHandler:) method instead.
とかかれている通り、メソッドを呼び出しても反応しなくなるのですが、iOS18未満は問題なく動作します。
そのため、動作確認端末の環境によっては問題なくURLを開けてしまうため注意が必要です。
最後に
今回、弊社のwithアプリでXcode16にアップデートした際の対応内容を共有させていただきました。
これからXcode16へ移行をする方はもちろん、Xcode16移行は終わったがトラブルが発生した場合にも参考にしていただければと思います。
PR
withではエンジニアの採用を行っています。
興味がありましたら、ぜひお問い合わせください!
https://enito.co.jp/careers/