I am new to Swift and iOS development, so I am trying to build a calculator app for learning purposes. However, I am encountering an error. I have titled all of my buttons with the number they represent, so I am retrieving the title in the buttonPress IBAction via sender.titleLabel.text. Then, I pass that into a switch statement to determine if the button was a number or an operator.
func handleButton (sender:UIButton) {
switch sender.titleLabel.text {
case "1","2","3","4","5","6","7","8","9","0" :
println(sender.titleLabel.text)
default:
break
}
}
The error is that sender.titleLabel.text will not bind to the string values I have entered - nor any string values - even though it is is of type String.
There seems to be a bug in the complier at the moment where Implicitly Unwrapped Optionals cannot be used in switch statements. Instead you can use optional binding to satisfy the compiler. As an extra plus, this will handle the case where titleLabel.text is nil.
func handleButton (sender:UIButton) {
if let text = sender.titleLabel.text {
switch text {
case "1","2","3","4","5","6","7","8","9","0" :
println(sender.titleLabel.text)
default:
break
}
}
else {
// sender.titleLabel.text is nil
}
}
Related
I want to develop a recursive Either type as a Swift enum (at least a partially working version (;), and so have been playing around with generics (trying to work with the type system).
Here is what I have so far:
enum NumericEitherEnum<Left, Right> {
case left(Left)
case right(Right)
var left: Left? {
switch self {
case .left(let leftie) where leftie is NumericEitherEnum<Int, Float>:
return (leftie as? NumericEitherEnum<Int, Float>)?.left // This won't work (obviously)
case .left(let left):
return left
case .right(_):
return nil
}
}
var right: Right? {
switch self {
case .left(_):
return nil
case .right(let right) where right is NumericEitherEnum:
return (right as? NumericEitherEnum)?.right // This assumes the generic parameters of Left, Right in the current enum so won't work.
case .right(let right):
return right
}
}
}
I get cryptic diagnostic error messages in Xcode, which I haven't been able to get around:
'Replace (leftie as? NumericEitherEnum<Int, Float>)? with NumericEitherEnum<Int, Float>'
'Enum case 'left' cannot be used as an instance member'
What I am trying to achieve:
print(NumericEitherEnum<NumericEitherEnum<Int, Float>, NumericEitherEnum<Int, Float>>.left(.left(3)).left ?? "failed :(") // Parses the innermost value 3
The fix-it doesn't fix the error or advise how to actually address the underlying cause. I think this is probably an edge case for the compiler to parse (maybe even bug) ;). I also realise that in fact it doesn't make sense logically to return an Int for a Left? type but is there any way I can express this within the type system (I tried associated types but I still don't know how to make this dynamic). There's also a problem to handle nested enums that have a different generic type signature (not sure how to express this).
How can I resolve this issue in a better way? Ideally, I want to have a clean call site without too much indirection and extra data structures but would be open to trying out a different data structure if this isn't practically achievable.
So I discovered that Swift 2 introduced support for recursive enums ages ago - source! While this is great, I didn't manage to find any SO questions for this specific problem i.e. Generic Recursive Enums that had also been answered (here). I will summarise the final (partial solution) code now.
It turns out that I needed to describe what the problem is in terms of data structures. Essentially we want .left to contain either a value of type Left or a RecursiveEither<Left, Right>. With this simple idea, we can create two enums - one for the wrapper enum, and one for the nested enum which can take a Value or another wrapper.
enum RecursiveEither<Left, Right> {
case left(ValueOrLeftOrRight<Left, Left, Right>)
case right(ValueOrLeftOrRight<Right, Left, Right>)
var left: Left? {
guard case .left(let leftie) = self else { return nil }
return leftie.left
}
var right: Right? {
guard case .right(let rightie) = self else { return nil }
return rightie.right
}
}
enum ValueOrLeftOrRight<Value, Left, Right> {
case value(Value)
indirect case left(ValueOrLeftOrRight<Left, Left, Right>)
indirect case right(ValueOrLeftOrRight<Right, Left, Right>)
var left: Left? {
switch self {
case .value(let left): return left as? Left
case .left(let content): return content.left
default: return nil
}
}
var right: Right? {
switch self {
case .value(let right): return right as? Right
case .right(let content): return content.right
default: return nil
}
}
}
The call site is nice with this design:
let e = RecursiveEither<Int, Int>.left(.left(.left(.left(.value(3)))))
That being said, there are still limitations to this answer - assuming Left and Right won't change even in nested Either. Also, it's possible to design this differently with only one indirect case to wrap the Either type in another associated value - that might be more memory efficient since fewer frames get pushed onto the call stack at runtime.
I have posted a gist with attempts on this problem.
If anyone has suggestions to improve this implementation, feel free to add to this.
I have notice weird swift behaviour, because in my opinion colours variable shouldn't be force unwrapped in case of switch written below, but without unwrapping compiler shows me an error message.
enum Colours: Int {
case Red = 0, White, Black
}
var colours: Colours!
colours = .Red
switch colours! { // <-- why I have to unwrap colours? As you can see colours are declared as '!'
case .Red: break
default: break
}
if colours variable is not unwrapped compiler shows me that error:
in my opinion it is swift inconsistency, does anyone have some ideas?
Update: This has been fixed in Swift 5.1. From the CHANGELOG:
SR-7799:
Enum cases can now be matched against an optional enum without requiring a '?' at the end of the pattern.
This applies to your case of implicitly unwrapped optionals as well:
var colours: Colours!
switch colours {
case .red:
break // colours is .red
default:
break // colours is .white, .black or nil
}
Previous answer:
When used in a switch statement, even implicitly unwrapped
optionals are not automatically unwrapped. (A reason might be that you
could not match them against nil otherwise.)
So you have to unwrap (either forcibly with
colours! which will crash if colours == nil, or with optional binding), or – alternatively – match against .Red?
which is a shortcut for .Some(.Red):
var colours: Colours!
switch colours {
case .Red?:
break // colours is .Red
default:
break // colours is .White, .Black or nil
}
The same holds for other pattern-matching expressions, e.g.
if case .Red? = colours {
// colours is .Red
} else {
// colours is .White, .Black or nil
}
Also this has nothing to do with enumeration types, only with implicitly
unwrapped optionals in a pattern:
let x : Int! = 1
switch x {
case nil:
break // x is nil
case 1?:
break // x is 1
default:
break // x is some other number
}
This is because you create colours variable like optional type. If you do like this:
var colours: Colours
colours = .Red
you will not have to unwrappe this value
If we look at what the optional type is, we will see that this is enum like:
enum Optional<T> {
case Some(T)
case None
}
And it can be Some Type like Int for example or None and in this case it's have nil value.
When you make this:
var colours: Colours!
you directly is indicated by the ! that this is not Colours type but this is the enum ImplicitlyUnwrappedOptional<Colours> type. At moment of creation it's will be Some<Colours> if equal it value but with this ! you have that it is enum ImplicitlyUnwrappedOptional<Colours> and in some next moment it will be the None. That's why you have to use ! in switch:
Your colours value is ImplicitlyUnwrappedOptional<Colours> type and it may be Colours or nil and you have to directly indicate that this is Colours type in `switch``.
Instead of using :
var colours: Colours!
colours = .Red
Simply use
var colours = Colours.Red
That should do the trick.
To make my code more readable & maintainable, what's the best way to use labels instead of hardcoded Ints for case labels in a switch statement with a control expression of type Int?
E.g., inside my SettingsTableViewController, I tried
enum Section : Int {
case Phone
case LogOut
case DeleteAccount
}
and in – tableView:didSelectRowAtIndexPath:
switch indexPath.section {
case .Phone:
// Push EditPhoneViewController
case .LogOut:
// Log out
case .DeleteAccount:
// Present action-sheet confirmation
}
but got the compile error
Enum case pattern cannot match values of the non-enum type 'Int'
In the switch you can't simply use indexPath.section as that is not the same type as your enum.
Use switch Section(rawValue: indexPath.section)!
import SpriteKit
let NumOrientations: UInt32 = 4
enum Orientation: Int, Printable {
case Zero = 0, Ninety, OneEighty, TwoSeventy
var description: String {
switch self {
case .Zero:
return "0"
case .Ninety:
return "90"
case .OneEighty:
return "180"
case .TwoSeventy:
return "270"
}
}
static func random() -> Orientation {
return Orientation(rawValue: Int(arc4random_uniform(NumOrientations)))!
}
}
I am new to swift, but I have a lot of programming experience. However, I've never encountered anything like the variable "wrapping" when dealing with unknowns in Swift.
I have the static function random, which returns an Orientation. There is NOTHING optional about the Orientation class. However, I have to use an exclamation point on the return statement in the random function.
Why is this? Excuse my complete lack of knowledge about swift.
Well, obviously the initializer can fail. Let's assume:
Orientation(rawValue: 10)
This won't find a value in your enum, so what do you expect it to return? It will return nil. That means the return value must be an optional because nil can be returned.
This is explicitly mentioned in Swift Language Guide, Enumerations, Initializing from a Raw Value:
NOTE
The raw value initializer is a failable initializer, because not every raw value will return an enumeration member. For more information, see Failable Initializers.
However, in this case (method random) you are sure that a nil won't be returned, so the best solution is to unwrap the optional before returning it from random.
I am new to Swift and iOS development, so I am trying to build a calculator app for learning purposes. However, I am encountering an error. I have titled all of my buttons with the number they represent, so I am retrieving the title in the buttonPress IBAction via sender.titleLabel.text. Then, I pass that into a switch statement to determine if the button was a number or an operator.
func handleButton (sender:UIButton) {
switch sender.titleLabel.text {
case "1","2","3","4","5","6","7","8","9","0" :
println(sender.titleLabel.text)
default:
break
}
}
The error is that sender.titleLabel.text will not bind to the string values I have entered - nor any string values - even though it is is of type String.
There seems to be a bug in the complier at the moment where Implicitly Unwrapped Optionals cannot be used in switch statements. Instead you can use optional binding to satisfy the compiler. As an extra plus, this will handle the case where titleLabel.text is nil.
func handleButton (sender:UIButton) {
if let text = sender.titleLabel.text {
switch text {
case "1","2","3","4","5","6","7","8","9","0" :
println(sender.titleLabel.text)
default:
break
}
}
else {
// sender.titleLabel.text is nil
}
}