I'm looking for a way to obtain a protocol name dynamically from the protocol type, without having to use the #objc attribute in the protocol declaration.
I know that this works:
func keyForProtocol(aProtocol: Protocol) -> String {
return NSStringFromProtocol(aProtocol)
}
but only if the protocol has the #obj attribute:
#objc protocol Test {}
var key = keyForProtocol(Test.self) // Key contains "TestApp.Test"
however as soon as I remove the #objc attribute, compilation fails with this error:
'Test.Protocol' is not convertible to 'Protocol'
Is there any way to achieve that?
Note: the reason why I want to avoid #objc is that it doesn't allow usage of associated types (i.e. generics in protocol) - in those cases compilation fails with this error: Method cannot be marked #objc because the type of parameter xx cannot be represented in Objective-C
I recently found a solution by implementing the keyForProtocol method as follows:
func keyForProtocol<P>(aProtocol: P.Type) -> String {
return ("\(aProtocol)")
}
It works with any type, not just for protocols, but I'm ok with that.
Some examples from a playground:
protocol Test {}
keyForProtocol(Test.self) // Prints "__lldb_expr_92.Test"
class MyClass {}
keyForProtocol(MyClass.self) // Prints "__lldb_expr_92.MyClass"
keyForProtocol(Int.self) // Prints "Swift.Int"
keyForProtocol(UIView.self) // Prints "UIView"
As Antonio said:
let protocolDescription = "\(aProtocol)"
will return a (sometime strange) protocol name.
I finally completed my task converting back this string to a protocol with:
let protocolReference = NSProtocolFromString(protocolDescription)
This technique is perfect if you need to retrieve the protocol type from a generic parameter like in this example (a lightweight/homemade dependency manager):
class DependencyManager<T> {
private static func inject(_ aProtocol: T.Type) -> T {
let application = UIApplication.shared.delegate as! LMApplication
let dependencies = application.applicationDependencies
let protocolReference = NSProtocolFromString("\(aProtocol)")
let dependency = dependencies!.object(for: protocolReference)
return dependency! as! T
}
}
Usage:
let object: Protocol = DependencyManager.inject(Protocol.self)
Related
Since Swift 4, objects have gained subscript(keyPath:) which can be used to retrieve values using AnyKeyPath and its subclasses. According to the Swift book, the subscript is available on all types. For example, an instance of a class TestClass may be subscripted with an AnyKeyPath like so:
class TestClass {
let property = true
}
let anyKeyPath = \TestClass.property as AnyKeyPath
_ = TestClass()[keyPath: anyKeyPath]
This compiles correctly as expected. Use of any other valid subclass would also compile including PartialKeyPath<TestClass>, KeyPath<TestClass, Bool>, etc. This functionality is unavailable in a protocol extension. For example, the following is invalid:
class TestClass {
let property = true
}
protocol KeyPathSubscriptable {
}
extension KeyPathSubscriptable {
func test() {
let anyKeyPath = \TestClass.property as AnyKeyPath
_ = self[keyPath: anyKeyPath] // Value of type 'Self' has no subscripts
}
}
If we want to use that keyPath subscript in the protocol, we can include it in the protocol definition. However, the compiler will not resolve it automatically:
protocol KeyPathSubscriptable {
subscript(keyPath: AnyKeyPath) -> Any? { get }
}
extension KeyPathSubscriptable {
func test() {
let anyKeyPath = \TestClass.property as AnyKeyPath // This can be any valid KeyPath
_ = self[keyPath: anyKeyPath]
}
}
class TestClass: KeyPathSubscriptable { // Type 'TestObject' does not conform to protocol 'KeyPathSubscriptable'
let property = true
}
With this, we get a compile error: Type 'TestObject' does not conform to protocol 'KeyPathSubscriptable'. In order to resolve this, we must include a redundant implementation of that subscript in TestClass:
class TestClass: KeyPathSubscriptable {
let property = true
subscript(keyPath: AnyKeyPath) -> Any? {
fatalError() // This is never executed
}
}
This resolves the conformance issue and produces the goal result although it is seemingly unnecessary and illogical. I'm not sure how, but the subscript implementation is never even used. It's finding the expected implementation of subscript(keyPath:) and using that instead, but how? Where is that and is there any way to use it in a protocol? Why is this required by the compiler even though it's never used?
The context of this use case is in a logging module. The goal is that an object should be able to adopt a particular protocol which, with no additional setup on the object, would provide a human readable description of the object, instead of the default for many objects which is a memory address. The protocol would use Mirror to fetch KeyPaths of an object, read the values, and print them to the console. It is intended for debugging purposes and would not run in any production environment.
Please let me know if I can make any clarifications. I may post this to the Swift team if others think that this could potentially be a bug of sorts. All help is appreciated. Thanks in advance.
Full gist located here.
I'm trying to get a container that implements a set of protocols that i pass as parameter to a function on the original container.
struct Container {
let someProperty: String
let otherProperty: String
}
// Subcontainers
protocol Base {}
protocol SomePropertyContainer: Base {
var someProperty: String { get }
}
protocol OtherPropertyContainer: Base {
var otherProperty: String { get }
}
extension Container: SomePropertyContainer, OtherPropertyContainer {}
// Sub Container Provisioning Protocol
protocol SubContainerProviderProtocol {
func subContainer<T: Base>(protos: T.Type) -> T?
}
extension Container: SubContainerProviderProtocol {
func subContainer <T: Base>(protos: T.Type) -> T? {
return self as? T
}
}
// Example container
let subContainerProvider: SubContainerProviderProtocol = Container(someProperty: "Why does this not work!", otherProperty: "Seriously.")
Getting this up and running would allow me to inject the ContainerProviderProtocol into consumers while giving them the possibility to specify themselves which SubContainer they actually want.
E.g. a class that would be interested in only the someProperty could look like this
// Example Container Provider consumer
class SomeClass {
let subContainerProvider: SubContainerProviderProtocol
init(subContainerProvider: SubContainerProviderProtocol) {
self.subContainerProvider = subContainerProvider
}
func printSomeProperty() {
let someProperty = subContainerProvider
.subContainer(protos: SomePropertyContainer.self)?
.someProperty
print(someProperty)
}
}
// Example call
let someClass = SomeClass(subContainerProvider: subContainerProvider)
someClass.printSomeProperty() // "Why does this not work!"
This solution would be incredible for dependency injection & testability.
However the restriction T: Base is causing the compiler error
In argument type 'SomePropertyContainer.Protocol', 'SomePropertyContainer' does not conform to expected type 'Base'
Not specifying conformance to Base will compile, but would also allow to pass any type as T.
I've tried with associated types within an additional protocol etc, however have not figured it out. And while this issue is incredibly fun, I'm running out of ideas.
Possibly related to (but not exactly same) https://bugs.swift.org/browse/SR-55
Here's the problem: at some point you have to start working with actual types, and not just protocols. Your line:
func container<T: Base>(protos: T.Type) -> T?
is telling the compiler that you're going to give this function a type, generically T, that conforms to the protocol Base, not another protocol. You need something like this:
class SPC: SomePropertyContainer {
var someProperty: String = ""
}
class SomeClass {
let containerProvider: ContainerProviderProtocol
init(containerProvider: ContainerProviderProtocol) {
self.containerProvider = containerProvider
}
func printSomeProperty() {
let someProperty = containerProvider
.container(protos: SPC.self)?
.someProperty
print(someProperty)
}
}
SPC is a type that conforms to the SomePropertyContainer protocol, which itself conforms to the Base protocol, so this is what your code is expecting.
I couldn't find any good explanation to my questions so I'd like to ask you directly. First of all I'd like to refine my code in this post.
My problem is the protocol AnyObject and the Self type. I didn't implement AnyObject into my code because it is marked with #objc and I don't want any Objective-C stuff involved in my code (don't judge me for that). I also couldn't find any explanation about the Self type. It just worked as expected, but Xcode does not replace Self with the type the static function is called at.
Here is some example:
extension Int : Instance {}
Int.singleton { (customInstanceName) -> Self in 0 } // Self shall be replaced with Int
As you can see Xcode produces a Self instead an Int. Is there any chance I could fix this? Am I right that Self does return the dynamicType and my implementation is fine as it is in my post above? I would really appreciate any good explanation about the Self type.
As you have seen in my code. I am using a custom protocol to check whether my instance is a class or not. Is there any other shiny implementation to check my instances if they are classes or structure types, or am I forced to use AnyObject if I want to get rid of my ClassInstance protocol?
Thank you for your time.
UPDATE:
protocol Test {}
class A : Test {}
struct B : Test {}
let aClass : Test = A()
let aStruct : Test = B()
if let someClass = aClass as? AnyObject {
print(someClass) // only this will print
}
if let someStruct = aStruct as? AnyObject {
print(someStruct)
}
This will work, but AnyObject is still marked as an #objc protocol.
The Self type can be only used in protocols where it is a implicit typealias of the type which conforms to it:
protocol Testable {
func test() -> Self
}
If you want to conform to this protocol you than have to replace Self with the name of the type. For instance:
struct Product: Testable {
func test() -> Product {
return Product()
}
}
Important Edit:
As DevAndArtist pointed out in the comments there is a working class check in Swift 1.2 (without automatic bridging to Objective C) but not Swift 2 (Xcode 7 beta 3; probably a bug):
if instance.dynamicType is AnyClass {
// instance is a class
} else {
// instance is not a class
}
You can see workaround (mainly) for Swift 2 below.
End Edit
With respect to classes you should use AnyObject if you want to keep it simple but you can also use reflection which would be much more effort.
Below you can see some reflection results of string interpolations (only the first few characters):
"\(reflect(classType))" // Swift._ClassMirror
"\(reflect(0))" // Swift._LeafMirror
"\(reflect(enumType))" // Swift._EnumMirror
"\(reflect(structure))" // Swift._StructMirror
"\(reflect([0, 4]))" // Swift._ArrayTypeMirror
"\(reflect(NSDate()))" // Foundation._NSDateMirror
"\(reflect(NSURLRelationship.Contains))" // Swift._EnumMirror
"\(reflect(Int?(2)))" // Swift._OptionalMirror
As you can see enums are consistent if they are not defined in the Swift standard library (unfortunately also Optional...). So you can distinguish also structs and enums:
public enum Type {
case Enum, Class, Struct
}
public func getType<T>(anything: T) -> Type {
if anything is AnyObject {
return .Class
}
if "\(reflect(anything))".hasPrefix("Swift._EnumMirror") {
return .Enum
}
return .Struct
}
So for a better result you have to put some effort into it to differentiate between all the different cases.
But the easiest way to distinguish only between reference types and value types (aka classes and structs/enums) is still (unfortunately only works for own declared structs and not built in types because they can be bridged to Objective C; I'm working on it...):
if instance is AnyObject {}
// or: if instance is of type Any
if let classInstance = instance as? AnyObject {}
I have a protocol that uses an associated type, as such:
protocol Populatable {
typealias T
func populateWith(object: T)
}
and classes that implement the protocol:
class DateRowType: Populatable {
func populateWith(object: NSDate) {
print(object.description)
}
}
class StringRowType : Populatable {
func populateWith(object: String) {
print(object)
}
}
but when I try to cast or test for conformance, like this:
let drt = DateRowType()
let srt = StringRowType()
let rowTypes = [drt, srt]
let data = [NSDate(), "foo"]
for (i, p: Populatable) in enumerate(rowTypes) {
p.populateWith(data[i])
}
I get the error:
Protocol 'Populatable' can only be used as a generic constraint because it has Self or associated type requirements
What's the correct way to test if the object conforms to the Populatable protocol?
Note: all the code required to try this out is contained in the question, just copy the code blocks into a playground.
As the error says, you cannot cast it to Populatable here. I think the correct way is to cast it to EventRowType.
if let rowController = self.table.rowControllerAtIndex(i) as? EventRowType {
And you already tested that 'EventRowType' class conforms 'Populatable' protocol. Because if the EventRowType doesn't have function named 'populate', swift compiler says,
Type 'EventRowType' does not conform to protocol 'Populatable'
I don't think you will be able to go generic the whole way, unless possibly by using AnyObject and testing the class of the parameter in each populateWith function.
But this will work:
for (i, p) in enumerate(rowTypes) {
if let dateRow = p as? DateRowType {
dateRow.populateWith(data[i] as! NSDate)
}
else if let stringRow = p as? StringRowType {
stringRow.populateWith(data[i] as! String)
}
}
You will just need to expand this for every Populatable class you add.
I want to be able to pass a class (not an initialized object) of a certain protocol type to a method, then call the class functions of that class in the method. Code below.
I am using Swift and have an protocol defined like this
//Protocol for any object to be used with an FAUAPIConnection
protocol FAUAPIModel{
//Used to parse the object from a given dictionary to an object
class func parseFromJSON(JSON:AnyObject) -> Self
//Required default init
init()
}
What I would like to do is have a method like this
func getSomeParsingDone<T:FAUAPIModel>(model:T.Type? = nil, getPath:path, callingObj:CallingClass) -> Void
{
//GetIt is inconsequential, just logic to get an object from a certain path
var returnObj:AnyObject = GetIt.get(path)
if(model != nil){
returnObj = model!.parseFromJSON() <<<<<< Type 'T' does not conform to protocol 'AnyObject'
}
callingObj.done(returnObj)
}
Object that implements the protocol
import Foundation
class MyObj: FAUAPIModel{
var neededVal:String
var nonneededVal:String
required convenience init(){
self.init(neededVal:"VALUE")
}
init(neededVal:String, nonneededVal:String = ""){
self.neededVal = neededVal
self.nonneededVal = nonneededVal
}
class func parseFromJSON(JSON:AnyObject) -> WGMPart
{
return WGMPart() <<<<<<<< Method 'parseFromJSON' in non-final class 'WGMPart' must return 'Self' to conform to protocol 'FAUAPIModel'
}
}
However, I keep getting two errors. I have indicated these above with '<<<<<<<<<<<<'
compile error.
Lots of little things to consider here, but let's get to the heart of your question. The signature you want looks like this:
func getSomeParsingDone<T:FAUAPIModel>(model:T.Type, path:String) -> T?
I'm making the return optional beause there are a lot of things that could fail here, and you really shouldn't turn all of those into crashes.
I'd recommend your protocol look like this:
protocol FAUAPIModel {
class func parseFromJSON(JSON:AnyObject) -> Self
}
That way, you're promising that your return your own class, not just anything that is parseable. That does tend to mean that you need to make your classes final. If you don't want them to be final, you'll need to promise some init method in order to construct it. See Protocol func returning Self for more details on how to deal with that if you need it.
So putting it together, it might look something like this in practice:
protocol FAUAPIModel {
class func parseFromJSON(JSON:AnyObject) -> Self
}
func createObjectOfClass<T: FAUAPIModel>(model: T.Type, path: String) -> T? {
if let json: AnyObject = GetJSON(path) {
return model.parseFromJSON(json)
}
return nil
}
// Bogus JSON reader
func GetJSON(path: String) -> AnyObject? {
let json: AnyObject? = NSJSONSerialization.JSONObjectWithData(path.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!, options: NSJSONReadingOptions(0), error: nil)
return json
}
// Bogus model class that returns trivial version of itself
final class Something: FAUAPIModel {
class func parseFromJSON(JSON:AnyObject) -> Something {
return Something()
}
}
// Using it
let something = createObjectOfClass(Something.self, "/path/to/file")
I just want to note that the answer to your exact question would be to declare your function like this:
func getSomeParsingDone(model:FAUAPIModel.Type? = nil, getPath:path) -> FAUAPIModel