I'm finding that the following code does not work, but I don't exactly understand why.
Say I have a class name saved in a string. I want to cast a view controller as the class that this string refers to.
let controller = self.navigationController as! NSClassFromString("MyUIViewController")
Swift doesn't seem to understand this - I get this error:
Undeclared use of NSClassFromString.
Or the error:
Consecutive statements must be separated by `,`
Can anyone explain why this is the case? I can't cast (using as?) a type based on some variable?
Thanks
No, you cannot cast to a runtime type object. You must cast to a compile-time type. This is why we write x as Int, not x as Int.self: Int is a type, and Int.self is an object that represents that type at runtime.
What would it mean to cast to NSClassFromString("MyUIViewController")? Now you have a variable, controller, whose value is some type that the compiler knows nothing about, so the compiler cannot let you do anything with controller. You can't call methods or access properties on it, because the compiler doesn't know what methods or properties it has. You can't pass it as an argument to a function, because the compiler doesn't know whether it is the right type for that argument.
If you edit your question to explain what you want to do with controller (what methods you want to call on it or what properties you want to access or what functions you want to pass it to), then I will revise my answer to address your goal.
Related
let screen = mainStoryBoard.instantiateViewController(withIdentifier: self.screenPresenter.screenPresenterIdentifier) as? variable_name
I have to use some variable_name in as? and
variable_name = type(of: object_name)
and
object_name = ExampleViewController() // ExampleViewController is of type UIViewController
Edit:-
means i want to ask after as? we have to provide some Class in which we have to convert like let screen = mainStoryBoard.instantiateViewController(withIdentifier: self.screenPresenter.screenPresenterIdentifier) as? ExampleViewController here ExampleViewController is hardcoded so I want to make it dynamic and take input from user in which class user wants to convert?
First let's get some terms right. When you add as? followed by a Type after a variable, it means you are Type Casting (casting for short) the variable into the Type. Basically you are telling the compiler that you want to handle the object as the type you are casting to. Casting only works if the object is actually the type or the type family you are casting to. You can not cast to unrelated types. There are two forms of casting. The as? form is the conditional form while the as! is the forced form. Because casting can failed, therefore the as? will return the results wrapped in optionals while the as! will crash your app when fail to cast.
There is no way to cast an assignment to a type defined in runtime (dynamic as you have put it) for Swift. The type needs to be defined at compile time. However, there are ways to use type erasure to not care about the exact type during assignment.
Cast as AnyObject. By casting as AnyObject, you're wrapping the object with the type AnyObject (similar to optionals). However when you want to use the object within, you'll need to cast the AnyObject type back to the actual type of the object to use it. Unless the code you are writing is for storage or transporting of these objects, it is not very useful otherwise.
Cast as parent class. Cast to parent class type of all possible class you'll be assigning to the variable. The downside is that the object you are trying to use must be in a class hierarchy and the cast need to use the parent to all that may be assigned. For example. Class A is the parents of Class B and Class C. Class B is the parents of Class D. If you case using Class A, the assignment of A, B, C, or D, will succeed but if you cast as Class C, Class D will fail to be cast as Class C. Therefore, it's only useful to you if the objects you will ever need to cast is the child or grand child of the class you are casting to.
Cast as Protocol. This is similar to #2 except all the object you try to cast must implement the protocol. You can always use extension to add protocol to any class in Swift but you will need to do that before you can cast that class to a particular protocol.
For further information, please read Apple's documentation on Type Casting here.
I'm kind of stuck trying to understand .self in swift, always I register a custom cell in an collectionView or an tableView I have to register like customCollectionViewCell.self but why not for example customCollectionViewCell() ?
I've tried in the playground to type String.self in a var to see what happens and it give me no errors but when I try to give it a value it give me the following error
Cannot assign value of type 'String' to type String.Type
what does it mean?
Type() is shorthand notation for Type.init(), so String() calls String.init() and hence initialises a String.
On the other hand, String.self returns the meta type of String, which is a type that represents the String type itself rather than any instance of that type. For more information on meta types, see the Metatype Type section of the official Swift language reference.
In your specific example, you need to register a UITableViewCell subclass type rather than an instance of a specific type, hence you need to use YourCellType.self.
I've been having some issues with conditional binding returning an invalid (but non-nil) object from the watch accelerometer. I was thinking maybe making a copy of the object could help the problem, but I wasn't sure if that was already occurring. If I use code such as:
if let data = recorder.accelerometerData(from: startDate, to: endDate){...}
is this already creating a copy of the CMSensorDataList object or am I simply getting a reference to it?
It just depends upon whether the type wrapped by the optional was a value type or reference type. If reference type, it's obviously pass by reference. If value type, it's copied (unless CoW, copy-on-write, in which case it's copied if and when it's mutated).
In this case, CMSensorDataList is a class, so it's a reference to that instance, not a copy of it.
As I started developing with Swift and searching through different tutorials and documentations about the language, I'm not sure about one thing.
You can declare an object / value with a specific data type like this:
var aString:String = "Test"
var anObject:SKScene = ASceneClass()
Or you can just do it like this:
var aString = "Test"
var anObject = ASceneClass()
The result will be exactly the same (ASceneClass inherits from SKScene of course)
As everyone is doing it different I wonder if there's a logical reason behind it or you do it for readability ?
Declaring type right after variable name is called Type Annotation
When you don't do that, you have to provide initial value
var aString = "Test"
Often value is not known at that moment, or you are not even sure if it's going to be not nil value, then you can declare it as optional
var aString:String?
If you would like to declare variable without any initiaization but you are sure it's not going to evaluate to nil, you force unwrap it
var aString:String!
This is the definition. In practice, it's always better to use type annotations even when you initialize variable with value, because later in your program you will notice anytime you mess something with the type of the variable.
Also, When you declare an array or dictionary, usually nested ones, Xcode might expect them to have type annotations since it might have some issues with writing values when the type is not known in advance.
To recap
You will want to use type annotations whenever you can, which means whenever you are sure about the variable's type in advance
Recommended/Documented way to declare a variable in swift is as follow:
var <variable name>: <type> = <initial value/expression>
Note: Given form declares a stored variable or stored variable property. Its used when you are clear about type annotation of it.
Though its valid to declare variable without its Type.
var variableName = <initial value>
Note: When you don't know type annotation its mandatory to assign 'Initial value' to that variable.
Refer Swift Documentation on Declaration for more details.
I'm doing something like this:
someFunction(&myClass)
where someFunction sorts an array on myClass.
someFunction(inout someclass:ClassA) {
someClass.sort({$0.price > $1.price})
}
If I print myClass after the function call, I notice the array is still unsorted. From what I know, Swift passes values by copy. But when I use inout, shouldn't it change to pass by reference?
This is because class instances and functions are reference types. Ints, structs, and everything else are value types. When you pass a reference type into a function as a parameter, you are already going to be referencing that instance. When you pass a value type as a parameter, the function gets a copy of that variable (by default), so inout is usually (see edit) only needed if you want to alter a value type from inside of a function.
Altering a class instance without & or inout:
More details
When you create a reference type var t = myClass(), you're really creating a variable t that is a pointer to a myClass instance in memory. By using an ampersand &t in front of a reference type, you are really saying "give me the pointer to the pointer of a myClass instance"
More info on reference vs value types: https://stackoverflow.com/a/27366050/580487
EDIT
As was pointed out in the comments, you can still use inout with reference types if you want to alter a pointer, etc, but I was trying to shed light on the general use case.
Below is an example of sorting an array inside of a function:
If you post your code here, it would be more meaningful. BTW, look at below links that might helpful for you,
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html#//apple_ref/doc/uid/TP40014097-CH10-ID173
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-ID545