Unwrapping Swift Optional Without Variable Reassignment

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:

  1. Model is not nil and call to prediction returns a valid result of type Inceptionv3Output without throwing. This result becomes Inceptionv3Output? because of the optional chain and Inceptionv3Output?? because of the try?.
  2. Model is not nil, but the call to prediction throws an error. The result of the try? is nil because of the throw.
  3. Model is nil. The result of the optional chain is nil, and that result gets wrapped in an optional again because of try ? resulting in Optional(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



Leave a reply



Submit