Date () comparison forcing unwrap in Swift - swift

I am wondering how to avoid ! in the following implementation. Date() does not do comparison without force unwrap.
let expiry = (Int(Date().timeIntervalSince1970) + 3600).description
let tData = TData(token: sasToken, expiration: expiryInSeconds)
private var tExpiryDate : Date? = nil
if (tData == nil ||
tExpiryDate == nil ||
Date() >= tExpiryDate!) {
// TODO : alert here
tExpiryDate = Date(timeIntervalSinceNow: -(5*60))
.addingTimeInterval(TimeInterval(tData!.expiration))
}

The other way around, NAND instead of OR
guard tData != nil, let expiryDate = tExpiryDate, Date() < expiryDate else {
// TODO : alert here
return
}

You could change the last comparison to
Date() >= (tExpiryDate ?? Date.distantPast)

You should always aim to unwrap optionals as early as possible. Ideally, your functions all small and specific enough that you could just guard let tExpiryDate = tExpiryDate else { return }
I would treat tData entirely separately. If it's not part of the "is in the past" comparison, it shouldn't be in the same predicate.
There are several options which work, but they all read pretty badly:
You could use Optional.map:
if tExpiryDate.map { $0 < Date.now } == true { ... }
You could use an if let to bind a name to the intermediate unwrapped value:
if let tExpiryDate = tExpiryDate, tExpiryDate < Date.now { ... }
I would recommend making a helper, which makes the call code obvious:
extension Date {
var isInThePast: Bool { self < Date.now }
}
if tExpiryDate?.isInThePast ?? true { ... }
The coalesce to true encodes the assumption that a nil date is considered in the past. You might need to change it to false if you want to treat nil dates as being in the future.

Related

Is there anyway to provide default value to a boolean comparison involving optionals in Swift?

The logic behind optional is to give default action if the value is nil. I usually give default value in conditional evaluating as a substitute instead of force unwrap a variable, such as:
var test : Bool?;
test = resultOfSomeFunction();
if test ?? false { doThis(); } else { failMiserably(); }
Or
var name : String?
name = resultOfSomeFunction();
if (name ?? "") == "Plato" { youAreAPhilosopher(); } else { youAreOrdinaryMan(); }
Or
enum CarType { case alphard; case jaguar; case bwm; case mercedes; case none; }
var carType : CarType?;
carType = resultOfSomeFunction();
if (carType ?? .none) == .jaguar { rideThisCar(); } else { sellThisCar(); }
But for the last type, this kind of coding gets weird because I have to provide a special case .none in order for this default value to works. So I'm thinking, can somebody create comparison methods (==, <=, >=, <, >, !=, &&, ||) that can take optionals and return optional bool, and if any of the side of the operator is nil, the comparison will return nil? So I want to change the comparison into like this:
enum CarType { case alphard; case jaguar; case bwm; case mercedes; case none; }
var carType : CarType?;
carType = resultOfSomeFunction();
if (carType == .jaguar) ?? false { rideThisCar(); } else { sellThisCar(); }
I think this is a more straightforward way to read the code than the previous one, and this can really shorten some of my code:
if let startDate = objectStartDate,
let endDate = objectEndDate,
startDate < Date,
Date < endDate {
promotionIsActive();
}
else { promotionExpired(); }
Becomes:
if ((startDate < Date) && (Date < endDate)) ?? false {
promotionIsActive();
}
else { promotionExpired(); }
I have seen some code somewhere that tackle optional comparison, but it forces the result of the comparison into non-optional bool. I need the optional so I can provide my own default action. Thanks.

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.

Multiple conditions in guard statement in Swift

Is there a way to include multiple conditions in a guard statement of Swift?
For example, if I want to check two optional values are nil using a guard, how should I do it using single guard statement?
Check this code
func demo(){
var str = [String: String]()
str["status"] = "blue"
str["asd"] = nil
guard let var2 = str["asd"], let var1 = str["status"]
else
{
print("asdsfddffgdfgdfga")
return
}
print("asdasdasd")
}
Guard will check one by one condition. If the first is true then it will check the next. Otherwise, it will execute the else part.
To answer Prabhav's question, yes, you are correct, each condition in a guard statement must be true in order to proceed (i.e., not go into the else block). In this sense, it is indeed like separating conditions with AND logic.
You can implement OR logic, not by using commas, but by using a Boolean condition:
guard
true || false // this guard statement evaluates to true
else
{
print("no, not all values in the guard were true")
return
}
print("yes, all of the values in the guard were true") // this is printed
or a combination of OR and AND logic, by using a combination of Boolean conditions and optional bindings:
let testString: String? = nil
guard
true || false,
let x = testString, // this guard statement evaluates to false
true
else
{
print("no, not all values in the guard were true") // this is printed
return
}
print("yes, all of the values in the guard were true")
This summary from Apple, written about optional bindings in if statements is equally applicable to guard statements:
You can include as many optional bindings and Boolean conditions in a
single if statement as you need to, separated by commas. If any of the
values in the optional bindings are nil or any Boolean condition
evaluates to false, the whole if statement’s condition is considered
to be false. The following if statements are equivalent:
if let firstNumber = Int("4"), let secondNumber = Int("42"),
firstNumber < secondNumber && secondNumber < 100
{
print("\(firstNumber) < \(secondNumber) < 100")
} // Prints "4 < 42 < 100"
if let firstNumber = Int("4")
{
if let secondNumber = Int("42")
{
if firstNumber < secondNumber && secondNumber < 100
{
print("\(firstNumber) < \(secondNumber) < 100")
}
}
} // Prints "4 < 42 < 100"

swift reflection causes impossible nil value for any

I'm trying to use swift reflection to check for changes in objects so I can send only changed properties up to the server. Some of my properties are optional. To compare those values, I need to unwrap them but, of course, you can ONLY unwrap actual values, not nil values. So, I need to check if one of the values is nil before I compare them.
In my playground, I tried the following:
import UIKit
class myClass
{
var fieldOne:String?
var fieldTwo:Int?
var fieldThree:Float?
}
var oneMyClass = myClass()
oneMyClass.fieldOne = "blah"
oneMyClass.fieldThree = 3.5
var oneOtherClass = myClass()
oneOtherClass.fieldOne = "stuff"
oneOtherClass.fieldTwo = 3
let aMirror = Mirror(reflecting: oneMyClass)
let bMirror = Mirror(reflecting: oneOtherClass)
for thing in aMirror.children
{
for thing2 in bMirror.children
{
if thing.label! == thing2.label!
{
print("property: \(thing.label!)")
print("before: \(thing.value)")
print("after: \(thing2.value)")
print("")
//let myTest = thing.value == nil ? "nil" : "not nil"
}
}
}
And it generates the following output:
property: fieldOne
before: Optional("blah")
after: Optional("stuff")
property: fieldTwo
before: nil
after: Optional(3)
property: fieldThree
before: Optional(3.5)
after: nil
As you can see, the expected properties are displayed as "nil". However, if you uncomment the let statement, you get an error stating:
playground52.swift:37:38: error: value of type 'Any' (aka 'protocol<>') can never be nil, comparison isn't allowed
And yet, we know from the output that it IS nil. How can this be and what can I do about it?
Based on this answer, I recommend using if case Optional<Any>.some(_).
For example:
aMirror.children.forEach {
guard let propertyName = $0.label else { return }
if case Optional<Any>.some(_) = $0.value {
print("property: \(propertyName) is not nil")
} else {
print("property: \(propertyName) is nil")
}
}
Thats look like some sort of bug. Look at that
let x = childMirror.value == nil ? "Nil" : "Not Nil" //dont compile.
let y = { (value:Any?) in
return value == nil ? "Nil" : "Not Nil"
}
let z = y(childMirror.value) //compile, but doesn't evaluate.
I guess the problem is because Any can store a Optional, but can't be wrapped around one. Try this:
func getValue(unknownValue:Any) -> Any {
let value = Mirror(reflecting: unknownValue)
if value.displayStyle != .Optional || value.children.count != 0 {
return "Not Nil"
} else {
return "Nil"
}
}

How to test the Optionality of a String?

I have two different scenarios where I need to test the "optionality" of an optional type. I have not been able to figure how to explicitly test if the variable is a .None or a .Some other than with an unwieldy switch statement. How can I test for Someness with an if statement?
Scenario 1
I am writing an address formatter and my inputs are a number of String? types. In this example a simple test for (str != nil) will work. However, since my other need is when dealing with a 'double optional' and a nil test can't distinguish between .Some(.None) and .None a solution to this problem will solve that problem too.
Here's a version that works using a switch
let address1:String? = "123 Main St"
let address2:String? = nil
let apt:String? = "101"
let components = [address1, address2, apt].filter( { (c) -> Bool in
switch c {
case .Some: return true
case .None: return false
}
}).map { return $0! } //Had to map because casting directly to [String] crashes
print(", ".join(components)) //"123 Main St, 101"
What's I'd like to see is something like with an if:
let nice = ["123 Main St", nil, "303"].filter { (c) -> Bool in
return (c == .Some)
}
print(", ".join(nice))
Scenario 2
This is where a nil test won't work. If something is a String?? it can be any of .None, .Some(.None), or .Some(.Some(String)). In my case, the variable is carrying the recordID from an api call which might either be missing entirely (.None), a value (.Some(.Some("ABDEFG")), or explicitly NULL (.Some(.None)).
let teamNoneNone: String?? = .None
let teamSomeNone: String?? = .Some(.None)
let teamSomeSome: String?? = "My favorite local sportsball team"
if teamNoneNone == nil {
print("teamNoneNone is nil but is it .None? We don't know!") //prints
} else {
print("teamNoneNone is not nil")
}
if teamSomeNone == nil {
print("teamSomeNone is nil")
} else {
print("teamSomeNone is not nil but is it .Some(.None)? We don't know!") //prints
}
if teamSomeSome == nil {
print("teamSomeSome is nil but is it .None? We don't know!")
} else {
print("teamSomeSome is not nil but is it .Some(.None) or .Some(.Some())? We don't know!") //prints
}
Via another SO post I found a workaround like this, but it's not very clear what's happening to a casual reader:
if let team: String? = teamSomeNone {
print("teamSomeNone is Some(.None)") //prints
} else {
print("teamSomeNone is .Some(.Some())")
}
if let tests if a value is .None, and if it isn’t, it unwraps it and binds it to a local variable within an if statement.
Using switch with .Some and .None is really a secondary way of handling optionals, if if let doesn’t cut it. But it almost always does, especially now you can do multiple if lets in a single statement, following the latest release of Swift 1.2 to production.
Wanting to filter out the nils in a collection is a common-enough task that Haskell has a standard function for it, called catMaybe. Here’s a version, which I’ll call catSome, that would do the trick in Swift:
func catSome<T>(source: [T?]) -> [T] {
var result: [T] = []
// iterate over the values
for maybe in source {
// if this value isn’t nil, unwrap it
if let value = maybe {
// and append it to the array
result.append(value)
}
}
return result
}
let someStrings: [String?] = ["123 Main St", nil, "101"]
catSome(someStrings) // returns ["123 Main St", "101"]
Doubly-wrapped optionals are a bit of a pain, so the best solution is to avoid them in the first place – often, via use of optional chaining or flatMap.
But if you do find yourself with some, and all you care about is the inner value, you can unwrap them using a double if let:
// later parts of the let can rely on the earlier
if let outer = teamSomeSome, teamName = outer {
println("Fully unwrapped team is \(teamName)")
}
If you want to explicitly know if a double-optional has an inner nil inside an outer value, but isn’t nil itself, you can use if let with a where clause:
if let teamSomeMaybe = teamSomeNone where teamSomeMaybe == nil {
// this will be executed only if it was .Some(.None)
println("SomeNone")
}
The where clause is an extra conditional that can be applied to the unwrapped value.