Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

iOS

how to store an array of type cllocationcoordinate2d in nsuserdefaults in swift 2.0

i want to save an array of location coordinates in nsuserdefaults.how to do that

2 Answers

This is indeed not to easy. You are hitting two limitations:

1. You cannot store an array of CLLocationCoordinate2D to UserDefaults directly

As the following excerpt of the UserDefaults describes, you cannot directly store an array [CLLocationCoordinate2D] to UserDefaults. Allowed are only combinations of [Int], [String] etc.

The NSUserDefaults class provides convenience methods for accessing
common types such as floats, doubles, integers, Booleans, and URLs.
A default object must be a property list—that is, an instance of (or for
collections, a combination of instances of): NSData, NSString, NSNumber,
NSDate, NSArray, or NSDictionary. If you want to store any other type of
object, you should typically archive it to create an instance of NSData.
For more details, see Preferences and Settings Programming Guide.

In order to store and retrieve custom objects to/from UserDefaults, you have to utilize NSKeyedArchiver and NSKeyedUnarchiver. However...

2. NSKeyedArchiver does not work for CLLocationCoordinate2D

I won't go into detail here why this does not work, however it does work for CLLocation which conforms to NSSecureCodingand thus can be archived. Therefore, one solution would be to convert [CLLocationCoordinate2D] to [CLLocation] before storing it to UserDefaults and vice-versa when retrieving them. The following example should work as expected.

import CoreLocation

// Store an array of CLLocationCoordinate2D
func storeCoordinates(_ coordinates: [CLLocationCoordinate2D]) {
    let locations = coordinates.map { coordinate -> CLLocation in
        return CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
    }
    let archived = NSKeyedArchiver.archivedData(withRootObject: locations)
    UserDefaults.standard.set(archived, forKey: "coordinates")
    UserDefaults.standard.synchronize()
}

// Return an array of CLLocationCoordinate2D
func loadCoordinates() -> [CLLocationCoordinate2D]? {
    guard let archived = UserDefaults.standard.object(forKey: "coordinates") as? Data,
        let locations = NSKeyedUnarchiver.unarchiveObject(with: archived) as? [CLLocation] else {
            return nil
    }

    let coordinates = locations.map { location -> CLLocationCoordinate2D in
        return location.coordinate
    }

    return coordinates
}

Please note that this will not in Playgrounds as UserDefaults are only available in sandboxed environments.

Now let's test our implementation:

let testCoordinates = [
    CLLocationCoordinate2D(latitude: 0.123456789, longitude: 0.123456789),
    CLLocationCoordinate2D(latitude: 1.123456789, longitude: 1.123456789),
    CLLocationCoordinate2D(latitude: 2.123456789, longitude: 2.123456789)
]

storeCoordinates(testCoordinates)

let coordinates = loadCoordinates()
dump(coordinates)

Hope that helps :)

thanks martin....:)