Skip to content

Instantly share code, notes, and snippets.

@callionica
Created May 26, 2016 19:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save callionica/fd7f4c4ef095f81aea538227d5e9effb to your computer and use it in GitHub Desktop.
Save callionica/fd7f4c4ef095f81aea538227d5e9effb to your computer and use it in GitHub Desktop.
// Dynamic cast without bridging
func dynamicCast<T>(_ type: T.Type, _ v: Any)->T? {
guard let result = v as? T where v.dynamicType is T.Type else {
return nil;
}
return result
}
// Return a 2uple if both objects are convertible to type T, otherwise nil
func matchCast<T>(_ type: T.Type, _ l: Any, _ r: Any)->(T, T)? {
guard let l = dynamicCast(type, l) else {
return nil;
}
guard let r = dynamicCast(type, r) else {
return nil;
}
return (l, r)
}
// False for different dynamic types
// Reference equality for objects
// Operator == for known primitive types (Bool, String, Int*, UInt*, Float*, Double)
// Tuples, structs, sets, collections, and dictionaries get memberwise comparison
// Enums get a string comparison on their dump output
// False for everything else
func equal(_ l: Any, _ r: Any)->Bool {
// Check if types are the same
if (l.dynamicType != r.dynamicType) {
return false
}
// Include this if you import Foundation
// if let m = matchCast(NSNumber.self, l, r) {
// return m.0 == m.1
// }
// if let m = matchCast(NSString.self, l, r) {
// return m.0 == m.1
// }
// Must come after primitive tests
if let m = matchCast(AnyObject.self, l, r) {
return m.0 === m.1 // Reference equality
}
if let m = matchCast(String.self, l, r) {
return m.0 == m.1
}
if let m = matchCast(Bool.self, l, r) {
return m.0 == m.1
}
if let m = matchCast(Int.self, l, r) {
return m.0 == m.1
}
if let m = matchCast(Int8.self, l, r) {
return m.0 == m.1
}
if let m = matchCast(Int16.self, l, r) {
return m.0 == m.1
}
if let m = matchCast(Int32.self, l, r) {
return m.0 == m.1
}
if let m = matchCast(Int64.self, l, r) {
return m.0 == m.1
}
if let m = matchCast(UInt.self, l, r) {
return m.0 == m.1
}
if let m = matchCast(UInt8.self, l, r) {
return m.0 == m.1
}
if let m = matchCast(UInt16.self, l, r) {
return m.0 == m.1
}
if let m = matchCast(UInt32.self, l, r) {
return m.0 == m.1
}
if let m = matchCast(UInt64.self, l, r) {
return m.0 == m.1
}
if let m = matchCast(Float.self, l, r) {
return m.0 == m.1
}
if let m = matchCast(Float80.self, l, r) {
return m.0 == m.1
}
if let m = matchCast(Double.self, l, r) {
return m.0 == m.1
}
func equalCompound(_ l: Any, _ r : Any)->Bool {
func isEnum(mirror: Mirror)->Bool {
guard let ds = mirror.displayStyle else {
return false;
}
return ds == .Enum
}
func equalDump(_ l: Any, _ r: Any)->Bool {
var o1 = ""
dump(l, &o1)
var o2 = ""
dump(r, &o2)
return o1 == o2
}
func isWorkingDisplayStyle(ds: Mirror.DisplayStyle?)->Bool {
guard let ds = ds else {
return false;
}
switch (ds) {
case .Class:
return false // Don't expect to use because we use ref equality
case .Enum:
return false // Can't use because case doesn't show up in mirror (but associated data does)
case .Collection:
return true // Looks OK
case .Dictionary:
return true // Looks OK
case .Optional:
return true // Looks OK - works because the cases have different number of children
case .Set:
return true // Looks OK
case .Tuple:
return true // Looks OK
case .Struct:
return true // Looks OK
}
}
let m1 = Mirror(reflecting: l)
let m2 = Mirror(reflecting: r)
if (isEnum(m1) && isEnum(m2)) {
// We can't get the case from the Mirror object or dynamicType
// so we resort to using dump and comparing the strings
return equalDump(l, r)
}
guard (isWorkingDisplayStyle(m1.displayStyle)) &&
(m1.displayStyle == m2.displayStyle) &&
(m1.children.count == m2.children.count) else {
return false;
}
for (p1, p2) in zip(m1.children, m2.children) {
if (!equal(p1.1, p2.1)) {
return false;
}
}
return true
}
return equalCompound(l, r)
}
@callionica
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment