PLIST read/write with generic dictionary in Swift - swift

I'm trying to add load/save functions to a generic caching class, like this:
class Cache<T:Hashable, U:AnyObject> {
private var cache: [T:U] = [:]
private var url: NSURL!
func load() {
if let path = self.url where NSFileManager().isReadableFileAtPath(path.path!) {
if let dict = NSDictionary(contentsOfURL: path) {
// The next line is rejected by the compiler as always failing the cast
cache = (dict as? DictType)!
}
}
}
func save() {
if let path = self.url where NSFileManager().isWritableFileAtPath(path.path!) {
// The next line is rejected by the compiler as the method not existing
let result = cache.writeToURL(path, atomically: false)
}
}
.
.
.
}
If I use concrete types this works, but translating to generics apparently takes away enough information that the compiler is no longer willing to accept it. Is there a way to do this?
I also tried looping through the NSDictionary, but for example the loop in the load routine then complains it doesn't have a type for the key (and won't accept what's returned from allKeys); nor can I cast / coerce to the type T.

Related

Is there a way to compile with a generic type?

This is something that has vexed a number of developers including myself. Let say we have a protocol that defines a subscript which we apply to a simple class.
protocol Cache {
subscript<Value>(_: String) -> Value? { get set }
}
class InMemoryCache: Cache {
private var cache: [String: Any] = [:]
subscript<Value>(key: String) -> Value? {
get {
cache[key] as? Value
}
set {
if let value = newValue {
cache[key] = value
} else {
cache.remove(key)
}
}
}
}
This works fine as long as we know the types:
cache["abc"] = 5
let x: Int? = cache["abc"]
but the developers want to do this:
cache["abc"] = nil
Which won't compile because the compiler cannot determine the Value generic type. This works however
cache["abc"] = nil as String?
I've tried a number of things but they all have drawbacks. Things like adding a second subscript with the Any type. Nothing seems to work well even though it would seem like a simple problem.
Has anyone found a solution that handles cache["abc"] = nil?
You can do this by changing your protocol requirements somewhat.
Have the protocol require a subscript that does not use generics, and returns an Any?.
protocol Cache {
subscript(key: String) -> Any? { get set }
}
This subscript will let you do the following:
cache["abc"] = 5
cache["abc"] = nil
let value = cache["abc"] // value is an `Any?`
but it will not let you do this:
let number: Int? = cache["abc"] // error
So, let's fix that by adding another subscript to Cache. This subscript is equivalent to your original subscript requirement, except it doesn't need a setter and will call the other subscript (the one required by the protocol):
extension Cache {
subscript<Value>(key: String) -> Value? {
self[key] as? Value
}
}
(If you're worried that this subscript calls itself, don't be. self[key] here actually calls the other subscript, not this one. You can confirm this in Xcode by command-clicking on the [ or the ] in self[key] to jump to the definition of the other subscript.)
Then, implement the required subscript in your class:
class InMemoryCache: Cache {
private var cache: [String: Any] = [:]
subscript(key: String) -> Any? {
get { cache[key] }
set { cache[key] = newValue }
}
}
This will allow all of the following to compile:
let cache = InMemoryCache()
cache["abc"] = 5
let x: Int? = cache["abc"]
cache["abc"] = nil
There is a workaround to have your desire output.
Because this is a dictionary so you get assign nil directly in your InMemoryCache
class InMemoryCache: Cache {
private var cache: [String: Any] = [:]
subscript<Value>(key: String) -> Value? {
get {
cache[key] as? Value
}
set {
if let value = newValue {
cache[key] = value
} else {
cache[key] = nil // make nil directly here
}
}
}
}
In here because of Value is a generic type. So you can not assign nil directly. It must have a specific type.
Instead you can do like this
let nilValue : Int? = nil // any type nil you want
cache["abc"] = nilValue
or directly cast it to nil of any tupe before assign to dictionary
cache["abc"] = (nil as String?)
It will refresh anything value is store in the key.
Example
// value
let nilValue : Int? = nil
var number : Int? = nil
var string : String? = nil
cache["abc"] = 5
number = cache["abc"] // Optional.some(5)
cache["abc"] = "abc"
number = cache["abc"] // nil
string = cache["abc"] // Optional.some("abc")
cache["abc"] = nilValue
number = cache["abc"] // nil
string = cache["abc"] // nil
The reason why you are having a hard time with this is because
cache["abc"] = nil
cannot be compiled. There is not enough information to infer the generic type of the subscript - or of the optional value. The compiler sees something like
cache<?>["abc"] = Optional<?>.none
How is it supposed to figure out what to put in place of the question marks?
There's another ambiguity. Your cache can contain any type, even Optional. When you are assigning nil to the subscript, how does anybody know if you want to remove the element or store an instance of Optional<Something>.none at the subscript?
When I find myself fighting the language in this way, I usually try to take a step back and ask if I am perhaps doing something fundamentally bad. I think, in this case, the answer is yes. You are trying to pretend something is more strictly typed than it really is.
I think your getter/setter should explicitly take a value that is of type Any. It works better and it has the advantage that it explicitly documents for the user that a Cache conforming type can store anything in it.
For this reason, I would say TylerP's solution is the best. However, I would not create a subscript in the extension, I would define a function
extension Cache
{
func value<Value>(at key: String) -> Value?
{
self[key] as? Value
}
}
The reason for this is that the compiler can get confused when you have multiple subscripts with similar signatures. With the extension above, I can conform Dictionary<String, Any> to the protocol and not need a new class.
extension Dictionary: Cache where Key == String, Value == Any {}
var dict: [String : Any] = [:]
dict["abc"] = 5
let y: Int? = dict.value(at: "abc")
dict["abc"] = nil
Obviously, the above won't be useful to you if you need reference semantics for your cache.
TylerP's solution was pretty much bang on the money. For completeness though, here's what the code now looks like:
protocol Cache {
/// Handles when we need a value of a specific type.
subscript<Value>(_: String) -> Value? { get }
/// Handles getting and setting any value.
/// The getter is rarely used because the generic getter above
/// is used. Setting a value compiles because we don't care what
/// type is it. Setting a `nil` also compiles for the same reason.
subscript(_: String) -> Any? { get set }
}
class InMemoryCache: Cache {
private var cache: [String: Any] = [:]
subscript(key: String) -> Any? {
get { cache[key] }
set {
if let value = newValue {
cache[key] = value
} else {
remove(key)
}
}
}
subscript<Value>(key: String) -> Value? {
cache[key] as? Value
}
}

Swift Generics and Protocols

Given the below, this will throw a compile time error about the protocol not being able to adhere to itself and only struct/enum can adhere to the protocol. This seems to defeat the purpose of being able to use protocols in generics. I'm trying to understand why this doesn't work, but if I remove the generic and just put the protocol where 'Z' is everything is fine. It seems antithetical to what protocols and generics should be allowed for.
**Edit for question clarity: I need to take a type of Any that can be cast to a dictionary of [String:MyProtocol] and pass it into the method printEm. printEm must use the generic as it will be instantiating instances of the class Z.
protocol MyProtocol {
init()
var whoAmI:String { get }
}
func genericPassing(unknownThing:Any) {
let knownThing = unknownThing as? [String:MyProtocol]
if(knownThing != nil){
self.printEm(knownThing)
}
}
func printEm<Z:MyProtocol>(theThings:[String:Z]) {
let zCollection:[Z] = []
for thing in theThings {
print(thing.whoAmI)
zCollection.append(Z())
}
}
**Edited printEm to illustrate why generic is needed.
** Edit for more complex code. The two primary requirements are the use of a generic to call Z() and the ability to take an Any and somehow type check it and/or cast it so that it can be used in a genericized method.
private func mergeUpdates<Z:RemoteDataSyncable>(source:inout Z, updates:[WritableKeyPath<Z, Any>:Any]) throws {
for key in updates.keys {
let value = updates[key]!
let valueDict = value as? [String:[WritableKeyPath<RemoteDataSyncable, Any>:Any]]
if(valueDict != nil) {
var currentValueArray = source[keyPath: key] as? [RemoteDataSyncable]
if(currentValueArray != nil) {
self.mergeUpdates(source: &currentValueArray!, updates: valueDict!)
}
else {
throw SyncError.TypeError
}
}
else {
source[keyPath: key] = value
}
}
}
private func mergeUpdates<Z:RemoteDataSyncable>(source:inout [Z], updates:[String:[WritableKeyPath<Z,Any>:Any]]) {
for key in updates.keys {
var currentObject = source.first { syncable -> Bool in
return syncable.identifier == key
}
if(currentObject != nil) {
try! self.mergeUpdates(source: &currentObject!, updates: updates[key]!)
}
else {
var newSyncable = Z()
try! self.mergeUpdates(source: &newSyncable, updates: updates[key]!)
source.append(newSyncable)
}
}
}
This is a perfect example of why protocols do not conform to themselves. In your code Z is MyProtocol, so Z() is MyProtocol(). How would that work? What type is it? How big is it? You then can't put them into a [Z], since they might be different types.
You mean to pass arbitrary MyProtocols and call init on the type of each element:
func printEm(theThings:[String: MyProtocol]) {
var zCollection:[MyProtocol] = []
for thing in theThings.values {
print(thing.whoAmI)
zCollection.append(type(of: thing).init())
}
}
When I suggested using closures, this is the kind of thing I mean. This Updater can accept arbitrary ReferenceWritableKeyPaths, and when you pass it Any update value, it'll assign it to every key path that can accept it. That's kind of useless, but shows the technique. (Keep in mind that the updater is retaining the object, so that may be a problem that you need to address.)
class Updater {
private(set) var updaters: [(Any) -> ()] = []
func add<Root, Value>(keyPath: ReferenceWritableKeyPath<Root, Value>, on root: Root) {
updaters.append { value in
if let value = value as? Value {
root[keyPath: keyPath] = value
}
}
}
func updateAll(with value: Any) {
for updater in updaters {
updater(value)
}
}
}
class Client {
var updateMe: Int = 0
}
let client = Client()
let updater = Updater()
updater.add(keyPath: \.updateMe, on: client)
updater.updateAll(with: 3)
client.updateMe // 3
The key lesson in this code is that the generic types on the add are erased (hidden) by the (Any) -> () closure at compile-time. And the runtime as? check is done inside that closure, where the types are all known.

How to return value from a closure in a Struct in Swift?

I'm retrieving data from a website.
Networking works well. Data is parsed correctly from JSON.
A couple of references - In this struct:
Replies is the datamodel for the JSON
PrepareQuestions is a func which performs the parsing (I have it in an extension of the same Struct)
I'd like to have an object within this struct (downloadedData - 'Replies' is the struct with the datamodel) containing all the information downloaded, but I incur into an error due to "self being an immutable capture". Any suggestions? Thank you!
struct QuestionsManager {
var downloadedData:Replies?
func useData() {
manageQuestions(url: K.urlForRetreival, numberOfQuestions: K.numberOfSquares) { [self] (replies, error) in
if let replies = replies {
DispatchQueue.main.async {
downloadedData = replies // Here I got the error
}
}
}
}
func manageQuestions(url: String, numberOfQuestions: String, myCompletion: #escaping (Replies?, Error?)->()) {
let generatedUrl = URL(string: url + numberOfQuestions)!
let urlSession = URLSession(configuration: .default)
let task = urlSession.dataTask(with: generatedUrl) { (data, response, error) in
if error == nil {
if let fetchedData = data {
let fetchedProcessedData = prepareQuestions(data: fetchedData)
myCompletion(fetchedProcessedData, nil)
return
}
} else {
myCompletion(nil, error)
return
}
}
task.resume()
}
}
You're seeing this error because the closure captures an immutable self.
Just like primitive types (e.g. Int), structs are value-types, and Swift is built with the notion of immutability of value-types.
In other words, if you had let questionManager = QuestionManager(), you'd expect questionManager not to change. Even if it was a var, it can only mutate via direct action by the caller, e.g. questionManager.doMutatingFunc().
But, if a closure was allowed to capture self, it could modify itself at some later point. This is not allowed.
This simplest (only?) way to fix this is to turn QuestionManager into a class:
class QuestionManager {
// ...
}
struct is a value type. For value types, only methods explicitly
marked as mutating can modify the properties of self, so this is not
possible within a computed property.
If you change struct to be a class then your code compiles without
problems.
Structs are value types which means they are copied when they are
passed around.So if you change a copy you are changing only that copy,
not the original and not any other copies which might be around.If
your struct is immutable then all automatic copies resulting from
being passed by value will be the same.If you want to change it you
have to consciously do it by creating a new instance of the struct
with the modified data.
From https://stackoverflow.com/a/49253452/11734662

Reference Types/Subclassing, and Changes to Swift 4 Codable & encoder/decoders

I'm struggling to understand class/reference type behavior and how this relates to changes as I try to upgrade and reduce code using Codable in Swift 4.
I have two classes – a SuperClass with all of the data that will be persistent and that I save to UserDefaults (a place name & string with coordinates), and a SubClass that contains additional, temporary info that I don't need (weather data for the SuperClass coordinates).
In Swift 3 I used to save data like this:
func saveUserDefaults() {
var superClassArray = [SuperClass]()
// subClassArray is of type [SubClass] and contains more data per element.
superClassArray = subClassArray
let superClassData = NSKeyedArchiver.archivedData(withRootObject: superClassArray)
UserDefaults.standard.set(superClassData, forKey: " superClassData")
}
SuperClass conformed to NSObject & NSCoding
It also included the required init decoder & the encode function.
It all worked fine.
In trying to switch to Swift 4 & codable I've modified SuperClass to conform to Codable.
SuperClass now only has one basic initializer and none of the encoder/decoder stuff from Swift 3. There is no KeyedArchiving happening with this new approach (below). SubClass remains unchanged. Unfortunately I crash on the line where I try? encoder.encode [giving a Thread 1: EXC_BAD_ACCESS (code=1, address=0x10)]. My assumption is that the encoder is getting confused with identical reference types where one is SuperClass and one SubClass (subClassArray[0] === superClassArray[0] is true).
I thought this might work:
func saveUserDefaults() {
var superClassArray = [SuperClass]()
superClassArray = subClassArray
// assumption was that the subclass would only contain parts of the superclass & wouldn't produce an error when being encoded
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(superClassArray){
UserDefaults.standard.set(encoded, forKey: " superClassArray ")
} else {
print("Save didn't work!")
}
}
Then, instead of creating an empty superClassArray, then using:
superClassArray = subClassArray, as shown above, I replace this with the single line:
let superClassArray: [SuperClass] = subClassArray.map{SuperClass(name: $0.name, coordinates: $0.coordinates)}
This works. Again, assumption is because I'm passing in the values inside of the class reference type & haven't made the superClassArray = subClassArray. Also, as expected, subClassArray[0] === superClassArray[0] is false
So why did the "old stuff" in Swift 3 work, even though I used the line superClassArray = subClassArray before the let superClassData = NSKeyedArchiver.archivedData(withRootObject: superClassArray)
? Am I essentially achieving the same result by creating the array in Swift 4 that was happening with the old Swift 3 encoder/decoder? Is the looping / recreation
Thanks!
Polymorphic persistence appears to be broken by design.
The bug report SR-5331 quotes the response they got on their Radar.
Unlike the existing NSCoding API (NSKeyedArchiver), the new Swift 4 Codable implementations do not write out type information about encoded types into generated archives, for both flexibility and security. As such, at decode time, the API can only use the concrete type your provide in order to decode the values (in your case, the superclass type).
This is by design — if you need the dynamism required to do this, we recommend that you adopt NSSecureCoding and use NSKeyedArchiver/NSKeyedUnarchiver
I am unimpressed, having thought from all the glowing articles that Codable was the answer to some of my prayers. A parallel set of Codable structs that act as object factories is one workaround I'm considering, to preserve type information.
Update I have written a sample using a single struct that manages recreating polymorphic classes. Available on GitHub.
I was not able to get it to work easily with subclassing. However, classes that conform to a base protocol can apply Codable for default encoding. The repo contains both keyed and unkeyed approaches. The simpler is unkeyed, copied below
// Demo of a polymorphic hierarchy of different classes implementing a protocol
// and still being Codable
// This variant uses unkeyed containers so less data is pushed into the encoded form.
import Foundation
protocol BaseBeast {
func move() -> String
func type() -> Int
var name: String { get }
}
class DumbBeast : BaseBeast, Codable {
static let polyType = 0
func type() -> Int { return DumbBeast.polyType }
var name:String
init(name:String) { self.name = name }
func move() -> String { return "\(name) Sits there looking stupid" }
}
class Flyer : BaseBeast, Codable {
static let polyType = 1
func type() -> Int { return Flyer.polyType }
var name:String
let maxAltitude:Int
init(name:String, maxAltitude:Int) {
self.maxAltitude = maxAltitude
self.name = name
}
func move() -> String { return "\(name) Flies up to \(maxAltitude)"}
}
class Walker : BaseBeast, Codable {
static let polyType = 2
func type() -> Int { return Walker.polyType }
var name:String
let numLegs: Int
let hasTail: Bool
init(name:String, legs:Int=4, hasTail:Bool=true) {
self.numLegs = legs
self.hasTail = hasTail
self.name = name
}
func move() -> String {
if numLegs == 0 {
return "\(name) Wriggles on its belly"
}
let maybeWaggle = hasTail ? "wagging its tail" : ""
return "\(name) Runs on \(numLegs) legs \(maybeWaggle)"
}
}
// Uses an explicit index we decode first, to select factory function used to decode polymorphic type
// This is in contrast to the current "traditional" method where decoding is attempted and fails for each type
// This pattern of "leading type code" can be used in more general encoding situations, not just with Codable
//: **WARNING** there is one vulnerable practice here - we rely on the BaseBeast types having a typeCode which
//: is a valid index into the arrays `encoders` and `factories`
struct CodableRef : Codable {
let refTo:BaseBeast //In C++ would use an operator to transparently cast CodableRef to BaseBeast
typealias EncContainer = UnkeyedEncodingContainer
typealias DecContainer = UnkeyedDecodingContainer
typealias BeastEnc = (inout EncContainer, BaseBeast) throws -> ()
typealias BeastDec = (inout DecContainer) throws -> BaseBeast
static var encoders:[BeastEnc] = [
{(e, b) in try e.encode(b as! DumbBeast)},
{(e, b) in try e.encode(b as! Flyer)},
{(e, b) in try e.encode(b as! Walker)}
]
static var factories:[BeastDec] = [
{(d) in try d.decode(DumbBeast.self)},
{(d) in try d.decode(Flyer.self)},
{(d) in try d.decode(Walker.self)}
]
init(refTo:BaseBeast) {
self.refTo = refTo
}
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
let typeCode = try container.decode(Int.self)
self.refTo = try CodableRef.factories[typeCode](&container)
}
func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
let typeCode = self.refTo.type()
try container.encode(typeCode)
try CodableRef.encoders[typeCode](&container, refTo)
}
}
struct Zoo : Codable {
var creatures = [CodableRef]()
init(creatures:[BaseBeast]) {
self.creatures = creatures.map {CodableRef(refTo:$0)}
}
func dump() {
creatures.forEach { print($0.refTo.move()) }
}
}
//: ---- Demo of encoding and decoding working ----
let startZoo = Zoo(creatures: [
DumbBeast(name:"Rock"),
Flyer(name:"Kookaburra", maxAltitude:5000),
Walker(name:"Snake", legs:0),
Walker(name:"Doggie", legs:4),
Walker(name:"Geek", legs:2, hasTail:false)
])
startZoo.dump()
print("---------\ntesting JSON\n")
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let encData = try encoder.encode(startZoo)
print(String(data:encData, encoding:.utf8)!)
let decodedZoo = try JSONDecoder().decode(Zoo.self, from: encData)
print ("\n------------\nAfter decoding")
decodedZoo.dump()
Update 2020-04 experience
This approach continues to be more flexible and superior to using Codable, at the cost of a bit more programmer time. It is used very heavily in the Touchgram app which provides rich, interactive documents inside iMessage.
There, I need to encode multiple polymorphic hierarchies, including different Sensors and Actions. By storing signatures of decoders, it not only provides with subclassing but also allows me to keep older decoders in the code base so old messages are still compatible.

Swift Conversion using a variable

Is there anyway to use conversion using a variable? I am using object stacking using type of "AnyObject" and I've been able to take the class type and populate a variable. Now I need to populate an array using conversion.
var myString = "Hello World"
var objectStack = [AnyObject]()
objectStack.append(myString)
let currentObject = String(describing: objectStack.last!)
var objectType = String()
let range: Range<String.Index> = currentObject.range(of: ":")!
objectType = currentObject.substring(to: range.lowerBound)
let range2: Range<String.Index> = objectType.range(of: ".")!
objectType = objectType.substring(from: range2.upperBound)
The code above will evaluate the class and set the value of "objectType" to "String". Now I'm trying to go the other way. Something like this:
for obj in objectStack{
obj = newObject as! objectType //this doesn't work
}
Is something like this possible?
There is a simpler, safer way to get the type:
let type = type(of: objectStack.last!) // String.Type
let typeString = String(describing: type) // "String"
The other way around is not possible because the type of the object is not known at compile time. Do you have a number of known types you want to try to cast to? In that case, use optional binding to check if the cast is successful:
let object = objectStack.last!
if let string = object as? String {
// do String stuff
}
else if let i = object as? Int {
// do Int stuff
}
// and so on
If you have a large number of possible types that share some common functionality: Use Protocols. See Swift Documentation for a nice introduction.
You define a protocol for some common functionality that different types can implement:
protocol Stackable {
func doStuff()
// (more methods or properties if necessary)
}
The protocol provides a contract that all types conforming to this protocol have to fulfill by providing implementations for all declared methods and properties. Let's create a struct that conforms to Stackable:
struct Foo: Stackable {
func doStuff() {
print("Foo is doing stuff.")
}
}
You can also extend existing types to make them conform to a protocol. Let's make String Stackable:
extension String: Stackable {
func doStuff() {
print("'\(self)' is pretending to do stuff.")
}
}
Let's try it out:
let stack: [Stackable] = [Foo(), "Cat"]
for item in stack {
item.doStuff()
}
/*
prints the following:
Foo is doing stuff.
'Cat' is pretending to do stuff.
*/
This worked for me:
var instance: AnyObject! = nil
let classInst = NSClassFromString(objectType) as! NSObject.Type
instance = classInst.init()