I am trying to save CLLocation
data in Core Data. My property is set as a Transformable object.
Some sample code out there indicates one can save the location data using this objc converted to an NSValue
.
CLLocationCoordinate2D myLocation;
NSValue *locationValue = [NSValue valueWithBytes:&myLocation objCType:@encode(CLLocationCoordinate2D)]
// then put locationValue into storage - maybe an ObjC collection class or core data etc.
Retrieving the object from the data store is supposed to work using this paradigm.
CLLocationCoordinate2D myLocation;
NSValue *locationValue = // restored from your database or retrieved from your collection class
[locationValue getValue:&myLocation];
I am attempting to extrapolate on this idea and capture the entire CLLocation
object into an NSValue
stored in CoreData
, with the goal to recover the CLLocation
object from the store at a later time.
While testing various methods I have found nil returned when trying to unwrap a CLLocation
object from a Core Data Store. I am placing a CLLocation
object into the store using the following:
//verified that I have a good CLLocation here
newManagedObject.setValue(location as CLLocation, forKey: "location")
Later I attempt to unwrap the CLLocation
let location = detail.valueForKey("location")! as CLLocation
But I find that the object does not unwrap properly.
The unwrapped object looks like:
(CLLocation) location = 0x00007fff59537c90 {
ObjectiveC.NSObject = {}
}
So any use of the CLLocation
object at this point returns the "found nil... error"
Am I missing something obvious about storing/retrieving CLLocation
objects using Swift?
UPDATE
Daniel's answer helped me realize that NSValueTransformer was not going to work for me so I went back to an approach I've used before. Basically encode the object graph using NSKeyedArchiver. My goal, as always, is to use the simplest possible solution so I chose not to use managed object subclasses since the properties I need are already in the existing CLLocation object. I changed the transformable field to Binary Data with the option checked (Store in External Record File). I set out to test this process with minimal changes to the master detail template.
//assuming location is a valid CLLocation and data model has a binary data field
let archivedLocation = NSKeyedArchiver.archivedDataWithRootObject(location)
newManagedObject.setValue(archivedLocation, forKey: "location")
On the main vc as part of the default prepare for segue the data is recovered via the fetch controller.
let object = self.fetchedResultsController.objectAtIndexPath(indexPath) as NSManagedObject
let controller = (segue.destinationViewController as UINavigationController).topViewController as DetailViewController
controller.detailItem = object
On the detail vc I unwrap my object graph using NSKeyedUnarchiver
var lc = NSKeyedUnarchiver.unarchiveObjectWithData(detail.valueForKey!("location") as NSData) as CLLocation
You will have much less trouble when you create a NSManagedObject
subclass for the CLLocation
objects. Then create methods for storing and retrieving for convenience:
import Foundation
import CoreData
import CoreLocation
class LocationPoint: NSManagedObject {
@NSManaged var latitude: NSNumber!
@NSManaged var longitude: NSNumber!
@NSManaged var altitude: NSNumber!
@NSManaged var timestamp: NSDate!
@NSManaged var horizontalAccuracy: NSNumber
@NSManaged var verticalAccuracy: NSNumber
@NSManaged var speed: NSNumber
@NSManaged var course: NSNumber
func initFromLocation(location: CLLocation) {
self.latitude = location.coordinate.latitude
self.longitude = location.coordinate.longitude
self.altitude = location.altitude
self.timestamp = location.timestamp
self.horizontalAccuracy = location.horizontalAccuracy > 0.0 ? location.horizontalAccuracy : 0.0
self.verticalAccuracy = location.verticalAccuracy > 0.0 ? location.verticalAccuracy : 0.0
self.speed = location.speed > 0.0 ? location.speed : 0.0
self.course = location.course > 0.0 ? location.course : 0.0
}
func location() -> CLLocation {
return CLLocation(
coordinate: CLLocationCoordinate2D(latitude: self.latitude.doubleValue, longitude: self.longitude.doubleValue),
altitude: self.altitude.doubleValue,
horizontalAccuracy: self.horizontalAccuracy.doubleValue,
verticalAccuracy: self.verticalAccuracy.doubleValue,
course: self.course.doubleValue,
speed: self.speed.doubleValue,
timestamp: self.timestamp
)
}
You have to set up your data model with an entity according to this class.
What you are missing is that NSValue is for storing types, i.e., contiguous blocks of memory, not objects. CLLocation is an object.
In order to store a CLLocation in core data, you will have to store each of the properties. An alternative is to use a Coder.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With