Swift subclassing, recursion, and super casting - swift

I'm trying to define a Swift class that has a recursive function that returns the names of all the variables in the class. It works for printing it's variables, but when I use this I'll be using it as a base class for my models, and I may have multiple layers of inheritance. I want the method to return an array of all variable names on the current instance, as well as the names of any variables on any super classes, until we reach the base Cool class.
class Cool:NSObject {
func doStuff() -> [String] {
var values = [String]()
let mirrorTypes = reflect(self)
for i in 0 ..< mirrorTypes.count {
let (name, type) = mirrorTypes[i]
if let superCool = super as! Cool while name == "super" {
values += superCool.doStuff()
}
}
return values
}
}
The problem is in:
if let superCool = super as! Cool while name == "super" {
It causes an Expected '.' or '[' after super compiler error.

Related

Variable 'xxx' was never mutated; in derived class

I'm posting my first message here, I've a logical question about swift language. For your information, I'm quite new in swift language, I'm use to code in c++ and it's a bit hard for me to have an objective point of view on how to do things right (in an elegant way), if you have some advices, pls feel free to do your suggestions.
I'm doing a homemade encapsulation using the following superclass :
class MultiLevel_encapsulation {
var separator = "";
var datas:[String:String] = [:]
func wrap() -> String{
var out:String = ""
var i = 0
for (key, data) in datas{
if i==0{
out += key + separator + data
}
else{
out += separator + key + separator + data
}
i+=1
}
return out
}
func unwrap(content:String){
let split = content.components(separatedBy: separator)
var i = 1
while(i < split.count){
datas[split[i-1]] = split[i]
i += 2
}
}
func getAttributesNames() -> [String]{
var out:[String] = []
for (key, _) in datas{
out.append(key)
}
return out
}
func getValue(name:String) -> String? {
return datas[name];
}
func setValue(name:String, value:String){
datas[name] = value;
}
}
and I want to create some subclasses including the superclass, I just change the separator depending of the subclass name :
class Level5_encapsulation: MultiLevel_encapsulation{
init(message:String) {
super.init()
separator = "&&LEVEL5&&"
unwrap(content:message)
}
override init() {
super.init()
separator = "&&LEVEL5&&"
}
}
So after it I just need to create the subclass as a var in my program, add values and wrap it to have an encapsulated string :
var l5message = Level5_encapsulation()
l5message.setValue(name: #anyTitle#, value: #anyValue#)
var output = l5message.wrap() // String with encapsulated message
Do you think it 's the right way to do it or is there a better way for that ?
My main question is about this compiler warning :
Variable 'l5message' was never mutated; consider changing to 'let' constant
I changed it for a let and it works.
So there is something I don't understand : Why can I change proprieties in the superclass as if the inherited subclass is declared as constant ? Where is the storage of the superclass and how does it works ?
In Swift classes and structs behave a bit differently than in C++. var and let prevent changes to the actual value, and since the variable type that you're using is a class the variable holds a reference, and not the actual data (Like Level5_encapsulation *l5message).
Since you're not mutating the value of the variable (A reference), the compiler raises a warning.

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

iterate over struct attributes in Swift

I am using struct in swift.
class Constants {
struct const {
static let signupFirstName = "signupFirstName"
}
}
I want to iterate the struct. For iterating I am using :
let mirrored_object = Mirror(reflecting: Constants.const())
for (index, attr) in mirrored_object.children.enumerate() {
if let property_name = attr.label as String! {
print("Attr \(index): \(property_name) = \(attr.value)")
}
}
But it does not enter into the code because of static value. Is there any way to iterate this struct?
Since static members are technically part of the type and not the instance, you would need to approach it this way to reflect on the type itself:
let mirrored_object = Mirror(reflecting: Constants.const.self)
However, Swift's automatic reflection for types themselves doesn't appear to be implemented at this time, so even the above line won't work.
That leaves one last option, which is defining your own custom mirror for reflecting on an instance of your type. That could look something like this:
class Constants {
struct const : CustomReflectable {
static let signupFirstName = "signupFirstName"
func customMirror() -> Mirror {
return Mirror(self, children: ["signupFirstName" : const.signupFirstName])
}
}
}
If you modify your code with a CustomReflectable implementation similar to the above, your loop to iterate through the struct members will now work.
Swift reflection will eventually get better out of the box, you might want to try a different approach until then.
You can do it directly by accessing the variable at class level
if let property_value = const.signupFirstName as String! {
print("hello \(property_value)")
}
Be sure to access it from the class it self const not an instance const(). Because the variable is static you can not access it from instance, you have to directly use the class

Get the real type of a protocol to compare with type stored in a variable

I would like to be able to check the object's type of an object that implements a certain protocol.
In the following method, I would like to be able to loop over all the objects from my array of IObject objects and find the one that has as its real type, the type passed in parameter.
func findObject(forType type: Any) -> IObject? {
for (key, value) in self.objects {
if value.Type == type {
return value
}
}
return nil
}
When I try to compile the above code, I've an error message saying: 'IObject' does not have a member named 'Type'.
Any suggestion?
I am using Swift 2 with Xcode 7 beta 6.
I found this question interesting. The only way I have managed to get this to work is by string comparison - i.e. if "\(object.dynamicType)" == "\(forType)".
Also - if I have properly understood the question then I think that the function possibly needs to return an optional array of IObject - because the array of objects may contain more than one of the given type.
protocol IObject: class {}
class SomeObject1: IObject {}
class SomeObject2: IObject {}
let myIObject01 = SomeObject1()
let myIObject02 = SomeObject2()
let myIObject03 = SomeObject1()
let myIObject04 = SomeObject1()
let myIObject05 = SomeObject2()
var objects: [IObject] = [myIObject01,myIObject02,myIObject03,myIObject04,myIObject05]
func findObject(forType: Any) -> [IObject]? {
var result = [IObject]()
for object in objects {
if "\(object.dynamicType)" == "\(forType)" {
result.append(object)
}
}
return result
}
let result = findObject(SomeObject1)
Try to replace this code:
if value.Type == type
to this:
if value.dynamicType == type
If it still doesn't work, try this:
if value.dynamicType == type.self

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>
}