Swift SDKのTipsです。mBaaSへのアクセスはほぼすべてネットワークが伴う処理で、非同期になります。非同期処理はバックグラウンドで処理しますが、その結果をそのままSwift UIで画面表示に反映すると、次のような警告が表示されます。
Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.
今回はこの警告を消すための方法について紹介します。
サンプルコード
上記の警告が出るコードです。
@ObservedObject var Todo = Todos() var body: some View { NavigationView { ZStack(alignment: .bottomTrailing) { // 省略 } .onAppear { let query : NCMBQuery<NCMBObject> = NCMBQuery.getQuery(className: "Todo") query.findInBackground(callback: { result in switch result { case let .success(array): self.Todo.todos = array // ここで警告が表示される case let .failure(error): print("取得に失敗しました: \(error)") } }) } // 省略 } }
回避策1
回避策として、非同期処理を同期にする方法があります。
let query : NCMBQuery<NCMBObject> = NCMBQuery.getQuery(className: "Todo") let result = query.find() switch result { case let .success(array): self.Todo.todos = array case let .failure(error): print("取得に失敗しました: \(error)") }
これでもエラーは出ませんが、ネットワーク処理中は画面が固まってしまうという問題があります。UXとしてお勧めできません。
回避策2
回避策2は画面を書き換える処理だけメインスレッドに戻ってくるという方法です。具体的にいうと DispatchQueue.main.async
で処理を囲みます。
let query : NCMBQuery<NCMBObject> = NCMBQuery.getQuery(className: "Todo") query.findInBackground(callback: { result in switch result { case let .success(array): DispatchQueue.main.async { self.Todo.todos = array } case let .failure(error): print("取得に失敗しました: \(error)") } })
これで画面を書き換える処理はメインスレッドで実行されるようになります。
まとめ
ネットワーク処理が伴うと非同期と同期を使い分ける必要が出ます。スレッドは目に見える訳ではないので分かりづらいでしょう。Swift6ではasync/awaitが実装されるという話なので、この方法ではなくなるかも知れませんが、現状はこのTipsをご利用ください。