Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to save an array of custom struct to NSUserDefault with swift?

I have an custom struct called 'News' that I want to append into the array to NSUserDefault. But it's showing error "Type 'News' does not conform to protocol 'AnyObject'".

I don't want to change the 'News' struct to a class since it's being used for other code already. Is there anyway that I can change NSUserDefaults.standardUserDefaults().arrayForKey("savedNewsArray") type to [News]?

var savedNews = NSUserDefaults.standardUserDefaults().arrayForKey("savedNewsArray")
var addSavedNews = savedNews as? [News]
addSavedNews.append(News(id: "00", title: newsTitle, source: source, imageURL: imageURL, url: url))
NSUserDefaults.standardUserDefaults().setObject(addSavedNews, forKey: "savedNewsArray")
NSUserDefaults.standardUserDefaults().synchronize()

Here is the 'News' struct.

public struct News {
    public var id: String
    public var title: String
    public var source: String?
    public var imageURL: String?
    public var date: NSDate?
    public var url: String

    init(id: String, title: String, source: String, imageURL: String, url: String) {
        self.id = id
        self.title = title
        self.source = source
        self.imageURL = imageURL
        self.url = url
    }
}
like image 496
puffxixi Avatar asked Jul 16 '16 00:07

puffxixi


2 Answers

NSUserDefaults can only save a very small set of types: NSData, NSString, NSNumber, NSDate, NSArray containing only these types, or NSDictionary containing only these types. So your best bet is to encode your struct using an NSKeyedUnarchiver, which required a value that conforms to NSCoding. You could make your type conform to this, but I think it's cleaner to hide that from your users and simply have a private class for the internal representation, like this:

struct Foo {
    var a : String
    var b : String?
}

extension Foo {
    init?(data: NSData) {
        if let coding = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? Encoding {
            a = coding.a as String
            b = coding.b as String?
        } else {
            return nil
        }
    }

    func encode() -> NSData {
        return NSKeyedArchiver.archivedDataWithRootObject(Encoding(self))
    }

    private class Encoding: NSObject, NSCoding {
        let a : NSString
        let b : NSString?

        init(_ foo: Foo) {
            a = foo.a
            b = foo.b
        }

        @objc required init?(coder aDecoder: NSCoder) {
            if let a = aDecoder.decodeObjectForKey("a") as? NSString {
                self.a = a
            } else {
                return nil
            }
            b = aDecoder.decodeObjectForKey("b") as? NSString
        }

        @objc func encodeWithCoder(aCoder: NSCoder) {
            aCoder.encodeObject(a, forKey: "a")
            aCoder.encodeObject(b, forKey: "b")
        }

    }
}

Then to save your array you can simply map .encode over your array:

let fooArray = [ Foo(a: "a", b: "b"), Foo(a: "c", b: nil) ]
let encoded = fooArray.map { $0.encode() }
NSUserDefaults.standardUserDefaults().setObject(encoded, forKey: "my-key")

and to get it back you can simply pass the NSData to the init:

let dataArray = NSUserDefaults.standardUserDefaults().objectForKey("my-key") as! [NSData]
let savedFoo = dataArray.map { Foo(data: $0)! }
like image 200
ahruss Avatar answered Sep 27 '22 18:09

ahruss


Following is the flow that I have follow

  1. Make your struct object
  2. Convert struct to Json object (I will give you file link to convert struct to JsonObject and jsonString -> https://github.com/vijayvir/SwiftApi/blob/master/StructureToJson/StructToJson.swift ) import this file in your project :-)
  3. Append that value to array
  4. If you want to save in user default I prefer you to make class object of array as follow ,(as you can access from any where )

  5. save your array in user default as follow

Struct to save

struct CardDetails : StructJSONSerializable {
var emailId :String

var accountNo : String

var expDate : String

var cvc : String

var isCurrrent : Bool = false      


init(card : Dictionary<String, Any>  )
{
    emailId =  card["emailId"]! as! String

    accountNo =  card["accountNo"]! as! String

    expDate  =  card["expDate"]! as! String

    cvc =  card["cvc"]! as! String

    isCurrrent = card["isCurrrent"]! as! Bool

}

 }

Save Array to User Default

  class var  cards : [AnyObject] {
    get
        {
            if (UserDefaults.standard.object(forKey:"cardss") != nil)
            {

            if let data = UserDefaults.standard.object(forKey: "cardss") as? Data
            {
                let unarc = NSKeyedUnarchiver(forReadingWith: data)

                let newBlog = unarc.decodeObject(forKey: "root")



                return newBlog as! [AnyObject]
            }
            else
            {
                return []
            }

          }
            else
            {
            return []
            }

        }

    set
    {
        //print(type(of: newValue) , newValue)

        let archiveData = NSKeyedArchiver.archivedData(withRootObject: newValue)

          let ud = UserDefaults.standard
          ud.set(  archiveData  ,forKey: "cardss")
          ud.synchronize()
    }

}

How to save Struct element to array Here convert you struct object to json Object

     let  firstCard = CardDetails(emailId: "asdf",
                                 accountNo: "dada",
                                 expDate: "nvn",
                                 cvc: "e464w",
                                 isCurrrent: true)

      CardsViewController.cards.append(firstCard.toJsonObect())

How to access object from array Here convert you json Object object to Struct

let sameCardIs = CardDetails(card:  CardsViewController.cards.last
as! Dictionary<String, Any>)



    print ("Element Function " , sameCardIs )

:-)

like image 23
Vijayvir Sing Pantlia Avatar answered Sep 27 '22 17:09

Vijayvir Sing Pantlia