Error when delegating initialization in swift struct - swift

I am trying to use another initalizer when some edge case happens during initialization, but I realized that once I delegate initialization to another initalizer, I can't treat that initializer like I would a normal one, instead I have to delegate initialization in all code branches.
(real use case is to initialize a struct containing email and name from a ASAuthorizationAppleIDCredential But because this object only returns the email and name the first time the user signs up with Apple, I have to check if it contains it, if no then an initalizer is called, that can create one by loading it from NSUbiquitousKeyValueStore. I ended up solving this by creating a function returning this struct instead of an init function)
The errors I get are
'self' used before 'self.init' call or assignment to 'self'
'self.init' isn't called on all paths before returning from initializer
These come up when I try to use a different initializer in a guard statement's else closure. Here is a simplified example:
struct Test {
var name : String
var age : Int
init(name : String) {
self.name = name
self.age = -1
}
init(name : String, age : Int?) {
guard let age = age else {
self.init(name: name)
return
}
self.name = name
self.age = age
}
}
Another solution I found is to create an init that can be called after the guard statement, in this case:
init(name: String, age : Int) {
self.name = name
self.age = age
}
and then use this init instead of assigning values directly.
My question is: why is this the case, and where is this behaviour mentioned or explained? The only documentation I found about this was the swift docs here:
https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID215

Not sure about other languages but in Swift you have to first initialise existing initialiser before initialising extra property/properties.
import UIKit
struct Test {
var name : String
var age : Int
init(name : String) {
self.name = name
self.age = -1
}
init(name : String, age : Int?) {
self.init(name: name)
if let age = age {
self.name = name
self.age = age
}
}
}
let x = Test(name: "x", age: nil)
let y = Test(name: "y", age: 18)
let z = Test(name: "z")

Related

Swift checking nil using if-let from a class level

I've been using nested if let(s) to check nils. For example, a class that contains name and age
class Person {
var name: String?
var age: Int?
}
if let p = Person() as? Person {
if let name = p.name {
}
if let age = p.age {
}
}
When I'm unsure if a variable is empty/null, is it necessary to use if let on everything and everytime? just trying to make sure if im doing it right.
You need if-let only when you need to check for nil and use the unwrapped value. Otherwise you can simply check for nil using != or == operator.
Also there is no need to explicitly typecast p to Person using as? Person, since the type can be inferred already.
let p = Person()
if p.name != nil {
}
if p.age != nil {
}
You do not need to check anything
p = Person()
creating a new Person object with no initial values means that all properties are nil so the following checks are unnecessary, we already know they are nil
if let name = p.name {
if let age = p.age {
I would suggest you add a constructor with parameters so that you can set the properties directly
Either with mandatory values so you don't have to check for nil afterwards
init(name: String, age: Int) {
self.name = name
self.age = age
}
or with optional values
init(name: String?, age: Int?) {
self.name = name
self.age = age
}
but then you need to check
var name: String?
var age: Int?
//...other code
let p = Person(name: name, age: age)
if let personName = p.name {
}
if let personAge = p.age {
}

self.name = name in Swift . I don't understand why this codes are needed

class person {
var name : String
init(name: String) {
self.name = name
}
}
I am learning Swift class chapter
I don't understand why init(name:String) self.name = name code is needed
what the purpose of this code is.
I declared var name: String
and again init(name: String), why is self.name = name needed?
what's different between just var name and self.name = name?
Look into something called variable scope. In your code - there are two "name variables - a class (or instance) variable and a "parameter in your init.
The latter - init(name:) only is in use within your initialization function, meaning the instance has no name without the self.name = name once you've created the instance.
The former is available - to the instance - as long as your instance of the class person is.
To explain further, try this. Change your code to be:
class Person {
var myName : String
var myOtherName : String
init(name: String) {
self.myName = name
}
}
In your app or view controller, do this:
let myPerson = Person(name: "john")
print(myPerson.myName) // prints "jihoon"
print(myPerson.myOtherName) // prints nothing
print(myPerson.name) // generates a build error because name doesn't exist
One last note - in Swift class names are capitalized, so the best name is Person, not person.
Classes and structures must set all of their stored properties to an appropriate initial value by the time an instance of that class or structure is created. Stored properties cannot be left in an indeterminate state.
class person {
var name : String // undetrmined state
init(name: String) {
self.name = name
}
}
class person2 {
var name : String = "default value" // detrmined state
// Now these intializer are not optional. You can use these initializer or not.
init(name: String) {
self.name = name
}
init() {
}
}
// Aother example for optional stored property
class person3 {
var name : String? // detrmined state, because default value for optional is nil
// Now these intializer are not optional. You can use these initializer or not.
init(name: String) {
self.name = name
}
init() {
}
}
For more info read this one Apple Doc

Initializers in class swift

What is the difference in initializing a variable:
class Person {
var name = String()
}
instead of:
class Person {
var name : String
init(name: String) {
self.name = name
}
}
thanks
First snippet
You can call
let person = Person()
Second snippet:
You must call
let person = Person(name:"")
to get an instance with an empty name property
PS: "" is easier to write than String()

my swift method gives no output

I don't really know what is wrong with this that i cant get any output
class Shape {
var numberOfSides : Int = 3
var name : String
init(name : String){
self.name = name
}
func print() -> String {
return("numberOfSides : \(numberOfSides)")
}
}
var Square = Shape("moraba")
println(Square.print())
Unlike normal methods and functions init() methods require to pass the first parameter name
let square = Shape(name:"moraba")
println(square.print())

Access properties via subscripting in Swift

I have a custom class in Swift and I'd like to use subscripting to access its properties, is this possible?
What I want is something like this:
class User {
var name: String
var title: String
subscript(key: String) -> String {
// Something here
return // Return the property that matches the key…
}
init(name: String, title: String) {
self.name = name
self.title = title
}
}
myUser = User(name: "Bob", title: "Superboss")
myUser["name"] // "Bob"
Update: The reason why I'm looking for this is that I'm using GRMustache to render from HTML templates. I'd like to be able to just pass my model object to the GRMustache renderer…
GRMustache fetches values with the keyed subscripting objectForKeyedSubscript: method and the Key-Value Coding valueForKey: method. Any compliant object can provide values to templates.
https://github.com/groue/GRMustache/blob/master/Guides/view_model.md#viewmodel-objects
This is a bit of a hack using reflection. Something along the lines of the following could be used.
protocol PropertyReflectable { }
extension PropertyReflectable {
subscript(key: String) -> Any? {
let m = Mirror(reflecting: self)
for child in m.children {
if child.label == key { return child.value }
}
return nil
}
}
struct Person {
let name: String
let age: Int
}
extension Person : PropertyReflectable {}
Then create a Person and access it's keyed properties.
let p = Person(name: "John Doe", age: 18)
p["name"] // gives "John Doe"
p["age"] // gives 18
You could modify the subscript to always return an interpolated string of the property value.
Adding some syntax sugar to Benzi's answer:
protocol PropertyReflectable { }
extension PropertyReflectable {
subscript(key: String) -> Any? {
let m = Mirror(reflecting: self)
return m.children.first { $0.label == key }?.value
}
}
struct Person: PropertyReflectable {
let name: String
let age: Int
}
Then create a Person and access it's keyed properties.
let p = Person(name: "John Doe", age: 18)
p["name"] // gives "John Doe"
p["age"] // gives 18
Using valueForKey should enable you to access properties using their names. Be sure that you're working with a object that inherit NSObject
class people: NSObject {
var age: NSString = "44"
var height: NSString = "153"
}
let person:people = people()
let stringVariable = "age"
person.valueForKey("age")
// Print "44"
person.valueForKey("\(stringVariable)")
// Print "44"
(GRMustache author here)
Until a swift-oriented Mustache library is out, I suggest having your classes inherit from NSObject (so that they have the valueForKey: method). GRMustache will then fetch values with this method.
In case this would still not work (blank values in the rendering), you may try to disable GRMustache security features (see https://github.com/groue/GRMustache/blob/master/Guides/security.md#disabling-safe-key-access)
Should you experience any other trouble, please open an issue right into the repository: https://github.com/groue/GRMustache/issues
EDIT February 2, 2015: GRMustache.swift is out: http://github.com/groue/GRMustache.swift
Shim's answer above doesn't work anymore in Swift 4. There are two things you should be aware of.
First of all, if you want to use value(forKey:) function, your class must inherit NSObject.
Secondly, since Objective-C doesn't know anything about value type, you have to put the #objc keyword in front of your value type properties and Swift will do the heavy-lifting for you.
Here is the example:
import Foundation
class Person: NSObject {
#objc var name: String = "John Dow"
#objc var age: Int = 25
#objc var height: Int = 180
subscript(key: String) -> Any? {
return self.value(forKey: key)
}
}
let person: Person = Person()
person["name"] // "John Dow"
person["age"] // 25
person["height"] // 180
I suppose you could do:
class User {
let properties = Dictionary<String,String>()
subscript(key: String) -> String? {
return properties[key]
}
init(name: String, title: String) {
properties["name"] = name
properties["title"] = title
}
}
Without knowing your use case I would strongly advise against doing this.
Another approach:
class User {
var name : String
var title : String
subscript(key: String) -> String? {
switch key {
case "name" : return name
case "title" : return title
default : return nil
}
}
init(name: String, title: String) {
self.name = name
self.title = title
}
}
It might be worth noting that Swift doesn't appear to currently support reflection by names. The reflect function returns a Mirror whose subscript is Int based, not String based.