How to Unwrap an Optional Value from Any Type

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.

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

Safely unwrapping optional values and add it to Alamofire parameters

There is no "one line" solution, but you can use KeyPaths 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 Parameterables. 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]
}

Value of optional type 'String?' must be unwrapped to a value of type 'String' to an array?

The randomElement() function returns an Optional because the collection you are fetching from might be empty. if it is, the function returns nil.

Get out of the habit of using the ! force-unwrap operator (and variants like implicitly unwrapped optionals.) I call ! the "crash-if-nil" operator. That's what it does.

You should rewrite your code using if let syntax (optional binding)

@IBAction func randomImage(_ sender: Any) {
if let path = Bundle.main.path(forResource: "imageList", ofType: "plist"),
let dict = NSDictionary(contentsOfFile: path),
let data = dict.object(forKey: "Images") as? [String],
let image = UIImage(named: data.randomElement()) {
imageView.image = image
} else {
// Unable to load image
}
}

With a compound if let like that the expression quits on each step if the result is nil. If the result is valid, it keeps going on the if statement and the new temporary variable is now a non-optional.

Once you make it through the last let in the compound if let, you have a UIImage in image that you can install into your image view.

If any of the steps fail, the else clause runs. you can do whatever you want there - install a default image, install nil into the imageView to remove it, print to the console, whatever. (Although you could simplify the code a little if you were going to install a nil image into the image view.)



Edit:

In addition to if let optional binding, you can also use guard statements.

if let optional binding says "try to unwrap this optional/optionals. If it succeeds, create a variable that's only defined inside the body of the if statement, and execute the if statement.

In contrast, guard says "try to unwrap this optional/optionals. If it succeeds, continue. If it fails, execute a block of code that exits the current scope.

Guard statements are useful when you need to check a whole series o things and want to keep going when everything is good, but bail out when if something goes wrong.

if let optional binding can lead to ever-increasing levels of indentation:

func foo() {
if let a = anOpitonal {
// Do stuff
if let b = a.someOtherOptional {
// Do more stuff
if let c = b.yetAnotherOptional {
// Still more code that only runs if all 3 unwraps work
}
}
}
}

In contrast, you could write that with guard like this:

func foo() {
guard let a = anOpitonal else { return }
// Do stuff

guard let b = a.someOtherOptional else { return >

// Do more stuff
guard let c = b.yetAnotherOptional else { return }

// Still more code that only runs if all 3 guard statements work
}

How to get the unwrapped type from an optional type in Swift?

I played with your idea a little bit, but I think there isn't a real way to do that, since you can't get the type of the associated value of an enumeration, yet. Hence Optionals are basically Enumerations, we have a problem here.

My idea would be to test for all possible value types in your model objects could be or hold. Like:

let myModelObject:Any? = someWayToGetTheData()
if let aString = myModelObject as? String {
// do everything you need to store a string
}
else if let anInteger = myModelObject as? Int {
// do everything you need to store an integer
}

// and so on ...

Since your json and your model must have a predefined number of supported conversions that is a possible way, and as far as I understand your original problem, it's basically as useful as testing for the dynamic associated value type of an Optional Enumeration, which will lead into a chain of if-else statements as well.

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)


Related Topics



Leave a reply



Submit