SwiftUI開発のノリが少しわかってきたかも。
SwiftUIのチュートリアルやら、書籍やらみて手を動かしているのだけれど、Swiftも初めて、XCodeも初めてということでもたついたけど、自分のパターンみたいなものがようやく見えてきた。
ViewはXMLや独自の画面定義構文ではなく、Flutterで画面をDartのコードで記述するように、Swiftのコードで記述し、ServiceとViewをMVなんちゃらで繋いであげればよさそう。
メモをとっておかないと、すぐに忘れてしまうので、次にちゃっちゃと進みたい気持ちを抑えて、簡単なサンプルアプリを作成、メモだけとっておく。
URLを入力してボタンを押すと、エディタに情報を表示するアプリを作ってみる。
1.Model
まず、データありき。HTTP GETで取得した、BODY部分を保持する、極シンプルなModelというかViewModelを定義する。
UIと共有して変化を動的にUIに反映させるには、ObservablerObjectを継承、また、反映させるメンバーには、@Published で修飾する必要があるようだ。
import Foundation public class WoLViewModel : ObservableObject { @Published var content:String = "" }
2.Service
ビジネスロジックとして、URLと上記モデルを引数としてとり、URLの内容を取得して、結果をモデルのメンバーにセットするロジックをかく。
一点、DispatchQueue.main.sync というおまじない、、、でもないのだけれど、で、GUIスレッドとの同期を取る必要がある。
Swiftのケースを調べていないが、イベントディスパッチスレッドにメインスレッドから即時アクセスできないので、キューに入れておいてGUIの都合が良い時に実行してくれる仕組みは同じだろう。
「ほとんどすべてのGUIツールキットはシングルスレッドのサブシステム(single-threded subsystem)として実装されています。すなわちすべてのGUI活動が一つのスレッドに拘束されています。」
「特に意識的に完全にシングルスレッドのプログラムを書かないかぎり、普通のGUIアプリケーションでは、アプリケーションのスレッドとGUIのイベントスレッド、少なくともこの二つのスレッドが動きます。」
「昔はGUIのアプリケーションそのものがシングルスレッドで、GUIのイベントはアプリケーションのメインスレッドの中の”メインイベントループ(main event loop)” と呼ばれるループの中で処理されました。今日のGUIフレームワークはほんの少しだけ違って、イベントディスパッチスレッド(event dispatch thread,EDT)と呼ばれる、GUIイベントを処理する専用のスレッドを作ります。」
import Foundation public struct WoLService { func doResourceGetTest(url _url:String, content:WoLViewModel?) { let url = URL(string: _url)! let task = URLSession.shared.dataTask(with: url) { data, response, error in if let error = error { print("\(error)\n") return } guard let data = data, let response = response as? HTTPURLResponse else { print("no data or no response.") return } if response.statusCode == 200 { if let text = String(bytes: data, encoding: .utf8) { print(text) if let content = content { DispatchQueue.main.sync { content.content = text } } } } } task.resume() } }
3.View
最後にView部分。ボタン押したら、Serviceを作成して、メソッドをよぶ。
ViewModelというのかどうかわからないが、ObservableObjectは、@ObservedObject で修飾してViewでインスタンス化する必要があるようだ。
あとは、View上のコンポーネントと、ViewModelかObservableObjectか知らないがの、公開メンバーと $data.content としてバインドする。
import SwiftUI struct ContentView: View { @State var url: String = "" @ObservedObject var data = WoLViewModel() var body: some View { VStack { TextField("https://example.com", text: $url) TextEditor(text: $data.content) Button(action: { getResource() }) { Text("Get Resource") }.padding() } } func getResource() { let service = WoLService(); service.doResourceGetTest(url:self.url, content:self.data) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
4.動いた
動いた動いた。
あとは拡張していけばよさそう。
全くわからない状態から、学習するには、以下の書籍は懇切丁寧に書かれてて良い。