How to make switch statement with optionals? - ios

I'm working with text fields and json data. I want to test if the json data is != nil, if it is, then I fill it's field with that info, if it's == nil then I put a placeholder in its field instead. Like this:
if memberInfo?.createDate != nil {
self.activeSinceTextField.text = stringToDateToString((memberInfo?.createDate)!)
} else {
placeHolder("Enter Here".localized())
self.activeSinceTextField.attributedPlaceholder = placeHolderText
}
if memberInfo?.birthDate != nil
{self.birthdayTextField.text = stringToDateToString((memberInfo?.birthDate)!)
} else
{ placeHolder("Enter Here".localized())
self.birthdayTextField.attributedPlaceholder = placeHolderText
}
I have a lot of fields and I'm thinking a switch statement would make this a lot cleaner. I'm just not sure how to make one for this type of situation.

Version 1
This is how you define the 4 outcomes with a switch
switch (memberInfo?.createDate, memberInfo?.birthDate) {
case (.Some, .Some): break
case (.Some, nil): break
case (nil, .Some): break
case (nil, nil): break
}
Now just replace the break(s) with the code you want to execute for each scenario.
Version 2
Here you have createDate and/or birthDate unwrapped when available
switch (memberInfo?.createDate, memberInfo?.birthDate) {
case (let .Some(createDate), let .Some(birthDate)):
print(createDate, birthDate)
case (let .Some(createDate), .None):
print(createDate)
case (.None, let .Some(birthDate)):
print(birthDate)
case (.None, .None):
print("Both are nils")
}
Legend
.Some: means here there is something
.None: means this is nil
_: means is a jolly, it means I don't care what there is here

Related

Exit from loop after finding the first positive element in swift

So, need you help, I have loops, where I'd like to find first positive element and it will be text of label and exit from all loops, but every time I get last element:
for j in getArrayOfAllTimes[i].timeForGetDifference()
{
switch j - timeNow() {
case let x where x > 0:
nextTimeLabel.text = String(j - timeNow())
break
default:
break
}
}
How get first element > 0?
The first(where:) method of Array
Instead of explicitly breaking out of a loop when a first element that fulfills som predicate is found, you could simply make use of the Array method first(where:).
Since you haven't provided us with a minimal, complete and verifiable example (which you should) we'll construct such an example:
/* Example setup */
struct Foo: CustomStringConvertible {
private let bar: Int
init(_ bar: Int) { self.bar = bar }
func timeForGetDifference() -> Int {
return bar
}
var description: String {
return String(bar)
}
}
func timeNow() -> Int { return 10 }
let getArrayOfAllTimes = [Foo(6), Foo(2), Foo(9), Foo(4), Foo(11), Foo(3), Foo(13)]
// nextTimeLabel: some UILabel
For the example as per above, we could set the text property of the nextTimeLabel as follows, using first(where:) to find the first element fulfilling our predicate, given that it exists (otherwise; will return nil in which case we will not enter the optional binding block below).
if let firstNonNegativeFoo = getArrayOfAllTimes
.first(where: { $0.timeForGetDifference() - timeNow() > 0 }) {
nextTimeLabel.text = String(describing: firstNonNegativeFoo) // 11
}
As to why your own approach does not work as intended: a break statement within a case of a switch statement will simply end the execution of the switch statement (not the loop which is one level above the switch statement.
From the Language Reference - Statements:
Break Statement
A break statement ends program execution of a loop, an if statement,
or a switch statement.
In you case, you've added the break statements as the last statements of each case: here, particularly, the break has truly no effect (since the switch statement would break out anyway, after exiting the case which it entered).
for i in 1...3 {
switch i {
case is Int: print(i); break // redundant 'break'
case _: ()
}
} // 1 2 3
// ... the same
for i in 1...3 {
switch i {
case is Int: print(i)
case _: ()
}
} // 1 2 3
for j in getArrayOfAllTimes[i].timeForGetDifference() {
bool a = NO;
switch j - timeNow() {
case let x where x > 0:
a = YES
nextTimeLabel.text = String(j - timeNow())
break
default:
break
}
if a == YES {
break;
}
}

Initialized enum returns wrong hashValue

This is Swift 1.2 and I'm using Xcode 6.4. The following enum has a failable initializer.
enum EstimateItemStatus: Int, Printable {
case Pending = 1
case OnHold = 2
case Done = 3
var description: String {
switch self {
case .Pending: return "Pending"
case .OnHold: return "On Hold"
case .Done: return "Done"
}
}
init?(id : Int) {
switch id {
case 1:
self = .Pending
case 2:
self = .OnHold
case 3:
self = .Done
default:
return nil
}
}
}
If I pass an ID and initialize an instance, the enum value I get is correct. However the hashValue is wrong. For example,
let status = EstimateItemStatus(id: 2)!
println("\(status.hashValue) - \(status)")
The output I get is 1 - On Hold.
But it should be 2 - On Hold.
What am I doing wrong here? Is this a compiler bug or am I missing something?
Demo playground
Maybe you're mixing up hashValue vs. rawValue.
The hash value is not enforced to be equal to the raw value

HKBiologicalSexObject.biologicalSex returns HKBiologicalSex instead of .Male or .Female

I have already authorized HealthKit, and I am getting BiologicalSex from HealthKitStore like this:
let healthKitStore:HKHealthStore = HKHealthStore()
var biologicalSexObject: HKBiologicalSexObject?
var biologicalSex: HKBiologicalSex?
do {
biologicalSexObject = try healthKitStore.biologicalSex()
biologicalSex = biologicalSexObject!.biologicalSex
} catch _ as NSError {
biologicalSex = nil
print("error reading biological sex")
}
However, when I try to print biologicalSex it returns HKBiologicalSex instead of .Male or .Female.
I have seen more or less this exact code in several tutorials, so I'm wondering if there have been any syntax changes I should be aware of in Swift 2. (The error handling has changed, so I'm curious if anything else of note has.)
The rawValue of biologicalSex = biologicalSexObject!.biologicalSex is required to do this. The enum for BiologicalSex looks like this:
typedef enum : NSInteger {
HKBiologicalSexNotSet = 0,
HKBiologicalSexFemale,
HKBiologicalSexMale,
HKBiologicalSexOther,
} HKBiologicalSex;
Using this information it is easy to design a switch statement to cover all of the possible values:
switch biologicalSex.rawValue{
case 0:
biologicalSex = nil
case 1:
biologicalSex = "Female"
case 2:
biologicalSex = "Male"
case 3:
biologicalSex = "Other"
default:
biologicalSex = nil
}

Switch statement inside if statement

I am trying to have a switch statement inside of an if statement checking integers
if variable <= 3 {
// code
switch variable {
case 0:
println("0 case")
case 1:
println("1 case")
case 2:
println("2 case")
case 3:
println("3 case")
default:
println("error")
}
}
But I am getting an error for each case
Binary operator '~=' cannot be applied to operands of type 'Int' and 'Int?'
I do not understand why this wouldn't work.
3 is an Int but variable is an Int? (an optional Int). You have to unwrap it.
For example, you can check if it's nil in the if statement. If it's not, then it's safe to force-unwrap (with !) inside the scope of that conditional:
One of many possible approaches:
if variable <= 3 && variable != nil {
// code
switch variable! {
case 0:
println("0 case")
case 1:
println("1 case")
case 2:
println("2 case")
case 3:
println("3 case")
default:
println("error")
}
}
var variable : Int? will not work
var variable = 2
//var variable : Int? // will not work
if variable <= 3 {
// code
switch variable {
case 0:
println("0 case")
case 1:
println("1 case")
case 2:
println("2 case")
case 3:
println("3 case")
default:
println("error")
}
}
If your variable is an Optional, be aware that if it's nil then in your initial if statement the expression nil <= 3 returns true.
So, the most comprehensive form your can choose (IMHO) is:
var variable : Int? = ...
switch variable {
case .Some(0):
print("1 case")
case .Some(1):
print("2 case")
case .Some(3):
print("3 case")
case .Some(let x):
print("We got \(x)")
case .None:
print("variable is nil")
}
Note:
print is Swift 2.0. Use println in Swift 1.2

Swift switch statement for matching substrings of a String

Im trying to ask for some values from a variable.
The variable is going to have the description of the weather and i want to ask for specific words in order to show different images (like a sun, rain or so)
The thing is i have code like this:
if self.descriptionWeather.description.rangeOfString("Clear") != nil
{
self.imageWeather.image = self.soleadoImage
}
if self.descriptionWeather.description.rangeOfString("rain") != nil
{
self.imageWeather.image = self.soleadoImage
}
if self.descriptionWeather.description.rangeOfString("broken clouds") != nil
{
self.imageWeather.image = self.nubladoImage
}
Because when i tried to add an "OR" condition xcode gives me some weird errors.
Is it possible to do a swich sentence with that? Or anyone knows how to do add an OR condition to the if clause?
I had a similar problem today and realized this question hasn't been updated since Swift 1! Here's how I solved it in Swift 4:
switch self.descriptionWeather.description {
case let str where str.contains("Clear"):
print("clear")
case let str where str.contains("rain"):
print("rain")
case let str where str.contains("broken clouds"):
print("broken clouds")
default:
break
}
Swift 5 Solution
func weatherImage(for identifier: String) -> UIImage? {
switch identifier {
case _ where identifier.contains("Clear"),
_ where identifier.contains("rain"):
return self.soleadoImage
case _ where identifier.contains("broken clouds"):
return self.nubladoImage
default: return nil
}
}
You can do this with a switch statement using value binding and a where clause. But convert the string to lowercase first!
var desc = "Going to be clear and bright tomorrow"
switch desc.lowercaseString as NSString {
case let x where x.rangeOfString("clear").length != 0:
println("clear")
case let x where x.rangeOfString("cloudy").length != 0:
println("cloudy")
default:
println("no match")
}
// prints "clear"
Swift language has two kinds of OR operators - the bitwise ones | (single vertical line), and the logical ones || (double vertical line). In this situation you need a logical OR:
if self.descriptionWeather.description.rangeOfString("Clear") != nil || self.descriptionWeather.description.rangeOfString("clear") != nil {
self.imageWeather.image = self.soleadoImage
}
Unlike Objective-C where you could get away with a bitwise OR in exchange for getting a slightly different run-time semantic, Swift requires a logical OR in the expression above.
If you do this a lot, you can implement a custom ~= operator that defines sub-string matching. It lends itself to this nice syntax:
switch "abcdefghi".substrings {
case "def": // calls `"def" ~= "abcdefghi".substrings`
print("Found substring: def")
case "some other potential substring":
print("Found \"some other potential substring\"")
default: print("No substring matches found")
}
Implementation:
import Foundation
public struct SubstringMatchSource {
private let wrapped: String
public init(wrapping wrapped: String) {
self.wrapped = wrapped
}
public func contains(_ substring: String) -> Bool {
return self.wrapped.contains(substring)
}
public static func ~= (substring: String, source: SubstringMatchSource) -> Bool {
return source.contains(substring)
}
}
extension String {
var substrings: SubstringMatchSource {
return SubstringMatchSource(wrapping: self)
}
}
I'd recommend using a dictionary instead, as a mapping between the substring you're searching for and the corresponding image:
func image(for weatherString: String) -> UIImage? {
let imageMapping = [
"Clear": self.soleadoImage,
"rain": self.soleadoImage,
"broken clouds": self.nubladoImage]
return imageMapping.first { weatherString.contains($0.key) }?.value
}
A dictionary gives you flexibility, adding new mappings is easy to do.
This link also describes overloading operator ~= which is actually used by the switch statement for matching cases to allow you to match regular expressions.

Resources