What's the correct way to delegate failable initializers in Swift? - swift

Say I have this class:
class EvenNumber {
var num: Int
var stringValue: String
init?(n: Int) {
guard n % 2 == 0 else { return nil }
self.num = n
}
init?(str: String) {
guard let n = Int(str) else { return nil }
self.init(n: n)
//set stringValue?
}
}
In the init that takes a string, I delegate back to the one that takes an Int. How do I know whether it succeeded so I can continue initialization? What's the proper syntax / common pattern here?

You don't need to check that the delegated initialiser has succeeded. If the delegated initialiser has failed, the whole initialisation process fails.
This is evident in this code:
class EvenNumber {
var num: Int
var stringValue: String
init?(n: Int) {
guard n % 2 == 0 else { return nil }
self.num = n
stringValue = "" // you forgot to initialise stringValue in both of your initialisers
}
// you forgot "convenience"
convenience init?(str: String) {
guard let n = Int(str) else { return nil }
self.init(n: n)
print("hello")
stringValue = ""
}
}
EvenNumber(str: "5")
"hello" does not get printed, which means that the rest of the init(str:) does not get executed if init(n:) fails.
Here's some supporting documentation (you need to scroll down a bit, under "Propagation of Initialization Failure"):
In either case, if you delegate to another initializer that causes initialization to fail, the entire initialization process fails immediately, and no further initialization code is executed.
(This section is included for completeness' sake.)
Now you (or whoever comes to this question in the future) might ask, "but what if I want to do something else if the delegated initialiser fails?" In that case, you must check for the condition that causes the initialiser to fail:
if n % 2 == 1 {
self.init(n: n)
} else {
// do something else
}
Why is this so "clumsy"? Well, let's say you could do this (warning: made-up syntax):
if self.init(n: n) {
// success!
} else {
// fail
}
To get to the "fail" branch, we must have already run self.init(n:). self.init(n:), before it failed, might have already initialised some let properties. Recall that let properties can only be initialised once. So now self.init(n: n) has been executed, but the compiler doesn't know which let properties have been initialised. See the problem? How is the compiler going to verify that you have initialised every property exactly once in the "fail" branch?

Related

Swift Scope - Returning if and for results from a function

How do i get the result of of i to return from the function here? I can return it from inside the if { } but then I get an error there’s no global return (for the function). My goal is to write a function that accepts an Integer and returns the square root. My next step is to throw an error if it's not an Int or between 1 and 10,000, but please assume simple numbers for this question.
let userInputInteger = 9
func squareRootCheckpoint4(userInputInteger: Int) -> Int {
for i in 1...100 {
let userInputInteger = i * i
if i == userInputInteger {
break
}
}
return i
}
update:
To be able to return it, you will have to declared a variable outside the for-loop, then based on the logic assign the "i" counter to that outside variable.
func squareRootCheckpoint4(userInputInteger: Int) -> Int {
var result = 0 // this is to keep the result
for i in 1...100 {
let powerOfTwoResult = i * i
if powerOfTwoResult == userInputInteger {
result = i // assign result variable based on the logic
break
}
}
return result // return result
}
The error shown is because if the for-loop finishes without any hit on the if statement, the function will not be able to return any Int. That's why the compiler produces that "no global return" error.
One way to do it if you don't want to return a default value is to throw an error. You could throw an error from a function (https://docs.swift.org/swift-book/LanguageGuide/ErrorHandling.html). For example for your goal of returning square root of an integer and throw error if the input integer is not between 1 and 10000 (if I understand correctly), you could do something like this
enum InputError: Error {
case invalidInteger
}
func squareRootCheckpoint4(userInputInteger: Int) throws -> Int {
if userInputInteger < 1 || userInputInteger > 10000 {
throw InputError.invalidInteger
}
// calculate square root
return resultOfSquareroot
}
// and to handle the error, you could encapsulate the squareRootCheckpoint4 function in a do-catch statement
do {
let squareRootResult = try squareRootCheckpoint4(userInputInteger: 4)
} catch InputError.invalidInteger {
// handle your error here
}
Alternatively, you could do a validation on the input integer separately before calling the squareRootCheckpoint4 function. For example
func validate(_ input: Int) -> Bool {
if userInputInteger < 1 || userInputInteger > 10000 {
return false
}
return true
}
func squareRootCheckpoint4(userInputInteger: Int) -> Int {
// calculate square root
return resultOfSquareroot
}
var input: Int = 9
if validate(input) {
let squareRootResult = squareRootCheckpoint4(input)
} else {
// handle when input is invalid
}
A couple of things are swapped around, and something (anything) needs to be returned from the function. The framing of the question only allows solutions that don't work if the input is not an integer with a square root.
List of Specifics:
The function argument name userInputInteger should not have been used to instantiate a new object. That's confusing. I did it because I'm learning and thought it was necessary.
Instead, i times i (or i squared) should have been assigned to a new object.
The if statement should be equal to this new object, instead of i.
Functions should return something. When i is returned in the if statement, it's not available anywhere outside the for loop. The print statements in the code example help explain what is available in these different scopes, as well as what's not.
The for loop stops when i is returned. That happens when the guess (i) is equal to the input (userInputInteger).
The for loop will continue through 100 if the input doesn't have a square root.
The function should return the correct number that the if statement was trying to guess, but you have to tell it to (with a return statement) and do it in the right spot. This is the "global return" error. However, because this approach in the question is setup poorly without error handling, only an Integer with a sqare root will return correctly. And in this case, the function's global return doesn't matter; Swift just requires something be returned, regardless of whether or not it's used.
Code Example - Direct Answer:
import Cocoa
let userInputInteger = 25
let doesNotMatter = 0
print("sqrt(\(userInputInteger)) is \(sqrt(25))") //correct answer for reference
func squareRootCheckpoint4(userInputInteger2: Int) -> Int {
var j: Int
for i in 1...100 {
print("Starting Loop \(i)")
j = i * i
if j == userInputInteger2 {
print("i if-loop \(i)")
print("j if-loop \(j)")
return i
}
print("i for-loop \(i)")
print("j for-loop \(j)")
}
//print("i func-end \(i)") //not in scope
//print("j func-end \(j)") //not in scope
//return i //not in scope
//return j //not in scope
print("Nothing prints here")
// but something must be returned here
return doesNotMatter
}
print("Input was \(userInputInteger)")
print("The Square Root of \(userInputInteger) is \(squareRootCheckpoint4(userInputInteger2: userInputInteger))")
Code Example - Improved with Error Handling
import Cocoa
enum ErrorMsg : Error {
case outOfBoundsError, noRootError
}
func checkSquareRoot (Input userInputInteger2: Int) throws -> Int {
if userInputInteger < 1 || userInputInteger > 10_000 {
throw ErrorMsg.outOfBoundsError
}
var j : Int
for i in 1...100 {
print("Starting Loop \(i)")
j = i * i
if j == userInputInteger2 {
print("i if-loop \(i)")
print("j if-loop \(j)")
return i
}
print("i for-loop \(i)")
print("j for-loop \(j)")
}
throw ErrorMsg.noRootError
}
let userInputInteger = 25
print("Input was \(userInputInteger)")
do {
let result = try checkSquareRoot(Input: userInputInteger)
print("The Square Root of \(userInputInteger) is \(result)")
} catch ErrorMsg.outOfBoundsError {
print("out of bounds: the userInputInteger is less than 1 or greater than 10,000")
} catch ErrorMsg.noRootError {
print("no root")
}
print("Swift built-in function for reference: sqrt(\(userInputInteger)) is \(sqrt(25))") //correct answer for reference

Getting the value of a property using it's string name in pure Swift using reflection

I want to use Swift (not Objective-C runtime) Reflection to create a method like this:
func valueFor(property:String, of object:Any) -> Any? {
...
}
To some extent, I can do this using:
func valueFor(property:String, of object:Any) -> Any? {
let mirror = Mirror(reflecting: object)
return mirror.descendant(property)
}
With
class TestMe {
var x:Int!
}
let t = TestMe()
t.x = 100
let result = valueFor(property: "x", of: t)
print("\(result); \(result!)")
This prints out what I'd expect:
Optional(100); 100
When I do:
let t2 = TestMe()
let result2 = valueFor(property: "x", of: t2)
print("\(result2)")
The output is:
Optional(nil)
This might seem reasonable, except that if I do:
var x:Int!
print("\(x)")
This prints out:
nil
and not Optional(nil). The bottom line is that I'm having difficulty programmatically determining that the value of t2.x is nil using my valueFor method.
If I continue the above code with:
if result2 == Optional(nil)! {
print("Was nil1")
}
if result2 == nil {
print("Was nil2")
}
Neither of these print statements output anything.
When I put a breakpoint into Xcode and look at the value of result2 with the debugger, it shows:
▿ Optional<Any>
- some : nil
So, my question is: How can I determine if the original member variable was nil using the result from valueFor?
Additional1:
If I do:
switch result2 {
case .some(let x):
// HERE
break
default:
break
}
and put a breakpoint at HERE, the value of x turns out to be nil. But, even if I assign it to an Any?, comparing it to nil is not true.
Additional2:
If I do:
switch result2 {
case .some(let x):
let z:Any? = x
print("\(z)")
if z == nil {
print("Was nil3")
}
break
default:
break
}
This prints out (only):
Optional(nil)
I find this especially odd. result2 prints out exactly the same thing!
This is a bit of a hack, but I think it's going to solve the problem for me. I'm still looking for better solutions:
func isNilDescendant(_ any: Any?) -> Bool {
return String(describing: any) == "Optional(nil)"
}
func valueFor(property:String, of object:Any) -> Any? {
let mirror = Mirror(reflecting: object)
if let child = mirror.descendant(property), !isNilDescendant(child) {
return child
}
else {
return nil
}
}
well, i know it has been 4 years, but I am on Xcode 12 and still facing the same issue. since this question seems to be unanswered, I will add what worked for me.
func valueFor(property: String, of object: Any) -> Any? {
let optionalPropertyName = "some"
let mirror = Mirror(reflecting: object)
if let child = mirror.descendant(property) {
if let optionalMirror = Mirror(reflecting: child), optionalMirror.displayStyle == DisplayStyle.optional {
return optionalMirror.descendant(optionalPropertyName)
} else {
return child
}
} else {
return nil
}
}
by using Mirror to check for optional and then extract the optional using "some" you get back either a true object or nil. when this is returned to the caller via the Any? return, you are now able to nil check the value and have that work appropriately.

Swift - Unwrap optional in for in loop with where clause

I have a class with an optional member :
class A {
var i: Int? = nil
}
Then I have an array of objects of type A. Some objects in the array have a value for i, some others don't.
I want to iterate over objects in the array that have a value for i while unwrapping the optional at the same time. I didn't find a way to do both at the same time (I don't even know if it's possible), forcing me to write a if let construct inside the loop.
For example :
// a1, a2 have a value for i
let arr: [A] = [a1, a2, a3]
for obj in arr where obj.i != nil {
// I want to avoid if let, or force unwrapping here
if let unwrapped = obj.i {
print(i)
}
// let unwrapped = obj.i! ...
}
Is it possible in Swift ?
1.Maybe you can use flatMap to get value i, then print it
arr.flatMap{ $0.i }.forEach{ print($0) }
2.or Trying simple guard statement
arr.forEach { element in
guard let i = element.i else { return }
print(i)
}
I don't think that's possible.
Even if you have a where clause in your loop the type of obj is still of type A and as such i still remains optional.
To see why this is so think about the fact that you can change the value of i on object obj inside the loop, so the compiler is not sure that the value of i is valid until you unwrapp it.
You can try something like this
for obj in arr where obj.i != nil {
guard let i = obj.i else { continue }
print( i )
}
but if you start using guard you also skip the where clause
for obj in arr {
guard let i = obj.i else { continue }
print( i )
}
You can use case let syntax, but not without the help of map, and the result isn't the most readable:
for case let .some(unwrapped) in arr.map(\.i) {
print(unwrapped)
}
It's more useful if you're e.g. casting the outer object, e.g.:
for case let object as String in arrayOfAny {
if object.hasPrefix("tw") {
print("Starts with 'tw'")
}
}
instead of:
for object in arrayOfAny where object is String {
if object.hasPrefix("tw") { // Error: Value of type 'Any' has no member 'hasPrefix'
print("Starts with 'tw'")
}
}

Working with optionals in Swift programming language

As far as I know the recommended way to use optionals (Int in this example) is the following:
var one:Int?
if var maybe = one {
println(maybe)
}
Is it possible to use a shorter way to do something like the following?
var one:Int?
var two:Int?
var three:Int?
var result1 = one + two + three // error because not using !
var result2 = one! + two! + three! // error because they all are nil
Update
To be more clear about what I'm trying to do: I have the following optionals
var one:Int?
var two:Int?
var three:Int?
I don't know if either one or two or three are nil or not. If they are nil, I wan't them to be ignored in the addition. If they have a value, I wan't them to be added.
If I have to use the recommended way I know, it would look something like this: (unnested)
var result = 0
if var maybe = one {
result += maybe
}
if var maybe = two {
result += maybe
}
if var maybe = three {
result += maybe
}
Is there a shorter way to do this?
Quick note - if let is preferred for optional binding - let should always be used where possible.
Perhaps Optionals aren't a good choice for this situation. Why not make them standard Ints with a default value of 0? Then any manipulation becomes trivial and you can worry about handling None values at the point of assignment, rather than when you're working on the values?
However, if you really want to do this then a tidier option is to put the Optionals into an Array and use reduce on it:
let sum = [one,two,three,four,five].reduce(0) {
if ($1) {
return $0 + $1!
}
return $0
}
That's exactly the point of optionals — they may be nil or non-nil, but unwrapping them when they're nil is an error. There are two types of optionals:
T? or Optional<T>
var maybeOne: Int?
// ...
// Check if you're not sure
if let one = maybeOne {
// maybeOne was not nil, now it's unwrapped
println(5 + one)
}
// Explicitly unwrap if you know it's not nil
println(5 + one!)
T! or ImplicitlyUnwrappedOptional<T>
var hopefullyOne: Int!
// ...
// Check if you're not sure
if hopefullyOne {
// hopefullyOne was not nil
println(5 + hopefullyOne)
}
// Just use it if you know it's not nil (implicitly unwrapped)
println(5 + hopefullyOne)
If you need to check multiple optionals at once here there are a few things you might try:
if maybeOne && maybeTwo {
println(maybeOne! + maybeTwo!)
}
if hopefullyOne && hopefullyTwo {
println(hopefullyOne + hopefullyTwo)
}
let opts = [maybeOne, maybeTwo]
var total = 0
for opt in opts {
if opt { total += opt! }
}
(It seems you can't use the let optional binding syntax with more than one optional at once, at least for now...)
Or for extra fun, something more generic and Swifty:
// Remove the nils from a sequence of Optionals
func sift<T, S: Sequence where S.GeneratorType.Element == Optional<T>>(xs: S) -> GeneratorOf<T> {
var gen = xs.generate()
return GeneratorOf<T> {
var next: T??
do {
next = gen.next()
if !next { return nil } // Stop at the end of the original sequence
} while !(next!) // Skip to the next non-nil value
return next!
}
}
let opts: [Int?] = [1, 3, nil, 4, 7]
reduce(sift(opts), 0) { $0 + $1 } // 1+3+4+7 = 15

How would I create a constant that could be one of several strings depending on conditions?

I want to have a constant using let that may be one of several values.
For instance:
if condition1 {
constant = "hi"
}
else if condition2 {
constant = "hello"
}
else if condition3 {
constant = "hey"
}
else if condition4 {
constant = "greetings"
}
I'm not sure how to do this with Swift and the let feature. But I'm inclined to believe it's possible, as this is in the Swift book:
Use let to make a constant and var to make a variable. The value of a constant doesn’t need to be known at compile time, but you must assign it a value exactly once.
How would I accomplish this?
As pointed out in the other answers you can't directly do this. But if you're looking to just variably set the initial value of a constant, then yes, that is possible. Here's an example with a computed property.
class MyClass {
let aConstant: String = {
if something == true {
return "something"
} else {
return "something else"
}
}()
}
I think you are looking for variable which will be assigned later inside switch-case:
let constant :String
switch conditions {
case condition1:
constant = "hi"
case condition2:
constant = "hello"
case condition3:
constant = "hey"
case condition4:
constant = "greetings"
default:
constant = "salute"
}
One option would be something like this, using a closure:
let constant: String = ({ value in
if conditionOne {
return "Hi"
} else if conditionTwo {
return "Bye"
}
return "Oops!"
})(myData /*needed for condition*/)
Or, for another twist, using generics:
func fancySwitch<S, T>(val: S, fn: S -> T) -> T {
return fn(val)
}
let x: String = fancySwitch(3) { val in
if val == 2 {
return "Hi"
} else if val < 5 {
return "Bye"
}
return "Oops"
}
let y: String = fancySwitch((3, 4)) { (a, b) in
if a == 2 {
return "Hi"
} else if b < 5 {
return "Bye"
}
return "Oops"
}
I understand what you're looking for. In Scala and some other functional languages this can be done using the match statement (kind of like switch) because the entire statement resolves to a value like this:
val b = true
val num = b match {
case true => 1
case false => 0
}
This is unfortunately not directly possible in Swift because there is no way to get a value from a branch statement. As stated in the Swift book, "Swift has two branch statements: an if statement and a switch statement." Neither of these statements resolve to a value.
The closest code structure I can think of is to first use a variable to retrieve the correct value and then assign it to a constant to be used in any later code:
let b = true
var num_mutable: Int
switch b {
case true:
num_mutable = 1
default:
num_mutable = 0
}
let num = num_mutable
Just add the line let constant: String before your if/else statement.
Below, an excerpt from Swift 1.2 and Xcode 6.3 beta - Swift Blog - Apple Developer elaborates.
let constants are now more powerful and consistent — The new rule is
that a let constant must be initialized before use (like a var), and
that it may only be initialized, not reassigned or mutated after
initialization. This enables patterns like:
let x : SomeThing
if condition {
x = foo()
} else {
x = bar()
}
use(x)
This formerly required the use of a var even though there is no
mutation taking place. Properties have been folded into this model to
simplify their semantics in initializers as well.
I found the Swift blog post above from the article "Let It Go: Late Initialization of Let in Swift", which I found by googling: swift let constant conditional initialize.