LoginSignup
25
16

More than 3 years have passed since last update.

iOS13対応したら、SKProductsRequest周りでつまずいた

Last updated at Posted at 2019-12-23

これiOS13、Xcode11 私はこうしてつまずいた Advent Calendar 2019 の23日目を担当するアガツマです
普段は、ビジネス版マッチングアプリ yenta のiOS版を開発をしています。

前日の記事
iOS13のメインスレッドチェック厳格化とNotificationCenterの組合せでつまづいた
と少し被るのですが、1つのケースとして誰かの役に立てたら幸いです!

つまずいた経緯

iOS13対応の諸々を完了し、アプリの課金プランについて記述している画面を開こうとしたところクラッシュ。。。
xcodeでは、以下のようなエラーメッセージが出力されていました。

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread.'

UIの更新は Main Thread で行うべきというのは以前からありましたが、iOS13に対応したらこのエラーが出るように。。。。

環境設定

iOS 13.2.2
Xcode11.2.0

やったこと

原因となっている箇所を特定

Main Thread Checker を活用して、Main Threadで呼ばれるべき処理が、実行時に Main Thread以外で呼ばれていないかをチェック!

Main Thread Checker 設定方法

Breakpoint Editor を開く
スクリーンショット 2019-12-23 15.09.03.png

Runtime Issue Breakpoint を追加
スクリーンショット 2019-12-23 15.13.17.png

追加した際に出る設定画面で type を Main Thread Checker を選択
スクリーンショット 2019-12-23 15.09.31.png

これで、実行時に Main Thread以外でUI更新をしている部分で処理が止まるので、原因となっている部分を特定しに行きました。

原因の特定と調査

このような過程で原因を調査していったところ、、、
SKProductsRequestDelegateメソッドの中で Main Thread 以外でUIに関する処理をしていることがわかりました。

SKProductsRequestは、アプリ内課金アイテムの情報を取得する際に利用しているのですが、色々と調べてみると下記2つのメソッドが Main Thread 以外で実行されるようになったみたいです。
なぜ、その様になったかはまだはっきりとはわかっていません。。。

この2つのメソッドが Main Thread で実行されることを想定していた実装をしている場合は注意が必要です。

func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {}
func request(_ request: SKRequest, didFailWithError error: Error) {}

改善案

上で挙げた2つのメソッド内の処理を、DispatchQueue.main.asyncを用いて明示的にメインスレッド Main Thread で実行されるようにしました。
ここではproductsRequest(...)の例のみ記載します。

修正前のコード

// MARK: - SKProductsRequestDelegate
extension ViewController: SKProductsRequestDelegate {
    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        //...(省略)...

        // 以下の処理でクラッシュする(Main Thread 以外でUI更新している)
        collectionView.reloadData()
        button.isHidden = false
    }
}

修正後のコード

// MARK: - SKProductsRequestDelegate
extension ViewController: SKProductsRequestDelegate {
    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        DispatchQueue.main.async { // 書く
            //...(省略)...

            // Main Threadで実行される
            collectionView.reloadData()
            button.isHidden = false
        }
    }
}

さいごに

iOS13に対応したことで、想定外の部分でクラッシュする画面が出てくるのはなかなかつまづくポイントだなと思います。
このようなアップデート時にテストを入念にするというのはもちろんですが、iOSの自動テストなどを組み込みアップデートにより起こってしまう問題を検知する方法もしっかり模索していかなければいけないなと思いました。

参考

Are SKProductsRequestDelegate methods always called on the main thread?
Multithreaded rendering only crashes on iOS 13
【iOS】iOS13からSKProductsRequestのDelegateメソッドがメインスレッドで呼ばれなくなった

25
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
16