Saving an array of NSURL to NSUserDefaults
when saving, save the absolute strings of the urls. when loading use NSURL(URLString:) to make the strings into urls again
import UIKit
class ViewController: UIViewController {
var urlsArray : [NSURL]?
func load () {
var urls : [NSURL] = []
let stringsArray = NSUserDefaults.standardUserDefaults().objectForKey("stringsArray") as [String]?
if let array = stringsArray {
for string in array {
var url = NSURL(string: string)
urls.append(url!) //no null check
}
}
self.urlsArray = urls
}
func save () {
var strings : [String] = []
if let array = self.urlsArray {
for url in array {
var string = url.absoluteString
strings.append(string!) ////no null check
}
}
NSUserDefaults.standardUserDefaults().setObject(strings, forKey: "stringsArray")
}
}
saving can be reduced with KVC.
func save () {
var strings : [String] = []
if let array = self.urlsArray as NSArray? {
strings = array.valueForKeyPath("absoluteStrings")
}
NSUserDefaults.standardUserDefaults().setObject(strings, forKey: "stringsArray")
}
Converting [NSURL] into [String] for NSUserDefaults?
Using NSData
You can convert each NSURL
to NSData
in order to save it
func save(urls: [NSURL]) {
let urlsData = urls.map { $0.dataRepresentation }
NSUserDefaults.standardUserDefaults().setObject(urlsData, forKey: "urlsData")
}
Later on you can retrieve the NSData
array and convert it back to [NSURL]
func load() -> [NSURL]? {
let retrievedData = NSUserDefaults.standardUserDefaults().arrayForKey("urlsData") as? [NSData]
return retrievedData?.map { NSURL(dataRepresentation: $0, relativeToURL: nil) }
}
Using String
Alternatively you can save the urls as String(s)
func save(urls: [NSURL]) {
let urlsData = urls.map { $0.absoluteString }
NSUserDefaults.standardUserDefaults().setObject(urlsData, forKey: "urlsData")
}
func load() -> [NSURL?]? {
let retrievedData = NSUserDefaults.standardUserDefaults().arrayForKey("urlsData") as? [String]
return retrievedData?.map { NSURL(string: $0) }
}
As discussed in the comments below, if data is written to NSUserDefaults
exclusively with the save
function, we know that every element of the array is a String
representing a valid NSURL
.
So we can change the return type of load
from [NSURL?]?
to [NSURL]?
using this alternate version of load
.
func load() -> [NSURL]? {
let retrievedData = NSUserDefaults.standardUserDefaults().arrayForKey("urlsData") as? [String]
return retrievedData?.flatMap { NSURL(string: $0) }
}
Saving an array to NSUserDefaults
You can't directly store an NSURL
in NSUserDefaults
, only NSData
, NSString
, NSNumber
, NSDate
, NSArray
, or NSDictionary
; also, any NSArray
or NSDictionary
may only contain objects of these types. You'll have to convert the NSURLs into one of these types, most likely by using absoluteString to convert them into NSStrings.
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))]
How to save an array of objects to NSUserDefault with swift?
From the Property List Programming Guide:
If a property-list object is a container (that is, an array or dictionary), all objects contained within it must also be property-list objects. If an array or dictionary contains objects that are not property-list objects, then you cannot save and restore the hierarchy of data using the various property-list methods and functions.
You'll need to convert the object to and from an NSData
instance using NSKeyedArchiver
and NSKeyedUnarchiver
.
For example:
func savePlaces(){
let placesArray = [Place(lat: 123, lng: 123, name: "hi")]
let placesData = NSKeyedArchiver.archivedDataWithRootObject(placesArray)
NSUserDefaults.standardUserDefaults().setObject(placesData, forKey: "places")
}
func loadPlaces(){
let placesData = NSUserDefaults.standardUserDefaults().objectForKey("places") as? NSData
if let placesData = placesData {
let placesArray = NSKeyedUnarchiver.unarchiveObjectWithData(placesData) as? [Place]
if let placesArray = placesArray {
// do something…
}
}
}
Related Topics
How to Parse Url # Fragments with Query Items in Swift
How to Draw an Image in an Nsopenglview with Swift
Why Does My Swift Bundle Get The Wrong Principal Class
Do Swift Hashable Protocol Hash Functions Need to Return Unique Values
Typecase Regular Swift Function to Curry Function
Firestore - Creating a Copy of a Collection
Swift Optionals Best Practices
Passing Data Between View Controllers (Swift)
Saving an Array of Nsurl to Nsuserdefaults
Getting The Parameterised Type of a Generic in Swift
How to Display an Alert Controller When Nsdate == to a Time (For Example 12:00 Am) in Swift
Swift Method Parameters in Documentation
Adding Constraints Programmatically in UIview with UItextview