3年前に作ったアプリをUIKitからSwiftUIに移行した話(Swift)

botman_blue iOS

Xcode-14.3 iOS-16.0

はじめに

ある日 Apple から「アップデートしなさすぎだから今月中にアップデートしなかったらアプリ消すわ」という連絡が来ました。すっかり忘れていて無事消されちゃいました。

とくに新機能も思いつかないし、ただバージョン上げるのもなと思い UIKit から SwiftUI へ移行することにしました。

そのときつまずいたポイントについてです。

開発環境

下記に修正しました。

  • iOS 14 -> iOS 16(最小ターゲット)
  • Xcode 11.3 -> Xcode 14.3

対象のアプリ

対象のアプリはこちら(Mac Catalyst で Mac 版も作成しました)。

入力画面 履歴画面
1 2

主要な機能は下記です。

  • 上に入力した文字を塩基配列にして下に表示する(どちらもスクロール可)
  • 変換結果を履歴に 10 件表示する
  • 音声入力ができる(Mac は不可)
  • クリップボードに変換結果のコピーができる
  • 変換結果を他アプリに共有できる
  • 変換結果をテキストファイルにしてダウンロードできる
  • 変換結果をツイートできる

つまずいたポイント

SwiftUI は毎年ちょっとさわってはいるのですが毎回忘れているので私の SwiftUI の理解度としてはどんな View があるかはなんとなく知っているけど遷移処理とかはどうするんだったっけ?くらいのレベルです。

2 画面しかないから余裕と思ってたら意外とつまずきました。主につまずいたのは下記です。

  • SFSpeechRecognizerDelegate の指定
  • UIActivityViewController の表示
  • UIDocumentPickerViewController の表示
  • キーボードのボタン追加
  • アプリ起動時の処理
  • トースト表示

SFSpeechRecognizerDelegate の指定

もともと SFSpeechRecognizerDelegate を下記のように viewDidLoad で指定していました。

SwiftUI の場合 View が struct なのでデリゲートが設定できません。
悩んだ末 SFSpeechRecognizer を別のクラスに持たせてそこにクロージャをつけて View の onAppear で設定するようにしました。

onAppear は何度も呼ばれる気もしますが今回のケースならまあいいかなと。

UIActivityViewController の表示

UIActivityViewController を表示するには遷移元の ViewController が必要です。View から表示したい場合どうするんだ?と思いましたが iOS 16 からは ShareLink というものがあるようです。こちらを使うことで解決しました。iPad と Mac の場合はちゃんとポップオーバー表示に切り替えてくれるようです👏

参考:【SwiftUI】iOS16.0 以降で使える ShareLinkでできることを調べてみた

UIDocumentPickerViewController の表示

こちらも UIActivityViewController 同様に遷移元の ViewController が必要です。こちらは SwiftUI でそれっぽいものがなく UIViewControllerRepresentable を使用しなければいけないんだろうなという感じですがデリゲートはどうするんだ?というところでつまずきました。

Coordinator というのがあるんだよと教えてもらったのでそれで解決しました。下記の記事の「更にSwiftUIらしく」のコードを UIDocumentPickerViewController に書き換えて使用しました。

参考:SwiftUIで端末の画像を表示する

キーボードのボタン追加

もともとは下記のように UITextView に設定してキーボードの上にボタンを表示していました。

これはたしか無理だったな諦めよと思ったのですが iOS 15 から下記でいけるみたいです!

参考:InputAccessoryView with SwiftUI

アプリ起動時の処理

アプリ起動時にアプリストアのバージョンと比較して古ければアラートを表示する下記の処理がありました。

SceneDelegate 消したしどこに書けばいいんだ?と悩んだのですが最終的に下記のように App の初期化時に記載しました。

とりあえず動いたのでまあいいでしょう。

トースト表示

もともと添付の before のようになにか処理が終わったときに画面下部にトースト表示をしていました。

一画面でしか使わないし ZStack 使えばいけるかな?と思ったのですがいい感じに下からにゅっと出す方法がわからない。そもそも画面下部に表示する方法がわからない。なんか真ん中に表示されてるしもういっそ表示を変えてしまおうと添付の after のように変更することで対応しました。

before after
before_toast after_toast

実装としてはこんな感じです。

ここはもうもとの表示は断念しました。

おまけ

リリース時につまずいた箇所のおまけです(SwiftUI 関係ないです)。

ビルドバージョン

ストアにアップロードしたところ輸出コンプライアンスの警告が出ていたので info.plist にキー追加してアップし直しました。まだ提出してないしビルドバージョン同じでいいかな?と思って変えずにアップしたところストア側で勝手に修正されるようでした。

どちらも 1.1.0 でアップしたのですが 2 回目にアップした方は 2.0 になっていました。とくに問題ないはずですが気持ち悪かったので 1.1.1 にしてアップしなおしました。

CFBundleVersion is empty

2 回目にストアへアップロードしようとしたところ突然下記のエラーが出るようになりました。

CFBundleVersion is empty but must be composed of one to three period-separated integers.

1 回目はアップできたし、バージョンも数値でちゃんと入力されている。なんでだ?とわりと悩んだのですが Git でコミットログを確認したところ輸出コンプライアンス関連のキーを追加したときになぜか CFBundleVersion が消えてしまっていたようでした。

git

消えたやつを追加したら無事なおりました。Git 管理していてよかった。

おわりに

わりと色々つまずいたのですがコミュニティの times でぐちぐちつぶやきながらやってたら優しい方々がアドバイスくれたのでリリースするところまでいけました!ありがとうございます!

レイアウト関連もわりとつまずきましたがてきとーにググるとそれっぽい表示にはなりそこまで困らなかった印象です。移行してていいなと思ったのが TextField まわりでした。複数行入力FocusState がよかったです。

SwiftUI は毎年 API が変わっている印象なのでなかなか追いかけるのがむずかしいですね。

参考サイト

以下は SwiftUI への移行で参考になったサイトです。

コメント

タイトルとURLをコピーしました