>.. as postfix in Swift - swift

I'm trying to create different cases matching ranges in a switch statement to check user's age:
let minimumAge: Int
let maximumAge: Int
if let age = Calendar.current.dateComponents([.year], from: date, to: Date()).year {
switch age {
case ..<minimumAge:
return .young
case (maximumAge+1)... :
return .old
default:
return .valid
}
}
I would like to use something like >.. to exclude maximumAge from executing the case .old. Is that possible?

Use either ... (maximumAge, which is you have done) or use minimumAge...maximumAge as suggest by #vacawama in the above comment, there's nothing like >..
switch age {
case ..<minimumAge:
return .young
case minimumAge...maximumAge :
return .valid
default:
return .old
}

Related

Is there a way in Swift to get an associated value without using a switch statement?

When I have a situation where I already know enum case statement I want to get the associated value of, is there a cleaner way than using a switch statement to pluck out the associated value?
To have to come up with a switch statement, provide multiple cases, or a default case just to extract the associated value is gaudy.
enum CircularReasoning {
case justPi(pi: Double)
case pizzaPie(howMany: Int)
}
var piInTheSky : Double
let whatLogic = CircularReasoning(pi: 3.1415926)
⬇️ 𝘸𝘒𝘯𝘡 𝘡𝘰 𝘒𝘷𝘰π˜ͺπ˜₯ ⬇️
switch whatLogic {
case .justPi(let pi):
piInTheSky = pi!
default:
break
}
You can use if case .<enum_case>(let value) as in TylerP's example,
or if case let .<enum_case>(value):
enum Foo {
case anInt(Int)
case aFloat(Float)
}
let aFoo: Foo = .anInt(9)
// Example of `if case .<enum_case)(let value)` syntax:
if case .anInt(let aValue) = aFoo {
print("aFoo = anInt(\(aValue))")
// Example of `if case let .enum_case(value)` syntax:
} else if case let .aFloat(aValue) = aFoo {
print("aFoo = aFloat(\(aValue))")
}
Both work. I'm not sure why the language includes both variants.
If you only care about one enum type, then either if syntax makes sense to me. If you are dealing with more than one possible enum value then the switch version seems cleaner.
Here's an adaptation of #DuncanC's excellent upvoted and accepted answer, as applied to a fictitious version of my real-world use case.
It illustrates a possible way to use his answer to reduce the space required to extract associated values, especially if one has a lot of one-off cases...
Note: Not implying this is appropriate or professional swift styling; it's clearly idiosyncratic, yet compact. (I usually don't compress things into one liners like this, unless they get really repetitive/redundant & produce lot of pointless vertical bloat).
enum RealmKey { case realmOfRealms, anyOldRealm, someOtherRealm }
.
.
.
enum SymbolToken {
case realm (key: RealmKey?)
case space (key: SpaceKey?)
case area (key: AreaKey?)
case region (key: RegionKey?)
case preserve (key: PeserveKey?)
case openParen
case closeParen
case logicalAnd
case logicalOr
case logicalNot
var realmKey : RealmKey? { if case .realm (let key) = self { return key } else { return nil } }
var spaceKey : SpaceKey? { if case .space (let key) = self { return key } else { return nil } }
var areaKey : AreaKey? { if case .area (let key) = self { return key } else { return nil } }
var regionKey : RegionKey? { if case .region (let key) = self { return key } else { return nil } }
var preserveKey : PreserveKey? { if case .preserve (let key) = self { return key } else { return nil } }
}
let realm = SymbolToken.realm(.realmOfRealms)
let realmKey = realm.realmKey

finding a number in a range swift 3

I have a number that I get from JSON, this number represents an age. Users give me a range of two ages and may code is supposed to check if this number I'm getting from JSON is in the range.
here is my code and it gives me error
Type of Expression is ambiguous without more context?
let age = "40"
if Int(AgeFrom) ... Int(AgeTO) ~= Int(age) {
print("yes")
}
Update
if let value: AnyObject = response.result.value as AnyObject? {
var ages = String
let json = JSON(value)
for (key, subJson) in json {
ages.append(subJson["age"].string!)
}
guard let min = Int(self.DropDownFrom.selectedItem!) else { return }
guard let max = Int(self.DropDownTo.selectedItem!) else { return }
for fitage in ages {
switch ages
{
case (min...max):
print ("Age is in range")
default:
print ("Nope, not this time")
}
}
Still gives me an error.
You need to unwrap the optionals because the Int(:String) method might not have a valid answer.
Best way to do this is kind of thing is with guard
guard let min = Int(AgeFrom) else { return }
guard let max = Int(AgeTo) else { return }
And from there you can go with the simple if statement:
if (min <= age && age <= max)
{
print ("Age is in range")
}
or get really fancy and use the switch statement pattern matching syntax (which I much prefer)
switch age
{
case (min...max):
print ("Age is in range")
default:
print ("Nope, not this time")
}
if - simplicity and readability
It is a basic thing in programming, checking if an optional is between two other optional values with an if:
if Int(AgeFrom)! <= Int(age)! && Int(AgeTO)! >= Int(age)! {
print("It is in the range!")
}
switch - multiple cases handling
However, I recommend using a switch for case handling:
switch(Int(AgeFrom)! <= Int(age)!, Int(AgeTO)! >= Int(age)!){
case (true,true): print("Yes, it fits the range")
case (false,true): print("Too young!")
case (true,false): print("Too old!")
}
The second solution is far better for multiple cases of the age value, especially when it's outside the range.
Hope it helps!
You can also use optional binding:
if let ageFrom = Int(ageFrom),
let ageTo = Int(ageTo),
ageFrom...ageTo ~= age
{
print("yes")
} else {
print("no")
}
You have to unwrap the optionals:
if Int(AgeFrom)!...Int(AgeTO)! ~= Int(age)! {
print("yes")
}
of course that is the unsafe way of unwrapping, since it will crash if the conversion of AgeFrom, AgeTO or age fail.

switch statement assign to NSlocalizedString

How can I assign updateHeaderNotifications and DebugAlert to NSlocalizedString? I tried to add updateHeaderNotifications = Nslocalized("text_text") but it doesnt work. Thanks for help
private func initViewToCurrentState(pocketStatus: pocketStatus?) {
if let status = pocketStatus {
switch status {
case .Created:
**updateHeaderNotifications** ("text1")
initpocketBeforeTripView()
case .Paid: fallthrough
case .Undone: fallthrough
case .Aborted: fallthrough
default:
PocketRideService.sharedInstance.removeCachedRide()
dismissViewControllerAnimated(false, completion: {
DebugAlert.show("text")
})
}
}
}
Based on your method definition, you can use like below:
private func updateHeaderNotifications(bigNotify: String, smallNotify: String) {
bigNotification.text = NSLocalizedString(bigNotify, comment: "bigNotify")//you can set any text for comment parameter
smallNotification.text = NSLocalizedString(smallNotify, comment: "smallNotify")
}
and in your switch statement:
case .Created:
updateHeaderNotifications("text1", smallNotify: "text2")

Using pattern matching to filter array

this is my code:
enum SymptomPeriod {
case Day
case Night
}
enum SymptomType {
case Breathing(SymptomPeriod)
case Breathlessness(SymptomPeriod)
case Opression(SymptomPeriod)
case Cough(SymptomPeriod)
case ActivityLimited()
case SecureTreatment()
}
struct Symptom {
let type: SymptomType
let date: NSDate
}
And i have an array of symptoms.
let symptomList: [Symptom] = ...
My need is to filter the list of symptoms with the SymptomPerion criteria, i trying to do like this:
let daySymtoms = symptomList.filter { (symptom) -> Bool in
return symptom.type = ???
}
My problem is in the filter function.
(My goal is to use a filter function and not a loop)
A few suggestions
Use your struct as namespace
Instead of repeating the word Symptom (e.g. SymptomPeriod, SymptomType) you should put your enums into you Symptom struct
Rename SymptomType as Kind
Once you moved SymptomType into Symptom you can drop the Symptom part of the name. However using Type as name will create a conflict so you should rename it Kind.
Add the period computed property to Kind
This will make the filtering much easier
Here's the code
struct Symptom {
enum Period {
case Day
case Night
}
enum Kind {
case Breathing(Period)
case Breathlessness(Period)
case Opression(Period)
case Cough(Period)
case ActivityLimited()
case SecureTreatment()
var period: Period? {
switch self {
case Breathing(let period): return period
case Breathlessness(let period): return period
case Opression(let period): return period
case Cough(let period): return period
default: return nil
}
}
}
let kind: Kind
let date: NSDate
}
The solution
Now the filtering has become very easy
let symptoms: [Symptom] = ...
let filtered = symptoms.filter { $0.kind.period == .Day }
This is how i am doing it:
let daySymtoms = symtoms.filter { (symptom) -> Bool in
switch symptom.type {
case .Breathing(.Day), .Breathlessness(.Day), .Opression(.Day), .Cough(.Day):
return true
default:
return false
}
}
Let me know if you have more simple way to do it.

How to compare enum with associated values by ignoring its associated value in Swift?

After reading How to test equality of Swift enums with associated values, I implemented the following enum:
enum CardRank {
case Number(Int)
case Jack
case Queen
case King
case Ace
}
func ==(a: CardRank, b: CardRank) -> Bool {
switch (a, b) {
case (.Number(let a), .Number(let b)) where a == b: return true
case (.Jack, .Jack): return true
case (.Queen, .Queen): return true
case (.King, .King): return true
case (.Ace, .Ace): return true
default: return false
}
}
The following code works:
let card: CardRank = CardRank.Jack
if card == CardRank.Jack {
print("You played a jack!")
} else if card == CardRank.Number(2) {
print("A two cannot be played at this time.")
}
However, this doesn't compile:
let number = CardRank.Number(5)
if number == CardRank.Number {
print("You must play a face card!")
}
... and it gives the following error message:
Binary operator '==' cannot be applied to operands of type 'CardRank' and '(Int) -> CardRank'
I'm assuming this is because it's expecting a full type and CardRank.Number does not specify an entire type, whereas CardRank.Number(2) did. However, in this case, I want it to match any number; not just a specific one.
Obviously I can use a switch statement, but the whole point of implementing the == operator was to avoid this verbose solution:
switch number {
case .Number:
print("You must play a face card!")
default:
break
}
Is there any way to compare an enum with associated values while ignoring its associated value?
Note: I realize that I could change the case in the == method to case (.Number, .Number): return true, but, although it would return true correctly, my comparison would still look like its being compared to a specific number (number == CardRank.Number(2); where 2 is a dummy value) rather than any number (number == CardRank.Number).
Edit: As Etan points out, you can omit the (_) wildcard match to use this more cleanly:
let number = CardRank.Number(5)
if case .Number = number {
// Is a number
} else {
// Something else
}
Unfortunately, I don't believe that there's an easier way than your switch approach in Swift 1.2.
In Swift 2, however, you can use the new if-case pattern match:
let number = CardRank.Number(5)
if case .Number(_) = number {
// Is a number
} else {
// Something else
}
If you're looking to avoid verbosity, you might consider adding an isNumber computed property to your enum that implements your switch statement.
Unfortunately in Swift 1.x there isn't another way so you have to use switch which isn't as elegant as Swift 2's version where you can use if case:
if case .Number = number {
//ignore the value
}
if case .Number(let x) = number {
//without ignoring
}
In Swift 4.2 Equatable will be synthesized if all your associated values conform to Equatable. All you need to do is add Equatable.
enum CardRank: Equatable {
case Number(Int)
case Jack
case Queen
case King
case Ace
}
https://developer.apple.com/documentation/swift/equatable?changes=_3
What I usually do to compare if two enum cases "match" no matter their associated value is:
I have a protocol Matchable:
protocol Matchable {
static func ~= (lhs: Self, rhs: Self) -> Bool
}
Then I make enums conform to it:
extension CardRank: Matchable {
static func ~= (lhs: Self, rhs: Self) -> Bool {
switch (lhs, rhs) {
case
(.number, .number),
(.jack, .jack),
(.queen, .queen),
(.king, .king),
(.ace, .ace):
return true
default:
return false
}
}
}
let card1: CardRank = .number(1)
let card2: CardRank = .number(2)
let card3: CardRank = .jack
print(card1 ~= card2) // true
print(card1 ~= card3) // false
Here's a simpler approach:
enum CardRank {
case Two
case Three
case Four
case Five
case Six
case Seven
case Eight
case Nine
case Ten
case Jack
case Queen
case King
case Ace
var isFaceCard: Bool {
return (self == Jack) || (self == Queen) || (self == King)
}
}
There's no need to overload the == operator, and checking for card type does not require confusing syntax:
let card = CardRank.Jack
if card == CardRank.Jack {
print("You played a jack")
} else if !card.isFaceCard {
print("You must play a face card!")
}
I didn't want to conform Equatable (it didn't help me either) and I wanted to filter for other cases than a specific one, so instead of simply writing card != .Number I had to write the following. (I adjusted my code to this question.)
enum CardRank {
...
var isNumber: Bool {
if case .Number = self { return true }
return false
}
}
So I can write not a number in a complex condition:
if something && !card.isNumber { ... }
I wish I could just write card != .Number, but the compiler was always complaining with Type of expression is ambiguous without more context. Maybe in an upcoming swift version!
You don't need func == or Equatable. Just use an enumeration case pattern.
let rank = CardRank.Ace
if case .Ace = rank { print("Snoopy") }
extension CardRank {
func isSameCaseAs(_ other: CardRank) -> Bool {
switch (self, other) {
case (.Number, .Number),
(.Jack, .Jack),
(.Queen, .Queen),
(.King, .King),
(.Ace, .Ace):
return true
default:
return false
}
}
}
let number = CardRank.Number(1)
let otherNumber = CardRank.Number(2)
number.isSameCaseAs(otherNumber) // true
Just create an extension and ignore the associated types.
From Swift 5.3, you can use the Comparable Enums feature:
enum CardRank: Comparable {
case Number(Int)
case Jack
case Queen
case King
case Ace
}
let cards: [CardRank] = [
.Queen, .Number(8), .Ace, .Number(3), .King
]
print(cards.sorted())
// [.Number(3), .Number(8), .Queen, .King, .Ace]