Consider the following code:
protocol MyProtocol {
static var name: String { get }
}
extension MyProtocol {
static var name: String {
return "unnamed"
}
}
// does not specify its own name
class MyClass: MyProtocol {
}
//specifies its own name!
class MyClass2: MyProtocol {
static var name: String {
return "Specific name"
}
}
let myClass = MyClass()
print("\(MyClass.name)")
//>>"unnamed"
let myClass2 = MyClass2()
print("\(MyClass2.name)")
//>>"Specific name"
Does swift guarantee that for classes (such as in this case MyClass2) that have an actual implementation of a protocol property, in this case "name", that this is used from the class, and not the one of the default "name" implementation via the protocol extension?
Having a default implementation for a required protocol function/property means that your conforming types won't have to implement that function/property, they can use the default implementation instead.
However, if a conforming type does implement the function/property, then the compiler will always call the more specific implementation, namely the one in your conforming class and not the default one.
So even if you stored an instance of MyClass2 in a variable of type MyProtocol, you'd still get the MyClass2 implementation when accessing the property on the variable.
let myClass2: MyProtocol = MyClass2()
type(of: myClass2).name // "Specific name"
The behaviour is different for non-required properties/functions declared and defined in a protocol extension. If you declare a property/function in the protocol extension only, then even if you provide a different implementation for that in a conforming class, you won't be able to access that implementation from a variable whose type is the protocol type rather than the specific conforming type.
protocol MyProtocol {
static var name: String { get }
}
extension MyProtocol {
static var name: String {
return "unnamed"
}
// Optional protocol requirement
static var nonRequired: String {
return "nonRequired"
}
}
// does not specify its own name
class MyClass: MyProtocol { }
//specifies its own name!
class MyClass2: MyProtocol {
static var name: String {
return "Specific name"
}
// Specific implementation
static var nonRequired: String {
return "Specific"
}
}
let myClass = MyClass()
MyClass.name
let myClass2: MyProtocol = MyClass2()
type(of: myClass2).name // "Specific name"
type(of: myClass2).nonRequired // "nonRequired"
MyClass2.nonRequired // "Specific"
Related
I have a protocol. This is implemented by many structs that fall into one of two types of category: TypeOne and TypeTwo. I want to be able to distinguish between their types, so I've added an enum ProtocolType that defines the types typeOne and typeTwo. By default I set the protocolType to be typeOne, but I manually specify typeTwo when it's a TypeTwo struct:
enum ProtocolType {
case typeOne
case typeTwo
}
protocol MyProtocol {
let name: String { get }
var protocolType: ProtocolType { get }
}
extension MyProtocol {
var protocolType: ProtocolType {
return .typeOne
}
}
enum TypeOne {
struct Foo: MyProtocol {
let name = "foo"
}
}
enum TypeTwo {
struct Bar: MyProtocol {
let name = "bar"
let protocolType = .typeTwo
}
}
Is there any way I can remove the necessity for defining protocolType in all structs and somehow use generics to identify what type a struct is? They're already defined under the TypeOne and TypeTwo convenience enums, I was wondering if I could utilise that some how?
Given some protocol:
protocol MyProtocol {
var name: String { get }
}
It sounds like you want to "tag" certain types as special, even though they have the same requirements. That's not an enum, that's just another type (protocol):
// No additional requirements
protocol SpecialVersionOfMyProtocol: MyProtocol {}
You can then tag these at the type level, not the value level:
struct Foo: MyProtocol {
let name = "foo"
}
struct Bar: SpecialVersionOfMyProtocol {
let name = "bar"
}
And you can tell the difference using is if you need to:
func f<T: MyProtocol>(x: T) {
if x is SpecialVersionOfMyProtocol {
print("special one")
}
}
In most cases, though, I wouldn't use this kind of runtime check. I'd just have two protocols (one for TypeOne and one for TypeTwo), and implement whatever you need as extensions on those. For example, say you want to print the name differently depending on the type. Start with a protocol that just expresses that:
protocol NamePrintable {
var name: String { get }
func printName()
}
func printIt<T: NamePrintable>(x: T) {
x.printName()
}
Then extend that for TypeOnes and TypeTwos:
protocol TypeOne: NamePrintable {}
extension TypeOne {
func printName() {
print("I'm a type one with the name \(name)")
}
}
protocol TypeTwo: NamePrintable {}
extension TypeTwo {
func printName() {
print("I'm a type two with the name \(name)")
}
}
And conform your structs:
struct Foo: TypeOne {
let name = "foo"
}
struct Bar: TypeTwo {
let name = "bar"
}
printIt(x: Foo()) // I'm a type one with the name foo
printIt(x: Bar()) // I'm a type two with the name bar
If you want a default implementation, you can hang it on NamePrintable, but I kind of recommend not doing that for what you've described. I'd probably just have "type one" and "type two" explicitly.
extension NamePrintable {
func printName() {
print("BASE FUNCTIONALITY")
}
}
A simplified version of what I currently have, taken from a Playground file I setup:
import Foundation
/// Simplified protocol here
protocol MyProtocol: CaseIterable {
static var count: Int { get }
var name: String { get }
}
/// Simplified extension. This works fine with app
extension MyProtocol {
static var count: Int {
return Self.allCases.count
}
}
/// Simplified enum, this works fine as well
enum MyEnum: MyProtocol {
case value
var name: String {
return "name"
}
}
Using the following works as expected:
print(MyEnum.count) // 1
let myEnum = MyEnum.value
print(myEnum.name) // name
However, I would like to create an object that is initialized with MyEnum.
First, I attempted the following:
final class MyManager {
private let myEnum: MyProtocol
init(myEnum: MyProtocol) {
self.myEnum = myEnum
}
}
However, both spots where I use MyProtocol provide the following error:
Protocol 'MyProtocol' can only be used as a generic constraint because
it has Self or associated type requirements
I then switched it up with the following that got rid of the error, but produces a new issue:
final class MyManager<MyProtocol> {
private let myEnum: MyProtocol
init(myEnum: MyProtocol) {
self.myEnum = myEnum
}
}
When I attempt to access properties for myEnum, they do not appear in Xcode:
I need to be able to access the properties defined in MyProtocol, but neither approach is working properly for me & I have run out of ideas.
The MyProtocol generic is not the same as the MyProtocol protocol. You need something like this
final class MyManager<MyP: MyProtocol> {
private let myEnum: MyP
init(myEnum: MyP) {
self.myEnum = myEnum
print(myEnum.name)
}
}
I also want to point out that a better way to implement count is by extending CaseIterable
extension CaseIterable {
static var count: Int {
return Self.allCases.count
}
}
In the code below, when I try to access genericVar.someFunc() I get the error
"Value of type 'MyProtocol?' has no member 'someFunc'".
Being a generic variable, when I initialize the MyOtherStruct object, I will have to pass a concrete implementation of a MyProtocol conforming object, so why would I be getting this error?
public protocol MyProtocol {
associatedtype T
func someFunc() -> T
}
public struct myStruct: MyProtocol {
public typealias T = Int16
public func someFunc() -> Int16 {
let myVar: Int16 = 7
return myVar
}
}
public struct myOtherStruct<MyProtocol> {
var genericVar: MyProtocol?
public init(localVal: MyProtocol?) {
self.genericVar = localVal
if genericVar != nil {
var my = genericVar.someFunc()
}
}
}
Your generic type declaration is wrong. MyProtocol in the brackets is the name of the generic type parameter rather than the actual protocol. You need to declare another name for the generic type parameter and constrain it to MyProtocol like this: struct MyOtherStruct<T:MyProtocol>. Here T will be the generic type parameter of the struct and the :MyProtocol syntax enforces that T conform to myProtocol.
public struct MyOtherStruct<T:MyProtocol> {
var genericVar: T?
public init(localVal: T?) {
self.genericVar = localVal
if let genericVar = genericVar {
let my = genericVar.someFunc()
}
}
}
Some other things to consider: you should conform to the Swift naming convention, which is UpperCamelCase for types and when you want to access a property/method on an Optional value, instead of doing a nil comparison like if genericVar != nil you should use optional binding using if let.
I have a protocol and a class which I want to extend. The protocol requires field of some type and the class has a field with the same name and the type as Implicitly Unwrapped Optional of this type.
Can this class be extended by this protocol? If yes, then how?
If I try to write an extension, Xcode give an error of not conforming. But if I add the field into the extension, it gives an error of redeclaration.
protocol Named {
var name: String { get }
}
class Person {
var name: String!
}
extension Person: Named {
// Type 'Finances.Account' does not conform to protocol 'Named'
}
Property names and types declared in a protocol must exactly be matched by the conforming classes.
So you cannot resolve the error without changing the property type in either the protocol or the conforming type. You could also rename one of the properties and add the matching property to the conforming type as a new field.
So either do:
protocol Named {
var name: String { get }
}
class Person {
var name: String
init(_ name:String) {
self.name = name
}
}
extension Person: Named {
}
Or
protocol Named {
var name: String { get }
}
class Person {
var _name: String!
}
extension Person: Named {
var name: String {
return _name
}
}
As #user28434 pointed out, there's a(n ugly) workaround. You can create a wrapper protocol that matches the optionality of the Person class, make that protocol inherit from the original protocol, declare the non-optional variable in an extension on the new protocol and make Person conform to the new protocol instead of the original Named.
protocol Named {
var name: String { get }
}
class Person {
var name: String!
}
protocol Namedd: Named {
var name: String! { get }
}
extension Namedd {
var name: String {
return name!
}
}
extension Person: Namedd {
}
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())
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.