There seems to be some inconsistencies with how the types of elements of NSArrays, keys of NSDictionaries, and values of NSDictionaries are inferred, and whether the .write(toFile:atomically:) works.
NSArrays where all the elements are literal strings, all the elements are Strings, or all the elements are NSStrings are all written correctly by the .write function. This implies that Swift Strings are property list objects because the docs say "This method recursively validates that all the contained objects are property list objects before writing out the file, and returns false if all the objects are not property list objects, since the resultant file would not be a valid property list."
NSDictionaries with all NSString keys, and values of types Optional Dates, NSNumbers, and Optional Strings are also successfully written by the .write function IF the values are added one at a time to an NSMutableDictionary. This implies that those Optional Dates and Optional Strings are property list objects because the docs say "This method recursively validates that all the contained objects are property list objects (instances of NSData, NSDate, NSNumber, NSString, NSArray, or NSDictionary) before writing out the file, and returns false if all the objects are not property list objects...".
NSArrays filled with a mixture of Optional Dates, NSNumbers, and Optional Strings are not written successfully by the .write function. Why not, if an NSDictionary with those values can be written?
NSDictionaries with the same value types as mentioned above, built by adding values one at a time to an NSMutableDictionary, but with keys that are string literals or Swift Strings are not written successfully by the .write function. Why not, if NSArrays with string literals or Swift Strings can be written?
NSDictionaries with the same value types as mentioned above, but intialized with [<key:value pairs>] instead of by adding them one at a time to an NSMutableDictionary, are not written successfully, even if the keys are NSStrings. Note that the type of the dictionary values printed is different when the keys are NSStrings and the values are added one by one, compared to when the keys are any of literal, String, or NSString and the values are initialized with [<key:value pairs>].
There are also some other differences in the optionality of the values, depending on how they are added to the dictionary.
Can anyone shed some light on this? It seems to be inconsistent at best and a bug at worst.
This requires a lot of code and output to demonstrate, so here goes:
import UIKit
let first = "First"
let second = "Second"
let third = "Third"
let nsFirst: NSString = "First"
let nsSecond: NSString = "Second"
let nsThird: NSString = "Third"
class ViewController: UIViewController {
private var appSupportDirectory: String!
func printIt(collectionName: String, collection: Any, saved:Bool) {
print(collectionName + ": \(collection)")
print("Collection type: \(type(of: collection))")
if collection is NSArray {
let aArray = collection as! NSArray
print("First element type: \(type(of: aArray[0]))")
} else {
let aDict = collection as! NSMutableDictionary
for k in aDict.allKeys {
print("First key type: \(type(of: k))")
print("First value type: \(type(of: aDict[k]!))")
break
}
}
print("Saved: \(saved)")
print()
}
override func viewDidLoad() {
super.viewDidLoad()
/
let directoriesArray = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true)
self.appSupportDirectory = directoriesArray[0]
let fMgr = FileManager.default
if !fMgr.fileExists(atPath: self.appSupportDirectory) {
do {
try fMgr.createDirectory(atPath: self.appSupportDirectory, withIntermediateDirectories: true, attributes: nil)
} catch {
preconditionFailure("Could not create Application Support directory!")
}
}
let literalArray: NSMutableArray = ["First", "Second", "Third"]
let literalArrayFileName = self.appSupportDirectory + "/literalArray.plist"
var saved = literalArray.write(toFile: literalArrayFileName, atomically: true)
printIt(collectionName: "Literal Array", collection: literalArray, saved: saved)
let globalStringsArray: NSMutableArray = [first, second, third]
let globalStringsArrayFileName = self.appSupportDirectory + "/globalStringsArray.plist"
saved = globalStringsArray.write(toFile: globalStringsArrayFileName, atomically: true)
printIt(collectionName: "Global Strings Array", collection: globalStringsArray, saved: saved)
let globalNSStringsArray: NSMutableArray = [nsFirst, nsSecond, nsThird]
let globalNSStringsArrayFileName = self.appSupportDirectory + "/globalNSStringsArray.plist"
saved = globalNSStringsArray.write(toFile: globalNSStringsArrayFileName, atomically: true)
printIt(collectionName: "Global NSStrings Array", collection: globalNSStringsArray, saved: saved)
let aDate: Date? = Date()
let aNumber = NSNumber(value: true)
let aString: String? = "3"
let initializedDirectArray: NSMutableArray = [Date() as Date?, NSNumber(value: true), "3" as String?]
let initializedDirectArrayFileName = self.appSupportDirectory + "/initializedDirectArray.plist"
saved = initializedDirectArray.write(toFile: initializedDirectArrayFileName, atomically: true)
printIt(collectionName: "Array initialized with direct values", collection: initializedDirectArray, saved: saved)
let initializedVariableArray: NSMutableArray = [aDate, aNumber, aString]
let initializedVariableArrayFileName = self.appSupportDirectory + "/initializedVariableArray.plist"
saved = initializedVariableArray.write(toFile: initializedVariableArrayFileName, atomically: true)
printIt(collectionName: "Array initialized with variable values", collection: initializedVariableArray, saved: saved)
let builtDirectArray = NSMutableArray()
builtDirectArray.add(Date() as Date?)
builtDirectArray.add(NSNumber(value: true))
builtDirectArray.add("3" as String?)
let builtDirectArrayFileName = self.appSupportDirectory + "/builtDirectArray.plist"
saved = builtDirectArray.write(toFile: builtDirectArrayFileName, atomically: true)
printIt(collectionName: "Array built with direct values", collection: builtDirectArray, saved: saved)
let builtVariableArray = NSMutableArray()
builtVariableArray.add(Date() as Date?)
builtVariableArray.add(NSNumber(value: true))
builtVariableArray.add("3" as String?)
let builtVariableArrayFileName = self.appSupportDirectory + "/builtVariableArray.plist"
saved = builtVariableArray.write(toFile: builtVariableArrayFileName, atomically: true)
printIt(collectionName: "Array built with variable values", collection: builtVariableArray, saved: saved)
let literalDictionaryFileName = self.appSupportDirectory + "/literalDictionary.plist"
var literalDictionary: NSMutableDictionary = ["First" : Date() as NSDate?, "Second" : NSNumber(value: true), "Third" : "3" as String?]
saved = literalDictionary.write(toFile: literalDictionaryFileName, atomically: true)
printIt(collectionName: "Literal Keys Dictionary initialized with direct values", collection: literalDictionary, saved: saved)
literalDictionary = ["First" : aDate, "Second" : aNumber, "Third" : aString]
saved = literalDictionary.write(toFile: literalDictionaryFileName, atomically: true)
printIt(collectionName: "Literal Keys Dictionary initialized with variable values", collection: literalDictionary, saved: saved)
literalDictionary = NSMutableDictionary()
literalDictionary["First"] = Date() as NSDate?
literalDictionary["Second"] = NSNumber(value: true)
literalDictionary["Third"] = "3" as String?
saved = literalDictionary.write(toFile: literalDictionaryFileName, atomically: true)
printIt(collectionName: "Literal Keys Dictionary built with direct values", collection: literalDictionary, saved: saved)
literalDictionary = NSMutableDictionary()
literalDictionary["First"] = aDate
literalDictionary["Second"] = aNumber
literalDictionary["Third"] = aString
saved = literalDictionary.write(toFile: literalDictionaryFileName, atomically: true)
printIt(collectionName: "Literal Keys Dictionary built with variable values", collection: literalDictionary, saved: saved)
let globalStringsDictionaryFileName = self.appSupportDirectory + "/globalStringsDictionary.plist"
var globalStringsDictionary: NSMutableDictionary = [first : Date() as NSDate?, second : NSNumber(value: true), third : "3" as String?]
saved = globalStringsDictionary.write(toFile: globalStringsDictionaryFileName, atomically: true)
printIt(collectionName: "Global String Keys Dictionary initialized with direct values", collection: globalStringsDictionary, saved: saved)
globalStringsDictionary = [first : aDate, second : aNumber, third : aString]
saved = globalStringsDictionary.write(toFile: globalStringsDictionaryFileName, atomically: true)
printIt(collectionName: "Global String Keys Dictionary initialized with variable values", collection: globalStringsDictionary, saved: saved)
globalStringsDictionary = NSMutableDictionary()
globalStringsDictionary[first] = Date() as NSDate?
globalStringsDictionary[second] = NSNumber(value: true)
globalStringsDictionary[third] = "3" as String?
saved = globalStringsDictionary.write(toFile: globalStringsDictionaryFileName, atomically: true)
printIt(collectionName: "Global String Keys Dictionary built with direct values", collection: globalStringsDictionary, saved: saved)
globalStringsDictionary = NSMutableDictionary()
globalStringsDictionary[first] = aDate
globalStringsDictionary[second] = aNumber
globalStringsDictionary[third] = aString
saved = globalStringsDictionary.write(toFile: globalStringsDictionaryFileName, atomically: true)
printIt(collectionName: "Global String Keys Dictionary built with variable values", collection: globalStringsDictionary, saved: saved)
let globalNSStringsDictionaryFileName = self.appSupportDirectory + "/globalStringsNSDictionary.plist"
var globalNSStringsDictionary: NSMutableDictionary = [nsFirst : Date() as NSDate?, nsSecond : NSNumber(value: true), nsThird : "3" as String?]
saved = globalNSStringsDictionary.write(toFile: globalNSStringsDictionaryFileName, atomically: true)
printIt(collectionName: "Global NSString Keys Dictionary initialized with direct values", collection: globalNSStringsDictionary, saved: saved)
globalNSStringsDictionary = [nsFirst : aDate, nsSecond : aNumber, nsThird : aString]
saved = globalNSStringsDictionary.write(toFile: globalNSStringsDictionaryFileName, atomically: true)
printIt(collectionName: "Global NSString Keys Dictionary initialized with variable values", collection: globalNSStringsDictionary, saved: saved)
globalNSStringsDictionary = NSMutableDictionary()
globalNSStringsDictionary[nsFirst] = Date() as NSDate?
globalNSStringsDictionary[nsSecond] = NSNumber(value: true)
globalNSStringsDictionary[nsThird] = "3" as String?
saved = globalNSStringsDictionary.write(toFile: globalNSStringsDictionaryFileName, atomically: true)
printIt(collectionName: "Global NSString Keys Dictionary built with direct values", collection: globalNSStringsDictionary, saved: saved)
globalNSStringsDictionary = NSMutableDictionary()
globalNSStringsDictionary[nsFirst] = aDate
globalNSStringsDictionary[nsSecond] = aNumber
globalNSStringsDictionary[nsThird] = aString
saved = globalNSStringsDictionary.write(toFile: globalNSStringsDictionaryFileName, atomically: true)
printIt(collectionName: "Global NSString Keys Dictionary built with variable values", collection: globalNSStringsDictionary, saved: saved)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
The output is:
Literal Array: (
First,
Second,
Third
)
Collection type: __NSArrayM
First element type: _NSContiguousString
Saved: true
Global Strings Array: (
First,
Second,
Third
)
Collection type: __NSArrayM
First element type: _NSContiguousString
Saved: true
Global NSStrings Array: (
First,
Second,
Third
)
Collection type: __NSArrayM
First element type: __NSCFString
Saved: true
Array initialized with direct values: (
"Optional(2016-08-26 16:49:18 +0000)",
1,
"Optional(\"3\")"
)
Collection type: __NSArrayM
First element type: _SwiftValue
Saved: false
Array initialized with variable values: (
"Optional(2016-08-26 16:49:18 +0000)",
1,
"Optional(\"3\")"
)
Collection type: __NSArrayM
First element type: _SwiftValue
Saved: false
Array built with direct values: (
"Optional(2016-08-26 16:49:18 +0000)",
1,
"Optional(\"3\")"
)
Collection type: __NSArrayM
First element type: _SwiftValue
Saved: false
Array built with variable values: (
"Optional(2016-08-26 16:49:18 +0000)",
1,
"Optional(\"3\")"
)
Collection type: __NSArrayM
First element type: _SwiftValue
Saved: false
Literal Keys Dictionary initialized with direct values: {
First = "Optional(2016-08-26 16:49:18 +0000)";
Second = 1;
Third = "Optional(\"3\")";
}
Collection type: __NSDictionaryM
First key type: __NSCFString
First value type: _SwiftValue
Saved: false
Literal Keys Dictionary initialized with variable values: {
First = "Optional(2016-08-26 16:49:18 +0000)";
Second = 1;
Third = "Optional(\"3\")";
}
Collection type: __NSDictionaryM
First key type: __NSCFString
First value type: _SwiftValue
Saved: false
Literal Keys Dictionary built with direct values: {
First = "Optional(2016-08-26 16:49:18 +0000)";
Second = "Optional(1)";
Third = "Optional(3)";
}
Collection type: __NSDictionaryM
First key type: __NSCFString
First value type: _SwiftValue
Saved: false
Literal Keys Dictionary built with variable values: {
First = "Optional(2016-08-26 16:49:18 +0000)";
Second = "Optional(1)";
Third = "Optional(3)";
}
Collection type: __NSDictionaryM
First key type: __NSCFString
First value type: _SwiftValue
Saved: false
Global String Keys Dictionary initialized with direct values: {
First = "Optional(2016-08-26 16:49:18 +0000)";
Second = 1;
Third = "Optional(\"3\")";
}
Collection type: __NSDictionaryM
First key type: __NSCFString
First value type: _SwiftValue
Saved: false
Global String Keys Dictionary initialized with variable values: {
First = "Optional(2016-08-26 16:49:18 +0000)";
Second = 1;
Third = "Optional(\"3\")";
}
Collection type: __NSDictionaryM
First key type: __NSCFString
First value type: _SwiftValue
Saved: false
Global String Keys Dictionary built with direct values: {
First = "Optional(2016-08-26 16:49:18 +0000)";
Second = "Optional(1)";
Third = "Optional(3)";
}
Collection type: __NSDictionaryM
First key type: __NSCFString
First value type: _SwiftValue
Saved: false
Global String Keys Dictionary built with variable values: {
First = "Optional(2016-08-26 16:49:18 +0000)";
Second = "Optional(1)";
Third = "Optional(3)";
}
Collection type: __NSDictionaryM
First key type: __NSCFString
First value type: _SwiftValue
Saved: false
Global NSString Keys Dictionary initialized with direct values: {
First = "Optional(2016-08-26 16:49:18 +0000)";
Second = 1;
Third = "Optional(\"3\")";
}
Collection type: __NSDictionaryM
First key type: __NSCFString
First value type: _SwiftValue
Saved: false
Global NSString Keys Dictionary initialized with variable values: {
First = "Optional(2016-08-26 16:49:18 +0000)";
Second = 1;
Third = "Optional(\"3\")";
}
Collection type: __NSDictionaryM
First key type: __NSCFString
First value type: _SwiftValue
Saved: false
Global NSString Keys Dictionary built with direct values: {
First = "2016-08-26 16:49:18 +0000";
Second = 1;
Third = 3;
}
Collection type: __NSDictionaryM
First key type: __NSCFString
First value type: __NSDate
Saved: true
Global NSString Keys Dictionary built with variable values: {
First = "2016-08-26 16:49:18 +0000";
Second = 1;
Third = 3;
}
Collection type: __NSDictionaryM
First key type: __NSCFString
First value type: __NSDate
Saved: true