1

I am using Swift to retrieve data from my Firebase database. When the user first logs in, I'd like to save the 'places' from my Firebase snapshot as a UserDefault.

static func getAllPlaces(){

    databaseRef = Database.database().reference()

    databaseRef.database.reference().child("places").observe(.childAdded) { (snapshot: DataSnapshot) in

        if let value = snapshot.value as? NSDictionary {

            let place = Place()

            let id = value["id"] as? String ?? "ID not found"
            let title = value["title"] as? String ?? "Title not found"
            let type = value["type"] as? String ?? ""

            place.id = id
            place.title = title
            place.type = type

            DispatchQueue.global().async {
                // Something here to append place data to UserDefaults?
                places.append(place) // appends to NSObject for later use
            }
        }

    }
}

The current code works fine - I just need to add something to get it stored so I can grab it later.

Bonus question: I am storing a good few hundred snapshots in the Firebase database. The reason I want to store them on the device is so that the user doesn't have to keep downloading the data. Is this a good idea? Would it take up a lot of memory?

Any help would be appreciated.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Ryan
  • 155
  • 1
  • 14
  • Limit yourself to one question per post please. If your bonus question is a new question, put it in a new post. But note that you might want to do some profiling of your app yourself before doing that. – Frank van Puffelen Nov 22 '18 at 19:48

1 Answers1

4

One way to save custom classes/data to UserDefaults is to encode them like this:

let encodedData: Data = NSKeyedArchiver.archivedData(withRootObject: place)
UserDefaults.standard.set(encodedData, forKey: "place")
UserDefaults.standard.synchronize()

Then you can decode it like this:

if UserDefaults.standard.object(forKey: "place") != nil{
    let decodedData = UserDefaults.standard.object(forKey: "place") as! Data
    let decodedPlace = NSKeyedUnarchiver.unarchiveObject(with: decodedData) as! Place
}

Updated for swift 4 and iOS 12:

do {
    let encodedData: Data = try NSKeyedArchiver.archivedData(withRootObject: place, requiringSecureCoding: false)
    UserDefaults.standard.set(encodedData, forKey: "place")
    UserDefaults.standard.synchronize()
} catch {
        //Handle Error
}

do {
    if UserDefaults.standard.object(forKey: "place") != nil{
        let decodedData = UserDefaults.standard.object(forKey: "place") as! Data
        if let decodedPlace = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(decodedData) as? Place {
             //Do Something with decodedPlace
        }
    }
}
catch {
        //Handle Error
}
Arie Pinto
  • 1,274
  • 1
  • 11
  • 19
  • Thank you for your answer. I receive this warning: `'archivedData(withRootObject:)' was deprecated in iOS 12.0: Use +archivedDataWithRootObject:requiringSecureCoding:error: instead` and for the decoder `'unarchiveObject(with:)' was deprecated in iOS 12.0: Use +unarchivedObjectOfClass:fromData:error: instead` – Ryan Nov 22 '18 at 23:22
  • I seem to be receiving this `-[Place encodeWithCoder:]: unrecognized selector sent to instance` any ideas? – Ryan Nov 23 '18 at 11:55
  • Yes, your Place class should conform to the NSCoding protocol, please refer to this discussion https://stackoverflow.com/questions/22168753/unrecognized-selector-sent-to-instance-while-archiving-data-nscoding – Arie Pinto Nov 23 '18 at 11:58
  • Thank you! I'm nearly there - why does `let decodedPlace = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(decodedData) as! Place` return ``? I tried printing decodedPlace.title but it returns nil? – Ryan Nov 23 '18 at 12:11
  • Well, i need to see more code to be able to help, maybe your Place class, any way, a better practice is to to an if let statement for the 'decodedPlace' variable so to prevent crashes, i will edit the answer – Arie Pinto Nov 23 '18 at 12:19
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/184142/discussion-between-ryan-and-arie-pinto). – Ryan Nov 23 '18 at 12:21