TL;DR
Objective-Cで書いていたSDKなどを使ったdelegateのサンプルをCoreLocation を参考に作ってみました。ソースはこちら> https://github.com/dropcontrol/LocationUpdata
またSwiftUIのProperty Wrapperを使った方法として@ObservableObject
と@ObserbedObject
を@State
の代わりに使っています。実際は単一のViewの中でのPropertyの変更なので@State
で充分なんですが。@ObservableObject
を使う場合は複数のViewに跨ってPropertyの変更が必要な場合と言うことです。
参考:
https://rusutikaa.github.io/docs/developer.apple.com/documentation/swiftui/managing-model-data-in-your-app.html
https://capibara1969.com/2508/
delegateをどう書くか?
アプリ自体はMapKitを使って緯度経度を取得し、それを表示する、と言うだけの単機能アプリです。で、その緯度経度を取得する部分はCLLocationManagerDelegateで定義されている、func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
で行います。なのでdelegateをどう書くか?
struct ContentView: View {
@ObservedObject var placeInfo = PlaceInfo()
var manager = CLLocationManager()
var managerDelegate = locationDelegate()
var body: some View {
VStack{
HStack {
Text("Latitude:")
Text(placeInfo.latitude)
}
HStack {
Text("longitude:")
Text(placeInfo.longitude)
}
Button(action: {
print("Button Tapped")
placeInfo.latitude = managerDelegate.currentLatitude
placeInfo.longitude = managerDelegate.currentLongitude
}){
Text("Current Location")
}
}
.onAppear() {
manager.delegate = managerDelegate
managerDelegate.locationManagerDidChangeAuthorization(manager)
}
}
}
として、
* CLLocationManager()のインスタンス var manager
を作る
* LocationDelegate()のインスタンス var managerDelegate
を作る
* .onAppear()で manager.delegate = managerDeleagte
してdelegateを設定する
Buttonの中にある、managerDelegate.locationManagerDidChangeAuthorization(manager)
や、.onAppear()のmanagerDelegate.locationManagerDidChangeAuthorization(manager)
はこれでアクセスできます。
delegate自体は以下のように記述。
class locationDelegate : NSObject, ObservableObject, CLLocationManagerDelegate {
// delegateから取り出すための変数
var currentLatitude: String = "none"
var currentLongitude: String = "none"
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
if manager.authorizationStatus == .authorizedWhenInUse {
print("authorized")
manager.startUpdatingLocation()
// add "Privacy - Location Default Accuracy Reduced" in info.plist
// and edit in souce code that value is <true/> or <false/>
if manager.accuracyAuthorization != .fullAccuracy {
print("reduce accuracy")
// add "Privacy - Location Temporary Usage Description Dictionary" in info.plist
// and set "Location" in Key
manager.requestTemporaryFullAccuracyAuthorization(withPurposeKey: "Location") {
(err) in
if err != nil {
print(err!)
return
}
}
}
} else {
print("not authorized")
// add "Privacy - Location When In Use Usage Description" in info.plist
manager.requestWhenInUseAuthorization()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.last {
print(String(format: "%+.06f", location.coordinate.latitude))
print(String(format: "%+.06f", location.coordinate.longitude))
currentLatitude = String(format: "%+.06f", location.coordinate.latitude)
currentLongitude = String(format: "%+.06f", location.coordinate.longitude)
}
}
}
先の managerDelegate.locationManagerDidChangeAuthorization(manager)
は、CoreLocationgが持つメソッドで、ユーザーに位置情報の使用許諾をとるアラートを出すものです(そこを通過しないと位置情報は取れない)。func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {}
で定義されています。info.plistにて設定をすることでどのレベルでどのように情報を取得の許諾をとるのか?を設定できます。
次の func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {}
で位置情報のアップデートを行っています。これがdelegateのファンクションです。実際このfunctionはどこからも呼ばれていなくて、位置情報が更新されるたびに呼ばれています。
@ObservableObject
と@ObserbedObject
チラッと書いたようにこのアプリではProperty Wrapperを@State
で書いても全く問題ない(ソースにはそれもコメントアウトして残してあります)のですが、@ObservableObject
を使うと@Publised
を変数に着けることで更新を自動で反映し、かつ複数のビューに跨ってその変数を共有し、かつ1箇所にまとめてかける、と言う利点があります。
書き方
@ObservableObject
は以下のようにクラスとして定義します。
class PlaceInfo: ObservableObject {
@Published var latitude: String = "none"
@Published var longitude: String = "none"
}
ここで自動保管で@ObservedObject
を間違えて打ってるとエラーになります(当たり前ですが、やりがち)。
使うときは、、、
@ObservedObject var placeInfo = PlaceInfo() //こっちが Observed。
でインスタンス化して、あとは placeInfo.latitude
のようにすれば呼び出しも変数の書き換えも行えます。@State
で書くよりもこっちの方がすっきり書けるな、と個人的には思います。単一のビューで使っても問題がある書き方には見えないのですが、変数のスコープを管理したい、と言う場合にはきちんと使い分けするのがいいんでしょうね。