In Swift, can you find all types in a module that adhere to a specific protocol?
I don't believe Swift currently has a 'native' (not dependant on the Objective-C runtime) API for doing this kind of reflection.
However, if you're on an Apple platform (and therefore have Obj-C interoperability), you could get a list of all classes registered with the Obj-C runtime, and then filter those that conform to a given protocol. This will work for Swift classes (even those that don't inherit from NSObject
) because under the hood Swift classes are built on top of Obj-C classes (when there's Obj-C interop).
Because this will only work for classes (not structures or enumerations), you may want to restrict your protocol such that only classes can conform to it:
protocol Favorite : class {}
You can then do the following, using objc_copyClassList
:
import Foundation
protocol Animal {}
protocol Vehicle {}
protocol Favorite : class {}
class Dog : Animal {}
class Cat : Animal, Favorite {}
class Car : Vehicle {}
class Bicycle : Vehicle, Favorite {}
/// Invokes a given closure with a buffer containing all metaclasses known to the Obj-C
/// runtime. The buffer is only valid for the duration of the closure call.
func withAllClasses<R>(
_ body: (UnsafeBufferPointer<AnyClass>) throws -> R
) rethrows -> R {
var count: UInt32 = 0
let classListPtr = objc_copyClassList(&count)
defer {
free(UnsafeMutableRawPointer(classListPtr))
}
let classListBuffer = UnsafeBufferPointer(
start: classListPtr, count: Int(count)
)
return try body(classListBuffer)
}
// .flatMap in Swift < 4.1
let classes = withAllClasses { $0.compactMap { $0 as? Favorite.Type } }
print(classes) // [Bicycle, Cat]
Here we're calling compactMap(_:)
on the UnsafeBufferPointer
to get back an array of metatypes that represent types conforming to Favorite
(i.e those that can be cast to the existential metatype Favorite.Type
).
How to list all classes conforming to protocol in Swift?
Since you're using the Objective-C runtime to get the type introspection you need to add @objc
to your code in this manner:
@objc protocol Animal {
func speak()
}
class Cat:Animal {
@objc func speak() {
print("meow")
}
}
class Dog: Animal {
@objc func speak() {
print("Av Av!")
}
}
class Horse: Animal {
@objc func speak() {
print("Hurrrr")
}
}
Note that this kind of type introspection might be very slow.
Swift Mirror API - Which protocols an object conforms to
Not possible at all in Swift. Swift reflection is a very limited affair. If you are willing to bridge your class into ObjC, you can use the ObjC Runtime functions to get what you want:
@objc protocol ProtocolA {}
@objc protocol ProtocolB {}
@objc protocol ProtocolC {}
class User : NSObject, ProtocolA, ProtocolC {}
var count: UInt32 = 0
let protocols = class_copyProtocolList(User.self, &count)
for i in 0..<Int(count) {
let cname = protocol_getName(protocols[i])
let name = String.fromCString(cname)
print(name)
}
Each of your protocol must be prefixed with @objc
and your class must inherit from NSObject
.
How do I list the Protocols an Object Conforms to?
You can't. Swift protocols that are not ObjC ones only exist at compile time and do not really exist on the object itself (which is why their methods are dispatched statically based on the type of the variable).
SWIFT reflect all implementations of a certain protocol
The definition says: Reflection
is a common programming language feature that enables us to inspect, and work with, the members of a type — dynamically, at runtime. Members
are list of properties inside a class/struct.
So, if you want to iterate over all your types that confirm Animal
protocol, you need to have them as members in a type, and then iterate over it.
Check code below-:
protocol Animal {
var name: String { get }
}
class Cat: Animal {
var name: String = "Joshua"
}
class Dog: Animal {
var name: String = "Anna"
}
class TestAnimals {
var cat:Cat
var dog:Dog
init(cat:Cat,dog:Dog) {
self.cat = cat
self.dog = dog
}
func testAnimals() {
let mirror = Mirror(reflecting: self)
for child in mirror.children {
if let animal = child.value as? Animal {
print(animal.name)
}
}
}
}
var obj = TestAnimals(cat: Cat(), dog: Dog())
obj.testAnimals()
Or, you could also do something like below, by making composition.This allows you to assign any Animal
type at runtime.
class TestAnimals {
var myAnimal:Animal
init(animal:Animal) {
myAnimal = animal
}
func testAnimals() {
let mirror = Mirror(reflecting: self)
for child in mirror.children {
if let animal = child.value as? Animal {
print(animal.name)
}
}
}
}
let objects :[Animal] = [Cat(),Dog()]
for myobj in objects{
let obj = TestAnimals(animal: myobj)
obj.testAnimals()
Resolving a class of and object that conforms to protocol
You can get the type of the view model class from its instance by controller.viewModel.dynamicType
:
func resolveViewModel<T : IViewModelAware>(controller : T) {
let viewModel = container.resolve(controller.viewModel.dynamicType)
// ...
}
How to make a generic class conform to a protocol for specific type?
No, such construct isn't possible (circa Swift 3.1 at least).
For instance:
class SomeClass { }
protocol SomeProtocol { }
extension Matrix: SomeProtocol where T == SomeClass { }
Gives a very clear error message:
Extension of type
Matrix
with constraints cannot have an inheritance clause.
But all is not lost... as correctly noted by Alexander, there is already a proposal lined up for Swift 4! The feature will be called Conditional Conformances (SE-0143).
A nice example for all protocol-oriented programming hackers out there:
extension Array: Equatable where Element: Equatable {
...
}
If an array contains equatable elements than said array is also equatable.
Update. Swift 4 is out but this feature hasn’t landed yet. We may need to wait until Swift 5 for this...
Related Topics
Why Can't I Divide Integers in Swift
How to Hide the Navigationbar When Embedding Swiftui in Uikit
How to Change "Return" Key to "Done" on Keyboard with Swiftui
In Swift, Does Resetting the Property Inside Didset Trigger Another Didset
Unsafemutablepointer in Swift as Replacement for Properly Sized C Array in Obj-C
Fbsdkapplicationdelegate Application Openurl:Sourceapplication:Annotation Deprecated
Change Tabview Indicator Swiftui
How to Make List with Single Selection with Swiftui
Blue Highlighting/Focus Ring on Catalyst App
How to Open File Dialog with Swiftui on Platform "Uikit for MAC"
Remove Multiple Indices from Array
Firebase Says That My Rules Are Insecure, Why
How to Check Object Is Nil or Not in Swift
How to Hide the Navigation Back Button in Swiftui
What Is 'Where Self' in Protocol Extension
Error: Unable to Spawn Process (Argument List Too Long) in Xcode Build