Unwrapping an object created by failable initializer

Hello guys,

I've just started playing around with Swift so I apologise in advance if the level of the question does not reach the minimum requirements of this site hehe :slight_smile: .

I am just trying to figure out how to unwrap automatically an object just created by a failable initializer without having to do that manually with the rest of the code... For example I see that there are some functions in the Apple library that do that, among them... the Int initializer as I show in the code below.
I do not have access to the source code so I do not know how it was done. What I want to achieve is, if the initialization was successful, to access the object without having to unwrap it outside the class, just like the Apple function does!

class Person{
var name:String
var age:Int
convenience init?(){
self.init("No Name",0)
}

init?(_ name: String , _ age:Int){
    if (name.isEmpty || age < 0){
            print("Initialization failed")
            return nil
    }
    self.name = name
    self.age = age
}

}

var Jony = Person("hhhh",-6)

if Jony != nil{
print ("(Jony!.name) (Jony!.age)")
}

print(Int(5.3))
print(Int("Hello"))

I've modified your example by providing an overload of the print function for your Person class.

class Person {
	var name:String
	var age:Int

	convenience init(){
		self.init("No Name",0)!
	}

	init?(_ name: String , _ age:Int){
		if (name.isEmpty || age < 0){
			print("Initialization failed")
			return nil
		}
		self.name = name
		self.age = age
	}
}

func print(_ person: Person) {
	print ("\(person.name) \(person.age)")
}

func print(_ person: Person?) {
	guard let person = person
	else {
		return // or what ever you want to do for nil value
	}
	print(person)
}

var Jony = Person("hhhh",-6)
print(Jony)

In your example, print(Int(5.3)), the expression Int(5.3) actually returns an Int, not an Int?, because Int has an initializer that can convert a floating point literal into an Int.

In your example, print(Int("Hello")), the expression Int("Hello"), returns an Int? set to nil. This value has to be handled like any other optional. The print function for Optional values knows how to handle the nil case. If you try something like:

let j: Int = Int("Hello")

you'll get an error indicating that you can't assign an Int? to an Int without force unwrapping or assigning a default via ??.

1 Like

As @jonprescott mentioned, there is no magic, aside from providing overload(s) that accept optional. To expand on that a little further:

print(Int(5.3))

Int(5.3) uses this non-failable initialiser. So you're printing Int, not Int?.

print(Int("Hello"))

This uses this failable initialiser. So it is printing Int?.

1 Like

Thanks Jon and Lantua for your replies.
Actually I was taking a look at the wrong Apple initializer, I had checked out init?(exactly other:Double) but God knows why I tested a different one. This one does return an optional when succeeds, just as mine :slight_smile:
I see that the fact of overloading the methods used within the class is a great way to get around this. Thank you for that! Also, as Lantua pointed out, there is not magical way to deal with this so, in the end the questions was not as dumb as I thought :slight_smile:

If you want to check if it is not nil, and extract the value, you can use if let or guard let for that

var jony = Person("Jony", age) // We don't know yet if it succeeds.

if let person = jony {
  // We just checked. `jony` is not nil, and now we have a non-optional version in `persion`.
  // Name doesn't need to be different, you can use `jony` in place of `person` as well
} else {
  // `jony` is `nil`. Do whatever you must do here.
}

So this snippet is probably more Swifty like this:

var jony = Person("hhhh",-6)

if let jony = jony {
  print ("\(jony.name) \(jony.age)")
}

If you know that it never returns nil (in this case, it never fails), you can force unwrap the result. This may be because you know that, however you retrieve the data, name is not empty and age is never negative.

var jony = Person("Jony", 3)! // This always succeed, so let's unwrap it.

// `jony` is of type `Person`, not `Person?`. So we don't need to check for `nil` anymore
print ("\(jony.name) \(jony.age)")
1 Like