Search code examples
core-dataswiftcllocationnsvalue

Saving Swift CLLocation in CoreData


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.

cllocation

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.

data-model

Saving the CLLocation Object Graph

//assuming location is a valid CLLocation and data model has a binary data field
let archivedLocation = NSKeyedArchiver.archivedDataWithRootObject(location)
newManagedObject.setValue(archivedLocation, forKey: "location")

Recovering the object graph from the data store

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

Solution

  • 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.