I try to persiste my object with NSCoding but i always get BAD_ACCESS ERROR To avoid multi multiple like variable, class, i put all common variable in RObject. I think i do something wrong the the init but i don't know what.
the error was thow in this function
func parseInfo(allInfos : String) -> Void {
if let all : JSON = JSON.parse(allInfos) as JSON? {
if let info = all.asArray
{
for description in info
{
var track : RInfo = SCTracks(js: description)
self.arrayTracks.addObject(track)
} // Therad 1: EXC_BAD_ACCESS(code=2, address=0x27...)
}
}
}
The Log doesn't show any thing
My Common Class
class RObject : NSObject, NSCoding {
var id : Int? = 0
var kind : String?
override init() { super.init() }
init(js :JSON) {
self.kind = js["kind"].asString
self.id = js["id"].asInt
super.init()
}
required
init(coder aDecoder: NSCoder) {
self.id = aDecoder.decodeIntegerForKey("id") as Int
self.kind = aDecoder.decodeObjectForKey("kind") as? String
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeInteger(self.id!, forKey: "id")
aCoder.encodeObject(self.kind, forKey: "kind")
}
}
My class Rinfo who inherits from RObject
class RInfo : RObject {
var title :String?
var uri :String?
var license :String?
var release :String?
var user :RUser!
override init() { super.init() }
required init(coder: NSCoder) {
self.title = coder.decodeObjectForKey("title") as? String
self.user = coder.decodeObjectForKey("user") as RUser
self.license = coder.decodeObjectForKey("license") as? String
self.uri = coder.decodeObjectForKey("uri") as? String
self.release = coder.decodeObjectForKey("release") as? String
super.init(coder: coder)
}
init(js :JSON) {
self.user = js(js: js["user"])
self.title = js["title"].asString
self.license = js["license"].asString
self.uri = js["uri"].asString
self.release = js["release"].asString
super.init(js: js)
}
override func encodeWithCoder(encoder: NSCoder) {
encoder.encodeObject(self.title, forKey: "title")
encoder.encodeObject(self.user, forKey: "user")
encoder.encodeObject(self.uri, forKey: "uri")
encoder.encodeObject(self.license, forKey: "license")
}
}
Thanks for any help !
I Solve my problem by remove variable release in RInfo. that strange
Related
I am using Core Data for persistent storage and I am getting the error listed below. I have looked up the message and I know it has something to do with the fact that I am using the transformable and a custom class. Despite my research I am not sure how to fix it. My attempt at conforming to the NSSecureCoding protocol failed miserably. I am posting my original code because I think it might be easier to try and solve the issue from scratch rather than trying to fix my poor attempt at NSSecureCoding. Thank you in advance! Any help is much appreciated.
'NSKeyedUnarchiveFromData' should not be used to for un-archiving and
will be removed in a future release
My Entity:
My Custom Class:
public class SelectedImages: NSObject, NSCoding {
public var images: [SelectedImage] = []
enum Key: String {
case images = "images"
}
init(images: [SelectedImage]) {
self.images = images
}
public func encode(with aCoder: NSCoder) {
aCoder.encode(images, forKey: Key.images.rawValue)
}
public required convenience init?(coder aDecoder: NSCoder) {
let mImages = aDecoder.decodeObject(forKey: Key.images.rawValue) as! [SelectedImage]
self.init(images: mImages)
}
}
public class SelectedImage: NSObject, NSCoding {
public var location: Int = 0
public var duration: Int = 10
public var localIdentifier: String = ""
enum Key: String {
case location = "location"
case duration = "duration"
case localIdentifier = "localIdentifier"
}
init(location: Int, duration: Int, localIdentifier: String) {
self.location = location
self.duration = duration
self.localIdentifier = localIdentifier
}
public override init() {
super.init()
}
public func encode(with aCoder: NSCoder) {
aCoder.encode(location, forKey: Key.location.rawValue)
aCoder.encode(duration, forKey: Key.duration.rawValue)
aCoder.encode(localIdentifier, forKey: Key.localIdentifier.rawValue)
}
public required convenience init?(coder aDecoder: NSCoder) {
let mlocation = aDecoder.decodeInt32(forKey: Key.location.rawValue)
let mduration = aDecoder.decodeInt32(forKey: Key.duration.rawValue)
let mlocalIdentifier = aDecoder.decodeObject(forKey: Key.localIdentifier.rawValue) as! String
self.init(location: Int(mlocation), duration:Int(mduration), localIdentifier:String(mlocalIdentifier))
}
}
View Controller:
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let userEntity = NSEntityDescription.entity(forEntityName: "EntityTest", in: managedContext)!
let selectedImages = NSManagedObject(entity: userEntity, insertInto: managedContext) as! EntityTest
let mImages = SelectedImages(images: selectionArrays)
selectedImages.setValue(mImages, forKeyPath: "image")
do {
try managedContext.save()
print("Images saved to core data")
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
let newViewController = PickerTest6()
self.navigationController?.pushViewController(newViewController, animated: true)
I haven't run this code, but it should work.
Make changes to the SelectedImage class.
public class SelectedImage: NSObject, NSSecureCoding { // Class must inherit from NSSecureCoding
public static var supportsSecureCoding: Bool = true // It's the required property
public var location: Int = 0
public var duration: Int = 10
public var localIdentifier: String = ""
private enum CodingKeys: String {
case location, duration, localIdentifier
}
public override init() {
super.init()
}
init(location: Int, duration: Int, localIdentifier: String) {
self.location = location
self.duration = duration
self.localIdentifier = localIdentifier
}
public required init?(coder: NSCoder) {
self.location = coder.decodeInteger(forKey: CodingKeys.location.rawValue)
self.duration = coder.decodeInteger(forKey: CodingKeys.duration.rawValue)
// Now instead of decodeObject(forKey:) you should use decodeObject(of: forKey:).
self.localIdentifier = coder.decodeObject(of: NSString.self, forKey: CodingKeys.localIdentifier.rawValue) as String? ?? ""
}
public func encode(with coder: NSCoder) {
coder.encode(location, forKey: CodingKeys.location.rawValue)
coder.encode(duration, forKey: CodingKeys.duration.rawValue)
coder.encode(localIdentifier, forKey: CodingKeys.localIdentifier.rawValue)
}
}
Create the SelectedImageTransformer class.
#objc(SelectedImageTransformer)
final class SelectedImageTransformer: NSSecureUnarchiveFromDataTransformer {
static let name = NSValueTransformerName(rawValue: String(describing: SelectedImageTransformer.self))
override class var allowedTopLevelClasses: [AnyClass] {
return super.allowedTopLevelClasses + [SelectedImage.self]
}
public class func register() {
let transformer = SelectedImageTransformer()
ValueTransformer.setValueTransformer(transformer, forName: name)
}
}
Edit the CoreData model as follows.
Call the register method in AppDelegate (if you use the UIKit) before initializing the persistent container.
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
// Register the transformer
SelectedImageTransformer.register()
let container = NSPersistentContainer(name: "AppName")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
For more details, you can read this or this article.
When try to encode my custom object in iOS swift get this error from Xcode 8.3
unrecognized selector sent to instance 0x60800166fe80
*** -[NSKeyedArchiver dealloc]: warning: NSKeyedArchiver deallocated without having had -finishEncoding called on it.
And my code like this:
import UIKit
import Foundation
class Place: NSObject {
func setCustomObject(CustomObject obj:Any,Key key:String) {
let encodedObject : Data = NSKeyedArchiver.archivedData(withRootObject: obj)
UserDefaults.standard.set(encodedObject, forKey: key)
}
}
Here's an example how to make an object to conform to NSCoding. Basically you need to provide implementation of two methods - required convenience init?(coder decoder: NSCoder) and encode(with aCoder: NSCoder)
class Book: NSObject, NSCoding {
var title: String?
var pageCount: Int?
// Memberwise initializer
init(title: String,pageCount: Int) {
self.title = title
self.pageCount = pageCount
}
// MARK: NSCoding
// Here you will try to initialize an object from archve using keys you did set in `encode` method.
required convenience init?(coder decoder: NSCoder) {
guard let title = decoder.decodeObject(forKey: "title") as? String else { return nil }
self.init(title: title, pageCount: decoder.decodeInteger(forKey: "pageCount"))
}
// Here you need to set properties to specific keys in archive
func encode(with aCoder: NSCoder) {
aCoder.encode(self.title, forKey: "title")
aCoder.encodeCInt(Int32(self.pageCount), forKey: "pageCount")
}
}
Also I would recommend changing your setCustomObject method to this:
func setCustomObject(obj:NSCoding, key:String) {
let encodedObject : Data = NSKeyedArchiver.archivedData(withRootObject: obj)
UserDefaults.standard.set(encodedObject, forKey: key)
}
This way compiler prevent you passing NSKeyedArchiver an object that does not conform to NSCoding protocol.
If you don't want to provide all properties in the init method you can use default values:
init(title : String? = nil, pageCount: Int? = nil){
self.title = title
self.pageCount = pageCount
}
Now you can just init your object without any properties. Like that Book()
Here is the solutions, you have to implement the two methods
Encode Method
func encode(with aCoder: NSCoder)
Decoding method
required init?(coder aDecoder: NSCoder)
Complete Example code
class User: NSObject , NSCoding
{
var userID : Int = 0
var name : String = ""
var firstName : String = ""
var lastName : String = ""
var username : String = ""
var email : String = ""
override init(){
super.init();
}
func encode(with aCoder: NSCoder) {
aCoder.encode(self.userID, forKey: "id");
aCoder.encode(self.firstName, forKey: "first_name");
aCoder.encode(self.lastName, forKey: "last_name");
aCoder.encode(self.name, forKey: "name");
aCoder.encode(self.username,forKey: "username");
aCoder.encode(self.email, forKey: "email");
}
required init?(coder aDecoder: NSCoder) {
super.init()
self.userID = aDecoder.decodeInteger(forKey: "id");
self.firstName = aDecoder.decodeObject(forKey: "first_name") as! String;
self.lastName = aDecoder.decodeObject(forKey: "last_name") as! String;
self.name = aDecoder.decodeObject(forKey: "name") as! String
self.username = aDecoder.decodeObject(forKey: "username") as! String
self.email = aDecoder.decodeObject(forKey: "email") as! String;
}
init(data : [String: AnyObject]) {
super.init()
self.userID = String.numberValue(data["user_id"]).intValue;
self.firstName = String.stringValue(data["first_name"]);
self.lastName = String.stringValue(data["last_name"]);
self.email = String.stringValue(data["email"]);
self.username = String.stringValue(data["user_name"]);
}
class func loadLoggedInUser() -> User {
if let archivedObject = UserDefaults.standard.object(forKey:"CurrentUserAcc"){
if let user = NSKeyedUnarchiver.unarchiveObject(with: (archivedObject as! NSData) as Data) as? User {
return user;
}
}
return User()
}
func saveUser(){
let archivedObject : NSData = NSKeyedArchiver.archivedData(withRootObject: self) as NSData
UserDefaults.standard.set(archivedObject, forKey: "CurrentUserAcc");
UserDefaults.standard.synchronize();
}
func deleteUser(){
UserDefaults.standard.set(nil, forKey: "CurrentUserAcc")
UserDefaults.standard.synchronize();
}
}
I have a class Person, which is a model
import Foundation
public class Person : NSObject , NSCoding{
public var name:String="";
public var gender:Int = 0;
public var status:Int = 0
override init (){
super.init()
}
public init (name:String, gender:Int){
self.name=name;
self.gender=gender;
}
required public init(coder aDecoder: NSCoder) { }
public func encodeWithCoder(_aCoder: NSCoder) { }
}
Now when I try to retreive [Person] array I use method getPersons(arr : String!) . If there is no data in NSUserDefaults , I create it, put 1 element into it and store using setPersons()method.
The problem is that on retrieving I get an array with correct count (1), but the object is not filled, having "" in String and 0 in Int variables.
private func archivePeople (people : [Person]) -> NSData{
return NSKeyedArchiver.archivedDataWithRootObject(people as NSArray)
}
public func getPersons(arr : String!) -> [Person]{
var array : [Person] = []
if let unarchivedObject = NSUserDefaults.standardUserDefaults().objectForKey(arr) as? NSData {
array = (NSKeyedUnarchiver.unarchiveObjectWithData(unarchivedObject) as? [Person])!
}
if array.count == 0 {
array.append(Person(name: A.DEFAULT_THEIR_NAMES,gender: 0))
setPersons(arr, people: array)
}
return array
}
public func setPersons(key: String, people : [Person]){
let archivedObject = archivePeople(people)
userDefaults.setObject(archivedObject, forKey: key)
userDefaults.synchronize()
}
What is wrong? Do I set or retrieve this in a wrong way?
You need to override properly the NSCoding functions.
It should look like something the code below:
required public convenience init(coder aDecoder: NSCoder) {
let name: String = aDecoder.decodeObject(forKey: "name") as! String
let gender = aDecoder.decodeInt64(forKey: "gender")
let status = aDecoder.decodeInt64(forKey: "status")
self.init(name: name, gender: Int(gender))
self.status = Int(status)
}
public func encodeWithCoder(_aCoder: NSCoder) {
_aCoder.encode(self.name, forKey: "name")
_aCoder.encode(Int64(self.gender), forKey: "gender")
_aCoder.encode(Int64(self.status), forKey: "status")
}
I'm trying to use NSKeyedArchiver to store custom class instance.
class feedBack: NSObject, NSCoding {
var choiceA = 0
var choiceB = 0
var choiceC = 0
var choiceD = 0
var choiceNULL = 0
var sheetName = ""
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(self.choiceA, forKey: "choiceA")
aCoder.encodeObject(self.choiceB, forKey: "choiceB")
aCoder.encodeObject(self.choiceC, forKey: "choiceC")
aCoder.encodeObject(self.choiceD, forKey: "choiceD")
aCoder.encodeObject(self.choiceNULL, forKey: "choiceNULL")
aCoder.encodeObject(self.sheetName, forKey: "sheetName")
}
required init(coder aDecoder: NSCoder) {
self.choiceA = aDecoder.decodeObjectForKey("choiceA") as! Int
self.choiceB = aDecoder.decodeObjectForKey("choiceB") as! Int
self.choiceC = aDecoder.decodeObjectForKey("choiceC") as! Int
self.choiceD = aDecoder.decodeObjectForKey("choiceD") as! Int
self.choiceNULL = aDecoder.decodeObjectForKey("choiceNULL") as! Int
self.sheetName = aDecoder.decodeObjectForKey("sheetName") as! String
}
}
Before feedBack conforms to NSCoding, I use var fb = feedBack() to create a new instance of feedBack.
Now the compiler throws Missing argument for parameter coder in call error.
Since the initWithCoder is required, how do I call the previous initializer with no parameter?
Just override init() and that should do it.
class feedBack: NSObject, NSCoding {
var choiceA = 0
var choiceB = 0
var choiceC = 0
var choiceD = 0
var choiceNULL = 0
var sheetName = ""
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(self.choiceA, forKey: "choiceA")
aCoder.encodeObject(self.choiceB, forKey: "choiceB")
aCoder.encodeObject(self.choiceC, forKey: "choiceC")
aCoder.encodeObject(self.choiceD, forKey: "choiceD")
aCoder.encodeObject(self.choiceNULL, forKey: "choiceNULL")
aCoder.encodeObject(self.sheetName, forKey: "sheetName")
}
required init(coder aDecoder: NSCoder) {
self.choiceA = aDecoder.decodeObjectForKey("choiceA") as! Int
self.choiceB = aDecoder.decodeObjectForKey("choiceB") as! Int
self.choiceC = aDecoder.decodeObjectForKey("choiceC") as! Int
self.choiceD = aDecoder.decodeObjectForKey("choiceD") as! Int
self.choiceNULL = aDecoder.decodeObjectForKey("choiceNULL") as! Int
self.sheetName = aDecoder.decodeObjectForKey("sheetName") as! String
}
override init(){
}
}
feedBack()
I found this black of code on NSCoding and it almost does want I want it to. the link for where I found it is below.
How do I create a NSCoding class and user in in other classes? The below code dies not work. I hope some can help me with this.
import Foundation
import UIKit
class User: NSObject, NSCoding {
var name: String
init(name: String) {
self.name = name
}
required init(coder aDecoder: NSCoder) {
self.name = aDecoder.decodeObjectForKey("name") as String
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(name, forKey: "name")
}
}
//new class where I want to set and get the object
class MyNewClass: UIViewController {
let user = User(name: "Mike")
let encodedUser = NSKeyedArchiver.archivedDataWithRootObject(user)
let decodedUser = NSKeyedUnarchiver.unarchiveObjectWithData(encodedUser) as User
}
//http://stackoverflow.com/questions/24589933/nskeyedunarchiver-fails-to-decode-a-custom-object-in-swift
I'm cutting and pasting from my own project below. I have limited this to one string parameter to store to file. But you can more of different types. You can paste this into a single swift file and use it as the ViewController plus added classes to test. It demonstrates using NSCoding with swift syntax to save and retrieve data in an object.
import UIKit
import Foundation
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var instanceData = Data()
instanceData.name = "testName"
ArchiveData().saveData(nameData: instanceData)
let retrievedData = ArchiveData().retrieveData() as Data
println(retrievedData.name)
}
}
class Data: NSObject {
var name: String = ""
func encodeWithCoder(aCoder: NSCoder!) {
aCoder.encodeObject(name, forKey: "name")
}
init(coder aDecoder: NSCoder!) {
name = aDecoder.decodeObjectForKey("name") as String
}
override init() {
}
}
class ArchiveData:NSObject {
var documentDirectories:NSArray = []
var documentDirectory:String = ""
var path:String = ""
func saveData(#nameData: Data) {
documentDirectories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
documentDirectory = documentDirectories.objectAtIndex(0) as String
path = documentDirectory.stringByAppendingPathComponent("data.archive")
if NSKeyedArchiver.archiveRootObject(nameData, toFile: path) {
//println("Success writing to file!")
} else {
println("Unable to write to file!")
}
}
func retrieveData() -> NSObject {
var dataToRetrieve = Data()
documentDirectories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
documentDirectory = documentDirectories.objectAtIndex(0) as String
path = documentDirectory.stringByAppendingPathComponent("data.archive")
if let dataToRetrieve2 = NSKeyedUnarchiver.unarchiveObjectWithFile(path) as? Data {
dataToRetrieve = dataToRetrieve2 as Data
}
return(dataToRetrieve)
}
}