Swift unwrapping for multiple optionals
If you're attempting to print the non-nil
values as a comma-separated list, then I think @MartinR's suggestion of using flatMap()
is the best:
let one: String?
let two: String?
let three: String?
one = "one"
two = nil
three = "three"
let nonNils = [one, two, three].flatMap { $0 }
if !nonNils.isEmpty {
print(nonNils.joinWithSeparator(","))
}
Output:
one,three
Unwrap an Optional Within a Multiple Parameter Function
You can make use of the nil coalescing operator for the optional middleName
property. If the value is nil
, a default ""
will be printed, whereas if the value holds a non-nil string, it will be unwrapped.
return ("\(name.firstName) \(name.middleName ?? "") \(name.lastName)")
Note however that you needn't let the return type of returnFullName(..)
function to be optional, as that value will never be nil.
func returnFullName (name: (firstName: String, middleName: String?,
lastName: String)) -> String {
return ("\(name.firstName) \(name.middleName ?? "") \(name.lastName)")
}
print(returnFullName(("Matthew", middleName: "Matt", lastName: "Stevenson")))
// Matthew Matt Stevenson
print(returnFullName(("Matthew", middleName: nil, lastName: "Stevenson")))
// Matthew Stevenson <-- extra unwanted space
Also, I don't see any apparent reason to make use of a tuple here in the function signature, rather than separate parameters for the different name components. Hence, an alternative is as follows:
func returnFullName (firstName: String, middleName: String?,
lastName: String) -> String {
return "\(firstName) \(middleName ?? "") \(lastName)"
}
print(returnFullName("Matthew", middleName: "Matt", lastName: "Stevenson"))
// Matthew Matt Stevenson
print(returnFullName("Matthew", middleName: nil, lastName: "Stevenson"))
// Matthew Stevenson <-- extra unwanted space
Finally, to avoid the extra space for nil
-valued middleName
, I would suggest leaving the single-line return and instead making use of optional binding with an if let - else
clause:
func returnFullName(firstName: String, middleName: String?, lastName: String) -> String {
if let middleName = middleName {
return ("\(firstName) \(middleName) \(lastName)")
}
else {
return ("\(firstName) \(lastName)")
}
}
print(returnFullName("Matthew", middleName: "Matt", lastName: "Stevenson"))
// Matthew Matt Stevenson
print(returnFullName("Matthew", middleName: nil, lastName: "Stevenson"))
// Matthew Stevenson
Now, the question concerned unwrapping an optional variable for a given example, which is well-covered above by now. I thought I'd mention, however, that you can use a closure and Swifts in-built functional methods to condense your solution, as follows:
// instead of a function: make use of a closure
let returnFullName : (String, String?, String) -> String = {
[$0, $1, $2].flatMap{ $0 }.joinWithSeparator(" ") }
print(returnFullName("Matthew", "Matt", "Stevenson"))
// Matthew Matt Stevenson
print(returnFullName("Matthew", nil, "Stevenson"))
// Matthew Stevenson
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"
}
Unwrapping an Optional value in swift and realm
As from comments if appears that your view is not yet loaded and some of your views are still nil
. Your app crashes because in line limitLabel.text = limit[0].limitSum
the limitLabel
is nil
. It would crash regardless of Realm even by calling limitLabel.text = "Hello world!"
You can always guard data that you need to avoid changes in your code. Simply add
guard let limitLabel = limitLabel else { return nil }
guard let ForThePeriod = ForThePeriod else { return nil }
and so on.
I tried to clean up your code a bit. It is hard to understand what exactly are you trying to achieve but something like the following may seem a bit more appropriate:
func leftLabels() {
// Elements needed for method to execute.
guard let limitLabel = limitLabel else { return }
guard let forThePeriodLabel = forThePeriodLabel else { return }
guard let availableForSpendingLabel = availableForSpendingLabel else { return }
// Items that will be reused throughout the method later on
let limits: [Limit]
let firstLimit: Limit
let dates: (start: Date?, end: Date?)
let filterLimit: Int
limits = self.realm.objects(Limit.self)
guard limits.isEmpty == false else { return }
firstLimit = limits[0]
// limitLabel
limitLabel.text = firstLimit.limitSum
// Date components
dates = {
let calendar = Calendar.current
let formatter = DateFormatter()
formatter.dateFormat = "yyyy/MM/dd HH:mm"
let firstDay = firstLimit.limitDate as Date
let lastDay = firstLimit.limitLastDate as Date
let firstComponent = calendar.dateComponents([.year, .month, .day], from: firstDay)
let lastComponent = calendar.dateComponents([.year, .month, .day], from: lastDay)
let startDate = formatter.date(from: "\(firstComponent.year!)/\(firstComponent.month!)/\(firstComponent.day!) 00:00")
let endDate = formatter.date(from: "\(lastComponent.year!)/\(lastComponent.month!)/\(lastComponent.day!) 23:59")
return (startDate, endDate)
}()
// forThePeriodLabel
filterLimit = realm.objects(SpendingDB.self).filter("self.date >= %@ && self.date <= %@", startDate ?? "", endDate ?? "").sum(ofProperty: "cost")
forThePeriodLabel.text = String(filterLimit)
// availableForSpendingLabel
availableForSpendingLabel.text = {
guard let a = Int(firstLimit.limitSum) else { return "" }
let b = filterLimit
let c = a - b
return String(c)
}()
}
Note some practices which help you better to structure and solve your code.
- Guard dangerous data at first
- Create a list of reusable items for your method (there should be as fewer as possible, in most cases none). Note how these can be later assigned to. And if you try using it before assigning to it, you will be warned by your compiler.
- Wrap as much code into closed sections such as
availableForSpendingLabel.text = { ... code here ... }()
- Use tuples such as
let dates: (start: Date?, end: Date?)
- Don't be afraid of using long names such as
availableForSpendingLabel
I would even further try and break this down into multiple methods. But I am not sure what this method does and assume that you have posted only part of it...
========== EDIT: Adding alternate approach ==========
From comments this is a financial application so probably at least dealing with Decimal
numbers would make sense. Also introducing approach with adding a new structure which resolves data internally. A formatter is also used to format the number. And some other improvements:
struct Limit {
let amount: Decimal
let startDate: Date
let endDate: Date
}
struct Spending {
let cost: Decimal
let date: Date
}
struct LimitReport {
let limitAmount: Decimal
let spendingSum: Decimal
let balance: Decimal
init(limit: Limit) {
let limitAmount: Decimal = limit.amount
let spendingSum: Decimal = {
let calendar = Calendar.autoupdatingCurrent // Is this OK or should it be some UTC or something?
func beginningOfDate(_ date: Date) -> Date {
let components = calendar.dateComponents([.day, .month, .year], from: date)
return calendar.date(from: components)!
}
let startDate = beginningOfDate(limit.startDate)
let endDate = calendar.date(byAdding: .day, value: 1, to: startDate)
let spendings: [Spending] = realm.objects(Spending.self).filter { $0.date >= startDate && $0.date < endDate }
return spendings.reduce(0, { $0 + $1.cost })
}()
let balance = limitAmount - spendingSum
self.limitAmount = limitAmount
self.spendingSum = spendingSum
self.balance = balance
}
}
func leftLabels() {
// Elements needed for method to execute.
guard let limitLabel = limitLabel else { return }
guard let forThePeriodLabel = forThePeriodLabel else { return }
guard let availableForSpendingLabel = availableForSpendingLabel else { return }
guard let limit = self.realm.objects(Limit.self).first else { return }
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.currencySymbol = "$"
let report = LimitReport(limit: limit)
limitLabel.text = formatter.string(from: report.limitAmount)
forThePeriodLabel.text = formatter.string(from: report.spendingSum)
availableForSpendingLabel.text = formatter.string(from: report.balance)
}
flatMap on Optional type complaining about unwrapping inside closure
Reason
You can't add an optional and a number
var v1 : Int?
let sum = v1 + 2 //Will throw a compilation error
It is a compilation error because v1
could be nil. It doesn't make sense to add 2 to nil
In your example foo.flatMap { $0 + bar.flatMap(id) }
bar.flatMap(id)
returns an optional
Solution
foo.flatMap { $0 + (bar.flatMap(id) ?? 0)}
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]
}
When should I compare an optional value to nil?
It is almost always unnecessary to check if an optional is not nil
. Pretty much the only time you need to do this is if its nil
-ness is the only thing you want to know about – you don’t care what’s in the value, just that it’s not nil
.
Under most other circumstances, there is a bit of Swift shorthand that can more safely and concisely do the task inside the if
for you.
Using the value if it isn’t nil
Instead of:
let s = "1"
let i = Int(s)
if i != nil {
print(i! + 1)
}
you can use if let
:
if let i = Int(s) {
print(i + 1)
}
You can also use var
:
if var i = Int(s) {
print(++i) // prints 2
}
but note that i
will be a local copy - any changes to i
will not affect the value inside the original optional.
You can unwrap multiple optionals within a single if let
, and later ones can depend on earlier ones:
if let url = NSURL(string: urlString),
data = NSData(contentsOfURL: url),
image = UIImage(data: data)
{
let view = UIImageView(image: image)
// etc.
}
You can also add where
clauses to the unwrapped values:
if let url = NSURL(string: urlString) where url.pathExtension == "png",
let data = NSData(contentsOfURL: url), image = UIImage(data: data)
{ etc. }
Replacing nil
with a default
Instead of:
let j: Int
if i != nil {
j = i
}
else {
j = 0
}
or:
let j = i != nil ? i! : 0
you can use the nil-coalescing operator, ??
:
// j will be the unwrapped value of i,
// or 0 if i is nil
let j = i ?? 0
Equating an optional with a non-optional
Instead of:
if i != nil && i! == 2 {
print("i is two and not nil")
}
you can check if optionals are equal to non-optional values:
if i == 2 {
print("i is two and not nil")
}
This also works with comparisons:
if i < 5 { }
nil
is always equal to other nil
s, and is less than any non-nil
value.
Be careful! There can be gotchas here:
let a: Any = "hello"
let b: Any = "goodbye"
if (a as? Double) == (b as? Double) {
print("these will be equal because both nil...")
}
Calling a method (or reading a property) on an optional
Instead of:
let j: Int
if i != nil {
j = i.successor()
}
else {
// no reasonable action to take at this point
fatalError("no idea what to do now...")
}
you can use optional chaining, ?.
:
let j = i?.successor()
Note, j
will also now be optional, to account for the fatalError
scenario. Later, you can use one of the other techniques in this answer to handle j
’s optionality, but you can often defer actually unwrapping your optionals until much later, or sometimes not at all.
As the name implies, you can chain them, so you can write:
let j = s.toInt()?.successor()?.successor()
Optional chaining also works with subscripts:
let dictOfArrays: ["nine": [0,1,2,3,4,5,6,7]]
let sevenOfNine = dictOfArrays["nine"]?[7] // returns {Some 7}
and functions:
let dictOfFuncs: [String:(Int,Int)->Int] = [
"add":(+),
"subtract":(-)
]
dictOfFuncs["add"]?(1,1) // returns {Some 2}
Assigning to a property on an optional
Instead of:
if splitViewController != nil {
splitViewController!.delegate = self
}
you can assign through an optional chain:
splitViewController?.delegate = self
Only if splitViewController
is non-nil
will the assignment happen.
Using the value if it isn’t nil
, or bailing (new in Swift 2.0)
Sometimes in a function, there’s a short bit of code you want to write to check an optional, and if it’s nil
, exit the function early, otherwise keep going.
You might write this like this:
func f(s: String) {
let i = Int(s)
if i == nil { fatalError("Input must be a number") }
print(i! + 1)
}
or to avoid the force unwrap, like this:
func f(s: String) {
if let i = Int(s) {
print(i! + 1)
}
else {
fatalErrr("Input must be a number")
}
}
but it’s much nicer to keep the error-handling code at the top by the check. This can also lead to unpleasant nesting (the "pyramid of doom").
Instead you can use guard
, which is like an if not let
:
func f(s: String) {
guard let i = Int(s)
else { fatalError("Input must be a number") }
// i will be an non-optional Int
print(i+1)
}
The else
part must exit the scope of the guarded value, e.g. a return
or fatalError
, to guarantee that the guarded value will be valid for the remainder of the scope.
guard
isn’t limited to function scope. For example the following:
var a = ["0","1","foo","2"]
while !a.isEmpty {
guard let i = Int(a.removeLast())
else { continue }
print(i+1, appendNewline: false)
}
prints 321
.
Looping over non-nil items in a sequence (new in Swift 2.0)
If you have a sequence of optionals, you can use for case let _?
to iterate over all the non-optional elements:
let a = ["0","1","foo","2"]
for case let i? in a.map({ Int($0)}) {
print(i+1, appendNewline: false)
}
prints 321
. This is using the pattern-matching syntax for an optional, which is a variable name followed by ?
.
You can also use this pattern matching in switch
statements:
func add(i: Int?, _ j: Int?) -> Int? {
switch (i,j) {
case (nil,nil), (_?,nil), (nil,_?):
return nil
case let (x?,y?):
return x + y
}
}
add(1,2) // 3
add(nil, 1) // nil
Looping until a function returns nil
Much like if let
, you can also write while let
and loop until nil
:
while let line = readLine() {
print(line)
}
You can also write while var
(similar caveats to if var
apply).
where
clauses also work here (and terminate the loop, rather than skipping):
while let line = readLine()
where !line.isEmpty {
print(line)
}
Passing an optional into a function that takes a non-optional and returns a result
Instead of:
let j: Int
if i != nil {
j = abs(i!)
}
else {
// no reasonable action to take at this point
fatalError("no idea what to do now...")
}
you can use optional’s map
operator:
let j = i.map { abs($0) }
This is very similar to optional chaining, but for when you need to pass the non-optional value into the function as an argument. As with optional chaining, the result will be optional.
This is nice when you want an optional anyway. For example, reduce1
is like reduce
, but uses the first value as the seed, returning an optional in case the array is empty. You might write it like this (using the guard
keyword from earlier):
extension Array {
func reduce1(combine: (T,T)->T)->T? {
guard let head = self.first
else { return nil }
return dropFirst(self).reduce(head, combine: combine)
}
}
[1,2,3].reduce1(+) // returns 6
But instead you could map
the .first
property, and return that:
extension Array {
func reduce1(combine: (T,T)->T)->T? {
return self.first.map {
dropFirst(self).reduce($0, combine: combine)
}
}
}
Passing an optional into a function that takes an optional and returns a result, avoiding annoying double-optionals
Sometimes, you want something similar to map
, but the function you want to call itself returns an optional. For example:
// an array of arrays
let arr = [[1,2,3],[4,5,6]]
// .first returns an optional of the first element of the array
// (optional because the array could be empty, in which case it's nil)
let fst = arr.first // fst is now [Int]?, an optional array of ints
// now, if we want to find the index of the value 2, we could use map and find
let idx = fst.map { find($0, 2) }
But now idx
is of type Int??
, a double-optional. Instead, you can use flatMap
, which “flattens” the result into a single optional:
let idx = fst.flatMap { find($0, 2) }
// idx will be of type Int?
// and not Int?? unlike if `map` was used
Avoid consecutive if let declarations in Swift
I wrote a little essay on the alternatives some time ago: https://gist.github.com/pyrtsa/77978129090f6114e9fb
One approach not yet mentioned in the other answers, which I kinda like, is to add a bunch of overloaded every
functions:
func every<A, B>(a: A?, b: B?) -> (A, B)? {
switch (a, b) {
case let (.Some(a), .Some(b)): return .Some((a, b))
default: return .None
}
}
func every<A, B, C>(a: A?, b: B?, c: C?) -> (A, B, C)? {
switch (a, b, c) {
case let (.Some(a), .Some(b), .Some(c)): return .Some((a, b, c))
default: return .None
}
}
// and so on...
These can be used in if let
statements, case
expressions, as well as optional.map(...)
chains:
// 1.
var foo: Foo?
if let (name, phone) = every(parsedName, parsedPhone) {
foo = ...
}
// 2.
switch every(parsedName, parsedPhone) {
case let (name, phone): foo = ...
default: foo = nil
}
// 3.
foo = every(parsedName, parsedPhone).map{name, phone in ...}
Having to add the overloads for every
is boilerplate'y but only has to be done in a library once. Similarly, with the Applicative Functor approach (i.e. using the <^>
and <*>
operators), you'd need to create the curried functions somehow, which causes a bit of boilerplate somewhere too.
Related Topics
Difference When Declaring Swift Protocol Using Inheritance from Another Protocol or Using Where Self
Does Cocoa Connection Binding to Nstoolbaritem Prevent Deinitializing
Reading from Txt File in Swift 3
How to Present a UIcollectionview in Swiftui with UIviewcontrollerrepresentable
Dependency Injection in View Controller
iOS Swift, Aws Cognito User Pool, Unable to Refresh Access Token
Swift 2.0 Replicate Objc_Association_Retain
Control a Nstabviewcontroller from Parent View
Where Do I Register a Valuetransformer in Swift
Horizontal Scrolling in Spritekit (Vs. a Viewcontroller)
How to Get Reliable Timing for My Audio App
Tableview Image Content Selection Color
"This Class Is Not Key Value Coding-Compliant" Using Coreimage