Runtime comparison of Swift Protocol MetaTypes - swift

I'm trying to dynamically match a Swift protocol to an implementation, but I've gotten blocked on trying to perform comparisons of Protocols at runtime - it seems that maybe protocols don't really exist at runtime?
Some examples of things I've tried:
var protocols[Any]
func findProtocol(aProtocol: Any) -> Bool {
// Nope, protocols don't implement equatable
aProtocol == protocols[0]
// Doesn't work, unsafeAddressOf() only applies to AnyObjects
let pointer: UnsafePointer = unsafeAddressOf(aProtocol)
}
I think I might have hit the boundaries of trying to defeat the type system... any thoughts?

If you know that you compare the types themselves you should use a more appropriate type (Any.Type):
var protocolArray: [Any.Type] = [...]
func findProtocol(aProtocol: Any.Type) -> Bool {
// you can do that because Any.Type has an == operator
return protocolArray.contains{ $0 == aProtocol }
}
For Any type you have to cast it:
var protocolArray: [Any] = [...]
func findProtocol(aProtocol: Any) -> Bool {
return protocolArray.contains{
if let p1 = $0 as? Any.Type, p2 = aProtocol as? Any.Type {
return p1 == p2
}
return false
}
}

I may be slightly misunderstanding what you're looking to do, but you should be able to use reflection for this. How about something like this?
protocol One {}
protocol Two {}
protocol Three {}
var protocols: [Any] = [One.self, Two.self]
func findProtocol(aProtocol: Any) -> Bool {
let findMirror = Mirror(reflecting: aProtocol)
for checkProtocol in protocols {
let mirror = Mirror(reflecting: checkProtocol)
if findMirror.subjectType == mirror.subjectType {
return true
}
}
return false
}
findProtocol(One) // Returns true
findProtocol(Two) // Returns true
findProtocol(Three) // Returns false

Related

How to compare two `Any` objects those actually conform to `Equatable`

I want to implement a ValueObjectSharedExampleConfiguration: QuickConfiguration using Quick.
class ValueObjectSharedExampleConf: QuickConfiguration {
override class func configure(_ configuration: Configuration) {
sharedExamples("Value Object") {
(context: #escaping SharedExampleContext) in
describe("same objects") {
it("should be equal") {
let obj1a = context()["1a"]
let obj1b = context()["1b"]
expect(obj1a == obj1b).to(beTrue())
}
}
describe("different objects") {
it("should not be equal") {
let obj1 = context()["1a"]
let obj2 = context()["2"]
expect(obj1 == obj2).to(beFalse())
}
}
}
}
}
And then I want to test any classes/structs that conforms to Equatable with this shared Example like this:
itBehavesLike("Value Object") { [ "obj1a": foo1a, "obj1b": foo1b, "obj2": foo2] }
But the problem is, SharedExampleContext is actually a closure returns [String: Any], so obj1a, obj1b, obj2 variables I get in sharedExample closure are all of type Any, which doesn't necessarily conform to Equatable. Thus the code obj1a == obj1b won't compile.
Actually if I check obj1a is Equatable it returns true. But I don't know how to cast it to a proper type that compiler will accept. obj1a as! Equatable won't compile because Equatable is a generic protocol.
I can't just write obj1a as! Foo because if there is another class Bar: Equatable I want my sharedExample also works for that.
The main problem here is: I have two variables cast to Any, which are guaranteed to be originally of same type that conforms to Equatable. How should I legally compare these two variables without knowledge of the actual type of them ?
You could have a MyComparable protocol with a function isEqualTo(object: Any) and implement it for Foo and Bar. You can then:
let object1 = obj1 as! MyComparable
object1.isEqualTo(obj2)
and in the implementation of isEqualTo check the type or force convert it if you are sure it is always the same:
class Foo: MyComparable {
func isEqualTo(_ object: Any) -> Bool {
let obj2 = object as! Foo
return self == obj2
}
}
I found an strange solution.
func == (lhs: any Equatable, rhs: any Equatable) -> Bool {
return ([lhs] as NSArray) == ([rhs] as NSArray)
}
Here is the code tested with swift 5.7.1
protocol GenericItem: Hashable {
associatedtype Element
var value: Element { get }
}
struct StringItem: GenericItem {
var value: String
}
struct IntItem: GenericItem {
var value: Int
}
// comparison function [A]
func == (lhs: any Equatable, rhs: any Equatable) -> Bool {
return ([lhs] as NSArray) == ([rhs] as NSArray)
}
let item1: any GenericItem = StringItem(value: "hello")
let item2: any GenericItem = StringItem(value: "hello")
let item3: any GenericItem = StringItem(value: "world")
let item4: any GenericItem = IntItem(value: 24)
// without [A] it causes error as follows
// item1 == item2 // Binary operator '==' cannot be applied to two 'any GenericItem' operands
([item1] as NSArray) == ([item2] as NSArray) // true
([item2] as NSArray) == ([item3] as NSArray) // false
item1 == item2 // true
item2 == item3 // false
item3 == item4 // false
Since StringItem nor IntItem are not inherited from NSObject, I am not sure how NSArray or NSDictionary compares two instances of any Equatable. If we know how it works behind the scenes, we may come up with better implementation.
Note: Strange to say, if you change Hashable to Equatable, it behaves differently.
protocol GenericItem: Equatable { // Hashable -> Equatable
associatedtype Element
var value: Element { get }
}
// ...snip...
item1 == item2 // false <-- !?
item2 == item3 // false
item3 == item4 // false
It may not be a stable behavior. If you like to try, please test you code well before ship your products.

Type erasure with method using `Self` as parameter

I'm trying to make a type erasure around Range and ClosedRange but I'm being stuck because they have some methods which take Self as parameter.
All types erasure samples found on the internet don't deal with that case.
Am I trying to do something impossible ?
Here's my implementation (simplified) :
protocol RangeType {
associatedtype _Bound: Comparable
func overlaps(_ other: Self) -> Bool
}
struct AnyRange<Bound: Comparable>: RangeType {
typealias _Bound = Bound
private let _overlaps: (AnyRange<Bound>) -> Bool
init<R: RangeType>(_ range: R) where R._Bound == Bound {
// Cannot assign value of type (R) -> Bool to type (AnyRange<...>) -> Bool
self._overlaps = range.overlaps
}
func overlaps(_ other: AnyRange<Bound>) -> Bool {
return _overlaps(other)
}
}
extension Range: RangeType {
typealias _Bound = Bound
}
extension ClosedRange: RangeType {
typealias _Bound = Bound
}
Before I propose my solution to the problem, first note that what you are trying to do may not be defined. The protocol RangeType ensures that overlaps(_:) is defined for instances whose type is the same as the type that is implementing the function. The type erasure you are attempting to get by mimicking that of AnyIterator cannot be achieved this way because while AnyRange may guarantee that the bounds are identical, the actual underlying types themselves may not be (a requirement of the protocol).
However, that is solved here. If you wish, you may add a special case to handle comparisons between two different types (although evaluation to false as is done here is perhaps desirable)
protocol RangeType {
associatedtype _Bound: Comparable
func overlaps(_ other: Self) -> Bool
}
struct RangeComparison<Range, Element>: Equatable where Range: RangeType, Range._Bound == Element {
static func ==(lhs: RangeComparison<Range, Element>, rhs: RangeComparison<Range, Element>) -> Bool { lhs.range.overlaps(rhs.range) }
var range: Range
}
struct AnyRange<Bound: Comparable>: RangeType {
// MARK: RangeType
typealias _Bound = Bound
// Calls the closure of the `_overlaps` property which shields each type and allows self to be passed through
func overlaps(_ other: AnyRange<Bound>) -> Bool { _overlaps.closure(other, self) }
// Shielding structure. Allows us to compare to `AnyRange` instances
private struct OverlapContainer<A, B> {
private(set) var closure: (A, B) -> Bool
init(closure: #escaping (A, B) -> Bool) { self.closure = closure }
}
private var _overlaps: OverlapContainer<AnyRange<Bound>, Self>
// Holds reference to the actual range type. Note that if this is a class type, a strong reference will be created
private let range: Any
/**
Represents this particular type. Should not be called elsewhere in the structure as the cast would fail if `RT != R`
passed to the initiliazer
NOTE: `RT` as the generic type is used instead of `R` to keep us aware of this fact
*/
private nonmutating func rangeComparison<RT: RangeType>() -> RangeComparison<RT, Bound> { RangeComparison<RT, Bound>(range: range as! RT) }
init<R: RangeType>(_ range: R) where R._Bound == Bound {
self.range = range
self._overlaps = .init { other, this in
let thisComparison: RangeComparison<R, Bound> = this.rangeComparison()
// If the two types are the same, the comparison can be made
if type(of: other.range).self == R.self {
let otherComparison: RangeComparison<R, Bound> = other.rangeComparison()
return thisComparison == otherComparison
}
else { print("Not the same type"); return false } // Otherwise the comparison is invalid
}
}
}
extension Range: RangeType {
typealias _Bound = Bound
}
extension ClosedRange: RangeType {
typealias _Bound = Bound
}
// Examples
let range: Range<Int> = .init(5...8)
let rangeII: ClosedRange<Int> = 1...6
let any: AnyRange<Int> = .init(range)
let anyII: AnyRange<Int> = .init(rangeII)
print(any.overlaps(anyII)) // false.` Range` is not the same type as `ClosedRange`
let rangeIII: ClosedRange<Double> = 3.0...5.5
let rangeIV: ClosedRange<Double> = 1.0...4.0
let anyIII: AnyRange<Double> = .init(rangeIII)
let anyIV: AnyRange<Double> = .init(rangeIV)
print(anyIII.overlaps(anyIV)) // true. Both are 'ClosedRange<Double>' and actually overlap one another
There is a lot here, so let me explain each piece
struct RangeComparison<Range, Element>: Equatable where Range: RangeType, Range._Bound == Element {
static func ==(lhs: RangeComparison<Range, Element>, rhs: RangeComparison<Range, Element>) -> Bool { lhs.range.overlaps(rhs.range) }
var range: Range
}
This structure is what is used to represent a given AnyRange type. As I mentioned, comparing any two RangeType instances is not defined if they are not of the same type. This provides a medium to ensure that this is the case as well as making it convenient to equate two AnyRange types through this structure.
The rangeComparison<RT: RangeType>() method uses the type of the RangeType (R) passed into the initializer and casts the range property (set as Any and assigned to the instance passed to the initializer) to this type to create a RangeComparison instance. The range property is what keeps hold of the actual underlying type.
private struct OverlapContainer<A, B> {
private(set) var closure: (A, B) -> Bool
init(closure: #escaping (A, B) -> Bool) { self.closure = closure }
}
private var _overlaps: OverlapContainer<AnyRange<Bound>, Self>
This structure actually allows us to (indirectly) make comparisons between two AnyRange instances through the overlaps(_:) method of AnyRange and a closure. We simply call the _overlaps property's closure property, supplying the other AnyRange instance and a copy of this instance. A copy is used to ensure that the closure can use self without having to use self , as the compiler will complain that "Escaping closure captures mutating self parameter" (hence the reason that OverlapContainer has two generic types).
init<R: RangeType>(_ range: R) where R._Bound == Bound {
self.range = range
self._overlaps = .init { other, this in
let thisComparison: RangeComparison<R, Bound> = this.rangeComparison()
// If the two types are the same, the comparison can be made
if type(of: other.range).self == R.self {
let otherComparison: RangeComparison<R, Bound> = other.rangeComparison()
return thisComparison == otherComparison
}
else { print("Not the same type"); return false } // Otherwise the comparison is invalid
}
}
Finally, we check if the two comparisons have the same type. If you try to specify each return type as RangeComparison<R, Bound>, it will compile but will crash if the types of each comparison's range property are not the same as the type R inferred from the generic initializer. You also "Cannot explicitly specialize a generic function", and so must specify a type for the result of rangeComparison(). For these two reasons, we check the type and then check if they overlap.

Is there way to define compare (`==`) function automatically for `struct` in Swift?

Let's assume we have a pretty big struct in Swift:
struct SuperStruct {
var field1: Int = 0
var field2: String = ""
// lots of lines...
var field512: Float = 0.0
}
.. and then we need to implement Equatable protocol:
extension SuperStruct: Equatable {
}
func ==(lhs: SuperStruct, rhs: SuperStruct) -> Bool {
return
lhs.field1 == rhs.field1 &&
lhs.field2 == rhs.field2 &&
// lots of lines...
lhs.field512 == rhs.field512
}
... and we need to write lots of lines of stupid code.
Is there a way "to ask" compiler "to do" it for us?
The following answer shows one possible solution; possibly not a recommended one (however possibly of interest for future readers of this question).
If you have a large number of properties which all belong to a somewhat limited of number different types, you could use a Mirror of your structure instances and iterate over over the structures' properties; for each attempting conversion to the different types that you know your properties to be.
I've edited the previous answer (to something I believe is quite much neater), after watching the following WWDC 2015 session (thanks Leo Dabus!):
WWDC 2015 session 408. Recommended.
I'll leave the initial answer in the bottom of this answer as well, as it shows an alternative, less protocol-oriented approach, to make use of this Mirror solution.
Mirror & protocol-oriented solution:
/* Let a heterogeneous protocol act as "pseudo-generic" type
for the different (property) types in 'SuperStruct' */
protocol MyGenericType {
func isEqualTo(other: MyGenericType) -> Bool
}
extension MyGenericType where Self : Equatable {
func isEqualTo(other: MyGenericType) -> Bool {
if let o = other as? Self { return self == o }
return false
}
}
/* Extend types that appear in 'SuperStruct' to MyGenericType */
extension Int : MyGenericType {}
extension String : MyGenericType {}
extension Float : MyGenericType {}
// ...
/* Finally, 'SuperStruct' conformance to Equatable */
func ==(lhs: SuperStruct, rhs: SuperStruct) -> Bool {
let mLhs = Mirror(reflecting: lhs).children.filter { $0.label != nil }
let mRhs = Mirror(reflecting: rhs).children.filter { $0.label != nil }
for i in 0..<mLhs.count {
guard let valLhs = mLhs[i].value as? MyGenericType, valRhs = mRhs[i].value as? MyGenericType else {
print("Invalid: Properties 'lhs.\(mLhs[i].label!)' and/or 'rhs.\(mRhs[i].label!)' are not of 'MyGenericType' types.")
return false
}
if !valLhs.isEqualTo(valRhs) {
return false
}
}
return true
}
Example usage:
/* Example */
var a = SuperStruct()
var b = SuperStruct()
a == b // true
a.field1 = 2
a == b // false
b.field1 = 2
b.field2 = "Foo"
a.field2 = "Foo"
a == b // true
Previous Mirror solution:
/* 'SuperStruct' conformance to Equatable */
func ==(lhs: SuperStruct, rhs: SuperStruct) -> Bool {
let mLhs = Mirror(reflecting: lhs).children.filter { $0.label != nil }
let mRhs = Mirror(reflecting: rhs).children.filter { $0.label != nil }
for i in 0..<mLhs.count {
switch mLhs[i].value {
case let valLhs as Int:
guard let valRhs = mRhs[i].value as? Int where valRhs == valLhs else {
return false
}
case let valLhs as String:
guard let valRhs = mRhs[i].value as? String where valRhs == valLhs else {
return false
}
case let valLhs as Float:
guard let valRhs = mRhs[i].value as? Float where valRhs == valLhs else {
return false
}
/* ... extend with one case for each type
that appear in 'SuperStruct' */
case _ : return false
}
}
return true
}
Example usage:
/* Example */
var a = SuperStruct()
var b = SuperStruct()
a == b // true
a.field1 = 2
a == b // false
b.field1 = 2
b.field2 = "Foo"
a.field2 = "Foo"
a == b // true
In Swift 4.1, Equatable/Hashable types now synthesize conformance to Equatable/Hashable if all of the types' members are Equatable/Hashable
SE-0185
Synthesizing Equatable and Hashable conformance
Developers have to write large amounts of boilerplate code to support equatability and hashability of complex types. This proposal offers a way for the compiler to automatically synthesize conformance to Equatable and Hashable to reduce this boilerplate, in a subset of scenarios where generating the correct implementation is known to be possible.
https://github.com/apple/swift-evolution/blob/master/proposals/0185-synthesize-equatable-hashable.md
You could make the struct Codable and compare the JSON encoded Data. Not efficient, but could be useful for some applications (e.g. unit tests).
struct SuperStruct: Encodable {
var field1: Int = 0
// ....
var field512: Float = 0.0
}
let s1 = SuperStruct()
let s2 = SuperStruct()
let encoder = JSONEncoder()
let data1 = try! encoder.encode(s1)
let data2 = try! encoder.encode(s2)
let result = (data1 == data2)
If you like this you could tidy it up into a protocol extension of Encodable.
No, it doesn't. At least not in any way that's not excessively complicated and based on use (abuse?) of runtime introspection. See dfri's answer for something that technically works, but that is way more complicated than just writing an == implementation that directly compares all fields.
As for your opinions on what "should" be available in Swift, you're more likely to see some effect if you share them with Apple or with the Swift open source community.

Swift generics: return type based on parameter type

Say I have a collection of objects inheriting from a common superclass (this is preferable to protocols in this case):
class ObjectSuperClass {
type: ObjectType
}
class ObjectClass1: ObjectSuperClass {
type = .Type1
}
class ObjectClass2: ObjectSuperClass {
type = .Type2
}
I'm looking to create a generic search function like this:
func objectsOfType<T: ObjectSuperClass>(T.class, otherFilter: Any?) -> [T]
Which could be used to search for a given sub-type, returning a more specific array of results:
let result = objectsOfType(ObjectClass2.class, otherFilter: nil) -> [ObjectClass2]
(pseudo-swift)
I feel like this is somewhere generics could help, but cannot see where constraints should be placed. Is it possible?
Well remarkably this works...
func filterType<T>(list: [AnyObject]) -> [T]
{
return list.filter{ $0 is T }.map{ $0 as! T }
}
...provided you assign the result to something that has been explicitly typed, as in the following example:
class ObjectSuperClass: CustomStringConvertible
{
let myType: String
init(aString: String)
{
myType = aString
}
var description: String { return myType }
}
class ObjectClass1: ObjectSuperClass
{
init()
{
super.init(aString: "<t 1>")
}
}
class ObjectClass2: ObjectSuperClass
{
init()
{
super.init(aString: "<t 2>")
}
}
let unfilteredList: [AnyObject] = [ ObjectClass1(), ObjectClass2(), ObjectSuperClass(aString: "<Who knows>")]
let filteredList1: [ObjectClass1] = filterType(list: unfilteredList)
print("\(filteredList1)") // <t 1>
let filteredList2: [ObjectClass2] = filterType(list: unfilteredList)
print("\(filteredList2)") // <t 2>
let filteredList3: [ObjectSuperClass] = filterType(list: unfilteredList)
print("\(filteredList3)") // [<t 1>, <t 2>, <Who knows>]
T is inferred in each case from the requested return type. The function itself filters the original array based on whether the elements are of the required type and then force casts the filtered results to the correct type.
If you want an "extra filter" you don't need to explicitly type the results as long as T can be inferred from your extra filter function.
func extraFilterType<T>(list: [AnyObject], extraFilter: T -> Bool) -> [T]
{
return list.filter{ $0 is T }.map{ $0 as! T }.filter(extraFilter)
}
let filteredList = extraFilterType(unfilteredList){
(element : ObjectClass2) -> Bool in
!element.description.isEmpty
}
print("\(filteredList)") // <t 2>
EDIT
A slicker version of the filterType function would use flatMap()
func filterType<T>(list: [Any]) -> [T]
{
return list.flatMap{ $0 as? T }
}
EDIT 2
Flatmap is deprecated for optionals, since Swift 4.something, use compactMap
func filterType<T>(list: [Any]) -> [T]
{
return list.compactMap{ $0 as? T }
}
This is the closest approximation I can come up with:
func objectsOfType<T: ObjectSuperClass>(type type: T.Type) -> [T] {
// Just returns an array of all objects of given type
}
func objectsOfType<T: ObjectSuperClass>(type type: T.Type, predicate: T -> Bool) -> [T] {
// Uses predicate to filter out objects of given type
}
Usage:
let bar = objectsOfType(type: ObjectClass1.self)
let baz = objectsOfType(type: ObjectClass2.self) {
// Something that returns Bool and uses $0
}
Technically, you can also go without type argument in the above, but then you will need to have explicitly typed receivers (bar and baz in the above example) so that Swift can correctly infer the types for you and use the right version of the generic function.
You can implement the function like this:
func objectsOfType<T: ObjectSuperClass>(objects: [ObjectSuperClass], subclass: T.Type, otherFilter: (T->Bool)?) -> [T] {
if let otherFilter = otherFilter {
return objects.filter{$0 is T && otherFilter($0 as! T)}.map{$0 as! T}
} else {
return objects.filter{$0 is T}.map{$0 as! T}
}
}
Usage example:
objectsOfType(arrayOfObjects, subclass: ObjectClass1.self, otherFilter: nil)
Note that I'm not a fan of forced casting, however in this scenario it should not cause problems.
Or, the more verbose version of the function, with one less forced cast:
func objectsOfType<T: ObjectSuperClass>(objects: [ObjectSuperClass], subclass: T.Type, otherFilter: (T->Bool)?) -> [T] {
return objects.filter({object in
if let object = object as? T {
if let otherFilter = otherFilter {
return otherFilter(object)
} else {
return true
}
} else {
return false
}
}).map({object in
return object as! T
})
}

Determine if Any.Type is Optional

I’m trying to determine if a given type t (Any.Type) is an optional type, I’m using this test
t is Optional<Any>.Type
but it returns always false.
So is there any way to achieve this?
Assuming that what you are trying to do is something like this:
let anyType: Any.Type = Optional<String>.self
anyType is Optional<Any>.Type // false
Sadly swift currently (as of Swift 2) does not support covariance nor contravariance and type checks directly against Optional.Type cannot be done:
// Argument for generic parameter 'Wrapped' could not be inferred
anyType is Optional.Type // Causes error
An alternative is to make Optional extend an specific protocol, and check for that type:
protocol OptionalProtocol {}
extension Optional : OptionalProtocol {}
let anyType: Any.Type = Optional<String>.self
anyType is OptionalProtocol.Type // true
A bit late for the party. But, I ran into the same problem. Here is my code.
func isOptional(_ instance: Any) -> Bool {
let mirror = Mirror(reflecting: instance)
let style = mirror.displayStyle
return style == .optional
}
let a: Int = 1 // false
let b: Int? = 2 // true
let c: Double = 3.0 // false
let d: Double? = 4.0 // true
let e: NSString = "Hello" // false
let f: NSString? = "Hello" // true
isOptional(a) // fasle
isOptional(b) // true - warning
isOptional(c) // false
isOptional(d) // true - warning
isOptional(e) // false
isOptional(f) // true - warning
It looks good to me. swift4
You can do something like this:
extension Mirror {
static func isOptional(any: Any) -> Bool {
guard let style = Mirror(reflecting: any).displayStyle,
style == .optional else { return false }
return true
}
}
Usage:
XCTAssertTrue(Mirror.isOptional(any: Optional(1)))
Or if you need to cast from Any to Optional
protocol _Optional {
var isNil: Bool { get }
}
extension Optional: _Optional {
var isNil: Bool { return self == nil }
}
func isNil (_ input: Any) -> Bool {
return (input as? _Optional)?.isNil ?? false
}
Usage:
isNil(nil as String?) // true
isNil("") // false
Optionals conform to ExpressibleByNilLiteral, so you may use that:
let t = Optional<String>.self
t is ExpressibleByNilLiteral.Type // true
You can do:
public func isOptionalType(_ type: Any.Type) -> Bool {
type is ExpressibleByNilLiteral.Type
// or
// String(describing: type).hasPrefix("Optional<")
}
(lldb) po isOptionalType(Int.self)
false
(lldb) po isOptionalType(Int?.self)
true
You could use generics to achieve this:
func isOptional<T>(x:T?)->Bool
{
return true
}
func isOptional<T>(x:T)->Bool
{
return false
}
Edit
The code above can be used to know if a variable is of optional type.
The only way i've figured out to know about a variable containing a type is by using reflection:
var t1:Any.Type=(String?).self
var t2:Any.Type=(String).self
Mirror(reflecting: t1).description
Mirror(reflecting: t2).description
The first call to Mirror gives the string "Mirror for Optional<String>.Type", and the second gives "Mirror for String.Type".
I understand that comparing string is not a convenient way to do this check, i will try again to find something more performant..