How to check if nested optional is nil in generic class? - swift

I have simple class:
class Values<T> {
let new: T
let old: T?
init(new: T, old: T? = nil) {
self.new = new
self.old = old
}
func changed<TProp: AnyObject>(_ getter: (T) -> TProp) -> Bool {
return old == nil || !(getter(old!) === getter(new))
}
func changed<TProp: Equatable>(_ getter: (T) -> TProp) -> Bool {
return old == nil || !(getter(old!) == getter(new))
}
}
When using it as Values<ChartViewModelData?> where ChartViewModelData is a class i got problems when old is nested optional - it is both nil and not equal to nil:
So changing function like this doesn't help:
return old == nil || old! == nil || !(getter(old!) === getter(new))
nil shouldn't be passed to the getter function, and i don't know how to achieve it.
Reproduce:
class PropClass {}
class TestClass {
var someProp = PropClass()
}
let values = Values<TestClass?>(new: TestClass(), old: Optional<Optional<TestClass>>(nil))
values.changed({ $0!.someProp }) /* Fatal error: Unexpectedly found nil while unwrapping an Optional value */
values.changed({ $0?.someProp }) /* error: cannot convert value of type '(TestClass?) -> PropClass?' to expected argument type '(TestClass?) -> _' */
Second error appears because it can't use === on two Optional.

Just spent couple of hours looking for a solution to the similar problem and I think I have finally found one. To check if nested value is nil for generic type you can use this:
if (newValue as AnyObject) is NSNull {
//value is nil
}

Related

Optional extension for any types

I want to write Optional extension for any types.
My code for integer:
extension Optional where Wrapped == Int {
func ifNil<T>(default: T) -> T {
if self != nil {
return self as! T
}
return default
}
}
var tempInt: Int?
tempInt.ifNil(default: 2) // returns 2
tempInt = 5
tempInt.ifNil(default: 2) // returns 5
It works but it is Optional(Int) extension (Optional where Wrapped == Int), I want to use this extension for any types like Date, String, Double etc.
What are your suggestions?
The answer to your basic question is to just remove the where clause:
extension Optional {
// ... the rest is the same
func isNil<T>(value: T) -> T {
if self != nil {
return self as! T
}
return value
}
}
Now it applies to all Optionals.
But this code is quite broken. It crashes if T is not the same as Wrapped. So you would really mean a non-generic function that works on Wrapped:
extension Optional {
func isNil(value: Wrapped) -> Wrapped {
if self != nil {
return self! // `as!` is unnecessary
}
return value
}
}
But this is just an elaborate way of saying ?? (as matt points out)
extension Optional {
func isNil(value: Wrapped) -> Wrapped { self ?? value }
}
Except that ?? is much more powerful. It includes an autoclosure that avoids evaluating the default value unless it's actually used, and which can throw. It's also much more idiomatic Swift in most cases. You can find the source on github.
public func ?? <T>(optional: T?, defaultValue: #autoclosure () throws -> T)
rethrows -> T {
switch optional {
case .some(let value):
return value
case .none:
return try defaultValue()
}
}
But I can imagine cases where you might a method-based solution (they're weird, but maybe there are such cases). You can get that by just rewriting it as a method:
extension Optional {
public func value(or defaultValue: #autoclosure () throws -> Wrapped) rethrows -> Wrapped {
switch self {
case .some(let value):
return value
case .none:
return try defaultValue()
}
}
}
tempInt.value(or: 2)
Optional is already a generic. It already takes any type as its parameterized type. Its parameterized already has a name: Wrapped. Just say Wrapped instead of T. Your T is Wrapped.
extension Optional {
func isNil<Wrapped>(value: Wrapped) -> Wrapped {
if self != nil {
return self as! Wrapped
}
return value
}
}
If you really prefer T for some reason, use a type alias. It's only a name:
extension Optional {
typealias T = Wrapped
func isNil<T>(value: T) -> T {
if self != nil {
return self as! T
}
return value
}
}
But in any case your extension is completely unnecessary because this is what the nil-coalescing operator ?? already does.
var tempInt: Int?
tempInt ?? 2 /// returns 2
tempInt = 5
tempInt ?? 2 /// returns 5

Check Non Optional value for nil

I'm creating a function in swift to check the if non optional value return nil. My aim is just to handle that exception and avoid app crash for unexpected nil values.
I have two variables in my class:
// My Class variables
var compulsoryValue: Any!
I dont want to check optionalValue as nil. The compiler is returning Optional.none or Optional.some enums instead of nil or some value.
My problem:
I'm facing the problem that I am not able to check if this value is empty or not. As for empty the compiler returning none while for a value it is return some as defined in Swift Optional Enum.
Implicitly Unwrapped Optional is just throwing an error while it has a nil value.
How I can check that the value nil which was supposed as a non-optional value?
Update# 1:
My code:
class myClass {
var compulsoryValue: Any!
init() {
if type(of: compulsoryValue) != Optional<Any>.self {
// Here I want to check if compulsoryValue is nil so I want to throw an exception
print("this is not optional: ", compulsoryValue)
}
}
}
_ = myClass()
class myClass {
var compulsoryValue: Any!
var optionalValue: Any?
init() {
let variabless = [compulsoryValue, optionalValue]
for (_, v) in variabless.enumerated() {
if(v != nil) { //Or v == nil
// Here I want to check if v is nil so I want to throw an exception
print("this is not optional: ", v)
}
}
}
}
_ = myClass()

swift affect value to inout generic variable

I want to simplify this piece of code with a T variable but could not succeed in compiling it. Hope could you give me the way.
here is the "duplicate" code I want to rewrite :
func getIntegerValue (listValues: [Any], numValueToRead: Int, readValue: inout Int) -> Bool {
if numValueToRead < 0 || numValueToRead >= listValues.count {
return false
}
let value = listValues [numValueToRead]
if type (of: value) == type(of: readValue) {
readValue = value as! Int
return true
} else {
return false
}
}
func getStringValue (listValues: [Any], numValueToRead: Int, readValue: inout String) -> Bool {
if numValueToRead < 0 || numValueToRead >= listValues.count {
return false
}
let value = listValues [numValueToRead]
if type (of: value) == type(of: readValue) {
readValue = value as! String
return true
} else {
return false
}
}
Here is the code I wrote but do not compile :
func getValue <T> (listValues: [Any], numValueToRead: Int, readValue: inout T) -> Bool {
if numValueToRead < 0 || numValueToRead >= listValues.count {
return false
}
let value = listValues [numValueToRead]
if type (of: value) == type(of: readValue) {
switch value {
case let integerValue as Int:
readValue = integerValue
case let stringValue as String:
readValue = stringValue
default:
return false
}
return true
} else {
return false
}
}
for those affectations I got those compilation errors :
readValue = integerValue -> 'Int' is not convertible to 'T'
readValue = stringValue -> 'String' is not convertible to 'T'
Is there a way to synthetise my two functions with a unique one using generics ?
You theoretically could make it compile by adding forced casts, since you already know that value has the type T:
case let integerValue as Int:
readValue = integerValue as! T
case let stringValue as String:
readValue = stringValue as! T
But the far better solution is to use a conditional cast (as? T) and
conditional binding (if let):
func getValue<T>(listValues: [Any], numValueToRead: Int, readValue: inout T) -> Bool {
if numValueToRead < 0 || numValueToRead >= listValues.count {
return false
}
let value = listValues[numValueToRead]
if let tvalue = value as? T {
readValue = tvalue
return true
} else {
return false
}
}
which then works for arbitrary types, not only Int and String.
A “swiftier” way would be return an optional value (with nil
indicating "no value"). The code can then be simplified to
func getValue<T>(listValues: [Any], numValueToRead: Int) -> T? {
guard listValues.indices.contains(numValueToRead) else {
return nil
}
return listValues[numValueToRead] as? T
}
This should work:
func getValue <T> (listValues: [Any], numValueToRead: Int, readValue: inout T) -> Bool {
if numValueToRead < 0 || numValueToRead >= listValues.count {
return false
}
let value = listValues [numValueToRead]
if type (of: value) == type(of: readValue) {
if let genericValue = value as? T {
readValue = genericValue
return true
}
return false
} else {
return false
}
}
At first sight, the function is wrongly named. You cannot call function getValue when it returns bool... I would call it transform or modify or something other than get value, because you are NOT getting value.
I think this method suits better your needs, not tested tought it should work.
func transformValue<T>(from listValues: [Any], numValueToRead: Int, readValue: inout T?) throws -> Bool {
// Guard suits better this case...
guard numValueToRead > 0 || numValueToRead < listValues.count else { return false }
let value = listValues[numValueToRead]
if type (of: value) == type(of: readValue) {
guard let value = value as? T else {
throw NSError(
domain: "Smth",
code: 1,
userInfo: ["Description": "Failed to cast to generic type T"]
)
}
readValue = value as? T
return true
}
return false // No need to call else...
}
Explenation: Returning optional generic type T is much safer. You try to cast it, you fail and you throw error that something went wrong. In my opinion saving force casts with throwing errors is much more safer approach, you know what went wrong and so.
As #MartinR pointed out, returning a nil value instead of an inout+Bool combination gives the same results, but with less, and more readable code. This is the path Swift also took when importing most of the NSError ** methods from Objective-C (i.e. dropped the last parameter, imported them as throwable functions).
These being said, another approach would be to add an extension over Array for extracting the value:
extension Array {
subscript<T>(_ index: Int, as type: T.Type) -> T? {
guard 0..<count ~= index else { return nil }
return self[index] as? T
}
}
let arr: [Any] = [1, "two", 3, "four"]
arr[1, as: String.self] // two
arr[2, as: String.self] // nil

Compare array of tuples in swift

I've declared the following for my tuple type:
public typealias Http2HeaderTableEntry = (field: String, value: String?)
func ==(lhs: [Http2HeaderTableEntry], rhs: [Http2HeaderTableEntry]) -> Bool {
guard lhs.count == rhs.count else { return false }
for (idx, element) in lhs.enumerated() {
guard element.field == rhs[idx].field && element.value == rhs[idx].value else {
return false
}
}
return true
}
But now when I try and use it in an assertion, like XCTAssertEqual(var1, var2) I'm getting a compiler error:
Cannot invoke 'XCTAssertEqual' with an argument list of type '([Http2HeaderTableEntry], [Http2HeaderTableEntry])'
What do I need to do in order to be able to compare two arrays of tuples in Swift 4?
Since tuples can't have conformances yet, there's not really a way to implement this in any general way at this point.
Reference here

Simplest way to convert an optional String to an optional Int in Swift

It seems to me there ought to be a simple way to do an optional conversion from a String to an Int in Swift but I can't figure it out.
value is a String? and I need to return an Int?.
Basically I want to do this, but without the boilerplate:
return value != nil ? Int(value) : nil
I tried this, which seems to fit with Swift's conventions and would be nice and concise, but it doesn't recognize the syntax:
return Int?(value)
You can use the nil coalescing operator ?? to unwrap the String? and use a default value "" that you know will produce nil:
return Int(value ?? "")
Another approach: Int initializer that takes String?
From the comments:
It's very odd to me that the initializer would not accept an optional and would just return nil if any nil were passed in.
You can create your own initializer for Int that does just that:
extension Int {
init?(_ value: String?) {
guard let value = value else { return nil }
self.init(value)
}
}
and now you can just do:
var value: String?
return Int(value)
You can use the flatMap() method of Optional:
func foo(_ value: String?) -> Int? {
return value.flatMap { Int($0) }
}
If value == nil then flatMap returns nil. Otherwise it
evaluates Int($0) where $0 is the unwrapped value,
and returns the result (which can be nil if the conversion fails):
print(foo(nil) as Any) // nil
print(foo("123") as Any) // Optional(123)
print(foo("xyz") as Any) // nil
With String extension you shouldn't worry about string == nil
extension String {
func toInt() -> Int? {
return Int(self)
}
}
Usage:
var nilString: String?
print("".toInt()) // nil
print(nilString?.toInt()) // nil
print("123".toInt()) // Optional(123)