How to Save and Read Array of Array in Nsuserdefaults in Swift

how to save and read array of array in NSUserdefaults in swift?

The question reads "array of array" but I think most people probably come here just wanting to know how to save an array to UserDefaults. For those people I will add a few common examples.

String array

Save array

let array = ["horse", "cow", "camel", "sheep", "goat"]

let defaults = UserDefaults.standard
defaults.set(array, forKey: "SavedStringArray")

Retrieve array

let defaults = UserDefaults.standard
let myarray = defaults.stringArray(forKey: "SavedStringArray") ?? [String]()

Int array

Save array

let array = [15, 33, 36, 723, 77, 4]

let defaults = UserDefaults.standard
defaults.set(array, forKey: "SavedIntArray")

Retrieve array

let defaults = UserDefaults.standard
let array = defaults.array(forKey: "SavedIntArray") as? [Int] ?? [Int]()

Bool array

Save array

let array = [true, true, false, true, false]

let defaults = UserDefaults.standard
defaults.set(array, forKey: "SavedBoolArray")

Retrieve array

let defaults = UserDefaults.standard
let array = defaults.array(forKey: "SavedBoolArray") as? [Bool] ?? [Bool]()

Date array

Save array

let array = [Date(), Date(), Date(), Date()]

let defaults = UserDefaults.standard
defaults.set(array, forKey: "SavedDateArray")

Retrieve array

let defaults = UserDefaults.standard
let array = defaults.array(forKey: "SavedDateArray") as? [Date] ?? [Date]()

Object array

Custom objects (and consequently arrays of objects) take a little more work to save to UserDefaults. See the following links for how to do it.

  • Save custom objects into NSUserDefaults
  • Docs for saving color to UserDefaults
  • Attempt to set a non-property-list object as an NSUserDefaults

Notes

  • The nil coalescing operator (??) allows you to return the saved array or an empty array without crashing. It means that if the object returns nil, then the value following the ?? operator will be used instead.
  • As you can see, the basic setup was the same for Int, Bool, and Date. I also tested it with Double. As far as I know, anything that you can save in a property list will work like this.

How to store array of values in NSUserDefaults in swift

let array = [1, 2, 9, 3, 22]

UserDefaults.standard.set(array, forKey: "studentIDs")

//get id's array from user defaults
let studentIdsArray = UserDefaults.standard.array(forKey: "studentIDs")

There are plenty of questions/solutions, here is one:
how to save and read array of array in NSUserdefaults in swift?

But as another user said, please take a look at the official documentation: https://developer.apple.com/documentation/foundation/userdefaults

(Swift) Storing and retrieving Array to NSUserDefaults

From your code I see you are storing some array

// Your code
NSUserDefaults.standardUserDefaults().setObject(myArray, forKey: "\(identity.text!)listA")

and retrieving a string

//Your code
let tabledata = NSUserDefaults.standardUserDefaults().stringForKey("\(identity.text!)listA")

There is probably a type mismatch, You store one type and retrieve another type.

While retrieving either use arrayForKey() or objectForKey() see the code below.

let tabledata = NSUserDefaults.standardUserDefaults().arrayForKey("\(identity.text!)listA") 

or

let tabledata = NSUserDefaults.standardUserDefaults().objectForKey("\(identity.text!)listA")

If it is an array I would go with First one.

How Do I Save an Array of Objects to NSUserDefaults?

To save a structure in UserDefaults you need to first encode it to be able to save it as Data. So you need to make your custom structure conform to Codable:

struct Event: Codable {
let id: UUID
let name: String
let start, end: Date
let fromTime, toTime: String
let color: Color
init(id: UUID = .init(),
name: String,
start: Date,
end: Date,
fromTime: String,
toTime: String,
color: Color) {
self.id = id
self.name = name
self.start = start
self.end = end
self.fromTime = fromTime
self.toTime = toTime
self.color = color
}
}

Note that you can not conform UIColor to Codable but you can create a custom Color structure:

struct Color: Codable {
let (r, g, b, a): (CGFloat, CGFloat, CGFloat, CGFloat)
}


extension Color {
init?(_ uiColor: UIColor) {
var (r, g, b, a): (CGFloat,CGFloat,CGFloat,CGFloat) = (0, 0, 0, 0)
guard uiColor.getRed(&r, green: &g, blue: &b, alpha: &a) else { return nil }
self.init(r: r, g: g, b: b, a: a)
}
var color: UIColor { .init(red: r, green: g, blue: b, alpha: a) }
}


extension UIColor {
convenience init(_ color: Color) {
self.init(red: color.r, green: color.g, blue: color.b, alpha: color.a)
}
var color: Color? { Color(self) }
}

Regarding your class you can also make it conform to Codable or inherit from NSObject and conform to NSCoding:

class Events: NSObject, NSCoding {
private override init() { }
static var shared = Events()
var events: [Event] = []
required init(coder decoder: NSCoder) {
events = try! JSONDecoder().decode([Event].self, from: decoder.decodeData()!)
}
func encode(with coder: NSCoder) {
try! coder.encode(JSONEncoder().encode(events))
}
}

Playground testing:

Events.shared.events = [.init(name: "a",
start: Date(),
end: Date(),
fromTime: "fromTime",
toTime: "toTime",
color: .init(r: 0, g: 0, b: 1, a: 1)),
.init(name: "b",
start: Date(),
end: Date(),
fromTime: "fromTimeB",
toTime: "toTimeB",
color: .init(r: 0, g: 1, b: 0, a: 1))]

print(Events.shared.events)
let data = try! NSKeyedArchiver.archivedData(withRootObject: Events.shared, requiringSecureCoding: false)
UserDefaults.standard.set(data, forKey: "events")
Events.shared.events = []
print(Events.shared.events)
let loadedData = UserDefaults.standard.data(forKey: "events")!
Events.shared = try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(loadedData) as! Events
print(Events.shared.events)

This will print

[Event(id: C7D9475B-773E-4272-84CC-56CAEAA73D0C, name: "a", start: 2021-01-26 05:17:30 +0000, end: 2021-01-26 05:17:30 +0000, fromTime: "fromTime", toTime: "toTime", color: Color(r: 0.0, g: 0.0, b: 1.0, a: 1.0)), Event(id: 0BEA4225-2F63-4EEB-AF10-F3EF4C84D050, name: "b", start: 2021-01-26 05:17:30 +0000, end: 2021-01-26 05:17:30 +0000, fromTime: "fromTimeB", toTime: "toTimeB", color: Color(r: 0.0, g: 1.0, b: 0.0, a: 1.0))]

[]

[Event(id: C7D9475B-773E-4272-84CC-56CAEAA73D0C, name: "a", start: 2021-01-26 05:17:30 +0000, end: 2021-01-26 05:17:30 +0000, fromTime: "fromTime", toTime: "toTime", color: Color(r: 0.0, g: 0.0, b: 1.0, a: 1.0)), Event(id: 0BEA4225-2F63-4EEB-AF10-F3EF4C84D050, name: "b", start: 2021-01-26 05:17:30 +0000, end: 2021-01-26 05:17:30 +0000, fromTime: "fromTimeB", toTime: "toTimeB", color: Color(r: 0.0, g: 1.0, b: 0.0, a: 1.0))]

Swift store array with UserDefaults

The reason of the error is that the compiler cannot infer the type of the array.

Spend an extra line to read, change and write the data, for example:

let defaults = UserDefaults.standard
var seenPosts : [Int]
if let actual = defaults.array(forKey: "seen_posts") as? [Int] {
seenPosts = actual
} else {
seenPosts = [Int]()
}
seenPosts.insert(321, at: 0)
defaults.set(seenPosts, forKey: "seen_posts")

or if the default key seen_posts is registered – as recommended – simpler

let defaults = UserDefaults.standard
var seenPosts = defaults.array(forKey: "seen_posts") as! [Int]
seenPosts.insert(321, at: 0)
defaults.set(seenPosts, forKey: "seen_posts")

How to save a Array with (Multiple Types) in NSUserDefaults

This is how I actually save a Custom Object created in the app in Swift 4.

First, we create 3 protocols for our purpose of saving the custom object in UserDefaults. The logic behind is to convert the Custom Object into a normalized Dictionary/Array form.

This can be applied to any kind of Object which you have created.

The 3 protocols are:

  1. Decoder (Used to decode the dictionary into custom object)
  2. Encoder (Used to encode the custom object into dictionary)
  3. UserDefaultsProtocol (Used to save, delete, update & retrieve the custom object from UserDefault)

Decoder Protocol

protocol Decoder {
associatedtype T
static func decode(dictionary: [String: Any]) -> T
}

Encoder Protocol

protocol Encoder {
func encode() -> [String: Any]
}

UserDefaultsProtocol

protocol UserDefaultsDelegate: class {
associatedtype T
func saveToUserDefaults()
static func removeFromUserDefaults()
static func retrieveFromUserDefaults() -> T?
}

As per your question, NotificationList Object would look like this

class NotificationList {
var type: String = ""
var imageName: String = ""
var text: String = ""
var date: String = ""
var seen: Bool = false
}

Now, you need to confirm all the 3 mentioned protocols to NotificationList. (Swift Best Practice: Use of Extensions & Protocols)

class NotificationList {
private struct Constants {
static let RootKey = "notification_list"
static let TypeKey = "type"
static let ImageNameKey = "image_name"
static let TextKey = "text"
static let DateKey = "date"
static let SeenKey = "seen"
}

var type: String = ""
var imageName: String = ""
var text: String = ""
var date: String = ""
var seen: Bool = false

typealias T = NotificationList
}

extension NotificationList: Encoder {
func encode() -> [String : Any] {
return [
Constants.TypeKey: type,
Constants.ImageNameKey: imageName,
Constants.TextKey: text,
Constants.DateKey: date,
Constants.SeenKey: seen
]
}
}

extension NotificationList: Decoder {
static func decode(dictionary: [String: Any]) -> NotificationList {
let type = dictionary[Constants.TypeKey] as! String
let imageName = dictionary[Constants.ImageNameKey] as! String
let text = dictionary[Constants.TextKey] as! String
let date = dictionary[Constants.DateKey] as! String
let seen = dictionary[Constants.SeenKey] as! Bool

let notificationList = NotificationList()
notificationList.type = type
notificationList.imageName = imageName
notificationList.text = text
notificationList.date = date
notificationList.seen = seen
return notificationList
}
}

extension NotificationList: UserDefaultsDelegate {

func saveToUserDefaults() {
UserDefaults.standard.setValue(encode(), forKey: Constants.RootKey)
}

static func retrieveFromUserDefaults() -> NotificationList? {
guard let encodedNotificationList = UserDefaults.standard.dictionary(forKey: Constants.RootKey) else {
return nil
}
return NotificationList.decode(dictionary: encodedNotificationList)
}

static func removeFromUserDefaults() {
UserDefaults.standard.removeObject(forKey: Constants.RootKey)
}
}

How to save NotificationList to UserDefaults?

var notificationList = NotificationList()
notificationList.type = "Default"
notificationList.imageName = "ClearPartioned"
notificationList.text = ""
notificationList.date = ""
notificationList.seen = true

Save to UserDefaults

notificationList.saveToUserDefaults()

Retrieve from UserDefaults

if let notificationList = NotificationList.retrieveFromUserDefaults() {
// You will get the instance of notification list saved in UserDefaults
}

HOW TO SAVE ARRAY OF NOTIFICATION LIST?

Say notificationLists contains the array of notificationList objects.

var notificationListsArray = [[String: Any]]()

notificationLists.forEach {
notificationListsArray.append($0.encode())
}

Save that array of dictionary to UserDefaults

UserDefaults.standard.setValue(notificationListsArray, forValue: "notificationLists")


Related Topics



Leave a reply



Submit