0

I am working on an app that tracks the user's location and saves some data related to the location in CoreData. I am new to SwiftUI, so my question is related to where/how is the best place in the app to save the location data into CoreData.

Currently, I have a setup that is very similar to the accepted solution here: How to get Current Location using SwiftUI, without ViewControllers?

Therefore I won't repeat the code that is already in the above link but will make references to it. So you may want to take a look at the answer before reading the rest of my problem statement.

So in class LocationManager I have:

@Published var userLocations: [CLLocation]? {
    willSet {
        objectWillChange.send()
    }
}


func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    guard let location = locations.last else { return }
    let howOldIsTheLastLocationData = abs(location.timestamp.timeIntervalSinceNow)
    if howOldIsTheLastLocationData < 15 && location.horizontalAccuracy > 0 && location.horizontalAccuracy < 15 {
        if userLocations == nil {
            userLocations = []
        }
        userLocations?.append(location)
    }
    print(#function, location, location.latitudeString, location.longitudeString, location.altitude, howOldIsTheLastLocationData)
}

Therefore userLocations is what I would like to observe in my View and also save into my CoreData.

Now in my View I have:

@Environment(\.managedObjectContext) var context
@State var segment: Segment?
@ObservedObject var locationManager = LocationManager()

var userPath: [CLLocationCoordinate2D]? {
    if let userLocations = locationManager.userLocations {            
        return userLocations.map( { $0.coordinate })
    }
    return nil
}

var body: some View {
    // A MapView Uses userPath to show the path on a MKMapView        
    MapView(pathCoordinates: userPath ?? [])
    
    // There is a button that by pressing it, it creates a Segment which is an Entity in my CoreData with time data.
}

So far everything that I mentioned above is working. I am getting the location updates, showing the path on a MapView. I am able to create Segments (and save into CoreData) by pressing the Start button. Now the piece I am looking for is for every location update that goes into userLocations, I would like to create a new instance of another CoreData Entity that is called PointOnPath that is associated with a Segment. So Segment has a one-to-many relationship to PointOnPath. What I don't know is where I should call something like the following lines of code:

  let point = PointOnPath(context: context)
  point.latitude = location.coordinate.latitude
  point.longitude = location.coordinate.longitude
  point.segment = segment
  try? context.save()

I thought about putting the above lines of code into:

var userPath: [CLLocationCoordinate2D]? {
    if let userLocations = locationManager.userLocations {  
        // Maybe add to CoreData for PointOnPath here?
        return userLocations.map( { $0.coordinate })
    }
    return nil
}

But I noticed this is being called many times. It seems to me it should only be called whenever a new userLocations?.append(location) happens but that's not the case and it's called many more times than userLocations?.append(location) happens. And in general I am not sure if that's the best place to save the location data in PointOnPath. Any insight is really appreciated.

Dogahe
  • 1,380
  • 2
  • 20
  • 50

1 Answers1

0

There still is absolutely no reason why the view should be responsible for saving entities to the database. (or even accessing) Neither with Storyboard nor with SwiftUI.

Depending on your architecture, you could create a ViewModel which would have access to your LocationManager and a, let's say, DataManger. This one would be responsible to save stuff to your database.

You might want to check out this article.

LoVo
  • 1,856
  • 19
  • 21