Swift Safely Unwrapping Optinal Strings and Ints

Swift safely unwrapping optinal strings and ints

I can think of three alternatives.

  1. if/let. Very similar to your current option, but you don't have to implicitly unwrap.

    if let time = time {
    timeLabel.text = "\(time)"
    }

    if let title = title {
    titleLabel.text = title
    }

    You can even unwrap them on the same line. The downside to this is that if one of them is nil, then neither label will be set.

    if let time = time, let title = title {
    timeLabel.text = "\(time)"
    titleLabel.text = title
    }
  2. guard/let. If these are in a function like setupViews(), then you can one-line your unwrapping like so:

    func setupViews() {
    guard let time = time, let title = title else { return }
    timeLabel.text = "\(time)"
    titleLabel.text = title
    }
  3. You can use default values and the ?? operator to unwrap quickly.

    timeLabel.text = "\(time ?? 0)"
    titleLabel.text = title ?? ""

How do you unwrap Swift optionals?

There are many similarities and just a handful of differences.

(Regular) Optionals

  • Declaration: var opt: Type?

  • Unsafely unwrapping: let x = opt!.property // error if opt is nil

  • Safely testing existence : if opt != nil { ... someFunc(opt!) ... } // no error

  • Safely unwrapping via binding: if let x = opt { ... someFunc(x) ... } // no error

    • Using new shorthand: if let opt { ... someFunc(opt) ... } // no error
  • Safely chaining: var x = opt?.property // x is also Optional, by extension

  • Safely coalescing nil values: var x = opt ?? nonOpt

Implicitly Unwrapped Optionals

  • Declaration: var opt: Type!

  • Unsafely unwrapping (implicit): let x = opt.property // error if opt is nil

    • Unsafely unwrapping via assignment:

      let nonOpt: Type = opt // error if opt is nil

    • Unsafely unwrapping via parameter passing:

      func someFunc(nonOpt: Type) ... someFunc(opt) // error if opt is nil

  • Safely testing existence: if opt != nil { ... someFunc(opt) ... } // no error

  • Safely chaining: var x = opt?.property // x is also Optional, by extension

  • Safely coalescing nil values: var x = opt ?? nonOpt

Safely unwrapping optional values and add it to Alamofire parameters

There is no "one line" solution, but you can use KeyPaths to reduce the series of if let ... statements down to a loop.

Start by creating a struct for your filter rather than using a tuple.

To facilitate this, we define a protocol for Parameterable - This protocol requires a dictionary that maps parameter names (String) to the property (KeyPath) that holds that parameter name as well as a function to return the parameters dictionary.

protocol Parameterable {
var paramNames: [String:KeyPath<Self,String?>] {get}
func parameters() -> [String:Any]
}

Use an extension to create a default implementation of the parameters() function, as the code will be the same for all Parameterables. It iterates over the dictionary entries and uses the associated KeyPath to access the relevant property and put it in the output dictionary. If a given property is nil then it simply isn't added to the output dictionary, because that is how dictionaries work. No need to explicitly check.

(If you import Alamofire then you can use the typedef Parameters where I have used [String:Any])

extension Parameterable {
func parameters() -> [String:Any] {
var parameters = [String:Any]()

for (paramName,keypath) in self.paramNames {
parameters[paramName]=self[keyPath:keypath]
}
return parameters
}
}

Use this protocol to create a DoctorsFilter implementation:

struct DoctorsFilter: Parameterable {
var mainCategoryId: String?
var page: String?
var specialtyId: String?
var cityID: String?
var regionId: String?
var name: String?
var companyId: String?
var orderBy: String?

let paramNames:[String:KeyPath<Self,String?>] = [
"main_category_id":\.mainCategoryId,
"page":\.page,
"specialty_id":\.specialtyId,
"city_id":\.cityID,
"region_id":\.regionId,
"name":\.name,
"company_id":\.companyId,
"order_by":\.orderBy]
}
private var parameters: Parameters? {
switch self {
case .searchForDoctors(let doctorsFilter):
return doctorsFilter.parameters()
case .someOtherThing(let someOtherThing):
return someOtherThing.parameters()
default:
return nil
}
}
}

The other approach is to simply split your creation of the parameters dictionary into multiple lines; If you assign nil against a dictionary key then there is no key/value pair stored in the dictionary for that key. In this case I have left your tuple approach in place, but you could use the struct (and I strongly suggest you do so)

private var parameters: Parameters? {
switch self {
case .searchForDoctors(let doctorsFilter):
var params: Parameters()
params["main_category_id"] = doctorsFilter.0
params["page"] = doctorsFilter.1
params["specialty_id"] = doctorsFilter.2
params["city_id"] = doctorsFilter.3
params["region_id"] = doctorsFilter.4
params["name"] = doctorsFilter.5
params["company_id"] = doctorsFilter.6
params["order_by"] = doctorsFilter.7
return params
default:
return nil
}
}

If we want to handle mixed properties, rather than just optional strings, we need to modify the code slightly. We need to use PartialKeyPath. This makes the code a little more complex since the subscript operator for a PartialKeyPath returns a double optional. This needs to be handled.

protocol Parameterable {
var paramNames: [String:PartialKeyPath<Self>] {get}
func parameters() -> [String:Any]
}

extension Parameterable {
func parameters() -> [String:Any] {
var parameters = [String:Any]()
for (paramName,keypath) in self.paramNames {
let value = self[keyPath:keypath] as? Any?
if let value = value {
parameters[paramName] = value
}
}
return parameters
}
}

struct DoctorsFilter:Parameterable {
var mainCategoryId: String?
var page: String?
var specialtyId: String?
var cityID: Int
var regionId: String?
var name: String?
var companyId: String?
var orderBy: String?
let paramNames:[String:PartialKeyPath<Self>] =
["main_category_id":\Self.mainCategoryId,
"page":\Self.page,
"specialty_id":\Self.specialtyId,
"city_id":\Self.cityID,
"region_id":\Self.regionId,
"name":\Self.name,
"company_id":\Self.companyId,
"order_by":\Self.orderBy]
}

How to unwrap an optional value from Any type?

For Xcode 7 and Swift 2:

func unwrap(any:Any) -> Any {

let mi = Mirror(reflecting: any)
if mi.displayStyle != .Optional {
return any
}

if mi.children.count == 0 { return NSNull() }
let (_, some) = mi.children.first!
return some

}

let int:Int? = 1
let str:String? = "foo"
let null:Any? = nil
let values:[Any] = [unwrap(int),2,unwrap(str),"bar", unwrap(null)]

This will give you [1, 2, "foo", "bar", {NSObject}]

Change NSNull() to nil and the return value of unwrap func to Any? will always unwrap any type.

Swift 3: What's the safest way to unwrap optional values coming from an array?

First off:

Any time you repeat the same block of code multiple times and only increase a value from 0 to some max, it is a code smell. You should think about a different way to handle it.

You should use an array to do this processing.

How about a set of enums for indexes:

enum companyIndexes: Int {
case apple
case google
case twitter
case tesla
//etc...
}

Now you can run through your array with a loop and install your values more cleanly:

var stockPrices = [String?]()
Alamofire.request(stockUrl).responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let json = JSON(responseData.result.value!)
let pricesArray = json["query"]["results"]["quote"]
for aPriceEntry in pricesArray {
let priceString = aPriceEntry["ask"].string
stockPrices.append(priceString)
}
}
}

And to fetch a price from the array:

let applePrice = stockPrices[companyIndexes.apple.rawValue]

That will result in an optional.

You could use the nil coalescing operator (??) to replace a nil value with a string like "No price available.":

let applePrice = stockPrices[companyIndexes.apple.rawValue] ?? "No price available"

or as shown in the other answer:

if let applePrice = stockPrices[companyIndexes.apple.rawValue] {
//we got a valid price
} else
//We don't have a price for that entry
}

Unwrap/coalesce a multi-level optional

This code does what you ask for. The drawback is that you need to implement the Unwrappable protocol in every type you want to put inside optionals. Maybe Sourcery can help with that.

protocol Unwrappable {
associatedtype T

func unwrap(_ default: T) -> T
}

extension Optional {}

extension Optional: Unwrappable where Wrapped: Unwrappable {
typealias T = Wrapped.T

func unwrap(_ defaultValue: T) -> T {
if let value = self {
return value.unwrap(defaultValue)
}
return defaultValue
}
}

extension Int: Unwrappable {
typealias T = Int

func unwrap(_ default: Int) -> Int {
return self
}
}

let nestedOptionalValue: Int??? = 6
let nestedOptionalNil: Int??? = nil
let optionalValue: Int? = 6
let optionalNil: Int? = nil
print(nestedOptionalValue.unwrap(0)) // prints 6
print(nestedOptionalNil.unwrap(0)) // prints 0
print(optionalValue.unwrap(0)) // prints 6
print(optionalNil.unwrap(0)) // prints 0

The trick is that the Unwrappable protocol marks types that can be eventually unwrapped (as in after 0, 1 or more unwraps) to a certain type.

The difficulty in this problem comes from the fact that in an extension to Optional, you can get the Wrapped type, but if Wrapped is optional again, you can't access Wrapped.Wrapped (in other words, Swift doesn't support Higher Kinded Types).

Another approach, would be to try to add an extension to Optional where Wrapped == Optional. But again you'd need Higher Kinded Types support to access the Wrapped.Wrapped type. If we try to extend Optional where Wrapped == Optional<T>, we also fail, because we cant extend Optional generically over T.

How to unwrap double optionals?

Given a double optional such as this doubly wrapped String:

let a: String?? = "hello"
print(a as Any) // "Optional(Optional("hello"))\n"

@Leo, showed that you could use optional binding twice:

if let temp = a, let value = temp {
print(value) // "hello\n"
}

or force unwrap twice:

print(value!!)  // don't do this - you're just asking for a crash

Here are 5 more methods you can use to safely unwrap a double optional:

Method 1:

You can also use pattern matching:

if case let value?? = a {
print(value) // "hello\n"
}

As @netigger noted in their answer, this can also be written as:

if case .some(.some(let value)) = a {
print(value) // "hello\n"
}

which while less concise might be a bit easier to read.


Method 2:

Alternatively, you can use the nil coalescing operator ?? twice:

print((a ?? "") ?? "")  // "hello\n"

Note: Unlike the other methods presented here, this will always produce a value. "" (empty String) is used if either of the optionals is nil.


Method 3:

Or you can use the nil coalescing operator ?? with optional binding:

if let value = a ?? nil {
print(value) // "hello\n"
}

How does this work?

With a doubly wrapped optional, the value held by the variable could be one of 3 things: Optional(Optional("some string")), Optional(nil) if the inner optional is nil, or nil if the outer optional is nil. So a ?? nil unwraps the outer optional. If the outer optional is nil, then ?? replaces it with the default value of nil. If a is Optional(nil), then ?? will unwrap the outer optional leaving nil. At this point you will have a String? that is nil if either the inner or outer optional is nil. If there is a String inside, you get Optional("some string").

Finally, the optional binding (if let) unwraps Optional("some string") to get "some string" or the optional binding fails if either of the optionals is nil and skips the block.


Method 4:

Also, you can use flatMap with optional binding:

if let value = a.flatMap({ $0 }) {
print(value) // "hello\n"
}

Method 5:

Conditionally cast the value to the type. Surprisingly, this will remove all levels of optionals:

let a: String?? = "hello"
let b: String??????? = "bye"

if let value = a as? String {
print(value) // "hello\n"
}

print(b as Any) // "Optional(Optional(Optional(Optional(Optional(Optional(Optional("bye")))))))\n"

if let value = b as? String {
print(value) // "bye\n"
}

Swift double unwrapping of Optionals

The problem is that NSWorkspace().runningApplications returns an
array of AnyObject which has to be cast to an array of
NSRunningApplication:

let apps = NSWorkspace().runningApplications as! [NSRunningApplication]
let filteredApps = apps.filter {
$0.activationPolicy == NSApplicationActivationPolicy.Regular
}
for app in apps {
let name: String = app.localizedName!
}

Swift - Unwrap optional in for in loop with where clause

I don't think that's possible.

Even if you have a where clause in your loop the type of obj is still of type A and as such i still remains optional.

To see why this is so think about the fact that you can change the value of i on object obj inside the loop, so the compiler is not sure that the value of i is valid until you unwrapp it.

You can try something like this

for obj in arr where obj.i != nil {
guard let i = obj.i else { continue }

print( i )
}

but if you start using guard you also skip the where clause

for obj in arr {
guard let i = obj.i else { continue }

print( i )
}


Related Topics



Leave a reply



Submit