New with Swift 3, you can use Foundation value types to get compile time checks to eliminate many of the errors that couldn't be discovered until runtime when using reference-based Foundation types. Let's work through an example that demonstrates a runtime check in Swift 2.2.
In Swift 2.2:
if let filePath = NSBundle.mainBundle().pathForResource("testFile", ofType: "txt"){ let fileURL = NSURL.fileURLWithPath(filePath) let keys = [NSURLCreationDateKey, NSURLPathKey, NSURLFileSizeKey,NSURLTagNamesKey] var values = try fileURL.resourceValuesForKeys(keys) if let creationDate = values[NSURLCreationDateKey]{ print("creationDate: \(creationDate)") } values[NSURLTagNamesKey] = ["test", "sample", "playground"] values[NSURLCreationDateKey] = "now" // à creates an error print(values[NSURLTagNamesKey]) print(values[NSURLCreationDateKey]) try fileURL.setResourceValues(values) }
In our example, I created a reference to an existing file named testFile.txt
. I would like to know some of the attributes of the file, which I can get by passing an array of strings to the resourceValuesForKeys
method of my file reference. I can read the values using subscript notation and even update the values. Further, I can write the new values back to file with the setResourceValues
method. The problem we experience is subtle and not shown until we execute this block of code. The NSURLCreationDateKey
expects its value to be a valid NSDate
.
However, I passed a string value. At runtime, our code block crashes with an error as we attempt to update our file with the new resource values. What we want is a way to check our constraints at compile time. Swift 3 now gives us a way to do this, and the Foundation framework has been updated to reflect this new feature. Let's update our example to reflect using type safe access to our file resources.
In Swift 3:
if let filePath = Bundle.main.path(forResource: "testFile", ofType: "txt") { var fileURL = URL(fileURLWithPath: filePath) let keys: Set<URLResourceKey> = [.creationDateKey, .pathKey, .fileSizeKey, .tagNamesKey] let values = try fileURL.resourceValues(forKeys: keys) if let creationDate = values.creationDate{ print("creationDate: \(creationDate)") } var newvalues = values newvalues.creationDate = "now" //error: cannot assign value of type 'String' to type 'Date?' // newvalues.creationDate = "now" // ^~~~~ }
In our updated example, our resource types are now strongly typed values of type URLResourceKey
. When we request the resources from our file, we are returned a struct that contains strongly typed properties. These two changes allow us to have type safe access and help us to catch issues at compile time. That's a pretty nice feature to have and the Foundation team agrees, which is evident by all of the APIs they updated to provide us better type constraints.