func foo<T>(bar: T) {
if bar == nil {
// do something
} else {
// do something else
}
}
With this code, the compiler gives me following warning: Comparing non-optional value of type 'Value' to 'nil' always returns false.
The method should handle Optional and Non-Optional Types and I don't really want to write another overloaded method to distinguish between Optional and non optional types.
I tried several things like conditionally casting to an Optional, but without success.
Is there any possibility I can solve this problem? Any help or comment is appreciated.
I think (?) what you mean is that the method handles both nil and non-nilvalues - in other words, just regular optionals:
func foo<T>(bar: T?) {
guard let barValue = bar else {
// 'bar' is nil.
return
}
// barValue is available with type 'T'.
}
foo(bar: "hello, world") // works. T == String.
Does that help you? Or do you mean something else?
Thanks Karl, this seems to work and it's exactly what I wanted.
Seems like the compiler is smart enough in this case and T is never inferred to Optional<...>. I didn't know that.
Tho this will only work for generic methods and not for methods in generic classes where the compiler has to infer T earlier.
In the case, that T is Optional<String>, guard let barValue = bar will never fail because bar has the value .some(.none) when its nil.
Can you give an example of the issue you're seeing with classes? I don't see why the following wouldn't work:
class MyClass<T> {
var someData: T?
init(someData: T?) {
self.someData = someData
}
}
let test = MyClass(someData: "hello") // test is MyClass<String>
The key thing is that you're not parameterised over the optional type itself, but rather the type of data within the optional. To illustrate - imagine instead of being an optional, someData was an Array<T> (or [T], to use the shorthand form), and you used someData.isEmpty as a way of signalling nil. Going with something like OptionalValue would be analogous to that type being generic over the collection itself, not just the data within it.
I don't know if it helps to look at things that way, but it makes sense to me, so I figured I'd share it.
This is a simple example which is not working as I would like it to work (please ignore that the code is pointless):
class Foo<T> {
let value: T
init(value: T) {
self.value = value
}
func foo(bar: T?) {
if let barValue = bar {
print("bar is not nil \(barValue)")
} else {
print("bar is nil")
}
}
}
let value1: String = "foo"
let foo1 = Foo(value: value1)
foo1.foo(bar: value1) // prints: bar is not nil foo
let value2: String? = nil
let foo2 = Foo(value: value2)
foo2.foo(bar: value2) // prints: bar is not nil nil
The problem here is, that the type of the data within the optional is also an Optional.
It might also be helpful to be a bit more specific about why you are trying to distinguish between optionals and non-optionals. If a function/type is generic over a value of (unconstrained) type T, then from an internal perspective your implementation probably shouldn't care about whether T happens to be an optional type or not. Special-casing behavior when T is an optional will likely result in unexpected behavior for clients who expect the implementation to be agnostic with respect to the actual identity of the generic parameter.
My use case is pretty simple, I want a wrapper around UserDefaults which also supports Optionals.
If the value == nil -> remove the stored value for the key
If the value != nil -> store the value for the key
It seems like UserDefaults is not happy when I try to call the set(value, for: key) method with nil.
Ah, got it. Unless I'm missing something, it seems like this issue would be more general that just Optional—UserDefaults only supports a closed set of types, and trying to use any type other than the blessed types will result in a crash. E.g., this also results in a SIGABRT:
class C {}
UserDefaults.standard.set(C(), forKey: "key")
I'm not sure exactly what the design of your UserDefaults wrapper looks like, but I think you probably want to define a protocol to represent "types which can be stored into UserDefaults," something like this:
You could also leave UserDefaultsStorable as a no-requirement protocol to indicate "types that will work as-is with UserDefaults.set(_:forKey:)," but the main idea here is to make sure that your wrapper can only work with types that someone has endorsed as "able to be stored in UserDefaults", which notably does not include Optional.
class Foo<T> {
let value: T
init(value: T) {
self.value = value
}
func foo <U> (bar: U?) {
if let barValue = bar {
print("bar is not nil \(barValue)")
} else {
print("bar is nil")
}
}
}
let value1: String = "foo"
let foo1 = Foo (value: value1)
foo1.foo (bar: value1)
let value2: String? = nil
let foo2 = Foo (value: value2)
foo2.foo (bar: value2)
Has this changed? The following compiles fine for me:
UserDefaults.standard.set(nil, forKey: "key") // compiles fine
This is how it is declared in UserDefaults:
/**
-setObject:forKey: immediately stores a value (or removes the value if nil is passed as the value) for the provided key in the search list entry for the receiver's suite name in the current user and any host, then asynchronously stores the value persistently, where it is made available to other processes.
*/
open func set(_ value: Any?, forKey defaultName: String)
Interestingly there's another type specific set:
open func set(_ url: URL?, forKey defaultName: String)