THERAMPAGE
THERAMPAGE
THERAMPAGE
Switch to the Russian version
Main   |   Blog   |   EngRead   |   Dragon: The Eater   |   Rampage CMS
The "if case .some" problem in Swift
As it known, support for optional types in Swift is implemented through an enum type called Optional. So when we write:
let a: Int? = 1 
this is syntactic sugar, the full notation is:
let a: Optional<Int> = .some(1) 
In theory, everyone knows this, and the language hides the implementation details from us well. But one day you might consider having the data type in your program like this:
enum Progress {
    case none
    case some
}
For example, you want to track some process in your app. You initialize the variable to .none and then once there is any progress, the value is changed to .some. Quite elegant:
import Foundation
enum Progress {
    case none
    case some(Float)
}
class Processing {
    var loadingProgress: Progress?
    init(_ progress: Progress) {
        loadingProgress = progress
    }
    func start() {
        loadingProgress = .some(1)
    }
    func check() {
        if case .some(_) = loadingProgress {
            print("The loading process have some progress")
        }
    }
}
let p = Processing(.none)
p.start()
p.check() 
On startup, we get the expected message:
The loading process have some progress
It seems to work well, but all of a sudden you notice strange behavior - some conditions don't work correctly. What's the matter? Let's run the check without immediately calling the start() method:
let p = Processing(.none)
p.check()
Oops. We got The loading process have some progress again message.
But there was no progress. To verify this display the value of the loadingProgress variable:
let p = Processing(.none)
p.check()
print("The loading process have (p.loadingProgress!) progress")
The result will be two lines:
The loading process have some progress
The loading process have none progress
 
You probably already guessed what the matter is here, since I started with a description of optional types. But without a hint, it might not be so obvious. Of course, the fact is that the loadingProgress variable is Optional but it compares with the .some value of the Progress type, so the condition is met. If we change the .some value in our Progress type to .done for example, then the problem disappears. You can complain that the code is not quite optimal, but its purpose is to demonstrate the problem. In a large project, something like this can slip in and cause a lot of problems. Moreover, if we try to perform the same trick with the value .none, then nothing will come of it.
if case .none = loadingProgress {
	print("The loading process have no progress")
}
Xcode will issue a warning:
Assuming you mean 'Optional<Progress>.none'; did you mean 'Progress.none' instead?
While .some will execute without any warning.
 
¯\_(ツ)_/¯