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
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
how to unwrap swift optionals within a struct
You cannot directly set a property of an Optional
if you haven't assigned a value to the Optional
itself, since due to the optional chaining, the setter of usd
won't be called.
Instead, you need to assign a Price
to purchase.price
.
var purchase:Item = Item()
purchase.name = "lampshade"
purchase.price = Price(USD: 19.2)
Or if you want to assign an "empty" price
, then optional chaining on that works, since now price
is not nil
.
var purchase:Item = Item()
purchase.name = "lampshade"
purchase.price = Price()
purchase.price?.USD = 19.2
Also, you should try to make properties immutable immutable (let
) by default and only make properties mutable (var
) if they really need to change after initialisation. You should also only add default values to properties where it makes sense for them to have a default value. For instance, name
shouldn't have one, but rather, should be immutable with its value being set in the init
.
struct Item{
let name: String
var price: Price?
}
var purchase = Item(name: "lampshade")
purchase.price = Price(USD: 19.2)
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"
}
How to unwrap double optionals in switch statement -- Swift
This works, but I'm curious why I need to double unwrap the default
case? I was thinking once the lhs and rhs get unwrapped from the
switch inputs, the default case would work with a Bool? for lhs and
rhs.
You aren't changing lhs
and rhs
when you do lhs ?? nil
and rhs ?? nil
. You are creating new values. So when you get to the default
case, lhs
and rhs
are still Bool??
. You can use let lhs
and let rhs
to capture the unwrapped values as I did in my solution below.
Here is another way to do it. A bit cleaner with some pattern matching:
switch (lhs as? Bool, rhs as? Bool) {
case (nil, nil):
return nil
case (nil, let rhs):
return rhs
case (let lhs, nil):
return lhs
case (let lhs?, let rhs?):
return lhs || rhs
}
Explanation
Casting the Bool??
with as? Bool
leaves you with a Bool?
. The let rhs
and let lhs
in the pattern matching catch the Bool?
value so that it can be returned. In the final case
, let lhs?
and let rhs?
unwrap the Bool?
values to get Bool
values so that ||
can be performed.
Test Cases
test(nil, nil) // nil
test(nil, false) // false
test(nil, true) // true
test(nil, Optional(nil)) // nil
test(nil, Optional(false)) // false
test(nil, Optional(true)) // true
test(false, nil) // false
test(false, false) // false
test(false, true) // true
test(false, Optional(nil)) // false
test(false, Optional(false)) // false
test(false, Optional(true)) // true
test(true, nil) // true
test(true, false) // true
test(true, true) // true
test(true, Optional(nil)) // true
test(true, Optional(false)) // true
test(true, Optional(true)) // true
test(Optional(nil), nil) // nil
test(Optional(nil), false) // false
test(Optional(nil), true) // true
test(Optional(nil), Optional(nil)) // nil
test(Optional(nil), Optional(false)) // false
test(Optional(nil), Optional(true)) // true
test(Optional(false), nil) // false
test(Optional(false), false) // false
test(Optional(false), true) // true
test(Optional(false), Optional(nil)) // false
test(Optional(false), Optional(false)) // false
test(Optional(false), Optional(true)) // true
test(Optional(true), nil) // true
test(Optional(true), false) // true
test(Optional(true), true) // true
test(Optional(true), Optional(nil)) // true
test(Optional(true), Optional(false)) // true
test(Optional(true), Optional(true)) // true
Unwrapping Optional types in a Generic method in Swift 5
If the intention is to call unwrap()
only with key paths leading to an optional property then you can declare the argument type as KeyPath<A, T?>
, and the second placeholder type U
is not needed:
func unwrap<T>(_ path: KeyPath<A, T?>) throws -> T {
if let value = self[keyPath: path] {
return value
} else {
throw Error.missing("KeyPath '\(path)' is 'nil'")
}
}
The usage can be simplified to
func convert() throws -> B {
let name = try unwrap(\.name)
return B(name: name)
}
or just
func convert() throws -> B {
return try B(name: unwrap(\.name))
}
How can I safety unwrap an optional with a default value using `coalescing unwrapping` - Not workig - Swift
You can unwrap the access of the array and the conversion to Double
let pounds = Double(arrayOfStrings.first ?? "0") ?? 0.0
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)
}
How to unwrap an optional list of type Images in SwiftUI
The simplest direct answer to this is to use the same ??
operator you mentioned to provide an empty array:
struct ContentView : View {
@State var list_of_images : [Image]? = nil
var body: some View {
ForEach(0..<(list_of_images ?? []).count) {
list_of_images?[$0]
.resizable()
.aspectRatio(contentMode: .fill)
}
}
}
However, I'd be concerned in general about the idea of storing the Image
s themselves. I might look into another way of storing references to them (paths? names? UIImages?) that could be iterated over instead. In that case, you could do something like this:
struct ContentView : View {
@State var imageNames : [String]? = nil
var body: some View {
ForEach(imageNames ?? [], id: \.self) { imageName in
Image(imageName)
.resizable()
.aspectRatio(contentMode: .fill)
}
}
}
This approach doesn't work with the array of Image
s because Image
doesn't conform to Identifiable
Update, based on comments:
struct ContentView : View {
@State var images : [UIImage]? = nil
var body: some View {
ForEach(images ?? [], id: \.self) { image in
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fill)
}
}
}
Unwrapping optional in SwiftUI View
There are two ways to work with optionals in this context:
The first one, if you don't want this view to show at all if profile.height is nil:
profile.height.map({ TagBox(field: "height", value: String($0))})
The second one, if you want this view to show, but with a default value instead:
TagBox(field: "height", value: String(profile.height ?? 0))
Related Topics
Shall We Always Use [Unowned Self] Inside Closure in Swift
How to Use Swift 4 Codable in Core Data
Sending Json Array Via Alamofire
How to Set Associated Objects in Swift
Is Swift Pass by Value or Pass by Reference
Alternative to Performselector in Swift
Swift Protocol With "Where Self" Clause
How to Declare an Array of Weak References in Swift
How to Make a Random Color With Swift
== Overload for Custom Class Is Not Always Called
Calling a Swift Class Factory Method With Leading Dot Notation
Difference Between Computed Property and Property Set With Closure
Fix Warning "C-Style For Statement Is Deprecated" in Swift 3