Help us understand the problem. What is going on with this article?

アプリのiOS13対応でハマったこと

私はiOSのアプリの開発を行っているのですが、そのままではiOS13で動作しませんでした。iOS13対応で必要だったこと、ハマったことについて書いておきたいと思います。

Xcode11でストーリーボードとソースコードの両表示方法がわからない

(2019/9/21追記)
まず最初につまずいたのはここでした。XcodeのストーリーボードでUIコンポーネントをソースコードに対応づけるために隣り合わせて表示させるアレです。Xcode10までは右上の方に◯印2つが重なり合ったようなアイコンを押していたのですが、Xcode11ではそれがなくなってしまい、どうやって表示するのかわからなくなりました。

「アレ」はアシスタントエディタというのが正式名称のようで、ストーリーボードが表示された状態で、EditorメニューのAssistantを選ぶことで無事に表示されました。

UISearchBar内のテキストフィールドの公式サポート

従来、UISearchBar内のテキストフィールドへのアクセスは公式にはサポートしておらず、非公式のsearchBar.value(forKey: "_searchField") as? UITextField等として無理やり取得して使っていました。

iOS13ではこのテキストフィールドへのアクセスが公式APIとして提供され、

let searchField = searchBar.searchTextField

で取得できるようになりました。しかもこのクラスはUISearchTextFieldというクラスで、token表示機能(iOS標準のメールアプリの検索機能とかで、カードっぽい表示になるやつです)をサポートしています。

これだけなら既存アプリへの影響はなさそうですが、これに伴って2つハマったことがありました。

_searchFieldのアクセス禁止問題

元々非公式だったので問題というのも何ですが、テキストフィールドへのアクセスの公式化に伴って、アプリからの_searchFieldへのアクセスが禁止されました。このため、この方法でテキストフィールドを取得していた場合には公式APIに変更が必要になりました。

Any?からプロトコルへのキャスト問題

この問題は直接的にiOS13と関係あるわけではないのですが、自分のアプリでは元々_searchFieldで取得したテキストフィールドを使って、無理やりtoken表示っぽいことを行っていました。
今回、token機能が公式にサポートされたので、ありがたく使ってみることにしました。

UISearchTextFieldにトークン表示を追加する場合は、UISearchTokenというクラスを使うことになります。この時、表示上のアイコン・文字列もそうですが、何を検索するのか(例えば日時を検索するとか)も合わせて覚えておく必要があります。そのために、UISearchTokenにはrepresentedObjectというAny?型のプロパティがあり、ここにその情報を詰め込んでおくことで後から取り出して検索することが可能になります。

で、検索するキー項目が複数ある場合に(日時と名前とか)、自然とrepresentedObjectに詰め込むクラスは何かの共通プロトコル(ここでは仮にSearchKeyとします)を実装したクラスになると思います。
以下のような状況を想定します。

// 検索キー用の共通プロトコル
protocol SearchKey {
}
// 日付検索用のキー
class DateKey : SearchKey {
}
// 名前検索用のキー
class NameKey : SearchKey {
}

representedObjectDateKeyNameKeyのオブジェクトを入れて取り出すときに、

let searchKey = token.representedObject as? SearchKey

のように取り出したくなりますが、何とこの方法ではnilになってしまい取り出せません。

https://stackoverflow.com/questions/42033735/failing-cast-in-swift-from-any-to-protocol

上記URLにその情報があり、無事解決したのですが、SwiftではAny?型をプロトコル型に直接キャストすることができないようです。一度AnyObject型にすると良いという解決策があるとのことだったので、以下のようにして回避しました。

let searchKey = token.representedObject as AnyObject as? SearchKey

UITableViewCellのラベルの翻訳失敗問題

私のアプリではstoryboardを使用して画面を開発しています。英語・日本語両対応させているので、まずは英語で画面を作成し、Main.stringsで該当するObjectIDに対して日本語を設定するというやり方です。

ところが、Xcode11 GM Seedで開発していたところ、いつの間にか翻訳されないようになってしまいました。私のアプリの場合は、staticなUITableViewに配置しているUITableViewCellのタイトルが一律翻訳されなくなりました。それ以外のコンポーネントは普通に翻訳されているので、おそらくXcodeのバグではないかと思っているのですが、うまい解決方法がなく、以下の方法で暫定回避しました。

  1. 翻訳が失敗しているUILabelObjectIDの値をAccessibilityIdentifier(IB上で、ObjectIDのちょっと下にあるやつです)に手動でコピー
  2. そのUITableViewのDataSourceクラスに対して、以下を追加実装
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = super.tableView(tableView, cellForRowAt: indexPath)
    if let label = cell.textLabel, let id = label.accessibilityIdentifier, id.count > 0 {
        let key = id + ".text"
        let localizedString = NSLocalizedString(key, tableName: "Main", comment: "")
        if key != localizedString {
            label.text = localizedString
        }
    }
    return cell
}

要は、Accessibility Identifierが存在して、Main.stringsに対応する翻訳が存在する場合に、テキストを置き換えるというのを自前で実装しています。
これで無事翻訳されるようになりました。

注意点としては、手動でObjectIDをコピーする際に、間違えないようにすることでしょうか。間違って他のIDを貼り付けてしまうと、別の訳語が表示されてみっともないことになります。

なお、Stack overflowで同じ問題を見つけたので、上記の回避策を投稿してみました。
https://stackoverflow.com/questions/57905965/xcode-11-ios-13-localization-issue

UISearchDisplayController廃止によるアプリクラッシュ問題

一通り対応を終えて、AppStoreにアップロードしてTestFlightで動作確認しようとしたところ、起動時にアプリがクラッシュする現象が発生しました。シミュレータでのデバッグや、ローカルでの実機デバッグではうまく動いていたので調査は難航しましたが、

3   UIKitCore                       0x1a3227148 UISearchDisplayControllerNoLongerSupported + 247

このクラッシュログを手がかりに、以下の情報にたどり着きました。
https://github.com/mapbox/mapbox-gl-native/issues/15559

かなり前から非推奨になっていたUISearchDisplayControllerがiOS13で完全に廃止されたため、使っているとクラッシュするという問題のようです。

問題は、こんなクラスを使用している覚えがなく、ソース全体をUISearchDisplayControllerで検索しても見つからなかったので、何故こんなエラーが出るのか途方に暮れていましたが、実はありました。

スクリーンショット 2019-09-17 22.37.20.png

どうやら、検索機能を開発する際に試行錯誤して追加した実際には使われていないものが残っていたようです。この記述はUISearchDisplayControllerで検索しても見つからず、searchDisplayControllerで検索する必要があるので注意しましょう。

これを削除したら解決しました。

メインスレッド以外からのUIコンポーネントアクセスチェックの厳格化

元々画面描画関連の処理はメインスレッドから行わないといけませんでしたが、直接的に画面描画を伴う処理ではなくてもUIコンポーネントに対するアクセスのチェックが厳しくなったようです。私のアプリの場合は、UIViewControllersplitViewControllernavigationControllerに別スレッドからアクセスしている処理があり、チェックに引っかかりました。
単にそのViewControllerクラスで保持している自前のプロパティにアクセスしたいだけだったので、splitViewControllernavigationControllerを経由せずに直接目的のViewControllerにアクセスするようにして回避しました。

Xcode11 GM Seed2でiOS13のシミュレータが動かない

(2019/9/21追記)
Xcode11のGM Seed2にして以降、iOS13のシミュレータを起動しようとするとThe iOS 13.0 simulator runtime is not available.というメッセージが出て起動しなくなってしまいました。詳細表示するとruntime path not foundと出ていました。しばらく理由がわからなかったのですが、いろいろ検索した結果、以下の記事が当たりでした。
https://stackoverflow.com/questions/53261256/xcode-10-simulator-runtime-is-not-available-runtime-profile-not-found-error

$ sudo killall -9 com.apple.CoreSimulator.CoreSimulatorService

これでシミュレータサービスを再起動することで問題が解消しました。

以上、iOS13対応している方の何かの役に立てば幸いです。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away