Home > other >  How to properly use 'init' to prevent the error: "property initializers run before &#
How to properly use 'init' to prevent the error: "property initializers run before &#

Time:10-29

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))
    ]
}
  • Related