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 anNSArray
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 fromNSObject
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 theNSArray
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
How to Override Convenience Init in Uialertcontroller for Swift
Swift: Is Correct to Use Stored Properties as Computed Properties
Default Uifont Size and Weight But Also Support Preferredfontfortextstyle
How to Record and Save at 240 Frames Per Second
Spritekit Not Respecting Zposition
Fix Cursor Size for Modified Paragraph Spacing in Uitextview
How to Set Size Leftbarbuttonitem
How to Pair And/Or Bond to Ble on iOS Using Swift Code and an Hm-10 So Data Sent Is Encrypted
How to Animate Uilabel's Textcolor Change
Change Cell Height by The Content of The Textview Inside The Cell
Get Xcode 5 to Warn About New API Calls
How to Get Index of Xcuielement in Xcuielementquery
Uialertview's Textfield Does Not Show Keyboard in iOS8
Selecting All The Items in UIcollectionview iOS, Even The Cells That Are Not Visible
Url Opening Swift App - Open Works - Called Function Does Not Work