Calling an initializer having only the class name in Swift - ios

I wanted to know if there was a way to call an initializer by only having the class name in Swift.
class Shape {
let name: String
var numberOfSides: Int?
init(name: String) {
self.name = name
}
}
Pseudo code:
let shapeClass = stringToClass("Shape")
let shape: Shape = shapeClass(name: "Something")

More than just trying to call a class function, you are trying to call an initializer dynamically. To be able to call an initializer dynamically on a class that you get at runtime, the compile-time type of that class value must be a metatype of a class that has that initializer; but since initializers are not always inherited in Swift, that initializer must be declared required.
Here we use NSClassFromString to get a class from a string. (I am declaring the classes as #objc with an explicit name, because otherwise Swift class names are mangled from the perspective of Objective-C and it's would be a pain to deal with.) However, it returns AnyClass (i.e. AnyObject.Type), which is a metatype whose values are any class. We want it to be restricted to classes that inherit from Shape, so we can use Shape's initializer, so we cast it to Shape.Type (the metatype whose values are classes that descend from Shape, inclusive).
I also demonstrate that it is polymorphic as it works with the name of a subclass by simply changing the name.
import Foundation
#objc(Shape)
class Shape {
let name: String
var type: String
required init(name: String) {
self.name = name
self.type = "Shape"
}
}
#objc(SubShape)
class SubShape : Shape {
required init(name: String) {
super.init(name: name)
self.type = "SubShape"
}
}
let shapeClass = NSClassFromString("SubShape") as Shape.Type
let shape: Shape = shapeClass(name: "Something")
println(shape.type) // prints "SubShape"

Related

Missing argument for parameter 'from' in call. Insert 'from: <#Decoder#>'

I am trying to create a new instance of a codable struct
#State private var parcel = Parcel()
but I'm getting this error:
Missing argument for parameter 'from' in call
Insert 'from: <#Decoder#>'
struct Parcel: Codable {
var created_at: String
var height: Double
var id: String
var length: Double
var mode: String?
var object: String
var predefined_package: String?
var updated_at: String?
var weight: Double
var width: Double
}
Every object in Swift needs an initializer: some code to set up the object when it is first created. If your object is an instance of a class, the initializer needs to be explicitly defined by you. However if the object is an instance of a struct, Swift implicitly defines an initializer. For example, this struct
struct Foo {
let bar: Int
}
implicitly gets an initializer that looks like this
init(bar: Int) {
self.bar = bar
}
Initializers can also be implicitly created through protocol extensions. That means if your struct inherits a protocol (such as Codable), the protocol can define additional initializers for you. For this simple example, Codable would add something like this
init(from decoder: Decoder) throws {
// decode a key value pair with name "bar" and an Int value using decoder
let decodedBar = try ...
self.init(bar: decodedBar)
}
In your case, when you write parcel = Parcel() you are calling this kind of initializer
init() {
// initialize somehow with no input!
}
But you never defined anything like that! The compiler is suggesting that you call the initalizer you got from Codable since it's a close match, but that's probably not what you want either.
You can either define that missing initializer, or define default values for all of your struct's members. If you do that, the implicit initializer defined by Swift will have no arguments, making your code valid. For example
struct Foo {
let bar: Int = 3
}
let f = Foo() // now this is valid, implicit init has no arguments
By default, structs create an initialiser behind the scenes for every non-optional property that you declare. So by simply creating an instance of the struct you need to include all non-optional values it requires. If you don’t want to add the values when you initialise it, change them to vars and make them optional (add ? to the end).

Meaning of .type in Swift [duplicate]

What's the difference between metatype .Type and .self in Swift?
Do .self and .Type return a struct?
I understand that .self can be used to check with dynamicType. How do you use .Type?
Here is a quick example:
func printType<T>(of type: T.Type) {
// or you could do "\(T.self)" directly and
// replace `type` parameter with an underscore
print("\(type)")
}
printType(of: Int.self) // this should print Swift.Int
func printInstanceDescription<T>(of instance: T) {
print("\(instance)")
}
printInstanceDescription(of: 42) // this should print 42
Let's say that each entity is represented by two things:
Type: # entitiy name #
Metatype: # entity name # .Type
A metatype type refers to the type of any type, including class types, structure types, enumeration types, and protocol types.
Source.
You can quickly notice that this is recursive and there can by types like (((T.Type).Type).Type) and so on.
.Type returns an instance of a metatype.
There are two ways we can get an instance of a metatype:
Call .self on a concrete type like Int.self which will create a
static metatype instance Int.Type.
Get the dynamic metatype instance from any instance through
type(of: someInstance).
Dangerous area:
struct S {}
protocol P {}
print("\(type(of: S.self))") // S.Type
print("\(type(of: S.Type.self))") // S.Type.Type
print("\(type(of: P.self))") // P.Protocol
print("\(type(of: P.Type.self))") // P.Type.Protocol
.Protocol is yet another metatype which only exisits in context of protocols. That said, there is no way how we can express that we want only P.Type. This prevents all generic algorithms to work with protocol metatypes and can lead to runtime crashes.
For more curious people:
The type(of:) function is actually handled by the compiler because of the inconsistency .Protocol creates.
// This implementation is never used, since calls to `Swift.type(of:)` are
// resolved as a special case by the type checker.
public func type<T, Metatype>(of value: T) -> Metatype { ... }
First and foremost see Apple docs on type(of:)
The functions signature is interesting:
func type<T, Metatype>(of value: T) -> Metatype
Where is it used?
If you are writing/creating a function that accepts a type e.g. UIView.Type, not an instance e.g. UIView()then to you would write T.Type as the type of the parameter. What it expects as a parameter can be: String.self, CustomTableView.self, someOtherClass.self.
But why would a function ever need a type?
Normally a function which requires a type, is a function that instantiates objects for you. I can think of a few examples:
register function from tableview
tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "CustomTableViewCell")
Notice that you passed CustomTableViewCell.self. If later on you try to dequeue a tableView of type CustomTableViewCell but didn't register CustomTableViewCell type then it would crash because the tableView hasn't dequeued/instantiated any tableviewcells of CustomTableViewCell type.
decode function from JSONDecoder. Example is from the link
struct GroceryProduct: Codable {
var name: String
var points: Int
var description: String?
}
let json = """
{
"name": "Durian",
"points": 600,
"description": "A fruit with a distinctive scent."
}
""".data(using: .utf8)!
let decoder = JSONDecoder()
let product = try decoder.decode(GroceryProduct.self, from: json)
print(product.name)
Notice try decoder.decode(GroceryProduct.self, from: json). Because you passed GroceryProduct.self it knows that it needs to instantiate an object of type GroceryProduct. If it can't then it would throw an error. For more on JSONDecoder see this well written answer
Trying to find a value of a certain class type. Example trying to find a viewController of a certain type among all viewcontrollers of a navigationController:
func popBackTo<T>(type: T.Type, in nav: UINavigationController? = nil, completion: ((T?) -> Void)? = nil) {
let nav = window?.rootViewController as? UINavigationController
guard let nav = nav, let destinationVC = nav.viewControllers.first(where: { $0 is T }) else {
return
}
nav.popToViewController(destinationVC, animated: true)
}
# Example call site:
popBackTo(LoginVC.self)
As an alternate workaround for where types are needed see the following question: Swift can't infer generic type when generic type is being passed through a parameter. The accepted answer offers an intersting alternative.
More about the internals and how it works:
.Type
The metatype of a class, structure, or enumeration type is the name of
that type followed by .Type. The metatype of a protocol type—not the
concrete type that conforms to the protocol at runtime—is the name of
that protocol followed by .Protocol. For example, the metatype of the
class type SomeClass is SomeClass.Type and the metatype of the
protocol SomeProtocol is SomeProtocol.Protocol.
From Apple : metaType Type
Under the hood AnyClass is
typealias AnyClass = AnyObject.Type // which is why you see T.Type
Basically where ever you see AnyClass, Any.Type, AnyObject.Type, its because it's in need of a type. A very very common place we see it is when we want to register a class for our tableView using register func.
func register(_ cellClass: Swift.AnyClass?, forCellReuseIdentifier identifier: String)
If you are confused as to what does 'Swift.' do then above, then see the comments from here
The above could have also been written as:
func register(_ cellClass: AnyObject.Type, forCellReuseIdentifier identifier: String)
.self
You can use the postfix self expression to access a type as a value.
For example, SomeClass.self returns SomeClass itself, not an instance
of SomeClass. And SomeProtocol.self returns SomeProtocol itself, not
an instance of a type that conforms to SomeProtocol at runtime. You
can use a type(of:) expression with an instance of a type to access
that instance’s dynamic, runtime type as a value, as the following
example shows:
From Apple : metaType Type
Playground code:
Easy example
struct Something {
var x = 5
}
let a = Something()
type(of:a) == Something.self // true
Hard example
class BaseClass {
class func printClassName() {
print("BaseClass")
}
}
class SubClass: BaseClass {
override class func printClassName() {
print("SubClass")
}
}
let someInstance: BaseClass = SubClass()
/* | |
compileTime Runtime
| |
To extract, use: .self type(of)
Check the runtime type of someInstance use `type(of:)`: */
print(type(of: someInstance) == SubClass.self) // True
print(type(of: someInstance) == BaseClass.self) // False
/* Check the compile time type of someInstance use `is`: */
print(someInstance is SubClass) // True
print(someInstance is BaseClass) // True
I highly recommend to read Apple documentation on Types. Also see here
They appear in different places syntactically.
In a place syntactically where you have to specify a type, Something.Type is a valid type, corresponding to the type that is the metatype (which is metaclass for classes) of Something. Something.self is not a valid syntax for a type.
In a place syntactically where you have to write an expression, Something.self is a valid expression. It's an expression of type Something.Type, and the value is the thing ("class object" in the case of classes) that represents the type Something. Something.Type is not a valid expression syntax.
This was one of those topics that confused the hell out of me today.
I was writing a generic function:
func foo<T: Protocol>(ofType: T.Type) {
T.bar()
}
And tried calling it as follows:
foo(ofType: ClassImplementingProtocol.Type) // Compiler error
Spent about 30 min researching why it wasn't working. Then I tried this:
foo(ofType: ClassImplementingProtocol.self) // Works
Turns out Xcode's code completion is very bad at showing the difference between meta types and types... From the code completion pop-up it looks like .self and .Type are the same thing:
But the "explain like im 5" of it is, when you have a method parameter of Class.Type, it is expecting an instance of Class.Type.
Class.self returns an instance of Class.Type, whereas Class.Type is referring to Class.Type...
Very unclear if you ask me.
Metatype .Type
Metatype is a type which allows you to access to parts of Class and Struct[About] type(not instance) like initializers class and static[About] properties and methods
let var1: String = HelloWorld
let var2: String.Type = HelloWorld.self
Some experiments:
class SomeClass {
required init() { }
class func foo1() { }
static func foo2() { }
func foo3() { }
}
class SomeSubClass: SomeClass { }
let a1: SomeClass = SomeClass()
let a2: SomeClass = a1
let a3: SomeClass = a1.self
SomeClass.self.foo1() //class func
SomeClass.foo1() //class func
//static. metatype by type(class name) <class_name/structure_name>.self
let c1: SomeClass.Type = SomeClass.self
//dynamic. metatype by instance
let c2: SomeClass.Type = type(of: a1)
//access to type's init, class, static throught Metatype
let d1: SomeClass = c1.self.init()
let d2: SomeClass = c1.init()
//call
c1.foo1() //class func
c1.foo2() //static func
// c1.foo3() //instance func. Instance member 'foo3' cannot be used on type 'SomeClass'
// c1.foo3(SomeClass()) //Expression resolves to an unused function
//Sub
// <class_name>.Type allows to save class and sunclass
var e1: SomeClass.Type = SomeClass.self //class
e1 = SomeSubClass.self //sub class
//Any.Type allows to save class and struct
var e2: Any.Type = SomeClass.self //class
e2 = String.self //struct
//AnyObject.Type allows to save only class
var e3: AnyObject.Type = SomeClass.self //class
e3 = NSString.self //class
get String
let typeString = "\(SomeType.Type)"
func register<T>(instance: T) {
instanceString = String(describing: type(of: instance))
}

How to use AnyClass in Swift Generic class

I need to pass a type in my generic base class.
class SomeBaseClass<T: AnyClass> {
// Implementation Goes here
}
I get the following error:
Inheritance from non-protocol, non-class type 'AnyClass' (aka
'AnyObject.Type')
Ideally I would like to use 'T' to be as a specific type rather than AnyClass, but AnyClass is OK as well.
Thanks
You should use AnyObject if you want the type to be a class.
class SomeBaseClass<T: AnyObject> {
// Implementation Goes here
}
// Legal because UIViewController is a class
let o1 = SomeBaseClass<UIViewController>()
// Illegal (won't compile) because String is a struct
let o2 = SomeBaseClass<String>()
Instead of specifying T needs to be a class, you could instead do:
class SomeBaseClass<T> {
let type: T.Type
init(type: T.Type) {
self.type = type
}
}
If you're planning to be using T.Type a lot it may be worth using a typealias:
class SomeBaseClass<T> {
typealias Type = T.Type
let type: Type
...
}
Some example usage:
let base = SomeBaseClass(type: String.self)
And advantage of this method is T.Type could represent structs and enums, as well as classes.
You can do this trick:
protocol P {}
class C: P {}
class C1<T: P> {}
let c1 = C1<C>()
In this case you wrap you class C with protocol P, then you able to create new generic class C1 where T is your protocol P. That's allow you to create instance of C1 class with generic parameter class C.

Extra Argument in call when creating and calling a class initialiser and passing in the literal values Swift

I have a small problem in swift. Let's say I have a class called Pet.
Pet has a variable for name and noise, created like so:
class Pet
{
var name : String = ""
var canMakeNoise : Bool = true
}
Now, when I call initialise the class creating let's say a cat, I can easily do it like so:
var cat: Pet()
cat.name = "Garfield"
cat.canMakeNoise = false
This works smoothly, however when trying to pass it in directly using literal values like so:
let cat : Pet("Garfield",true)
or
let cat : Pet(name:"Garfield",canMakeNoise:true)
I get this error:
Swift Compile Error - Extra Argument in call
Why is that? How can I fix it? Thanks in advance.
If you want to add arguments to the initializer than you need to specify a new init function instead of relying on the default one. Here's how you'd do it in your case:
class Pet {
var name : String = ""
var canMakeNoise : Bool = true
init( name : String, canMakeNoise : Bool ) {
self.name = name
self.canMakeNoise = canMakeNoise
}
}
var kitty = Pet(name: "Cat", canMakeNoise: true)
Since you use default values for your variables, xCode does not find a need for initializer. So when you create a new instance of the class it will be a Pet with name = ”” and canMakeNoice = true.
If you want to change these default values you will need to supply a init method (and you can than remove these default values)
class Pet{
var name:String
var canMakeNoise:Bool
init(name:String, canMakeNoise:Bool){
self.name = name
self.canMakeNoise = canMakeNoise
}
convenience init (name:String){
self.name = name
self.init(name:name, canMakeNoise: true)
}
}
I’ve supplied two init methods here. The first one takes 2 arguments:
var cat: Pet = Pet(name: "Kitten", canMakeNoise: true)
This will create a Pet with the supplied name and the supplied canMakeNoise.
If you want to you can also supply a convenience init This is a shortened init. In this case used to be able to make another instance of the Pet class.
var dog: Pet = Pet(name: "Doggy")
As you see we don’t supply the canMakeNoise property here cause the convenience initializer does that for us (and uses true as the canMakeNoice)

Swift Objects initialization (Class factory method, default init, convenience init)

I'm trying to figure out the best pattern to work with objects in Swift.
i think i got it right with the initializers, both convenience and default... but what happen with the class factory methods?
I tried to create a simple class Person and a subclass Student, with few properties and methods. is it the most correct way to do it?
class Person{
var _name: String
var _surname: String
var _dateOfBirthday: String
var _phoneNumb: [String]
init(name:String, surname:String, dateOfBirthday:String, phone:[String]){
self._name = name
self._surname = surname
self._dateOfBirthday = dateOfBirthday
self._phoneNumb = phone
}
convenience init() {
self.init(name:"",surname:"",dateOfBirthday:"", phone:[])
}
convenience init(name:String){
self.init(name:name,surname:"",dateOfBirthday:"", phone:[])
}
}
class Student:Person{
var _studentId:Int
init(name: String, surname: String, dateOfBirthday: String, phone: [String], id:Int) {
self._studentId = id
super.init(name: "", surname: "", dateOfBirthday: "", phone: [])
}
convenience init(){
self.init(name: "", surname: "", dateOfBirthday: "", phone: [], id:0)
}
convenience init(name:String){
self.init(name:name,surname:"",dateOfBirthday:"", phone:[], id:0)
}
}
what if i want to add a class factory method? would it be something like this or i'm doing it wrong?
class func Person() -> Person {
var x = Person()
x._telephoneNumber = [String]() // is this needed? or i can initialize it later?
return x
}
class func PersonWithName(name:String) -> Person {
var x = Person(name:name, surname:"", dateOfBirthday:"", telephoneNumber:[])
return x
}
is this correct? why would it be better to use the init instead of the class factory?
is this correct? why would it be better to use the init instead of the class factory?
Why would you create a "class factory" if you can use init? init is idiomatic Swift way of creating new objects of a class.
Adding convenience initializers is the right choice in most cases when you want to add a shortcut to class's main (designated) initializer. However, in your case, they are completely unnecessary, because Swift supports default argument values.
Just define your initializer like so:
init(name:String = "", surname:String = "", dateOfBirthday:String = "", phone:[String] = []) { ... }
This way, you can invoke it as Person() or Person(name: "Andrew") or with any other combination of arguments.
EDIT:
As a side note, prefixing instance variables with an underscore generally doesn't seem to be idiomatic Swift. It's okay to omit the underscore and use self. to disambiguate between local and instance variables:
self.name = name
self.surname = surname
self.dateOfBirthday = dateOfBirthday
self.phoneNumb = phone
Prior to the recent Xcode 6.1 and Swift 1.1, it was necessary to use factory pattern if construction could fail because init() could not return optionals. This was also why many cocoa/objective-c libraries imported had factory methods.
With the release of Xcode 6.1 and Swift 1.1 and the support for init() that can return optionals, you should use the init() pattern with convenience initializers. With this release, Apple also changed their cocoa/objective-c imports to use the init() -> T? pattern over the factory method.
See https://developer.apple.com/library/ios/releasenotes/DeveloperTools/RN-Xcode/Chapters/xc6_release_notes.html for the release notes.

Resources