前提・実現したいこと
SwiftUIと、Django REST Frameworkを利用してiOSアプリを開発しています。
GET
のHTTPメソッド
でAPI接続をして、ある値を取得し、その値である計算をして、計算結果を戻り値として変えるメソッドを作りたいです。
引数で指定した2つの数字をAPIで取得し、それぞれを足してその合計値を返すメソッドを以下のようになコードで作成していますが、うまく機能しません。
取得した数値に計算がされず、戻り値に指定している変数の初期値がリターンされてしまいます。
どのように改善すれば、目的の動作となるでしょうか?
該当のソースコード
viewModel
class AppState: ObservableObject { @Published var numbers:[Numbers]? func getTotalNumber(firstNo:Int,secondNo:Int ) -> Int { // Set up the URL request var totalNo:Int = 0 let endpoint: String = "https://sample.com/api/?firstNo=(firstNo)&secondNo=(secondNo)" print(firstNo) // 1 print(secondNo) // 2 let url = URL(string: endpoint) let urlRequest = URLRequest(url: url!) // set up the session let config = URLSessionConfiguration.default let session = URLSession(configuration: config) // make the request let task = session.dataTask(with: urlRequest) { (data, response, error) in // check for any errors guard error == nil else { print("error calling GET") return } // make sure we got data guard let responseData = data else { print("Error: did not receive data") return } // parse the result as JSON, since that's what the API provides DispatchQueue.main.async { do{ self.numbers = try JSONDecoder().decode([Numbers].self, from: responseData) for number in self.numbers!{ totalNo += number.number } }catch{ print("Error: did not decode") return } } } task.resume() print(totalNo) //3を期待するが0と表示される return totalNo } }
JsonModel
struct Numbers:Codable,Identifiable { var id: Int var number: Int }
補足情報(FW/ツールのバージョンなど)
Xcode:Version 12.0.1
DjangoRESTframework: 3.12.1
一般的な話をすると、非同期なクロージャに取り込まれたブロック外のプロパティ(self.をつけるようにコンパイラに指示されるプロパティ)は、実際にはいつ更新されるかわからないため、ご提示いただいたコードのように記述してもうまく更新することはできません。
つまり、
let task = session.dataTask(with: urlRequest) {
でdataTask を開始し、その中で
totalNo += number.number
と更新したところで、ブロック外にある
return totalNo
の方が先に実行されるため0となってしまいます。
一般的な解決策は
https://teratail.com/questions/299401
に書いてありますので、そちらを参考にしていただければと思います。
ただ、上記のことを回答欄に書かなかったのには理由があります。
非同期処理内で JSONDecoder() から返される値を代入しているプロパティは
@Published var numbers:[Numbers]?
と @Published 属性付きで宣言されていますが、私が SwiftUI 関連のこの辺りの扱いをよく把握していないため、全体的な影響まで把握できないためです(万が一不都合が起こっても切り分けできないため)。
なので、あくまで参考意見ということで実験していただければとおもます。
コメントいただきありがとうございます。
ご丁寧に解説していただきありがとうございます。こちらの内容参考にさせていただき解決にできるよう方法を試してみようと思います。
参考先リンクに挙げた例は、クロージャを使って非同期処理実行後の処理を記述する例ですが、たとえば JSON でのデコードが完了し、その値をなんらかのプロパティに入れた時点で処理を実行させる(いわゆるプロパティオブザーバ)でも実現可能だと思っています。
その方法が、たとえばSwiftUIで使われるようなプロパティ監視の枠組みで実現できるようであれば、それが一番自然ではないかと思っています。
なるほど、ご丁寧に教えていただきありがとうございます!
解決への良いアドバイスとなりそうです。
あなたの回答
tips
プレビュー