Propagate an optional through a function (or Init) in Swift - swift

does anyone have a (better) way to do this?
Lets say I have a optional Float
let f: Float? = 2
Now I want to cast it to a Double
let d = Double(f) //fail
This will obviously fail but is there a way to chain the optional through the function like you can with calculated variables? What I am doing now is this:
extension Float {
var double: Double { return Double(self) }
}
let d: Double? = f?.double
But I really do not like putting a cast as a calculated variable.
Another option I have considered using is this:
public func optionalize<A,B>(_ λ : #escaping (A) -> B) -> (A?) -> B? {
return { (a) in
guard let a = a else { return nil }
return λ(a)
}
}
let d: Double? = optionalize(Double.init)(f)
I realize I can guard the value of 'f' to unwrap it. However in many cases the optional value will be the parameter for a function that returns an optional. This leads to intermediate values in the guard. As seen in this example:
func foo(_ a: String?) throws -> Float {
guard
let a = a,
let intermediate = Float(a)
else { throw.something }
return intermediate
}
Here it is possible for the cast from String to Float to fail also.
At least with a calculated variable this foo function is a bit cleaner
extension String {
var float: Float? { return Float(self) }
}
func foo(_ a: String?) throws -> Float {
guard
let a = a?.float
else { throw.something }
return a
}
I do not want to rewrite optional versions of frequent inits.
Any ideas will be much appreciated. Thanks!

You can simply use Optional's map(_:) method, which will return the wrapped value with a given transform applied if it's non-nil, else it will return nil.
let f : Float? = 2
// If f is non-nil, return the result from the wrapped value passed to Double(_:),
// else return nil.
let d = f.map { Double($0) }
Which, as you point out in the comments below, can also be said as:
let d = f.map(Double.init)
This is because map(_:) expects a transformation function of type (Float) -> Double in this case, and Double's float initialiser is such a function.
If the transform also returns an optional (such as when converting an String to a Int), you can use flatMap(_:), which simply propagates a nil transform result back to the caller:
let s : String? = "3"
// If s is non-nil, return the result from the wrapped value being passed to the Int(_:)
// initialiser. If s is nil, or Int($0) returns nil, return nil.
let i = s.flatMap { Int($0) }

Related

Double Extension Swift

I am trying to create an iOS Swift extension in a playground to the Double type that implements a method titled add that receives a String and returns an optional Double (Double?).
Extend the Double type, using an extension, and add a method called add that takes a String as a parameter with no label and returns a Double?.
If the String parameter can be turned into a Double, return the String’s Double value plus the value of the Double on which add is being called. If the String cannot be turned into a Double, return nil.
For testing this code I need to use:
let value1: Double? = 3.5.add("1.2") //value should be 4.7
let value3: Double? = 1.5.add("words") //value should be nil
extension Double {
func add(_ value: String) -> Double? {
guard let someNum = Double?(self) else {
return nil
}
return (someNum + value)
}
}
https://i.stack.imgur.com/VLM2E.png
Looks like you got a little confused about what to unwrap in your guard statement and what needed to be turned from a String into a Double. In your current code, you try to turn self, which is already a Double into a Double? -- then, you try to add that Double? to a String.
Here's a fixed version:
extension Double {
func add(_ value: String) -> Double? {
guard let numValue = Double(value) else {
return nil
}
return self + numValue
}
}

Swift - unwrapping a Double value with a Guard let statement

to unwrap a Double from my default values, I seem to have to do it with two guard let statements to unwrap the value safely such as in what follows:
guard let distanceAwayPreference = self.defaults.string(forKey: "distancePreference") else {
return
}
guard let doubleDAP = Double(distanceAwayPreference) else {
return
}
– because if I do it like this:
guard let DistanceAwayPreference = self.defaults.double(forKey: "distancePreference") else {
return
}
– I get the error:
Initializer for conditional binding must have Optional type, not 'Double'
Is there a better way so i can do it once, or have less code throughout my app?
The method double(forKey:) is not returning an optional at all. According to the documentation it returns:
The double value associated with the specified key. If the key doesn‘t exist, this method returns 0.
So you can't optional bind it to a variable. This is why you've got that error:
Initializer for conditional binding must have Optional type, not 'Double'
For less and cleaner code (when using it), you can use a simple property wrapper like:
#propertyWrapper
struct UserDefault<T> {
let key: String
let defaultValue: T
init(_ key: String, defaultValue: T) {
self.key = key
self.defaultValue = defaultValue
}
var wrappedValue: T {
get { UserDefaults.standard.object(forKey: key) as? T ?? defaultValue }
set { UserDefaults.standard.set(newValue, forKey: key) }
}
}
Then you can have a custom helper struct like this:
struct UserDefaultsConfig {
#UserDefault("distancePreference", defaultValue: 0)
static var distancePreference: Double
}
And use it late like:
UserDefaultsConfig.distancePreference = 12
I think it's better to return Any from defaults instead of String. So, this way you can use optional casting.
guard let doubleDAP = self.defaults.object(forKey: "distancePreference") as? Double else {
return
}

overloading methods where only difference is optional vs. non-optional type

I was under the impression that swift can have overloaded methods that differ only in the type of object that the methods return. I would think that I could have two funcs with the same signature yet they differ in return type.
import Foundation
// ambiguous use of 'IsTextEmpty(text:)'
func IsTextEmpty(text : String?) -> Bool? {
return text?.isEmpty
}
func IsTextEmpty(text : String?) -> Bool {
guard let text = text else {
return true
}
return text.isEmpty
}
let text: String? = nil
if let empty = IsTextEmpty(text:"text") {
print("Not Empty")
}
if IsTextEmpty(text: text) {
print("Empty")
}
Here, both functions have the same input parameters but one func returns an optional Bool? and the other returns a Bool. In this case I get an error:
ambiguous use of 'IsTextEmpty(text:)'
If I change the name of one of the input parameters I no longer get the ambiguous error:
// Works
func IsTextEmpty(foo : String?) -> Bool? {
return foo?.isEmpty
}
func IsTextEmpty(text : String?) -> Bool {
guard let text = text else {
return true
}
return text.isEmpty
}
let text: String? = nil
if let empty = IsTextEmpty(foo:"text") {
print("Not Empty")
}
if IsTextEmpty(text: text) {
print("Empty")
}
Shouldn't the compiler detect that they are two distinct methods even though their return types are different, since an optional Bool? is a different type from a non-optional Bool?
The compiler needs some context to decide which method to call, e.g.
let e1 = IsTextEmpty(text: text) as Bool
let e2: Bool = IsTextEmpty(text: text)
to call the non-optional variant, or
let e3 = IsTextEmpty(text: text) as Bool?
let e4: Bool? = IsTextEmpty(text: text)
to call the optional variant. Now
if IsTextEmpty(text: text) {
print("Empty")
}
compiles without problems: the if-statement requires a boolean
expression, so there is no ambiguity here.
But apparently the compiler is not smart enough to figure out that
in the context of an optional binding the expression on the right-hand side must be some optional
and to infer the unwrapped type automatically.
You need to annotate the type of empty explicitly:
if let empty: Bool = IsTextEmpty(text: text) { ... }
or to make the return type explicit:
if let empty = (IsTextEmpty(text: text) as Bool?) { ... }

Chaining type(of:) with an optional type in swift 3 [duplicate]

does anyone have a (better) way to do this?
Lets say I have a optional Float
let f: Float? = 2
Now I want to cast it to a Double
let d = Double(f) //fail
This will obviously fail but is there a way to chain the optional through the function like you can with calculated variables? What I am doing now is this:
extension Float {
var double: Double { return Double(self) }
}
let d: Double? = f?.double
But I really do not like putting a cast as a calculated variable.
Another option I have considered using is this:
public func optionalize<A,B>(_ λ : #escaping (A) -> B) -> (A?) -> B? {
return { (a) in
guard let a = a else { return nil }
return λ(a)
}
}
let d: Double? = optionalize(Double.init)(f)
I realize I can guard the value of 'f' to unwrap it. However in many cases the optional value will be the parameter for a function that returns an optional. This leads to intermediate values in the guard. As seen in this example:
func foo(_ a: String?) throws -> Float {
guard
let a = a,
let intermediate = Float(a)
else { throw.something }
return intermediate
}
Here it is possible for the cast from String to Float to fail also.
At least with a calculated variable this foo function is a bit cleaner
extension String {
var float: Float? { return Float(self) }
}
func foo(_ a: String?) throws -> Float {
guard
let a = a?.float
else { throw.something }
return a
}
I do not want to rewrite optional versions of frequent inits.
Any ideas will be much appreciated. Thanks!
You can simply use Optional's map(_:) method, which will return the wrapped value with a given transform applied if it's non-nil, else it will return nil.
let f : Float? = 2
// If f is non-nil, return the result from the wrapped value passed to Double(_:),
// else return nil.
let d = f.map { Double($0) }
Which, as you point out in the comments below, can also be said as:
let d = f.map(Double.init)
This is because map(_:) expects a transformation function of type (Float) -> Double in this case, and Double's float initialiser is such a function.
If the transform also returns an optional (such as when converting an String to a Int), you can use flatMap(_:), which simply propagates a nil transform result back to the caller:
let s : String? = "3"
// If s is non-nil, return the result from the wrapped value being passed to the Int(_:)
// initialiser. If s is nil, or Int($0) returns nil, return nil.
let i = s.flatMap { Int($0) }

Compare value and return a bool in Swift

I am converting my code from Objective-C to Swift. I declared a function to compare values of two properties and return a Bool.
And I am confused about why this code not work in Swift.
private var currentLineRange: NSRange?
var location: UInt?
func atBeginningOfLine() -> Bool {
return self.location! == self.currentLineRange?.location ? true : false
}
Compiler gave me an error:
Could not find an overload for == that accepts the supplied arguments
Thanks.
You have two optional values and you want to check if they’re equal. There is a version of == for comparing two optionals – but they need to be of the same type.
The main problem here is that you are comparing NSRange.location, which is a Int, with location, which is a UInt. If you tried to do this even without the complication of the optionals, you’d get an error:
let ui: UInt = 1
let i: Int = 1
// error: binary operator '==' cannot be applied to operands of
// type 'Int' and ‘UInt'
i == ui
There’s two ways you can go. Either change location to be an Int, and you’ll be able to use the optional ==:
private var currentLineRange: NSRange?
var location: Int?
func atBeginningOfLine() -> Bool {
// both optionals contain Int, so you can use == on them:
return location == currentLineRange?.location
}
Or, if location really does need to be a UInt for some other reason, map one of the optionals to the type of the other to compare them:
private var currentLineRange: NSRange?
var location: UInt?
func atBeginningOfLine() -> Bool {
return location.map { Int($0) } == currentLineRange?.location
}
One thing to be careful of – nil is equal to nil. So if you don’t want this (depends on the logic you’re going for), you need to code for it explicitly:
func atBeginningOfLine() -> Bool {
if let location = location, currentLineRange = currentLineRange {
// assuming you want to stick with the UInt
return Int(location) == currentLineRange.location
}
return false // if either or both are nil
}
Swift has operator overloading, so == is a function. You have to define a function that takes your two types.
If you remove the UInt it works:
class Document {
private var currentLineRange: NSRange?
var location: Int?
func atBeginningOfLine() -> Bool {
if let currentLocation = self.location, lineRange = self.currentLineRange {
return currentLocation=lineRange?.location
} else {
return false
}
}
}
Modified to be null safe.