Swift (Beta 3) "Nsdictionary? Does Not Conform to Protocol 'Equatable'"

Swift (beta 3) NSDictionary? does not conform to protocol 'Equatable'

There is a regression in Beta 3 causing that Optional<T> cannot be compared with nil if T is not Equatable or Comparable.

It's a bug caused by the removal of the _Nil type for which the equality operators were defined. nil is now a literal. The bug has been confirmed by Chris Lattner on Apple Dev Forums

Some workarounds:

You can still use .getLogicValue()

if launchOptions.getLogicValue() && ... {

or directly

if launchOptions && ... { //calls .getLogicValue()

or you can use the "Javascript object to boolean" solution

var bool = !!launchOptions

(first ! calls the getLogicValue and negates, the second ! negates again)

or, you can define those equality operators by yourself until they fix it:

//this is just a handy struct that will accept a nil literal
struct FixNil : NilLiteralConvertible {
static func convertFromNilLiteral() -> FixNil {
return FixNil()
}
}

//define all equality combinations
func == <T>(lhs: Optional<T>, rhs: FixNil) -> Bool {
return !lhs.getLogicValue()
}

func != <T>(lhs: Optional<T>, rhs: FixNil) -> Bool {
return lhs.getLogicValue()
}

func == <T>(lhs: FixNil, rhs: Optional<T>) -> Bool {
return !rhs.getLogicValue()
}

func != <T>(lhs: FixNil, rhs: Optional<T>) -> Bool {
return rhs.getLogicValue()
}

Example:

class A {
}

var x: A? = nil

if x == nil {
println("It's nil!")
}
else {
println("It's not nil!")
}

However, this workaround might cause other subtle problems (it probably works similarily to the _Nil type in Beta 2 which was removed because it was causing problems...).

Why do integers not conform to the AnyObject protocol?

There are two kinds of anything types in Swift – Any, which can truly hold anything – a struct, enum or class, and AnyObject, which can only hold classes.

The reason why it seems like AnyObject can hold structs sometimes is that some specific types are implicitly converted to their NSEquivalent for you as needed, to make Objective-C interop less painful.

When you write let ao: AnyObject = Int(1), it isn’t really putting an Int into an AnyObject. Instead, it’s implicitly converting your Int into an NSNumber, which is a class, and then putting that in.

But only some types have this implicit conversion. Int does, but Int32 doesn’t, hence this behaviour:

// fine
let aoInt: AnyObject = Int(1) as NSNumber
// also fine: implicit conversion
let aoImplicitInt: AnyObject = Int(1)
// not fine: error: 'Int32' is not convertible to 'NSNumber'
let aoInt32: AnyObject = Int32(1) as NSNumber
// but the implicit error is less, ahem, explicit
// error: type 'Int32' does not conform to protocol 'AnyObject'
let aoImplicitInt32: AnyObject = Int32(1)

It could be argued there should be more implicit conversions to grease the wheels, but then again, these implicit conversions are the source of much confusion already and the direction in the latest beta is to have fewer of them rather than more.

How to check if two [String: Any] are identical?

For Xcode 7.3, swift 2.2
A dictionary is of type : [String:AnyObject] or simply put NSDictionary

let actual: [String: AnyObject] = ["id": 12345, "name": "Rahul Katariya"]

var expected: [String: AnyObject] = ["id": 12346, "name": "Aar Kay"]

print(NSDictionary(dictionary: actual).isEqualToDictionary(expected))//False

For Xcode 8.beta 6, Swift 3

Dictionary is defined as:

struct Dictionary<Key : Hashable, Value> : Collection, ExpressibleByDictionaryLiteral

NSDictionary has the following convenience initializer:

convenience init(dictionary otherDictionary: [AnyHashable : Any])

So you can use AnyHashable type for Key and Any type for Value

let actual: [String: Any] = ["id": 12345, "name": "Rahul Katariya"]

var expected: [String: Any] = ["id": 12346, "name": "Aar Kay"]

print(NSDictionary(dictionary: actual).isEqual(to: expected))//False

Implementing Either struct in Swift

This has been fixed in Beta 3, the last case now correctly triggers a compiler error forcing you to specify the generic type explicitely

The problem is in types - when creating Either from literals:

let b4 = Either(first: nil, second: nil)

then the compiler can't infer the type for the nil parameters. If you check the debugger, you will see that the inferred type is a type called _Nil.

(lldb) p b4
(SwiftConsole.Either<_Nil, _Nil>) $R1 = {
first = Some
second = Some
}

Now, this is a type which accepts nil but it is not an optional type itself.

let x: _Nil = nil //this is not an optional

The initialiser is making an optional of it, thus making a value Optional.Some(nil) and because it's a .Some(...) value, comparing it with nil will be false.

I don't see any easy generic workarounds but in this case it would help to specify generic types explicitly:

let b4 = Either<String, Int>(first: nil, second: nil)

I suggest you report a bug because this is just silly. Instead of inferring some special types which lead to undefined behaviour, the compiler should instead trigger an error.

The same problem would arise with type Any because it can also accept nil without being an optional.

let x: Any = nil //this is not an optional

Either<Any, Any>(first: nil, second: nil).which() //true !!!

Edit:
This will be fixed in Beta 3. nil will be made a literal, _Nil type will be removed and _Any won't accept nil any more.

Confirmed here: https://devforums.apple.com/thread/234463?tstart=0

XCTAssertEquals with two dicts in Swift

You can check equality of dictionaries as long as the values are Equatable. Modify XCTAssertEqualDictionaries to include a generic constraint:

func XCTAssertEqualDictionaries<S, T: Equatable>(first: [S:T], _ second: [S:T]) {
XCTAssert(first == second)
}

Swift strange '==' behavior with Dictionary

There is a == operator comparing two Swift dictionaries, however it
requires that the value type is Equatable:

public func ==<Key : Equatable, Value : Equatable>(lhs: [Key : Value], rhs: [Key : Value]) -> Bool

The problem is that even for equatable types T, Array<T> does not conform to the Equatable protocol. See for example
Why can't I make Array conform to Equatable? for a discussion
in the Apple developer forum.

That explains why

let a = [1: [1]]
let b = [1: [1]]
a == b // => binary operator '==' cannot be applied to two '[Int : Array<Int>]' operands

does not compile. In your first example

let a = [1: [1]]
a == [1: [1]]

the compiler is so smart to take the literal array [1] as an
NSArray literal, using

extension NSArray : ArrayLiteralConvertible {
/// Create an instance initialized with `elements`.
required public convenience init(arrayLiteral elements: AnyObject...)
}

And this compiles because all classes inheriting from
NSObject conform to Equatable.

Here is a simpler example demonstrating the same issue:

func foo<T : Equatable>(x : T) {
print(x.dynamicType)
}

let ar = [1, 2, 3]
// This does not compile:
foo(ar) // error: cannot invoke 'foo' with an argument list of type '([Int])'

foo([1, 2, 3]) // Output: __NSArrayI

As one can see, the literal array is converted to some subclass of the
NSArray class cluster when passed to a function expecting an Equatable argument.

Note also that if you only import Swift, but not Foundation (or UIKit, ...) then neither

foo([1, 2, 3])

nor your first example compiles.

Putting two generic Arrays into one Swift Dictionary with Generics

Elaborating on the answer from @RobNapier, here is a similar approach that uses enum for both, keys and values of the dictionary:

enum Key: Equatable, Hashable {
case IntKey(Int)
case StringKey(String)

var hashValue: Int {
switch self {
case .IntKey(let value) : return 0.hashValue ^ value.hashValue
case .StringKey(let value) : return 1.hashValue ^ value.hashValue
}
}

init(_ value: Int) { self = .IntKey(value) }
init(_ value: String) { self = .StringKey(value) }
}

func == (lhs: Key, rhs: Key) -> Bool {
switch (lhs, rhs) {
case (.IntKey(let lhsValue), .IntKey(let rhsValue)) : return lhsValue == rhsValue
case (.StringKey(let lhsValue), .StringKey(let rhsValue)) : return lhsValue == rhsValue
default: return false
}
}

enum Value {
case IntValue(Int)
case StringValue(String)

init(_ value: Int) { self = .IntValue(value) }
init(_ value: String) { self = .StringValue(value) }
}

var dict = [Key: Value]()

dict[Key(1)] = Value("One")
dict[Key(2)] = Value(2)
dict[Key("Three")] = Value("Three")
dict[Key("Four")] = Value(4)

Using JSONEncoder for Equatable method in Swift

The answer seems to be in your question: "JSON keys don't have any order." Also, two things that are logically equal might encode differently (if their equality is dependent only on their id for example). Also, reference types that encode the same values may not be equatable at all (UIView for example). Also, this creates a very broad == overload that makes type-checking more expensive (much as + is extremely expensive).

What you're doing is trying to auto-conform to Equatable when it can be synthesized. Swift could have done this; in fact, for most Encodable cases, Equatable can be synthesized just by adding : Equatable. The Swift team explicitly chose not to auto-conform in those cases and force you to request it when you mean it, because it doesn't always mean "all the properties contain the same bits." When the Swift team explicitly chooses to avoid a feature, it is rarely a good idea to re-add it.



Related Topics



Leave a reply



Submit