Swift Conversion using a variable - swift

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()

Related

Cast SomeType<Protocol> to SomeType<ProtocolImpl> in Swift

I'm currently experimenting with Generics in Swift and came to some problem with casting some types around such as SomeType<Protocol> to SomeType<ProtocolImpl>. So basically I have some type that takes a generic parameter which is handled as a Protocol and which at a later point is casted to a more concrete type. My question is if that isn't possible to do?
/// 'dict' is of type [String: SomeType<Protocol>]
if let element = dict["str"], // 'element' here is of type SomeType<Protocol>
let castedElement = element as? SomeType<ProtocolImpl> { // This is always false
return castedElement // Here I want to return castedElement with type SomeType<ProtocolImpl>
}
Is there any way to make this cast work? I'm already working on another solution for my problem, but I'm still interested if there's a way to make this work somehow.
Edit: Because #jtbandes wanted a example he can paste somewhere, here:
class SomeType<T> {
let value: T
init(value: T) {
self.value = value
}
}
protocol Protocol {}
class ProtocolImpl: Protocol {}
var dict: [String: SomeType<Protocol>] = ["str": SomeType(value: ProtocolImpl())]
if let element = dict["str"],
let castedElement = element as? SomeType<ProtocolImpl> {
print(castedElement.value) // I want to get here
}
Long story short, generics in Swift are not covariant, which means that SomeType< ProtocolImpl> is not convertible SomeType<Protocol>, even if ProtocolImpl conforms to Protocol. Thus the direct answer to your question is: this is not currently possible in Swift.
However you might ask yourself is why do you need the downcast in the first place. As you're storing the instances in a container, polymorphic behaviour might be better suited. You could declare the functionality you need to access as part of the protocol, and access is though the protocol interface. This way you don't need to know which is the concrete implementation under the hood, which is one of the main reason of using a protocol.
It's hard for me to tell what you're trying to achieve from the question. Still, maybe the below will help you.
class SomeType<T>: Protocol { // Maybe SomeType IS your ProtoImpl?
let value: T
init(value: T) {
self.value = value
}
}
protocol Protocol {}
//class ProtocolImpl: Protocol {}
//var dict: [String: SomeType<Protocol>] = ["str": SomeType(value: ProtocolImpl())]
var dict: [String: Protocol] = ["str1": SomeType<String>(value: "Some Type"),
"str2": SomeType<Int>(value: 1)
]
if let castedElement = dict["str1"] as? SomeType<String> {
print(castedElement.value) // --> "Some Type"
}
if let castedElement = dict["str2"] as? SomeType<Int> {
print(castedElement.value) // --> "1"
}

"Generic parameter 'T' could not be inferred" error in Swift

I am trying to practice "class with generic". I encountered 2 errors:
Generic parameter 'T' could not be inferred
Reference to generic type 'GenericObject' requires arguments in <...>
The 2 errors in GenericManager class. Please reference the following code. How do I solve this issue?
class User {
var name: String
init(name: String) {
self.name = name
}
}
class Employee {
var name: String
var position: String
init(name: String, position: String) {
self.name = name
self.position = position
}
}
class GenericObject<T> {
var items = [T]()
init(forType: T.Type) {}
func addObject(_ obj: T) {
self.items.append(obj)
}
}
class GenericManager {
//issue: Generic parameter 'T' could not be inferred
var objects = [GenericObject]()
//issue: Reference to generic type 'GenericObject' requires arguments in <...>
func addObject(_ obj: GenericObject) {
self.objects.append(obj)
}
}
let u = User(name: "User")
let uo = GenericObject(forType: User.self)
uo.addObject(u)
let e = Employee(name: "Employee", position: "session manager")
let eo = GenericObject(forType: Employee.self)
eo.addObject(e)
let manager = GenericManager()
manager.addObject(uo)
manager.addObject(eo)
The compiler needs to know the type of T, and in this case you haven't supplied it.
You can do it like this:
var objects = [GenericObject<YourTypeHere>]()
For example, if GenericObject will hold an array of Int, it would look like this:
var objects = [GenericObject<Int>]()
I noticed you updated your question. It would be helpful to know what you're trying to achieve, but I'll try to help you anyway.
When you have a generic object, you need to tell the compiler the type of the generic at compile time, that's why it's complaining that the type can't be inferred, it needs to know.
Since you want to be able to add objects to the GenericManager array, you need the generic in those two cases to be the same, so you can modify your class like this:
class GenericManager<T> {
var objects = [GenericObject<T>]()
func addObject(_ obj: GenericObject<T>) {
self.objects.append(obj)
}
}
However, since the objects have to be of the same generic, you can't add a GenericObject<User> and GenericObject<Employee> to the same manager, what you can do is to implement those as GenericObject<Any>, and do the same with the GenericManager, then it will look like this:
let u = User(name: "User")
let uo = GenericObject(forType: Any.self)
uo.addObject(u)
let e = Employee(name: "Employee", position: "session manager")
let eo = GenericObject(forType: Any.self)
eo.addObject(e)
let manager = GenericManager<Any>()
manager.addObject(uo)
manager.addObject(eo)
Keep in mind that this will lose you any advantage that generics would do, what you could do is to create a protocol or common superclass and use that instead of Any, but that depends on what you're trying to achieve.
If you have any further questions, please add a comment instead of silently updating your question.
The problem you are having is that you are trying to use generics, but want to ignore that in GenericManager and store references to objects of different types.
Consider this - when you call manager.objects[0] what would you expect to be returned?
You can solve this by type-erasure using Any as EmilioPelaez suggested. However this is often a codesmell which leads to casting hacks throughout your code.
One alternative would be to use an enum to specify the different types of data you want to represent:
enum GenericObject {
case users([User])
case employees([Employee])
}
...
let uo = GenericObject.users([ u ])
...
let eo = GenericObject.employees([ e ])
Now when you access the properties inside GenericManager you would be required to switch over the different supported types, and when you add a new type you would be required to implement code whenever you use a GenericObject

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.

Using a Type Variable in a Generic

I have this question except for Swift. How do I use a Type variable in a generic?
I tried this:
func intType() -> Int.Type {
return Int.self
}
func test() {
var t = self.intType()
var arr = Array<t>() // Error: "'t' is not a type". Uh... yeah, it is.
}
This didn't work either:
var arr = Array<t.Type>() // Error: "'t' is not a type"
var arr = Array<t.self>() // Swift doesn't seem to even understand this syntax at all.
Is there a way to do this? I get the feeling that Swift just doesn't support it and is giving me somewhat ambiguous error messages.
Edit: Here's a more complex example where the problem can't be circumvented using a generic function header. Of course it doesn't make sense, but I have a sensible use for this kind of functionality somewhere in my code and would rather post a clean example instead of my actual code:
func someTypes() -> [Any.Type] {
var ret = [Any.Type]()
for (var i = 0; i<rand()%10; i++) {
if (rand()%2 == 0){ ret.append(Int.self) }
else {ret.append(String.self) }
}
return ret
}
func test() {
var ts = self.someTypes()
for t in ts {
var arr = Array<t>()
}
}
Swift's static typing means the type of a variable must be known at compile time.
In the context of a generic function func foo<T>() { ... }, T looks like a variable, but its type is actually known at compile time based on where the function is called from. The behavior of Array<T>() depends on T, but this information is known at compile time.
When using protocols, Swift employs dynamic dispatch, so you can write Array<MyProtocol>(), and the array simply stores references to things which implement MyProtocol — so when you get something out of the array, you have access to all functions/variables/typealiases required by MyProtocol.
But if t is actually a variable of kind Any.Type, Array<t>() is meaningless since its type is actually not known at compile time. (Since Array is a generic struct, the compiler needs know which type to use as the generic parameter, but this is not possible.)
I would recommend watching some videos from WWDC this year:
Protocol-Oriented Programming in Swift
Building Better Apps with Value Types in Swift
I found this slide particularly helpful for understanding protocols and dynamic dispatch:
There is a way and it's called generics. You could do something like that.
class func foo() {
test(Int.self)
}
class func test<T>(t: T.Type) {
var arr = Array<T>()
}
You will need to hint the compiler at the type you want to specialize the function with, one way or another. Another way is with return param (discarded in that case):
class func foo() {
let _:Int = test()
}
class func test<T>() -> T {
var arr = Array<T>()
}
And using generics on a class (or struct) you don't need the extra param:
class Whatever<T> {
var array = [T]() // another way to init the array.
}
let we = Whatever<Int>()
jtbandes' answer - that you can't use your current approach because Swift is statically typed - is correct.
However, if you're willing to create a whitelist of allowable types in your array, for example in an enum, you can dynamically initialize different types at runtime.
First, create an enum of allowable types:
enum Types {
case Int
case String
}
Create an Example class. Implement your someTypes() function to use these enum values. (You could easily transform a JSON array of strings into an array of this enum.)
class Example {
func someTypes() -> [Types] {
var ret = [Types]()
for _ in 1...rand()%10 {
if (rand()%2 == 0){ ret.append(.Int) }
else {ret.append(.String) }
}
return ret
}
Now implement your test function, using switch to scope arr for each allowable type:
func test() {
let types = self.someTypes()
for type in types {
switch type {
case .Int:
var arr = [Int]()
arr += [4]
case .String:
var arr = [String]()
arr += ["hi"]
}
}
}
}
As you may know, you could alternatively declare arr as [Any] to mix types (the "heterogenous" case in jtbandes' answer):
var arr = [Any]()
for type in types {
switch type {
case .Int:
arr += [4]
case .String:
arr += ["hi"]
}
}
print(arr)
I would break it down with the things you already learned from the first answer. I took the liberty to refactor some code. Here it is:
func someTypes<T>(t: T.Type) -> [Any.Type] {
var ret = [Any.Type]()
for _ in 0..<rand()%10 {
if (rand()%2 == 0){ ret.append(T.self) }
else {
ret.append(String.self)
}
}
return ret
}
func makeArray<T>(t: T) -> [T] {
return [T]()
}
func test() {
let ts = someTypes(Int.self)
for t in ts {
print(t)
}
}
This is somewhat working but I believe the way of doing this is very unorthodox. Could you use reflection (mirroring) instead?
Its possible so long as you can provide "a hint" to the compiler about the type of... T. So in the example below one must use : String?.
func cast<T>(_ value: Any) -> T? {
return value as? T
}
let inputValue: Any = "this is a test"
let casted: String? = cast(inputValue)
print(casted) // Optional("this is a test")
print(type(of: casted)) // Optional<String>
Why Swift doesn't just allow us to let casted = cast<String>(inputValue) I'll never know.
One annoying scenerio is when your func has no return value. Then its not always straightford to provide the necessary "hint". Lets look at this example...
func asyncCast<T>(_ value: Any, completion: (T?) -> Void) {
completion(value as? T)
}
The following client code DOES NOT COMPILE. It gives a "Generic parameter 'T' could not be inferred" error.
let inputValue: Any = "this is a test"
asyncCast(inputValue) { casted in
print(casted)
print(type(of: casted))
}
But you can solve this by providing a "hint" to compiler as follows:
asyncCast(inputValue) { (casted: String?) in
print(casted) // Optional("this is a test")
print(type(of: casted)) // Optional<String>
}

testing protocol conformance with associated types

I have a protocol that uses an associated type, as such:
protocol Populatable {
typealias T
func populateWith(object: T)
}
and classes that implement the protocol:
class DateRowType: Populatable {
func populateWith(object: NSDate) {
print(object.description)
}
}
class StringRowType : Populatable {
func populateWith(object: String) {
print(object)
}
}
but when I try to cast or test for conformance, like this:
let drt = DateRowType()
let srt = StringRowType()
let rowTypes = [drt, srt]
let data = [NSDate(), "foo"]
for (i, p: Populatable) in enumerate(rowTypes) {
p.populateWith(data[i])
}
I get the error:
Protocol 'Populatable' can only be used as a generic constraint because it has Self or associated type requirements
What's the correct way to test if the object conforms to the Populatable protocol?
Note: all the code required to try this out is contained in the question, just copy the code blocks into a playground.
As the error says, you cannot cast it to Populatable here. I think the correct way is to cast it to EventRowType.
if let rowController = self.table.rowControllerAtIndex(i) as? EventRowType {
And you already tested that 'EventRowType' class conforms 'Populatable' protocol. Because if the EventRowType doesn't have function named 'populate', swift compiler says,
Type 'EventRowType' does not conform to protocol 'Populatable'
I don't think you will be able to go generic the whole way, unless possibly by using AnyObject and testing the class of the parameter in each populateWith function.
But this will work:
for (i, p) in enumerate(rowTypes) {
if let dateRow = p as? DateRowType {
dateRow.populateWith(data[i] as! NSDate)
}
else if let stringRow = p as? StringRowType {
stringRow.populateWith(data[i] as! String)
}
}
You will just need to expand this for every Populatable class you add.