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 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]
}
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
How to Get the Current Date in Short Format in Swift
Difference Between 'Let' and 'Var' in Swift
Swift - Sort Array of Objects With Multiple Criteria
#Ifdef Replacement in the Swift Language
Nsdate() or Date() Shows the Wrong Time
Overriding Methods in Swift Extensions
How to Use String.Substringwithrange? (Or, How Do Ranges Work in Swift)
How to Dispatch_Sync, Dispatch_Async, Dispatch_After, etc in Swift 3, Swift 4, and Beyond
Error-Handling in Swift-Language
Instantiated Optional Variable Shows as Nil in Xcode Debugger
How to Print the Type or Class of a Variable in Swift
How to Convert Double to Int in Swift
Nsdate() or Date() Shows the Wrong Time
Swift - How to Convert String to Double
Is Key-Value Observation (Kvo) Available in Swift
How to Get Ip Address in Swift
What Does "Fatal Error: Unexpectedly Found Nil While Unwrapping an Optional Value" Mean