Optional Binding followed by casting to an optional in swift - swift

I'm trying to parse data from a dictionary. I have code that currently works but I think there is a better more concise way to do it.
I have three options for what my dictionary can equal
let dictionary:[String:Any] = ["pic":"picture"] //opt1
let dictionary:[String:Any] = ["pic":2] //opt2
let dictionary:[String:Any] = ["pi":"this"] //opt3
This is the code that I am currently using to parse the data that I would like to be improved.
let _pic = dictionary["pic"]
if _pic != nil && !(_pic is String) {
print("error")
return
}
let pic = _pic as? String
For each option i'd like different things to happen for:
opt1
pic:String? = Optional(picture)
opt2 An error to be shown
opt3
pic:String? = nil

You can try this,
guard let _pic = dictionary["pic"] as? String else { return }

let _pic = dictionary["pic"]
This by default gives you an optional value for _pic that's of type Any?. As such, your code seems OK based on your requirements and I don't think you need the last line let pic = _pic as? String

I think you need to do two tests. Here's one way:
guard let picAsAny = dictionary["pic"]
else { /* No key in the dictionary */ }
guard let pic = picAsAny as? String
else { /* error wrong type */ }
// pic is now a (nonoptional) string
Obviously you can use if statements instead of guards depending on context.

Related

Says type is non-optional but prints optional

I have this code:
let result_string = "\(String(describing: value))"
because I need to convert this from type Any to String. However when I print result string, it prints:
optional(abcd)
How do I make it print just
abcd
?
When I tried the other fixes for optionals it told me that result_string is not optional and didn't let me force unwrap.
EDIT:
Value was an optional, so force unwrapping it fixed my problem.
let result_string = "\(String(describing: value!))"
You can try
if let res = value {
print(res)
}
OR
guard let res = value { return }
print(res)
you can try like below:
var value : Any? = "abcd"
let result_string = value as! String
print(result_string)
We have to downcast Any to string.
Worked in playground.

if let with try? gives optional value [duplicate]

I am using an SQLite library in which queries return optional values as well as can throw errors. I would like to conditionally unwrap the value, or receive nil if it returns an error. I'm not totally sure how to word this, this code will explain, this is what it looks like:
func getSomething() throws -> Value? {
//example function from library, returns optional or throws errors
}
func myFunctionToGetSpecificDate() -> Date? {
if let specificValue = db!.getSomething() {
let returnedValue = specificValue!
// it says I need to force unwrap specificValue,
// shouldn't it be unwrapped already?
let specificDate = Date.init(timeIntervalSinceReferenceDate: TimeInterval(returnedValue))
return time
} else {
return nil
}
}
Is there a way to avoid having to force unwrap there? Prior to updating to Swift3, I wasn't forced to force unwrap here.
The following is the actual code. Just trying to get the latest timestamp from all entries:
func getLastDateWithData() -> Date? {
if let max = try? db!.scalar(eventTable.select(timestamp.max)){
let time = Date.init(timeIntervalSinceReferenceDate: TimeInterval(max!))
// will max ever be nil here? I don't want to force unwrap!
return time
} else {
return nil
}
}
Update: As of Swift 5, try? applied to an optional expression does not add another level of optionality, so that a “simple” optional binding is sufficient. It succeeds if the function did not throw an error and did not return nil. val is then bound to the unwrapped result:
if let val = try? getSomething() {
// ...
}
(Previous answer for Swift ≤ 4:) If a function throws and returns an optional
func getSomething() throws -> Value? { ... }
then try? getSomething() returns a "double optional" of the
type Value?? and you have to unwrap twice:
if let optval = try? getSomething(), let val = optval {
}
Here the first binding let optval = ... succeeds if the function did
not throw, and the second binding let val = optval succeeds
if the return value is not nil.
This can be shortened with case let pattern matching to
if case let val?? = try? getSomething() {
}
where val?? is a shortcut for .some(.some(val)).
I like Martin's answer but wanted to show another option:
if let value = (try? getSomething()) ?? nil {
}
This has the advantage of working outside of if, guard, or switch statements. The type specifier Any? isn't necessary but just included to show that it returns an optional:
let value: Any? = (try? getSomething()) ?? nil

How to check if a field type Any? is nil o NSNull

I'm actually trying to parse a Json object with Swift3 on Xcode8.1.
This is my code:
if let objData = objJson["DATA"] as! NSDictionary? {
var msg: String = ""
if let tmp = objData.object(forKey: "Message") {
msg = tmp as! String
} else {
print("NIIILLLLL")
}
}
I'm getting this error message: Could not cast value of type 'NSNull' (0x4587b68) to 'NSString' (0x366d5f4) at this line msg = tmp as! String.
I'm not understanding why I'm getting this error because the type of tmp is Any and it should display the print instead of convert tmp as! String
Thank you for the help,
You can add casting in let.
if let tmp = objData.object(forKey: "Message") as? String {
msg = tmp
}
With Swift 3, for example:
fileprivate var rawNull: NSNull = NSNull()
public var object: Any {
get {
return self.rawNull
}
}
You can check field object as:
if self.object is NSNull {
// nil
}
So to answer your question in why you are getting that error, in your code "tmp" is not nil its something of type NSNull (if you want to know more about NSNull check the docs) but its basically "A singleton object used to represent null values in collection objects that don’t allow nil values."
The rest is just you are force casting which I recommend avoiding this is a safer way to do what you are doing.
guard let objData = objJson["DATA"] as? [String: Any], let msg = objData["Message"] else { return }
// now you can use msg only if exists and also important keeping its unmutable state

Can I use "guard let" if you want to pass over an optional string to "rawValue:" in enum in Swift?

I want to initialize a enum from a variable of type String?, like:
guard let rawId = request.queryParameters["id"] else {
return
}
guard let id = MyIdentifier(rawValue: rawId) else {
return
}
In this case, request.queryParameters["id"] returns String?. Then after I ensure that it is String in rawId, I convert it into an enum instance id.
However, the code is dirty and I want to write it in one-line if at all possible.
However, I don't like to make it unwrapped via forced optional unwrapping, because if it can not be transformed to String, the app would end up with an error, since rawValue: only takes String. I meant something like the following, which I don't like:
guard let id = MyIdentifier(rawValue: request.queryParameters["id"]!) else {
return
}
So is it still possible to define the guard let in one-line, maybe using where and/or case in guard?
You have two conditions there, trying to combine them into one condition is not always possible.
In your exact case I believe an empty id will behave the same as a nil id, therefore nil coalescing can be used:
guard let id = MyIdentifier(rawValue: request.queryParameters["id"] ?? "") else {
return
}
However, there is nothing dirty about splitting two checks into two statements. Code is not written to be short, it's written to be clear:
guard let rawId = request.queryParameters["id"],
let id = MyIdentifier(rawValue: rawId) else
return
}
Also, there is nothing wrong with creating a custom initializer for your enum:
init?(id: String?) {
guard let id = id else {
return nil
}
self.init(rawValue: id)
}
and then
guard let id = MyIdentifier(id: request.queryParameters["id"]) else {
return
}
Try this:
You can simply combine both the statements into a single statement ,i.e,
guard let id = request.queryParameters["id"], let id2 = MyIdentifier(rawValue: id) else {
return
}

A good way to do optional chaining

I currently do this in my code to cope with optionals...
I do a
fetchedResultController.performFetch(nil)
let results = fetchedResultController.fetchedObjects as [doesnotmatter]
// add all items to server that have no uid
for result in results {
let uid = result.valueForKey("uid") as String?
if uid == nil
{
let name = result.valueForKey("name") as String?
let trainingday = result.valueForKey("trainingdayRel") as Trainingdays?
if let trainingday = trainingday
{
let trainingUID = trainingday.valueForKey("uid") as String?
if let trainingUID = trainingUID
{
let urlstring = "http://XXXX/myGym/addTrainingday.php?apikey=XXXXXX&date=\(date)&appid=47334&exerciseUID=\(exerciseUID)"
let sUrl = urlstring.stringByAddingPercentEscapesUsingEncoding(NSASCIIStringEncoding)
let url = NSURL(string: sUrl!)
// save the received uid in our database
if let dictionary = Dictionary<String, AnyObject>.loadJSONFromWeb(url!)
{
trainingday.setValue(uid, forKey: "uid")
}
self.managedObjectContext!.save(nil)
}
}
}
}
Actually I would also need an "else"-clause for each and every "if let" statement. That seems totally terrible code to me! Is there no better way to do this?
Yes, with switch-case and pattern matching you can achieve this:
var x : SomeOptional?
var y : SomeOptional?
switch (x, y)
{
case (.Some(let x), .Some(let y)): doSomething() // x and y present
case (.None(let x), .Some(let y)): doSomethingElse() // x not present, but y
// And so on for the other combinations
default: break
}
Have a look at this blog post: Swift: Unwrapping Multiple Optionals
Edit (slightly off-topic and opinion-based): this is one of my favorite features in Swift. It also lets you implement FSMs with only few code, which is great.