I've written this in order to easily format floating point numbers as strings with various levels of precision.
extension FloatingPoint {
func str(_ precision: Int) -> String {
return String(format: "%."+String(precision)+"f", self as! CVarArg)
}
}
It works great for non-optional variables with floating point types:
var myDouble: Double = 3.1415
var text = myDouble.str(2) // sets text = "3.14"
Is there a way of getting something like this to work for an optional Double?
var myNilDouble: Double? = nil
var text = myNilDouble.str(2) // I'd like it to set text = ""
I'd like the implementation to support nil and non-nil conversion to string.
You're not calling the method on a Double, you're calling it on an Optional<Double>; completely different type. So you need the method to exist on Optional:
extension Optional where Wrapped : FloatingPoint {
func string(usingPrecision precision: Int) -> String {
guard let double = self else { return "" }
return double.str(precision)
}
}
I don't like this interface, though; I don't think it's Optional's job to decide to emit an empty string. Instead I would suggest an initializer on String itself that takes an Optional floating point:
import Foundation
extension String {
init<N : FloatingPoint>(_ value: N?, precision: Int) {
guard let value = value else { self.init(); return }
self.init(format: "%.\(precision)f", value as! CVarArg)
}
}
Now you write
let value: Double? = nil
let text = String(value, precision: 2) // ""
Another option is a free function, again taking an Optional. (You can also of course make it the caller's choice as to what nil resolves to, as Sulthan said.)
The simplest solution would be to use optional chaining and nil coalescing:
var text = myNilDouble?.str(2) ?? ""
Although you might end up with many repetitions of this pattern, the advantage is that you have better control over the nil scenario, maybe in some situations you'll want to use "(null)" as default value.
Related
This is something that has vexed a number of developers including myself. Let say we have a protocol that defines a subscript which we apply to a simple class.
protocol Cache {
subscript<Value>(_: String) -> Value? { get set }
}
class InMemoryCache: Cache {
private var cache: [String: Any] = [:]
subscript<Value>(key: String) -> Value? {
get {
cache[key] as? Value
}
set {
if let value = newValue {
cache[key] = value
} else {
cache.remove(key)
}
}
}
}
This works fine as long as we know the types:
cache["abc"] = 5
let x: Int? = cache["abc"]
but the developers want to do this:
cache["abc"] = nil
Which won't compile because the compiler cannot determine the Value generic type. This works however
cache["abc"] = nil as String?
I've tried a number of things but they all have drawbacks. Things like adding a second subscript with the Any type. Nothing seems to work well even though it would seem like a simple problem.
Has anyone found a solution that handles cache["abc"] = nil?
You can do this by changing your protocol requirements somewhat.
Have the protocol require a subscript that does not use generics, and returns an Any?.
protocol Cache {
subscript(key: String) -> Any? { get set }
}
This subscript will let you do the following:
cache["abc"] = 5
cache["abc"] = nil
let value = cache["abc"] // value is an `Any?`
but it will not let you do this:
let number: Int? = cache["abc"] // error
So, let's fix that by adding another subscript to Cache. This subscript is equivalent to your original subscript requirement, except it doesn't need a setter and will call the other subscript (the one required by the protocol):
extension Cache {
subscript<Value>(key: String) -> Value? {
self[key] as? Value
}
}
(If you're worried that this subscript calls itself, don't be. self[key] here actually calls the other subscript, not this one. You can confirm this in Xcode by command-clicking on the [ or the ] in self[key] to jump to the definition of the other subscript.)
Then, implement the required subscript in your class:
class InMemoryCache: Cache {
private var cache: [String: Any] = [:]
subscript(key: String) -> Any? {
get { cache[key] }
set { cache[key] = newValue }
}
}
This will allow all of the following to compile:
let cache = InMemoryCache()
cache["abc"] = 5
let x: Int? = cache["abc"]
cache["abc"] = nil
There is a workaround to have your desire output.
Because this is a dictionary so you get assign nil directly in your InMemoryCache
class InMemoryCache: Cache {
private var cache: [String: Any] = [:]
subscript<Value>(key: String) -> Value? {
get {
cache[key] as? Value
}
set {
if let value = newValue {
cache[key] = value
} else {
cache[key] = nil // make nil directly here
}
}
}
}
In here because of Value is a generic type. So you can not assign nil directly. It must have a specific type.
Instead you can do like this
let nilValue : Int? = nil // any type nil you want
cache["abc"] = nilValue
or directly cast it to nil of any tupe before assign to dictionary
cache["abc"] = (nil as String?)
It will refresh anything value is store in the key.
Example
// value
let nilValue : Int? = nil
var number : Int? = nil
var string : String? = nil
cache["abc"] = 5
number = cache["abc"] // Optional.some(5)
cache["abc"] = "abc"
number = cache["abc"] // nil
string = cache["abc"] // Optional.some("abc")
cache["abc"] = nilValue
number = cache["abc"] // nil
string = cache["abc"] // nil
The reason why you are having a hard time with this is because
cache["abc"] = nil
cannot be compiled. There is not enough information to infer the generic type of the subscript - or of the optional value. The compiler sees something like
cache<?>["abc"] = Optional<?>.none
How is it supposed to figure out what to put in place of the question marks?
There's another ambiguity. Your cache can contain any type, even Optional. When you are assigning nil to the subscript, how does anybody know if you want to remove the element or store an instance of Optional<Something>.none at the subscript?
When I find myself fighting the language in this way, I usually try to take a step back and ask if I am perhaps doing something fundamentally bad. I think, in this case, the answer is yes. You are trying to pretend something is more strictly typed than it really is.
I think your getter/setter should explicitly take a value that is of type Any. It works better and it has the advantage that it explicitly documents for the user that a Cache conforming type can store anything in it.
For this reason, I would say TylerP's solution is the best. However, I would not create a subscript in the extension, I would define a function
extension Cache
{
func value<Value>(at key: String) -> Value?
{
self[key] as? Value
}
}
The reason for this is that the compiler can get confused when you have multiple subscripts with similar signatures. With the extension above, I can conform Dictionary<String, Any> to the protocol and not need a new class.
extension Dictionary: Cache where Key == String, Value == Any {}
var dict: [String : Any] = [:]
dict["abc"] = 5
let y: Int? = dict.value(at: "abc")
dict["abc"] = nil
Obviously, the above won't be useful to you if you need reference semantics for your cache.
TylerP's solution was pretty much bang on the money. For completeness though, here's what the code now looks like:
protocol Cache {
/// Handles when we need a value of a specific type.
subscript<Value>(_: String) -> Value? { get }
/// Handles getting and setting any value.
/// The getter is rarely used because the generic getter above
/// is used. Setting a value compiles because we don't care what
/// type is it. Setting a `nil` also compiles for the same reason.
subscript(_: String) -> Any? { get set }
}
class InMemoryCache: Cache {
private var cache: [String: Any] = [:]
subscript(key: String) -> Any? {
get { cache[key] }
set {
if let value = newValue {
cache[key] = value
} else {
remove(key)
}
}
}
subscript<Value>(key: String) -> Value? {
cache[key] as? Value
}
}
Suppose I wanted to be able to check if a String or Any is a valid Date:
extension Dictionary {
mutating func prepareCustomEncoding() {
for(key, value) in self {
if let dte = value as? Date {
self[key] = MyTimestamp(date: dte) as? Value
}
}
}
}
This will always fail, since you can't typecast either of those to Date. Is there a way to extend the functionality of as?
NOTE: I know that I could do the check for this specific example by writing constructors for Date using String and Any. I'm interested in the functionality of the as keyword.
I am new to Swift, but am curious about the behaviour of optional unwrapping.
Is there any explanation why guarding against != nil is not unwrapping the optional?
As a simple example:
func hasC(_ s: String?) -> Bool {
guard s != nil else {
return false
}
return s!.contains("c")
}
I need to put the exclamation sign and explicitly unwrap the value, even though it seems to be clear that at the last line s is not an optional string anymore.
I would love to hear the reasoning behind such a behaviour and how to unwrap the value properly in such a case?
creating extra variable seems ugly:
guard let s = s else { ... }
and guarding against not boolean condition is not working:
guard s else { ... }
Repl: https://repl.it/#valerii/optional-unwrap
The type of s is still Optional, so whether you did a nil check or not is irrelevant. The nil check is done in runtime, while the type system is doing a compile-time check. The only way to ensure s can never be nil is via optional binding if let or guard let.
The simple answer is that an equating operator, e.g. ==, != or any comparing operators have nothing to do with unwrapping an optional value from the point of view of the compiler.
A guard statement has 2 general use cases. One is to guard against a boolean value, which is produced by an equating or comparing operator, like in the following example:
guard ["foo", "bar"].contains("foo") == true else { return }
guard 10 > 5 else { return }
While another role of the guard statement is to unwrap optionals, which is written in a guard let or guard var syntax. For example:
guard let unwrappedValue = anOptionalValue else { return }
That specific syntax, lets the compiler know that you're trying to unwrap the value, and you get an unwrapped value as a result if the unwrapping is successful.
Sure, while creating another variable/constant may seem "ugly", that is the way to go in Swift.
You are not declaring a new variable using guard, function parameter is optional so thats why its allowing you to unwrap even after guard statement
func hasC(_ s: String?) -> Bool {
if let containsValueInString = s , containsValueInString.contains("c"){
return true
}
return false
}
or Simpler you can use Optional chaining with nil coalescing operator
func hasC(_ s: String?) -> Bool {
return s?.contains("c") ?? false
}
guard doesn't unwrap anything just by itself. It's like a special kind of if. guard let unwraps, but it's just a syntactic sugar.
you are using the wrong format
func hasC(_ s: String?) -> Bool {
guard let input = s else {return false}
return input.contains("c")
}
and you can do like that:
func hasC(_ s: String?) -> Bool {
if let input = s {
return input.contains("c")
}
return false
}
or you can make your output nullable like this:
func hasC(_ s: String?) -> Bool? {
return s?.contains("c")
}
or
func hasC(_ s: String?) -> Bool {
return s?.contains("c") ?? false
}
it is not ugly anymore. with lower lines.
best regards
In the interest of efficient coding, I was wondering if there is a way to write an extension on Optional for all wrapper types With a declared exception.
In this particular instance, that would be String.
My desired use-case would be as follows:
extension Optional where Wrapped != String {
var asString: String? { return self as? String }
}
I'm getting a whole bunch of compilation errors here, where of course this is not possible.
I want to be able to do this so that I don't see the asString property on String? objects, as there is some redundancy there, of course.
Note: this should be able to be applied to any valid type that Wrapped can take.
extension Optional {
var asString: String? {
guard let unwrapped = self else {
return nil
}
return "\(unwrapped)"
}
}
This solution is only allowed by the language
You can't exclude some classes or protocols in where block.
Example,
let some: Range? = 0..<1
some.asString
"0..<1"
I'm wondering if I can do something like this:
override func someCocoaFunc(someParameter:AnyObject?) {
if let parameter = someParameter as! Tuple {
let parameterType = parameter.1
if parameterType == "Heads up, this is an Int" {
print(parameter.0 + 1) //prints 2
}
else {
//fallback
}
}
}
let myTuple = (1,"Heads up, this is an Int")
someCocoaFunc(myTuple)
Obviously this doesn't work because Tuple is not a class, or at least not one I can cast to anyway. Is there a way to get this to work, if so how? If not, what is the best way to determine the type of an AnyObject? I tried:
if parameter is Bool {
//some code
}
else if parameter is Int {
//some code
}
But it doesn't seem to work, I think because Bool is just a typealias'd Int or something? So to summarize, can I use Tuples here, and if not, what should I do? To add some context, I'm writing one class that is used in serval different targets, so I can't be sure what the value will be, just that it is either an Int or Bool.
You cannot use tuples here because they are compound types and therefore no classes.
The problem with the if is that for compatibility Bool and Int get converted to NSNumber that has a boolValue and integerValue property which gets called if you cast them. So 0 and 1 is equivalent to true and false and therefore ambiguous.
As solution I would suggest to make a class which holds both values as Optionals:
class Holder {
let boolValue: Bool?
let intValue: Int?
}