Subclassing MKAnnotation cause Set collection doesn’t work - ios

I've just found that Set of type MKAnnotation doesn't work as expected.
class MyAnnotation: MKPointAnnotation {
let id: String
init(_ id: String) {
self.id = id
}
override var hash: Int {
return id.hash
}
static func ==(lhs: MyAnnotation, rhs: MyAnnotation) -> Bool {
return lhs.hashValue == rhs.hashValue
}
}
let m1 = MyAnnotation("1")
let m2 = MyAnnotation("2")
let n1 = MyAnnotation("1")
let n2 = MyAnnotation("2")
m1.hashValue //918
n1.hashValue //918
m2.hashValue //921
n2.hashValue //921
if m1 == n1 && m2 == n2 {
print(true)
}
// prints true
let s1 = Set(arrayLiteral: m1, m2)
let s2 = Set(arrayLiteral: n1, n2)
let i = s1.intersection(s2)
// empty
Intersection of m's and n's is empty even so hashes are the same. Please, compare with example below:
class MyAnnotation: Hashable, Equatable {
let id: String
init(_ id: String) {
self.id = id
}
var hashValue: Int {
return id.hash
}
static func ==(lhs: MyAnnotation, rhs: MyAnnotation) -> Bool {
return lhs.hashValue == rhs.hashValue
}
}
let m1 = MyAnnotation("1")
let m2 = MyAnnotation("2")
let n1 = MyAnnotation("1")
let n2 = MyAnnotation("2")
m1.hashValue //918
n1.hashValue //918
m2.hashValue //921
n2.hashValue //921
if m1 == n1 && m2 == n2 {
print(true)
}
// prints true
let s1 = Set(arrayLiteral: m1, m2)
let s2 = Set(arrayLiteral: n1, n2)
let i = s1.intersection(s2)
// {{id "1"}, {id "2"}}
Intersection of m's and n's is as expected.
Isn't it weird? Maybe there is something in the middle I don't know nor understand.
Xcode 10.1

In first code, you haven't use Equatable protocol but in second code you have used Equatable protocol so it is working.
Equatable protocol is used for comparison. You can refer below link for more information:
https://useyourloaf.com/blog/swift-equatable-and-comparable/

The solution is to override isEqual(_ object: Any?) -> Bool.
Because MKAnnotation is derived from NSObject we need to override NS's method. If we dig into implementation (^ + ⌘) we would find:
Subclasses of NSObject can customize Equatable conformance by overriding isEqual(_:). If two objects are equal, they must have the same hash value, so if you override isEqual(_:), make sure you also override the hash property.

Related

Basic == overloading fails

My desired end result is to be able to compare to [Server] objects for equality, like so:
let server1 = Server(username: "1")
let server2 = Server(username: "2")
let server1Array = [server1]
let server2Array = [server2]
print(server1Array == server2Array)
I have a class called Server, and I want to overload the == for arrays of Server:
// Server.swift
import Foundation
infix operator ==: AssignmentPrecedence
class Server: ServerRepresentable {
let username: String
init(username: String) {
self.username = username
}
}
// This protocol's only purpose is to allow me to use the `where` clause in the extension below
protocol ServerRepresentable {
var username: String { get }
}
extension Array where Element: ServerRepresentable {
static func == (lhs: Array<ServerRepresentable>, rhs: Array<ServerRepresentable>) {
let server1Usernames = lhs.map { $0.username }
let server2Usernames = rhs.map { $0.username }
// ERROR: Cannot convert value of type '[String]' to expected argument type 'Bool'
return server1Usernames == server2Usernames
}
}
As you can see, I get the error "Cannot convert value of type '[String]' to expected argument type 'Bool'" when I try to perform server1Usernames == server2Usernames, which are both arrays of strings. This is strange because the == operator should already come overloaded in [String] comparisons.
What gives? Is the compiler getting confused because I'm overloading the operator in this class?
There is no need to extend Array. Just make sure your Server conforms to Equatable protocol:
class Server: ServerRepresentable, Equatable {
let username: String
init(username: String) {
self.username = username
}
static func == (lhs: Server, rhs: Server) -> Bool {
lhs.username == rhs.username
}
}
let server1 = Server(username: "1")
let server2 = Server(username: "2")
let server1Array = [server1]
let server2Array = [server2]
print(server1Array == server2Array) // "false\n"
Regarding your question you forgot to add a returning type Bool but the error displayed by the code you have posted should be Unexpected non-void return value in void function
This only works for string, int, double, float arrays.
You could try something like this
To check if there is a difference
for server1 in servers1 {
var name1 = [String]()
var name2 = String()
for server2 in servers2 {name1.append(server2.name)}
name2 = server1.name
if name1.contains(name2) {
print("found duplication")
}
To find the differences
for server1 in servers1 {
var name1 = [String]()
var name2 = String()
for server2 in servers2 {name1.append(server2.name)}
name2 = server1.name
print(name1. difference(name2))
}
//
extension Array where Element: Hashable {
func difference(from other: [Element]) -> [Element] {
let thisSet = Set(self)
let otherSet = Set(other)
return Array(thisSet.symmetricDifference(otherSet))
}
}

How to alphabetize an array of objects within Swift 3?

I try to list an array of objects in alphabetic order. I create this simple test, but doesn't know how to achieve this with an array of objects:
let names = ["Martin", "Nick", "Alex", "Ewa", "Barry", "Daniella", "Chris", "Robert", "Andrew"]
func alphabetizeArray(_ s1: String, _ s2: String) -> Bool {
return s1 < s2
}
let alphabeticNames = names.sorted(by: names)
print(reversedNames)
When I try this for an array of objects I came up with something like this:
func sorterForIDASC(this:People, that:People) -> Bool {
return this.name > that.name
}
peoples.sort(by: sorterForIDASC)
But this will give me an error of: Binary operator '>' cannot be applied to two 'String?' operands
Anyone suggestions how to solve this. I would examine the names of each object that is from the type of String. I use Swift 3/Xcode8.
If you only need > then implementing > for your optional property is sufficient:
func >(lhs: People, rhs: People) -> Bool {
if let left = lhs.name, let right = rhs.name {
return left > right
}
return false
}
Now you can use > on an array of your objects:
let result = arrayOfObjects.sorted(by: >)
You could also have your object conform to Equatable and implement at least == and > for the optional property:
struct People: Equatable {
var name: String?
}
func ==(lhs: People, rhs: People) -> Bool {
if let left = lhs.name, let right = rhs.name {
return left == right
}
return false
}
func >(lhs: People, rhs: People) -> Bool {
if let left = lhs.name, let right = rhs.name {
return left > right
}
return false
}
This opens even more possibilities.
In Swift 3.0
you can do that simply like this.
peoples.sort(by: { (this: People, that: People) -> Bool in
this. name < that. name
})

~= operator in Swift

I recently downloaded the Advanced NSOperations sample app from Apple and found this code...
// Operators to use in the switch statement.
private func ~=(lhs: (String, Int, String?), rhs: (String, Int, String?)) -> Bool {
return lhs.0 ~= rhs.0 && lhs.1 ~= rhs.1 && lhs.2 == rhs.2
}
private func ~=(lhs: (String, OperationErrorCode, String), rhs: (String, Int, String?)) -> Bool {
return lhs.0 ~= rhs.0 && lhs.1.rawValue ~= rhs.1 && lhs.2 == rhs.2
}
It seems to use the ~= operator against Strings and Ints but I've never seen it before.
What is it?
Simply use as a shortcut to "range": you can construct a range and "~=" means "contains".
(other can add more theoretical details, but the sense is this). Read it as "contains"
let n: Int = 100
// verify if n is in a range, say: 10 to 100 (included)
if n>=10 && n<=100 {
print("inside!")
}
// using "patterns"
if 10...100 ~= n {
print("inside! (using patterns)")
}
try with some values of n.
Is used widely for example in HTTP response:
if let response = response as? HTTPURLResponse , 200...299 ~= response.statusCode {
let contentLength : Int64 = response.expectedContentLength
completionHandler(contentLength)
} else {
completionHandler(nil)
It is an operator used for pattern matching in a case statement.
You can take a look here to know how you can use and leverage it providing your own implementation:
http://oleb.net/blog/2015/09/swift-pattern-matching/
http://austinzheng.com/2014/12/17/custom-pattern-matching/
Here is a simple example of defining a custom one and using it:
struct Person {
let name : String
}
// Function that should return true if value matches against pattern
func ~=(pattern: String, value: Person) -> Bool {
return value.name == pattern
}
let p = Person(name: "Alessandro")
switch p {
// This will call our custom ~= implementation, all done through type inference
case "Alessandro":
print("Hey it's me!")
default:
print("Not me")
}
// Output: "Hey it's me!"
if case "Alessandro" = p {
print("It's still me!")
}
// Output: "It's still me!"
You can look into Define Swift
func ~=<I : IntervalType>(pattern: I, value: I.Bound) -> Bool
func ~=<T>(lhs: _OptionalNilComparisonType, rhs: T?) -> Bool
func ~=<T : Equatable>(a: T, b: T) -> Bool
func ~=<I : ForwardIndexType where I : Comparable>(pattern: Range<I>, value: I) -> Bool

Filter array of classes in Swift

I am trying to filter an array containing classes so that only the class that is found in another array will be added to an array. This is what I have so far:
class Match : Equatable {
var name: String
var value: String
init(name: String, value: String) {
self.name = name
self.value = value
}
func ==(lhs: Match, rhs: Match) -> Bool {
return lhs.name == rhs.name && lhs.value == rhs.value
}
// attempt to filter array containing Match structs
let terms = [Match]()
let someOtherObjects = [Match]()
let sampleMatch = Match(name: "someName", value: "someValue")
someOtherObjects.append(sampleMatch)
filteredTerms = terms.filter { term in
if attemptedCombos.contains(sampleMatch) {
return true
}
}
However the compiler does not let me build with the error:
"Cannot convert value of type 'Match' to expected argument type
'#noescape (Match) throws -> Bool'
Any ideas?
Updated using Set (as it seems as if you want the intersection of two [Match] arrays). In addition to Equatable, you must let your Match class conform to Hashable for instances of it to be allowed as elements in a Set.
class Match : Equatable, Hashable {
var name: String
var value: String
init(_ name: String, _ value: String) {
self.name = name
self.value = value
}
var hashValue: Int {
get {
return name.hashValue << 20 + value.hashValue
}
}
}
func ==(lhs: Match, rhs: Match) -> Bool {
return lhs.name == rhs.name && lhs.value == rhs.value
}
Example:
/* Example */
var mySetA : Set<Match> = [Match("foo", "bar"), Match("foo", "foo"), Match("barbar", "foo")]
var mySetB = Set<Match>()
mySetB.insert(Match("barbar", "bar"))
mySetB.insert(Match("bar", "foo"))
mySetB.insert(Match("foo", "bar"))
mySetB.insert(Match("foo", "foo"))
let myIntersect = mySetA.intersect(mySetB)
for match in myIntersect {
print("name: " + match.name + ", value: " + match.value)
}
/* name: foo, value: foo
name: foo, value: bar */
After chatting with the OP we've solved the issue in chat. I'm not sure what the convention are here, but I'll summarize the additional information given by the OP in chat, and the solution to the problem. Consider the block above as a solution to the question above, and the block below as a quite narrow solution to the question above complemented with more specifics from the OP.
The "filter" array of objects are of a different class type (Tern) than the array to be filtered (Match), where these two classes share some class properties.
A natural enquiry to the OP was if it was acceptable to let both these classes have one common superclass; it was.
In addition to the above, one of the properties common for both classes were of a custom enum type, posted in chat by the author.
The final solution used, as above, Set and .intersect():
/* custom enum given by OP in chat */
enum Declension : String {
case firstDeclensionFem = "a:a:am:ae:ae:a:ae:ae:as:arum:is:is"
case secondDeclensionMasc = "us:er:um:i:o:o:i:i:os:orum:is:is"
case secondDeclensionNeu = "um:um:um:i:o:o:a:a:a:orum:is:is"
case thirdDeclensionMasc = " : :em:is:i:e:es:es:es:um:ibus:ibus"
case thirdDeclensionMascSpecial = " : :em:is:i:e:es:es:es:ium:ibus:ibus"
case fourthFem = "us:us:um:us:ui:u:us:us:us:uum:ibus:ibus"
case fourthNeu = "u:u:u:us:u:u:ua:ua:ua:uum:ibus:ibus"
case fifthMasc = "es:es:em:ei:ei:e:es:es:es:erum:ebus:ebus"
case unknown
static let allValues = [firstDeclensionFem, secondDeclensionMasc, secondDeclensionNeu, thirdDeclensionMasc, thirdDeclensionMascSpecial, fourthFem, fourthNeu, fifthMasc]
}
/* use a superclass and let the sets below have members that
are declared to be of this superclass type */
class MyMatchTypes : Equatable, Hashable {
var latin: String
var declension: Declension
init(_ latin: String, _ declension: Declension) {
self.latin = latin
self.declension = declension
}
var hashValue: Int {
get {
return latin.hashValue << 20 + declension.hashValue
}
}
}
func ==(lhs: MyMatchTypes, rhs: MyMatchTypes) -> Bool {
return lhs.latin == rhs.latin && lhs.declension == rhs.declension
}
/* the two classes mentioned in chat: use as subclasses */
class Term : MyMatchTypes {
var meaning: String
var notes: String
var genStem: String
init(_ latin: String, _ declension: Declension, _ meaning: String, _ genStem: String, _ notes: String) {
self.meaning = meaning
self.notes = notes
self.genStem = genStem
super.init(latin, declension)
}
}
class Match : MyMatchTypes {
// ... add stuff
// super init is OK
}
/* Example */
/* ----------------------------------------------- */
/* Set of `Match` objects */
var mySetA = Set<MyMatchTypes>()
mySetA.insert(Match("foo", Declension.firstDeclensionFem))
mySetA.insert(Match("bar", Declension.fourthFem))
mySetA.insert(Match("foofoo", Declension.fourthFem))
mySetA.insert(Match("barbar", Declension.fifthMasc))
/* Set of `Term` objects */
var mySetB = Set<MyMatchTypes>()
mySetB.insert(Term("fooshy", Declension.fourthFem, "a", "b", "c"))
mySetB.insert(Term("barbar", Declension.fifthMasc, "a", "b", "c"))
mySetB.insert(Term("bar", Declension.fourthFem, "a", "b", "c"))
mySetB.insert(Term("foofoo", Declension.firstDeclensionFem, "a", "b", "c"))
mySetB.insert(Term("foobar", Declension.fourthFem, "a", "b", "c"))
/* compute intersection */
let myIntersect = mySetA.intersect(mySetB)
for obj in myIntersect {
print("latin: " + obj.latin + ", declension: \(obj.declension)")
}
/* latin: barbar, declension: fifthMasc
latin: bar, declension: fourthFem */
If Match confirms to Equatable protocol your code should work. Following code compiles successfully:
class Match : Equatable {
var name: String
var value: String
init(name: String, value: String) {
self.name = name
self.value = value
}
}
func ==(lhs: Match, rhs: Match) -> Bool {
return lhs.name == rhs.name && lhs.value == rhs.value
}
let sampleMatch1 = Match(name: "someNameA", value: "someValue")
let sampleMatch2 = Match(name: "someNameA", value: "someValue")
let sampleMatch3 = Match(name: "someNameB", value: "someValue")
let sampleMatch4 = Match(name: "someNameC", value: "someValue")
let terms = [sampleMatch1, sampleMatch3]
var someOtherObjects = [sampleMatch2, sampleMatch4]
let filteredTerms = terms.filter { term in
return someOtherObjects.contains(term)
}
Result is:

Equatable implementation doesn't seem working with generics

I am still fighting with Swift generics. Today I discovered that my implementation of Equatable protocol doesn't work, if it's called from generic class.
My model class:
func ==(lhs: Tracking, rhs: Tracking) -> Bool {
// This method never executes if called from BaseCache
return lhs.id == rhs.id
}
class Tracking: NSObject, Equatable, Printable {
var id: String?
.....
}
Class, which uses generic type:
class BaseCache<T: NSObject where T: Equatable, T: Printable> {
.....
func removeEntities(entities: [T]) {
var indexesToRemove = [Int]()
for i in 0...allEntities.count - 1 {
let item = allEntities[i]
for entity in entities {
println("equal: \(entity == item)")
// FOR SOME REASONS THE STATEMENT BELOW IS ALWAYS FALSE
if entity == item {
indexesToRemove.append(i)
break
}
}
}
for index in indexesToRemove {
allEntities.removeAtIndex(index)
}
didRemoveEntities()
}
}
and it's subclass:
class TrackingCache<T: Tracking>: BaseCache<Tracking> {
}
When I call removeEntities method of TrackingCache instance, I always get equal: false in the output, even if ids are the same.
But if I move the method to TrackingCache class directly, it seems working fine!
Any ideas why this is happening and how to fix this?
Beware: since == is not a member function, it will not give you dynamic dispatch by default, including if you use it alongside a generic placeholder.
Consider the following code:
class C: NSObject, Equatable {
let id: Int
init(_ id: Int) { self.id = id }
}
// define equality as IDs are equal
func ==(lhs: C, rhs: C) -> Bool {
return lhs.id == rhs.id
}
// create two objects with the same ID
let c1 = C(1)
let c2 = C(1)
// true, as expected
c1 == c2
OK now create two variables of type NSObject, and assign the same values to them:
let o1: NSObject = c1
let o2: NSObject = c2
// this will be false
o1 == o2
Why? Because you are invoking the function func ==(lhs: NSObject, rhs: NSObject) -> Bool, not func ==(lhs: C, rhs: C) -> Bool. Which overloaded function to choose is not determined dynamically at runtime based on what o1 and o2 refer to. It is determined by Swift at compile time, based on the types of o1 and o2, which in this case is NSObject.
NSObject == is implemented differently to your equals – it calls lhs.isEqual(rhs), which falls back if not overridden to checking reference equality (i.e. are the two references pointing at the same object). They aren’t, so they aren’t equal.
Why does this happen with BaseCache but not with TrackingCache? Because BaseCache is defined as constraining only to NSObject, so T only has the capabilities of an NSObject – similar to when you assigned c1 to a variable of type NSObject, the NSObject version of == will be called.
TrackingCache on the other hand guarantees T will be at least a Tracking object, so the version of == for tracking is used. Swift will pick the more "specific" of all possible overloads – Tracking is more specific than it's base class, NSObject.
Here’s a simpler example, just with generic functions:
func f<T: NSObject>(lhs: T, rhs: T) -> Bool {
return lhs == rhs
}
func g<T: C>(lhs: T, rhs: T) -> Bool {
return lhs == rhs
}
f(c1, c2) // false
g(c1, c2) // true
If you want to fix this, you can override isEqual:
class C: NSObject, Equatable {
...
override func isEqual(object: AnyObject?) -> Bool {
return (object as? C)?.id == id
}
}
// this is now true:
o1 == o2
// as is this:
f(c1, c2)
This technique (having == call a dynamically-dispatched class method) is also a way to implement this behaviour for your non-NSObject classes. Structs, of course, don’t have this issue since they don’t support inheritance – score 1 for structs!

Resources