Swift encode tuple using NSCoding
Tuple cannot be encoded because it is not a class, but one approach is to encode each component of a tuple separately and then upon decoding you decode each component and then set the value of the tuple to a tuple constructed from the decoded content.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let obj = SomeClass()
obj.foo = (6,5)
let data = NSKeyedArchiver.archivedDataWithRootObject(obj)
NSUserDefaults.standardUserDefaults().setObject(data, forKey: "books")
if let data = NSUserDefaults.standardUserDefaults().objectForKey("books") as? NSData {
let o = NSKeyedUnarchiver.unarchiveObjectWithData(data) as SomeClass
println(o.foo) // (Optional(6), Optional(5))
}
}
}
class SomeClass: NSObject, NSCoding {
var foo: (x: Int?, y: Int?)!
required convenience init(coder decoder: NSCoder) {
self.init()
let x = decoder.decodeObjectForKey("myTupleX") as Int?
let y = decoder.decodeObjectForKey("myTupleY") as Int?
foo = (x,y)
}
func encodeWithCoder(coder: NSCoder) {
coder.encodeObject(foo.x, forKey: "myTupleX")
coder.encodeObject(foo.y, forKey: "myTupleY")
}
}
NSCoding swift dictionary with swift tuple as values
The problem you are running into is that tuples are structs
and not class
(object) types. You'll notice this issue when you try to do the following:
if let dictionary = aDecoder.decodeObjectForKey("truthDict") as? [String : RelationshipType] { ... }
This issues unfortunately comes up a bit when trying to deal with value types in Swift. To get around this limitation, you can box your value types using a generic Box
class like so:
class Box<T>
{
var value : T?
init(_ value: T?)
{
self.value = value
}
}
With the Box
class, you can use this to encode and decode your dictionary tuples:
class TruthDictionary : NSObject, NSCoding, SequenceType
{
typealias RelationshipType = (ip: String, groupId: String?)
private var dictionary = [String : RelationshipType]()
subscript(key: String) -> RelationshipType?
{
get { return self.dictionary[key] }
set { self.dictionary[key] = newValue }
}
// MARK: - Initializers
override init()
{
super.init()
}
// MARK: - NSCoding
required init(coder aDecoder: NSCoder)
{
// Unbox each tuple from the decoded dictionary
if let boxedDictionary = aDecoder.decodeObjectForKey("truthDict") as? [String : Box<RelationshipType>]
{
for (key, boxedTuple) in boxedDictionary
{
self.dictionary[key] = boxedTuple.value
}
}
}
func encodeWithCoder(aCoder: NSCoder)
{
var boxedDictionary = [String: Box<RelationshipType>]()
// Box each tuple to the dictionary to be encoded
for (key, tuple) in self.dictionary
{
boxedDictionary[key] = Box(tuple)
}
aCoder.encodeObject(boxedDictionary, forKey: "truthDict")
}
// MARK: - SequenceType
func generate() -> DictionaryGenerator<String, RelationshipType>
{
return dictionary.generate()
}
}
Approach to encoding/decoding nontrivial property of a class in Swift
In addition to the information from Zaph's answer and his comments, I'm submitting my own solution as a standalone answer.
Because configuration
is an array of tuples, you won't be able to use encodeObject
because tuples are not supported by NSKeyedArchiver
and NSKeyedUnarchiver
.
As per "The Swift Programming Language" footnote on tuples:
Tuples are useful for temporary groups of related values. They are not suited to the creation of complex data structures. If your data structure is likely to persist beyond a temporary scope, model it as a class or structure, rather than as a tuple. For more information, see Classes and Structures.
The tuple can be represented as a class with 2 properties: one for the first part and one for the second part. While the extra boilerplate to make it a class and conform to NSObject and NSCoding is a lot, it makes the object more flexible for future use, so it has its trade-offs.
I created a Gist to show it in action with a sample project. Simply replace a new Single View Application project's ViewController.swift
with this file and run (class object defined at the top of the file).
The first time you run it there is no stored data on viewDidLoad
, and sample data will be created and stored on viewDidAppear
. Run it again and you'll see that on viewDidLoad
the stored data is loaded properly. You can uncomment the lines that remove the data from NSUserDefaults
and run again to reset the stored data.
How to encode a unichar array using NSCoding
A C array of fixed-size data is best encoded using encodeBytes:length:forKey:
. See Encoding and Decoding C Data Types for full details.
Models with refference to another models - saving using NSCoding
NSCoder
subclasses know how to avoid circular references, see https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Archiving/Articles/archives.html.
That said, you should probably look into introducing weak
references into your data model to avoid retain cycles.
Update a String in an NSCoding/NSObject Array using Swift 2
Probably i don't understand your problem. As I see, you wold like to update property of class 'saved' in Swift array. I don't see any relation to NSObject or NSCoding there.
class C {
var txt: String
init(txt: String) {
self.txt = txt
}
}
var arr: [C] = []
arr.append(C(txt: "a"))
arr.append(C(txt: "b"))
dump(arr)
/*
▿ 2 elements
▿ [0]: C #0
- txt: a
▿ [1]: C #1
- txt: b
*/
Updating some property of instance of class 'stored' in the array is simple. You need to know position of the instance in the array (index) and new value of the property, that's all.
arr[0].txt = "A"
dump(arr)
/*
▿ 2 elements
▿ [0]: C #0
- txt: A
▿ [1]: C #1
- txt: b
*/
Related Topics
Use Multiple Codingkeys for a Single Property
Swiftui - in Sheet Have a Fixed Continue Button That Is Not Scrollable
Convert Hex-Encoded String to String
Nsdatecomponents Weekofyear Returns Wrong Date
In Swift, Does Int Have a Hidden Initializer That Takes a String
Using Delegates on Generic Protocol
Extending View with Extra Function Without Using Anyview
Output Compile Durations for Swift Files
Differencebetween Final Class and Class
How to Set Font Size of Sklabelnode to Fit in Fixed Size (Swift)
Why Does Swiftui Uihostingcontroller Have Extra Spacing
Swift/Firebase - Sort Posts in Tableview by Date
Suppressing Implicit Returns in Swift
How to Pass Int.Init to a Function in Swift
Nsurlerrordomain with Code=-1100
Find Multiple Quoted Words in a String with Regex
How to Share Data Between Tab View Controllers
Difference Between Type Method and Type Instance Method, etc