Swift safely unwrapping optinal strings and ints
I can think of three alternatives.
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
}guard/let
. If these are in a function likesetupViews()
, 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
}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
- Using new shorthand:
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 KeyPath
s 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 Parameterable
s. 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
Search Multiple Words in One String in Swift
Updating a @Published Variable Based on Changes in an Observed Variable
Closure Use of Non-Escaping Parameter - Swift 3 Issue
Create Spotlight-Like Window in Swift 4
Why Do I Get "Static Member '...' Cannot Be Used on Instance of Type '...'" Error
How to Add Custom Init for String Extension
Swift Codable - Parse JSON Array Which Can Contain Different Data Type
Uiswipegesturerecognizer Doesn't Recognize Swipe Gesture Initiated Outside the View
How to Call Presentviewcontroller from Inside Class
Swift and Objectmapper: Nsdate with Min Value
Swift Objc_Getassociatedobject Always Nil
Can a Subclass Override a Function and Make More Restrictive Return
How to Get a Double Value Up to 2 Decimal Places
How to Get the Kvc-String from Swift 4 Keypath