LoginSignup

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

@Published var をメインスレッドで受け取る方法

解決したいこと

Modelを Published var として ViewModel内に保持し、ViewからObserveしています。

モデルの処理がバックグラウンドスレッドに入った時に、モデルの値をPublishすると、Xcodeのスレッドチェッカーが反応して

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.

というワーニングが出ます。

ContentView.swift
@StateObject var viewModel = ViewModel()
ViewModel.swift
class ViewModel: ObservableObject {

    @Published var model = Model()
    var thisValue:String {
        return model.thisValue // この値をViewで使いたい
    }

Model.swift
struct Model {
    var thisValue:String = "value" // この値をバックグラウンドスレッドで変更して、メインスレッドでパブリッシュしたい

メインスレッドでモデルの値を受け取る方法をしりたいのですが、
なかなかわからず質問させていただきました。

教えていただけr教えていただけるととても嬉しいです。

4

3Answer

適当に作った参考例貼っておきますね^^

View
struct TestView: View {
    @StateObject var viewModel = ViewModel()
    var body: some View {
        VStack{
            TextField("入力してください。", text: $viewModel.viewText)
            Button {
                viewModel.viewModelFunc()
            } label: {
                Text("判定:"+viewModel.result)
            }
        }
    }
}
viewModel
class ViewModel: ObservableObject {
    private var model = Model()
    @Published var result = ""
    @Published var viewText = ""

    func viewModelFunc() {
        model.modelFunc(thisValue: viewText) { [weak self] error in
            self?.result = error
        } success: { [weak self] success in
            self?.result = success
        }
    }
}
model
class Model {
    func modelFunc(thisValue: String, failure: @escaping (String) -> Void, success: @escaping (String) -> Void) {
        if thisValue.isEmpty {
            return DispatchQueue.main.async {
                failure("error")
            }
        } else {
            return DispatchQueue.main.async {
                success("success")
            }
        }
    }
}

これでthisValueをどのスレッドで取得しても返すのはメインスレッドになるかと思います。
また以上のものだとviewModelがmodelに依存しているので必要に応じて疎結合(DI)の処理をすると良いかと^^

2

Comments

  1. @john-rocky

    Questioner
    ありがとうございます。

    ご提示いただいたように、ModelをClassにすることで解決しました。

    コードまで書いていただいて、本当にありがとうございます。

上記の例ですとそのまま使用してもエラーは出ないと思いますので具体的な解決策案を出すことができませんが、model.thisValueに対してDispatchQueue.main.asyncで囲ってあげるとエラーは消えそうかなと思います。(非同期処理でメインスレッドに返してあげる際に書きます。)
書き方としては以下のような感じです。

DispatchQueue.main.async {
                    //model.thisValueなどここにメインスレッドで返したい結果を書く
                }

参考になりそうな記事も貼っておきます。
SwiftUIでObservedObjectなオブジェクトでメインスレッドで実行するよう設定するSwiftUIらしい書き方が知りたい

1

Comments

  1. @john-rocky

    Questioner
    ありがとうございます。
    DispatchQueue.main.asyncのクロージャの中から値を返す方法がわからなくて、詰まっております。リンクも熟読いたします。
This answer has been deleted for violation of our Terms of Service.

Your answer might help someone💌