I am creating an application that receives the localization information from an Apple Watch and with the coordinates that I receive, I want to create a MapKit annotation on a, I also show the location of the Phone.
But when I try to create the array to hold the information for the annotation the error
Cannot use instance member 'connectivity' within property initializer; property initializers run before 'self' is available
The class that is giving me the error is the following:
MapView.swift
import MapKit
import SwiftUI
// Struct to display devices location
struct Place: Identifiable {
let id = UUID()
let name: String?
let coordinate: CLLocationCoordinate2D?
init(name: String? = nil,
coordinate: CLLocationCoordinate2D? = nil) {
self.name = name
self.coordinate = coordinate
}
}
struct MapView: View {
// Loads the ViewModels for the map
@StateObject private var viewModel = MapViewModel()
// Loads location of apple watch
@StateObject private var connectivity = Connectivity()
// Start tracking
@State var toggleTracking = true
@State var showTemporaryPopUp = true
// contains the location coordinates of device, to draw where is located on the map
var annotations = [
Place(name: "Apple Watch", coordinate: CLLocationCoordinate2D(latitude: connectivity.receivedLat, longitude: connectivity.receivedLong))
]
var body: some View {
ZStack(alignment: .top) {
// Displays Map
Map(coordinateRegion: $viewModel.region, showsUserLocation: true)
.ignoresSafeArea()
.accentColor(Color(.systemPink))
}
The part that is giving me the error is this one:
// contains the location coordinates of device, to draw where is located on the map
var annotations = [
Place(name: "Apple Watch", coordinate: CLLocationCoordinate2D(latitude: connectivity.receivedLat, longitude: connectivity.receivedLong))
]
And here is the class that contains the latitude and longitude, and the source of the error.
Connectivity.swift
import Foundation
import WatchConnectivity
class Connectivity: NSObject, ObservableObject, WCSessionDelegate {
@Published var receivedText = ""
@Published var receivedLat: Double = 0.0
@Published var receivedLong: Double = 0.0
override init() {
super.init()
if WCSession.isSupported() {
let session = WCSession.default
session.delegate = self
session.activate()
}
}
// This function receives the information sent from the watch,
//and then sets the published vars with the information
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
DispatchQueue.main.async {
if let lat = message["latitude"] as? Double, let lon = message["longitud"] as? Double {
self.receivedText = "\(lat),\(lon)"
self.receivedLat = lat
self.receivedLong = lon
replyHandler(["Response": "Be excellent to each other"])
}
}
}
}
The first thing that I tried is using the 'lazy' var, like this:
// contains the location coordinates of device, to draw where is located on the map
lazy var annotations = [
Place(name: "Apple Watch", coordinate: CLLocationCoordinate2D(latitude: connectivity.receivedLat, longitude: connectivity.receivedLong))
]
But then the next error appears when I try to get the coordinates:
Cannot use mutating getter on immutable value: 'self' is immutable
So this solution is out of the question.
And the other common solution is to use the init, the way I am doing it is the following way, on my Connectivity.swift file:
override init() {
super.init()
if WCSession.isSupported() {
let session = WCSession.default
session.delegate = self
session.activate()
}
self.receivedLat = 0.0
self.receivedLong = 0.0
}
But the error still exist, and I don't know some other way to change the init.
Is there other way to do it?
Sources:
- Hudson, P. (2020). Hacking with watchOS
CodePudding user response:
Make annotations
a computed property -- that way, it has access to connectivity
and it will get re-computed with the View
re-renders because a @Published
property of Connectivity
has changed:
var annotations : [Place] {
[
Place(name: "Apple Watch", coordinate: CLLocationCoordinate2D(latitude: connectivity.receivedLat, longitude: connectivity.receivedLong))
]
}