NSInvalidUnarchiveOperationException raised only in iOS extension, not main app - ios

I have archived an Array of my NSCoding-conforming class "Assignment" to a file in a shared (App Groups) container:
class private func updateSharedFiles() {
let path = NSFileManager().containerURLForSecurityApplicationGroupIdentifier("group.agenda-touch")!.path! + "/widgetData"
if let incompleteAssignmentsArray = self.incompleteAssignments {
NSKeyedArchiver.archiveRootObject(incompleteAssignmentsArray, toFile: path)
}
}
Now when my extension wants to read from that file I called NSFileManager.fileExistsAtPath: on the file and it returns true. Finally I call NSKeyedUnarchiver.unarchiveObjectWithFile(path) as? AssignmentArray
and get an NSInvalidUnarchiveOperationException. The thing is, when I unarchive the file in my main app, I get no such exception. And to be clear, I HAVE added Assignment.swift to my compile sources for the widget. So my TodayViewController knows what Assignment is, but can't decode it for some reason.
As an addendum, here is the NSCoding implementation for Assignment:
//MARK: NSCoding
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(self.assignmentName, forKey: "assignmentName")
aCoder.encodeObject(self.assignmentDueDate, forKey: "assignmentDueDate")
aCoder.encodeObject(self.assignmentSubject, forKey: "assignmentSubject")
aCoder.encodeBool(self.completed, forKey: "assignmentCompleted")
aCoder.encodeObject(self.creationDate, forKey: "assignmentCreationDate")
aCoder.encodeInteger(self.assignmentType.rawValue, forKey: "assignmentType")
aCoder.encodeBool(self.earmarkedForDeletion, forKey: "assignmentEarmarked")
aCoder.encodeObject(self.modificationDates, forKey: "assignmentModificationDates")
}
required convenience init(coder aDecoder: NSCoder) {
let assignmentName = aDecoder.decodeObjectForKey("assignmentName") as String
let assignmentDueDate = aDecoder.decodeObjectForKey("assignmentDueDate") as NSDate
let assignmentSubject = aDecoder.decodeObjectForKey("assignmentSubject") as String
let completed = aDecoder.decodeBoolForKey("assignmentCompleted")
self.init(name:assignmentName,dueDate:assignmentDueDate,subject:assignmentSubject,completed:completed)
self.creationDate = aDecoder.decodeObjectForKey("assignmentCreationDate") as NSDate
let assignmentTypeRaw = aDecoder.decodeIntegerForKey("assignmentType")
self.assignmentType = AssignmentType(rawValue: assignmentTypeRaw)!
self.earmarkedForDeletion = aDecoder.decodeBoolForKey("assignmentEarmarked")
self.modificationDates = aDecoder.decodeObjectForKey("assignmentModificationDates") as Dictionary<String,NSDate>
}

I have the exact same problem. I tried to remove and add the file to and from compile sources. Not working either. Have you found a solution yet?
Only difference with my problem: I try to unarchive an NSArray of objects xxx. It works, when archiving and unarchiving in the main app OR archiving and unarchiving in the WatchKit app, but not when I archive it in the main app and unarchive it in the WatchKit app (and otherwise).
let defaults: NSUserDefaults = NSUserDefaults(suiteName: "group.name")!
if let object: NSData = defaults.objectForKey(ArrayKey) as? NSData {
if let array: AnyObject = NSKeyedUnarchiver.unarchiveObjectWithData(object) {
return array as NSArray
}
}
return NSArray()
The exchange of objects through NSUserDefaults within my app group works. I only experience this problem with the NSKeyedArchiver and NSKeyedUnarchiver of an NSArray with objects xxx.
* Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: '* -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class xxx
EDIT: I resolved the issue in making my object NSSecureCoding compliant. I therefore replaced
propertyName = aDecoder.decodeObjectForKey("propertyName")
with
propertyName = aDecoder.decodeObjectOfClass(NSString.self, forKey: "propertyName") as NSString
and added
class func supportsSecureCoding() -> Bool {
return true
}
I also set the className of NSKeyedUnarchiver und NSKeyedArchiver in applicationFinishLaunchingWithOptions of the main app and in the first WKInterfaceController in the WatchKitApp
NSKeyedArchiver.setClassName("Song", forClass: Song.self)
NSKeyedUnarchiver.setClass(Song.self, forClassName: "Song")

I also had the exact same problem.
To sum up from previous answers and comments, the solution lies the setClass/setClassName statements. I wrap my encoding as functions in the example code below (Swift 5):
import Foundation
public class User: NSObject, NSSecureCoding
{
public var id: String
public var name: String
public init(id: String, name: String)
{
self.id = id
self.name = name
}
// NSSecureCoding
public static var supportsSecureCoding: Bool { return true }
public func encode(with coder: NSCoder)
{
coder.encode(id , forKey: "id")
coder.encode(name, forKey: "name")
}
public required init?(coder: NSCoder)
{
guard
let id = coder.decodeObject(forKey: "id") as? String,
let name = coder.decodeObject(forKey: "name") as? String
else {
return nil
}
self.id = id
self.name = name
}
// (Un)archiving
public static func decode(from data: Data) -> Self?
{
NSKeyedUnarchiver.setClass(Self.self, forClassName: String(describing: Self.self))
return try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? Self
}
public func encode() -> Data?
{
NSKeyedArchiver.setClassName(String(describing: Self.self), for: Self.self)
return try? NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: Self.supportsSecureCoding)
}
}

Related

Issues with userdefaults usage swift 3

I just started learning Swift so the level of this question may be obvious enough for you, however, I simply can't understand what to do.
I "created" a simple app which allows you to add logs of your day. Each cell stores the time you added, a custom icon that changes depending on the time, and, the log text itself (simple text).
Everything works fine. But, as I didn't know about the "Userdefaults" stuff, the clock resets every time I kill the app.
I read many articles about Userdefaults but I have no idea what to do to keep saving my data even when I kill the app.
Here's what I tried to do:
class ToDoItem: NSObject, NSCoding {
var title: String
var date: String
var type: String!
public init(title: String, date: String, type: String) {
self.title = title
self.date = date
self.type = type
}
required init?(coder aDecoder: NSCoder)
{
// Try to unserialize the "title" variable
if let title = aDecoder.decodeObject(forKey: "title") as? String
{
self.title = title
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .none
dateFormatter.timeStyle = .short
self.date = dateFormatter.string(from: Date())
let hour = NSCalendar.current.component(.hour, from: Date())
var tempType = ""
switch hour {
case 5..<9: tempType = "morning_1"
case 6..<12: tempType = "morning_2"
case 12: tempType = "noon_1"
case 13..<16: tempType = "afternoon_1"
case 16..<20: tempType = "dusk_1"
case 20..<23: tempType = "evening_1"
case 23..<00: tempType = "midnight_1"
default: tempType = "morning_1"
}
self.type = tempType
}
else
{
// There were no objects encoded with the key "title",
// so that's an error.
return nil
}
let userDefaults = UserDefaults.standard
userDefaults.set(true, forKey: "title")
}
func encode(with aCoder: NSCoder)
{
// Store the objects into the coder object
aCoder.encode(self.title, forKey: "title")
let defaults = UserDefaults.standard
defaults.set(false, forKey: "title")
}
}
extension Collection where Iterator.Element == ToDoItem
{
// Builds the persistence URL. This is a location inside
// the "Application Support" directory for the App.
private static func persistencePath() -> URL?
{
let url = try? FileManager.default.url(
for: .applicationSupportDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: true)
return url?.appendingPathComponent("todoitems.bin")
}
// Write the array to persistence
func writeToPersistence() throws
{
if let url = Self.persistencePath(), let array = self as? NSArray
{
let data = NSKeyedArchiver.archivedData(withRootObject: array)
try data.write(to: url)
}
else
{
throw NSError(domain: "com.example.MyToDo", code: 10, userInfo: nil)
}
}
// Read the array from persistence
static func readFromPersistence() throws -> [ToDoItem]
{
if let url = persistencePath(), let data = (try Data(contentsOf: url) as Data?)
{
if let array = NSKeyedUnarchiver.unarchiveObject(with: data) as? [ToDoItem]
{
return array
}
else
{
throw NSError(domain: "com.example.MyToDo", code: 11, userInfo: nil)
}
}
else
{
throw NSError(domain: "com.example.MyToDo", code: 12, userInfo: nil)
}
}
}
can anyone help me or at least point what I have to do? thank you!
You're using NSKeyedArchiver and NSKeyedUnarchiver which is a different mechanism than UserDefaults. Both are perfectly valid, but you have to pick one, they don't work together.
Here you are archiving and unarchiving an array of ToDoItem. For this to work, ToDoItem needs to be archivable, meaning it must implement the NSCoding protocol, which is:
public protocol NSCoding {
public func encode(with aCoder: NSCoder)
public init?(coder aDecoder: NSCoder) // NS_DESIGNATED_INITIALIZER
}
All the properties that you want to save must be added to/extracted from the NSCoder object. Here is an example:
class ToDoItem: NSObject, NSCoding
{
var title: String
var date: Date
required init?(coder aDecoder: NSCoder)
{
guard let title = aDecoder.decodeObject(forKey: "title") as? String else {
return nil
}
guard let date = aDecoder.decodeObject(forKey: "date") as? Date else {
return nil
}
self.title = title
self.date = date
}
func encode(with aCoder: NSCoder)
{
aCoder.encode(self.title, forKey: "title")
aCoder.encode(self.date, forKey: "date")
}
}

fatal error: unexpectedly found nil while unwrapping an Optional value in Swift 3

this Struct is work in swift 2
I have a Swift 3 struct like this.
let tempContacts = NSMutableArray()
let arrayOfArray = NSMutableArray()
I have encode The Person Object in this for loop
for person in tempContacts as! [Person] {
let encodedObject: Data = NSKeyedArchiver.archivedData(withRootObject: person) as Data
arrayOfArray.add(encodedObject)
}
I have decode the data in this for loop
let tempContacts2 = NSMutableArray()
for data in arrayOfArray {
let person: Person = NSKeyedUnarchiver.unarchiveObject(with: data as! Data) as! Person
tempContacts2.add(person)
}
but unarchiveObject is always return nil value
First your model class should conform to the NSCoder protocol. The rest is really simple, there's no need to store the archived results for each object in an array, you can pass the initial array directly to NSKeyedArchiver like this :
class Person: NSObject, NSCoding {
var name = ""
init(name: String) {
self.name = name
}
// NSCoder
required convenience init?(coder decoder: NSCoder) {
guard let name = decoder.decodeObject(forKey: "name") as? String else { return nil }
self.init(name: name)
}
func encode(with coder: NSCoder) {
coder.encode(self.name, forKey: "name")
}
}
let tempContacts = [Person(name: "John"), Person(name: "Mary")]
let encodedObjects = NSKeyedArchiver.archivedData(withRootObject: tempContacts)
let decodedObjects = NSKeyedUnarchiver.unarchiveObject(with: encodedObjects)
As a side note : if NSCoder compliance is correctly implemented in your model class, you can of course use your way of archiving/unarchiving individual objects too. So your original code works too, with some minor adjustments:
for person in tempContacts {
let encodedObject = NSKeyedArchiver.archivedData(withRootObject: person)
arrayOfArray.add(encodedObject)
}
var tempContacts2 = [Person]()
for data in arrayOfArray {
let person: Person = NSKeyedUnarchiver.unarchiveObject(with: data as! Data) as! Person
tempContacts2.append(person)
}
Note 2: if you absolutely wants to use NSMutableArrays that's possible too, just define tempContacts like this:
let tempContacts = NSMutableArray(array: [Person(name: "John"), Person(name: "Mary")])
The rest is working without changes.
Note 3: The reason it used to work in Swift 2 and it's not working anymore in Swift 3 is that the signature for the NSCoder method func encode(with coder:) changed in Swift 3.

NSKeyedArchiver Custom Object Array

NOTE: Xcode 8 Beta 6
I am not sure what I am missing but for some reason I am getting the following error on the NSKeyedArchiver.archivedData line.
'NSInvalidArgumentException', reason: '-[_SwiftValue encodeWithCoder:]: unrecognized selector sent to instance 0x60000024c690'
*** First throw call stack:
Here is my class which conforms to the NSCoding protocol:
enum PhraseType {
case create
case item
}
class Phrase: NSObject, NSCoding {
var englishPhrase :String!
var translatedPhrase :String!
var phraseType :PhraseType! = PhraseType.item
required init?(coder decoder: NSCoder) {
self.englishPhrase = decoder.decodeObject(forKey: "englishPhrase") as! String
self.translatedPhrase = decoder.decodeObject(forKey: "translatedPhrase") as! String
self.phraseType = decoder.decodeObject(forKey: "phraseType") as! PhraseType
}
func encode(with coder: NSCoder) {
coder.encode(self.englishPhrase, forKey: "englishPhrase")
coder.encode(self.translatedPhrase, forKey: "translatedPhrase")
coder.encode(self.phraseType, forKey: "phraseType")
}
init(englishPhrase :String, translatedPhrase :String) {
self.englishPhrase = englishPhrase
self.translatedPhrase = translatedPhrase
super.init()
}
}
And here is the code for the archiving:
let userDefaults = UserDefaults.standard
var phrases = userDefaults.object(forKey: "phrases") as? [Phrase]
if phrases == nil {
phrases = [Phrase]()
}
phrases?.append(phrase)
let phrasesData = NSKeyedArchiver.archivedData(withRootObject: phrases)
userDefaults.set(phrasesData, forKey: "phrases")
userDefaults.synchronize()
Any ideas?
You can't encode a Swift enum such as PhraseType. Instead, give this enum a raw value and encode the raw value (and on decode, use that to reconstruct the correct enum case).
When you've done that, you'll need to cast your Swift array to NSArray to get it archived properly.

NSKeyedUnarchiver won't return data

I am making an app that tracks a user's workouts. I have two custom classes, the first being ExerciseModel, which holds the data for each exercise performed during the workout, including the name, sets, reps, etc. Here is my data model:
import UIKit
class ExerciseModel: NSObject, NSCoding
{
// MARK: Properties
var name: String
var sets: Int
var reps: Int
var heartrate: Int?
var type: String?
//MARK: Archiving Paths
static let DocumentsDirectory = NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
static let ArchiveURL = DocumentsDirectory.URLByAppendingPathComponent("exercises")
// MARK: Initialization
init?(name: String, sets: Int, reps: Int, heartrate: Int?, type: String)
{
// MARK: Initlaize stored properties
self.name = name
self.sets = sets
self.reps = reps
self.heartrate = heartrate
self.type = type
super.init()
// Initialization should fail if there is no name or sets is negative
if name.isEmpty || sets < 0
{
return nil
}
}
struct PropertyKey
{
static let nameKey = "name"
static let setKey = "sets"
static let repKey = "reps"
static let heartrateKey = "heartrate"
static let typekey = "type"
}
// MARK: NSCoding
func encodeWithCoder(aCoder: NSCoder)
{
aCoder.encodeObject(name, forKey: PropertyKey.nameKey)
aCoder.encodeInteger(sets, forKey: PropertyKey.setKey)
aCoder.encodeInteger(reps, forKey: PropertyKey.repKey)
aCoder.encodeObject(type, forKey: PropertyKey.typekey)
}
required convenience init?(coder aDecoder: NSCoder)
{
let name = aDecoder.decodeObjectForKey(PropertyKey.nameKey) as! String
let sets = aDecoder.decodeIntegerForKey(PropertyKey.setKey)
let reps = aDecoder.decodeIntegerForKey(PropertyKey.repKey)
let heartrate = aDecoder.decodeIntegerForKey(PropertyKey.heartrateKey)
let type = aDecoder.decodeObjectForKey(PropertyKey.typekey) as? String
// Must call designated initializer
self.init(name: name, sets: sets, reps: reps, heartrate: heartrate, type: type!)
}
init?(name: String, sets: Int, reps: Int, heartrate: Int, type: String)
{
// Initialize stored properties.
self.name = name
self.sets = sets
self.reps = reps
self.heartrate = heartrate
self.type = type
}
}
My second custom class is called WorkoutStorage, and this is meant to allow the user to save entire workouts and retrieve them later. The exercise property is an array of ExerciseModel objects, described above. Here is my data model for WorkoutStorage:
//
import UIKit
#objc(WorkoutStorage)
class WorkoutStorage: NSObject, NSCoding
{
// MARK: Properties
var name: String
var date: NSDate
var exercises: [ExerciseModel]
var maxHR: Int
var avgHR: Int
// MARK: Archiving Paths
static let DocumentsDirectory = NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
static let ArchiveURL = DocumentsDirectory.URLByAppendingPathComponent("storedWorkouts")
// MARK: Initialization
init?(name: String, date: NSDate, exercises: [ExerciseModel], maxHR: Int, avgHR: Int)
{
//MARK: Initialize Stored Properties
self.name = name
self.date = date
self.exercises = exercises
self.maxHR = maxHR
self.avgHR = avgHR
super.init()
}
struct PropertyKey
{
static let nameKey = "name"
static let dateKey = "date"
static let exercisesKey = "exercises"
static let maxHRKey = "maxHR"
static let avgHRKey = "avgHR"
}
// MARK: NSCoding
func encodeWithCoder(aCoder: NSCoder)
{
aCoder.encodeObject(name, forKey: PropertyKey.nameKey)
aCoder.encodeObject(date, forKey: PropertyKey.dateKey)
aCoder.encodeObject(exercises, forKey: PropertyKey.exercisesKey)
aCoder.encodeInteger(maxHR, forKey: PropertyKey.maxHRKey)
aCoder.encodeInteger(avgHR, forKey: PropertyKey.avgHRKey)
}
required convenience init?(coder aDecoder: NSCoder)
{
let name = aDecoder.decodeObjectForKey(PropertyKey.nameKey) as! String
let date = aDecoder.decodeObjectForKey(PropertyKey.dateKey) as! NSDate
let exercises = aDecoder.decodeObjectForKey(PropertyKey.exercisesKey) as! [ExerciseModel]
let maxHR = aDecoder.decodeIntegerForKey(PropertyKey.maxHRKey)
let avgHR = aDecoder.decodeIntegerForKey(PropertyKey.avgHRKey)
// Must call designated initializer
self.init(name: name, date: date, exercises: exercises, maxHR: maxHR, avgHR: avgHR)
}
}
I followed the Apple tutorial for Persist Data to set up NSKeyedArchiver and NSKeyedUnarchiver for this, but I am still having trouble retrieving my data. When I try to load the Workouts, I call the following function:
func loadStoredWorkouts() -> WorkoutStorage
{
NSKeyedUnarchiver.setClass(WorkoutStorage.self, forClassName: "WorkoutStorage")
NSKeyedArchiver.setClassName("WorkoutStorage", forClass: WorkoutStorage.self)
print("\(WorkoutStorage.ArchiveURL.path!)")
return NSKeyedUnarchiver.unarchiveObjectWithFile(WorkoutStorage.ArchiveURL.path!) as! WorkoutStorage
}
Currently I can only return a single WorkoutStorage object, but when I attempt to retrieve an array containing all the stored WorkoutStorage objects, I get an error saying: Could not cast value of type 'Workout_Tracker.WorkoutStorage' (0x1000fcc80) to 'NSArray' (0x19f6b2418). I have read a lot of documentation trying to figure out why this will only return a single object, as well as checked out questions with similar issues, but to no avail. I originally set up my app following the Apple Persist Data tutorial to store and load my ExerciseModel objects, and that seems to work flawlessly. I set up the WorkoutStorage class the same way, but there seems to be an issue here.
Any help would be greatly appreciated!!
**Edit*
Here is the code I use to archive the WorkoutStorage object:
func saveWorkoutStorageObject(currentWorkout: WorkoutStorage)
{
NSKeyedUnarchiver.setClass(WorkoutStorage.self, forClassName: "WorkoutStorage")
NSKeyedArchiver.setClassName("WorkoutStorage", forClass: WorkoutStorage.self)
let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(currentWorkout, toFile: WorkoutStorage.ArchiveURL.path!)
if !isSuccessfulSave
{
print("Failed to save exercises")
}
if isSuccessfulSave
{
print("Successful save of current workout: \(currentWorkout)")
}
}
Workouts are only created one at a time by the user, so each time one is completed, I pass the object to the above function to archive it.
To unarchive all the objects, I was trying to do something along the lines of:
var workouts = [WorkoutStorage]()
override func viewDidLoad()
{
super.viewDidLoad()
workouts = loadStoredWorkouts()
}
where the loadStoredWorkouts() function would be:
func loadStoredWorkouts() -> [WorkoutStorage]
{
NSKeyedUnarchiver.setClass(WorkoutStorage.self, forClassName: "WorkoutStorage")
NSKeyedArchiver.setClassName("WorkoutStorage", forClass: WorkoutStorage.self)
print("\(WorkoutStorage.ArchiveURL.path!)")
return NSKeyedUnarchiver.unarchiveObjectWithFile(WorkoutStorage.ArchiveURL.path!) as! [WorkoutStorage]
}
Your saveWorkoutStorageObject only archives a single workout. It doesn't archive the array, so of course you can't unarchive an array.
You need to archive the workouts array if you want to be able to unarchive an array.
Each time you archive something to a file you replace the contents of the file. It doesn't append to the end.
Since NSKeyedArchiver.archiveRootObject automatically archives child objects, all you need to do is archive the array and your WorkoutStorage objects will be archived automagically
func saveWorkouts(workouts:[WorkoutStorage])
{
let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(workouts, toFile: WorkoutStorage.ArchiveURL.path!)
if isSuccessfulSave
{
print("Successful save of workouts: \(workouts)")
} else {
print("Failed to save exercises")
}
}

Saving Realm Object subclass in NSUserDefaults?

I'm introducing Realm into my swift project. I have a User class that I was saving an instance of into NSUserDefaults to keep track of the 1 logged in user.
After making User a subclass of Object, I get the following error when trying to unarchive (archiving seems to work OK):
Terminating app due to uncaught exception
'NSInvalidUnarchiveOperationException', reason: '***
-[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (RLMStandalone_User) for key (root); the class may be defined in
source code or a library that is not linked'
I have Realm installed as a Cocoapod, this are the relevant methods in the User clas
static var currentUser: User? {
get {
if let data = NSUserDefaults.standardUserDefaults().objectForKey(UserDefaultKeys.kUserData) as? NSData,
let user = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? User {
return user
} else {
return nil
}
}
set {
if let user = newValue {
let data = NSKeyedArchiver.archivedDataWithRootObject(user)
NSUserDefaults.standardUserDefaults().setObject(data, forKey: UserDefaultKeys.kUserData)
NSUserDefaults.standardUserDefaults().synchronize()
} else {
NSUserDefaults.standardUserDefaults().removeObjectForKey(UserDefaultKeys.kUserData)
NSUserDefaults.standardUserDefaults().synchronize()
}
}
}
// MARK: NSCoding
convenience init?(coder decoder: NSCoder) {
self.init()
guard let firstName = decoder.decodeObjectForKey("firstName") as? String,
let lastName = decoder.decodeObjectForKey("lastName") as? String,
let email = decoder.decodeObjectForKey("email") as? String,
let icloud = decoder.decodeObjectForKey("icloudUserID") as? String,
let userType = decoder.decodeObjectForKey("userType") as? String
else {
return nil
}
self.firstName = firstName
self.lastName = lastName
self.email = email
self.profilePic = decoder.decodeObjectForKey("profilePic") as? String
self.icloudUserID = icloud
self.userType = userType
self.coverPhoto = decoder.decodeObjectForKey("coverPhoto") as? String
self.facebookID = decoder.decodeObjectForKey("facebookID") as? String
self.placeID = decoder.decodeObjectForKey("placeID") as? String
}
func encodeWithCoder(coder: NSCoder) {
coder.encodeObject(self.firstName, forKey: "firstName")
coder.encodeObject(self.lastName, forKey: "lastName")
coder.encodeObject(self.icloudUserID, forKey: "icloudUserID")
coder.encodeObject(self.userType, forKey: "userType")
coder.encodeObject(email, forKey: "email")
if let coverPhotoUrl = self.coverPhotoUrl {
coder.encodeObject(coverPhotoUrl, forKey: "coverPhoto")
}
if let profilePicUrl = self.profilePicUrl {
coder.encodeObject(profilePicUrl, forKey: "profilePic")
}
if let fbID = self.facebookID {
coder.encodeObject(fbID, forKey: "facebookID")
}
if let placeID = self.placeID {
coder.encodeObject(placeID, forKey: "placeID")
}
}
It's not possible to store a Realm Object in NSUserDefaults as (As you saw in that error message) they cannot be serialized or deserialized by the NSCoding protocol.
Instead, it might be better to add a primary key property to your User object (So you can use it to query that exact object from Realm), and store the primary key itself in NSUserDefaults instead.
Or better yet, instead of relying on NSUSerDefaults, it might be better to simply have a boolean property, isCurrent in your model, and using that to work out which user is the current one.
You should keep your NSUserDefaults and your Realm data separate. They are two different methods of persistent data storage. If you've converted something to a Realm Object, you no longer need to (or should) try and put that into NSUserDefaults.

Resources