Unwrapping Swift optional without variable reassignment
No. You should unwrap your optionals just redefining it with the same name as you mentioned. This way you don't need to create a second var.
func someFunction(childTitle: String?) {
if let childTitle = childTitle {
...
}
}
update: Xcode 7.1.1 • Swift 2.1
You can also use guard as follow:
func someFunction(childTitle: String?) {
guard let childTitle = childTitle else {
return
}
// childTitle it is not nil after the guard statement
print(childTitle)
}
Unwrapping optionals to a pre-defined variable in the guard condition without creating a new constant
I would just test for nil
and then force unwrap when I know it's not:
var someString: String? = "hello"
let nonOptionalString: String // note, you don't have to initialize this with some bogus value
guard someString != nil else { return }
nonOptionalString = someString!
Or if someString
was a parameter to some method or closure, you can unwrap in the guard
statement using the same variable name, simplifying life even more:
func foo(someString: String?) {
guard let someString = someString else { return }
// now I can just use local `someString`, which is not optional anymore
}
If you're desperate to unwrap and exit-if-nil
in a single statement, you could theoretically write a function to unwrap if it can or throw an error if it can't:
extension Optional {
enum OptionalError: Error {
case unwrapFailed
}
func unwrap<T>() throws -> T {
if self == nil { throw OptionalError.unwrapFailed }
return self as! T
}
}
Then you can do:
do {
firstNonOptional = try firstOptional.unwrap()
secondNonOptional = try secondOptional.unwrap()
thirdNonOptional = try thirdOptional.unwrap()
} catch {
return
}
I think that's horrible overkill, but if you're desperate to distill it down to one line per unwrap, that's one way to do it.
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.
Optional variable force unwrapping that I haven't seen a solutions for
You could use a method invocation rather than an operator for subtraction:
func someFunction (index : Int?) {
guard var newIndex = index?.advanced(by: -1) else {
throw Abort.custom(status: .badRequest,
message: "blah blah")
}
let result = myArray[newIndex]
....
}
Which does not answer the question in general, but in your specific case.
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 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)
if let not unwrapping an optional value in Swift 4.0
Short answer:
You are creating a doubly-wrapped optional because of the combination of optional chaining and the use of try?
. So, if let
is only unwrapping the first layer of doubly-wrapped optional leaving you with an optional variable.
Why is this?
try?
is used with a call that throws. If the call does throw an error, the expression returns nil
. If the call succeeds, the value is wrapped in an optional.
You are using an optional chain to call prediction
on model
. If model is nil
, then the result of the optional chain is nil
. Note, this result didn't throw so the result gets wrapped in an optional by try?
resulting in Optional(nil)
which is not the same as nil
. If the call to prediction succeeds, the result gets wrapped in an optional because of the optional chain, and wrapped in an optional again because of the try?
.
So there are three possible results:
- Model is not
nil
and call toprediction
returns a valid result of typeInceptionv3Output
without throwing. This result becomesInceptionv3Output?
because of the optional chain andInceptionv3Output??
because of thetry?
. - Model is not
nil
, but the call toprediction
throws an error. The result of thetry?
isnil
because of the throw. - Model is
nil
. The result of the optional chain isnil
, and that result gets wrapped in an optional again because oftry ?
resulting inOptional(nil)
.
So, your if let
is unwrapping a value of type Inceptionv3Output??
which means prediction
has the type Inceptionv3Output?
. The if let
succeeds if the call doesn't throw. If the model
is nil
, then prediction
will be nil
, otherwise it contains the wrapped value which is the result of the prediction
call on the model
.
Related Topics
Implicit Return in a Closure Causing an Error
Segue from a Slpagingviewswift Vc and Dismiss the Destination Vc to Return
Ondelete Causing Nsrangeexception
Compare App Versions After Update Using Decimals Like 2.5.2
In Swift, How to Have a Uiscrollview Subclass That Has an Internal and External Delegate
Dynamically Change Text of Realitykit Entity
Core Data: Failed to Load Model
Select Multiple Rows in Tableview and Tick the Selected Ones
Lazy Loading Properties in Swift
Today Extension Failed to Inherit Coremedia Permissions From
Checking If a Double Value Is an Integer - Swift
Swift Uialertcontroller with Pickerview Button Action Stay Up
How to Filter Characters from a String in Swift 4
Osx Application: How to Make Window Maximized