Can I get the name of a type conforming to a protocol from that protocol? - swift

I would like to know if I can find the name of a type conforming to a given protocol, from that protocol. I was thinking of protocol extension to avoid repetition in every type conforming to that protocol. I tried this:
protocol T {
var type: String { get }
}
extension T {
var type: String {
return String(describing: T.self)
}
}
struct S: T {}
let s = S()
print(s.type)
But this is showing T instead of S.
Is there any way I can do this?

Naturally it's printing T, that's what you asked for with String(describing: T.self). T is always the protocol itself.
Inside the protocol extension Self (capital 'S') is how you refer to the conforming type.
So the extension should be:
extension T {
var typeName: String {
return String(describing: Self.self)
}
}
Aside, the built-in type(of:) function already gives you the dynamic type of any object, so it's not clear that you really need to duplicate this functionality on your own.

Related

Swift Generics - Protocol does not conform to Protocol

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.

Working with generic constraints

I know this question has been asked before but I have no idea how to solve this current problem. I have defined a protocol MultipleChoiceQuestionable with an associatedtype property:
protocol Questionable {
var text: String {get set}
var givenAnswer: String? {get set}
}
protocol MultipleChoiceQuestionable: Questionable {
associatedtype Value
var answers: Value { get }
}
struct OpenQuestion: Questionable {
var text: String
var givenAnswer: String?
}
struct MultipleChoiceQuestion: MultipleChoiceQuestionable {
typealias Value = [String]
var text: String
var givenAnswer: String?
var answers: Value
}
struct NestedMultipleChoiceQuestion: MultipleChoiceQuestionable {
typealias Value = [MultipleChoiceQuestion]
var text: String
var answers: Value
var givenAnswer: String?
}
Types which conform to this protocol are saved in an array as Questionable like so:
// This array contains OpenQuestion, MultipleChoiceQuestion and NestedMultipleChoiceQuestion
private var questions: [Questionable] = QuestionBuilder.createQuestions()
Somewhere in my code I want to do something like:
let question = questions[index]
if let question = question as? MultipleChoiceQuestionable {
// Do something with the answers
question.answers = .....
}
This is not possible because Xcode warns me: Protocol MultipleChoiceQuestionable can only be used as a generic constraint. I've been searching around on how to solve this issue since generics are quite new for me. Apparently Swift doesn't know the type of the associatedtype during compile time which is the reason this error is thrown. I've read about using type erasure but I don't know if that solves my problem. Maybe I should use generic properties instead or perhaps my protocols are defined wrong?
If the action you want to apply to your sub-protocol objects does not rely on the associated type (i.e. neither has the a generic parameter nor returns the generic type) you can introduce an auxiliary protocol which just exposes the properties/methods you need, let your type conform to that protocol, and declare the question in terms of that protocol.
For example, if you just want to know some info about the question:
protocol MultipleChoiceInfo {
var numberOfAnswers: Int { get }
}
extension MultipleChoiceQuestion: MultipleChoiceInfo {
var numberOfAnswers: Int { return answers.count }
}
// do the same for the other multiple-choice types
Then you can access the questions through the new protocol like this:
let question = questions[index]
if let info = question as? MultipleChoiceInfo {
print(info.numberOfAnswers)
}
As I said, if you cannot provide an abstract (non-generic) interface then this won't work.
EDIT
If you need to process the generic data inside your questions you can extract the logic depending on the concrete generic type into another "processing" type which provides an interface to your questions. Each question type then dispatches its data to the processor interface:
protocol MultipleChoiceProcessor {
func process(stringAnswers: [String])
func process(nestedAnswers: [MultipleChoiceQuestion])
}
protocol MultipleChoiceProxy {
func apply(processor: MultipleChoiceProcessor)
}
extension MultipleChoiceQuestion: MultipleChoiceProxy {
func apply(processor: MultipleChoiceProcessor) {
processor.process(stringAnswers: answers)
}
}
Just create a type conforming to MultipleChoiceProcessor and do the type-check dance again:
if let proxy = question as? MultipleChoiceProxy {
proxy.apply(processor:myProcessor)
}
As an aside, if you don't have more protocols and structs in your real application, you might also just ditch the protocol stuff altogether... for this kind of problem it seems a bit over-engineered.

Check Protocol Inheritance at Runtime

It's easy to check protocol conformance of a type at runtime:
guard type is Encodable.Type else { ... }
but this technique fails if type is not a struct or class, but instead a protocol that inherits from Encodable. Is there a way to make a similar check for protocols?
This is a tad hacky, but it will probably solve your problem:
Given these example types:
protocol Animal: Encodable {}
struct Person: Animal {}
protocol SomeOtherProtocol {}
struct Demon: SomeOtherProtocol {}
I'm simplifing the init logic for the example by assuming:
typealias Group = Array
let animals: Group<Animal> = [Person(), Person(), Person()]
let notAnimals: Group<SomeOtherProtocol> = [Demon(), Demon(), Demon()]
This should work for a custom collection class however... Continuing, define the following extension for your custom Collection
extension Group {
func asEncodables() -> Group<Encodable>?{
return self as? Group<Encodable>
}
var isElementEncodable: Bool {
return self is Group<Encodable>
}
}
You now have access to the following:
animals.asEncodables() //returns some
notAnimals.asEncodables() // returns none
animals.isElementEncodable //true
notAnimals.isElementEncodable //false
And so for your original question, you could make your check as follows:
guard notAnimals.isElementEncodable else { return }
Hope this helps you. Currently I know of no way to make the comparison with something similar to an if X is Y :/

How can I call a static function on a protocol in a generic way?

Is there a point to declaring a static function on a protocol? The client using the protocol has to call the function on a type conforming to the protocol anyway right? That breaks the idea of not having to know the type conforming to the protocol IMO. Is there a way to call the static function on the protocol in a way where I don't have to know the actual type conforming to my protocol?
Nice question. Here is my humble point of view:
Is there a point to declaring a static function on a protocol?
Pretty much the same as having instance methods declared in a protocol.
The client using the protocol has to call the function on a type conforming to the protocol anyway right?
Yes, exactly like instance functions.
That breaks the idea of not having to know the type conforming to the protocol IMO.
Nope. Look at the following code:
protocol Feline {
var name: String { get }
static func createRandomFeline() -> Feline
init()
}
extension Feline {
static func createRandomFeline() -> Feline {
return arc4random_uniform(2) > 0 ? Tiger() : Leopard()
}
}
class Tiger: Feline {
let name = "Tiger"
required init() {}
}
class Leopard: Feline {
let name = "Leopard"
required init() {}
}
let feline: Feline = arc4random_uniform(2) > 0 ? Tiger() : Leopard()
let anotherFeline = feline.dynamicType.createRandomFeline()
I don't know the real type inside the variable feline. I just know that it does conform to Feline. However I am invoking a static protocol method.
Is there a better way to do this?
I see, you would like to call a static method/function declared in a protocol without creating a value that conforms to the protocol.
Something like this:
Feline.createRandomFeline() // DANGER: compiler is not happy now
Honestly I don't know the reason why this is not possible.
yes this is possible:
Swift 3
protocol Thing {
static func genericFunction()
}
//... in another file
var things:[Thing] = []
for thing in things {
type(of: thing).genericFunction()
}
Thank you #appzYourLife for the help! Your answer inspired my answer.
#appzYourLife answered my question. I had an underlying issue I was trying to resolve and the following code resolves my issue, so I'll post this here, maybe it helps someone with my same underlying question:
protocol MyProtocol {
static func aStaticFunc()
}
class SomeClassThatUsesMyProtocolButDoesntConformToIt {
var myProtocolType: MyProtocol.Type
init(protocolType: MyProtocol.Type) {
myProtocolType = protocolType
}
func aFunction() {
myProtocolType.aStaticFunc()
}
}
I created another solution for this case. IMHO this is quite clean and simple.
First, create a protocol for accessing instance type.
protocol TypeAccessible {
func type() -> Self.Type
}
extension TypeAccessible {
func type() -> Self.Type {
return Swift.type(of: self)
}
}
then create your concrete class as here. The point is your protocol should conform to TypeAccessible protocol.
protocol FooProtocol: TypeAccessible {
static func bar()
}
class Foo: FooProtocol {
static func bar() { }
}
On call site use it as
let instance: FooProtocol = Foo()
instance.type().bar()
For further use cases, just make sure your protocols conform to TypeAccessible and that's all.
A little late to the party on this one.
Here's my solution for "adding" static properties/functions/types to a protocol using typealias.
For example:
enum PropertyScope {
case all
case none
}
struct PropertyNotifications {
static var propertyDidChange =
Notification.Name("propertyDidChangeNotification")
}
protocol Property {
typealias Scope = PropertyScope
typealias Notifications = PropertyNotifications
var scope: Scope { get set }
}
Then you can do this anywhere in your code:
func postNotification() {
let scope: Property.Scope = .all
NotificationCenter.post(name: Property.Notifications.propertyDidChange,
object: scope)
}
Using protocols like Java interfaces is rarely a good idea. They are meta types, meant for defining contracts, which is an entirely different kind of thing.
That being said, just for the point of understanding, I find the most simple and effective way for creating the equivalent of a static factory method of a protocol to write a free function.
It should contain the protocol's name, hoping that that will prevent name clashes, and improve discoverability.
In other languages, createP would be a static member of P, named create and be called as P.create(...), which would drastically improve discoverability and guarantee to prevent name clashes.
In swift, though, this is not an option for protocols, so if protocols are for some reason really actually used as a replacement for interfaces, at least including the protocol's name in the function's name is an ugly workaround that's still slightly better than nothing.
P.S. in case the goal is actually to achieve something like an inheritance hierarchy with structs, union style enums are the tool that's meant to serve that purpose :)
protocol P
{
var x: Int { get }
}
func createP() -> P
{
if (todayIsMonday())
{
return A()
}
else
{
return B()
}
}
class A: P
{
var x = 5
}
class B: P
{
var x = 7
}
This isn't an answer so much as it is an extension to the question. Say I have:
#objc public protocol InteractivelyNameable: Nameable {
static func alertViewForNaming(completion:#escaping((_ success: Bool, _ didCancel: Bool, _ error: Error?) -> Void)) -> UIAlertController?
}
And I have a generic view controller that manages various types (generic type is .fetchableObjectType... basically NSFetchResult). I need to check if a specific object type conforms to the protocol, and if so, invoke it.
something like:
// valid swift code
if self.dataSource.fetchableObjectType is InteractivelyNameable {
// not valid swift code
if let alert = (self.dataSource.fetchableObjectType as InteractivelyNameable).alertViewForNaming(....)
}
I had a situation where I need to create same DomainModel object from 2 different response. so this (static method in protocol helped me) approach helped me.
protocol BaseResponseKeyList: CodingKey {
static func getNameKey()->Self
}
enum FirstResponseKeyList: String, BaseResponseKeyList {
case name
func getNameKey()->FirstResponseKeyList {
return .name
}
}
enum SecondResponseKeyList: String, BaseResponseKeyList {
case userName
func getNameKey()->SecondResponseKeyList {
return .userName
}
}
struct MyDomainModel<T:BaseResponseKeyList> : Decodable {
var name:String?
required init(from d:Decoder) {
do {
let container = try d.container(keyedBy:T.self)
name = try container.decode(String.self, forKey:T.getNameKey())
}catch(_) {
print("error")
}
}
}
let myDomainModel = try JSONDecoder().decode(MyDomainModel <FirstResponseKeyList>.self, from: data)
let myDomainModel2 = try JSONDecoder().decode(MyDomainModel <SecondResponseKeyList>.self, from: data2)

Conform to a protocol misunderstanding

class SuperDelegate <T: AnyObject> {
func addDelegate(delegate: T)
{
}
}
My question is about T key, does it mean the same as id in Objective-c? I mean about case of uses.
how to understand the first line class SuperDelegate <T: AnyObject> Sorry I am new in Swift.
As Objective C program for me this line means that we make class to conform a protocol that has to implement all required method. But I don't understand func addDelegate(delegate: T) is this the same like
- (void)addDelegate:(id)delegate which is a property id <T> delegate.
Yes you are correct in your assumptions that AnyObject behaves like id:
You can call any Objective-C method and access any property on an
AnyObject value without casting to a more specific class type. This
includes Objective-C compatible methods and properties marked with the
#objc attribute.
but you have used it here as a generic type rather than as a concrete type that should be cast to. The class is requiring a type that adheres to the AnyObject protocol but it isn't forcing it to be AnyObject (see header files: cmd + click on AnyObject inside Xcode).
So your instance could be instantiated SuperDelegate<AnyObject> but it could also be instantiated SuperDelegate<NSDate>. This means that the whole subset of ObjC methods and properties cannot be guaranteed as they can with a cast to AnyObject as a concrete type because at runtime T might represent NSDate or NSNumber or any other class.
To achieve what you want you would need to write:
class SuperDelegate {
func addDelegate(delegate: AnyObject)
{
}
}
But Swift is a strongly-typed language and it would normally be the case that you had a delegate protocol and that the delegate for your type adhered to the delegate protocol:
protocol DelegateProtocol {
func somethingHappened()
}
struct MyTypeDelegate:DelegateProtocol {
func somethingHappened() {
print("Thanks for telling me!")
}
}
struct MyType {
var delegate:DelegateProtocol?
func tellDelegateSomethingHappened() {
delegate?.somethingHappened()
}
}
let del = MyTypeDelegate()
var type = MyType()
type.delegate = del
type.tellDelegateSomethingHappened()