问题
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.
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
回答1:
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.
回答2:
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.
回答3:
This post is still helpful but a bit outdated.
To save CLLocation I use:
let clocation: [CLLocation]
let archivedLocation = NSKeyedArchiver.archivedData(withRootObject: clocation)
And to Fetch with Core Data:
let unarchivedLocation = NSKeyedUnarchiver.unarchiveObject(with: (coreDataLocation.value(forKey: "location") as! NSData) as Data) as! CLLocation)
来源:https://stackoverflow.com/questions/26790492/saving-swift-cllocation-in-coredata