sean woodward
sean woodward

Reputation: 1783

What is the proper way to reference a static variable on a Swift Protocol?

Assume a protocol defined below:

protocol Identifiable {
  static var identifier: String { get }
}
extension Identifiable {
  static var identifier: String { return "Default Id" }
}

What is the best way to reference the static variable? The example below illustrates two ways to access the variable. What is the difference, and is the type(of:) better?

func work<I: Identifiable>(on identifiable: I) {
  let identifier: String = I.identifier
  print("from Protocol: \(identifier)")

  let identiferFromType: String = type(of: identifiable).identifier
  print("using type(of:): \(identiferFromType)")
}

struct Thing: Identifiable {
  static var identifier: String { return "Thing" }
}

work(on: Thing())

Upvotes: 6

Views: 9858

Answers (1)

Hamish
Hamish

Reputation: 80951

In the example you show, there is no difference. Because identifier is a protocol requirement, it will be dynamically dispatched to in both cases, therefore you don't need to worry about the wrong implementation being called.

However, one difference arises when you consider the value of self inside the static computed property when classes conform to your protocol.

self in a static method/computed property is the metatype value that it's is called on. Therefore when called on I, self will be I.self – which is the static type that the compiler infers the generic placeholder I to be. When called on type(of: identifiable), self will be the dynamic metatype value for the identifiable instance.

In order to illustrate this difference, consider the following example:

protocol Identifiable {
    static var identifier: String { get }
}

extension Identifiable {
    static var identifier: String { return "\(self)" }
}

func work<I : Identifiable>(on identifiable: I) {
    let identifier = I.identifier
    print("from Protocol: \(identifier)")

    let identiferFromType = type(of: identifiable).identifier
    print("using type(of:): \(identiferFromType)")
}

class C : Identifiable {}
class D : C {}

let d: C = D()

// 'I' inferred to be 'C', 'type(of: d)' is 'D.self'.
work(on: d)

// from Protocol: C
// using type(of:): D

In this case, "which is better" completely depends on the behaviour you want – static or dynamic.

Upvotes: 3

Related Questions