[Swift] Elements that make up the protocol

Protocol components

Like types, properties and methods are listed as components, Since the definition method is different, I will explain that.

I also make a lot of mistakes, so I hope it will be helpful for similar people.

Property

You can define properties for the protocol. By defining a property, you can request the implementation of the property for ** compliant types. ** **

Definition method

How to define protocol properties ** Define property name, type, getter and setter only **.

Also, the property is always declared with the var keyword, depending on the presence or absence of getters and setters in {}. Add the get and set keywords.

The reason why you cannot use the let keyword is that the protocol properties There is no distinction between stored and compute properties.


protocol protocol name{
var property name:Mold{ get set }
}

Getter implementation

If the property exists in a compliant protocol, You need to implement that property.

If the property has only getters, ** Whether to implement a variable or constant stored property You need to implement a compute property with a getter. ** **


protocol SampleProtocol {
    var value: Int { get }
}

//Variable stored property
struct Sample1: SampleProtocol {
    var value: Int
}

//Constant stored property
struct Sample2: SampleProtocol {
    let value: Int
}

//Getter-only Computed Properties
struct Sample3: SampleProtocol {
    var value: Int {
        return 1
    }
}

Implementation of setter

If the properties defined in the protocol require getters and setters, ** Implement a variable stored property or You need to implement a compute property that has both getters and setters. ** **

Compared to the getter implementation, the reason why there is no implementation with constant stored properties is This is because constant stored properties cannot be modified and therefore cannot meet the requirements.


protocol SampleProtocol {
    var value: Int { get set }
}

//Variable stored property
struct Sample1: SampleProtocol {
    var value: Int
}

//Getter and setter compute properties
struct Sample3: SampleProtocol {
    var value: Int {
        get {
            return 1
        }
        set { }
    }
}

//Constant stored property
struct Sample2: SampleProtocol {
    let value: Int    //Compile error
}

Error details: Type'Sample2' does not conform to protocol'SampleProtocol' Japanese translation: Type "Sample2" does not comply with protocol "Sample Protocol"

Method

You can define methods in the protocol. By defining a method, you can request the implementation of the method for a ** compliant type. ** **

Definition method

To define the method of the protocol, define only ** method name, argument type, and return type **. Also, like properties, it provides an implementation that meets that requirement with a protocol-compliant type.

Note that you do not need to write {} when defining a method in the protocol. (Please define such a method! It's a nuance.)


protocol protocol name{
func function name(argument) ->Return type
}

Method implementation

To comply with the protocol in which the method is defined Implement a method with the same interface.


protocol SampleProtocol {
    func sampleMethod() -> Int
    static func sampleStaticMethod() -> Void
}

struct Sample: SampleProtocol {
    func sampleMethod() -> Int {
        return 1
    }
    
    static func sampleStaticMethod() {
        
    }
}

mutating keyword

If you want to change the value of a property that you have in a value type method, I think I added the mutating keyword.

** If you define a method that can be changed with respect to the protocol, Add and define the mutating keyword. ** **

If you have defined a method in the protocol but it does not have the matating keyword You cannot add the muting keyword when implementing with a compliant type.

Conversely, even if the method defined in the protocol has the muting keyword. There is no need to add the muting keyword when implementing with a compliant type.

Also, in reference type methods, the mating keyword is used. Since it is not necessary to distinguish whether the instance has changed or not, ** You do not need to add the muting keyword to make the class compliant with the protocol. ** **


protocol SampleProtocol {
    mutating func sampleMutatingMethod()
    func sampleMethod()
}

struct SampleStruct: SampleProtocol {
    var value: Int
    
    mutating func sampleMutatingMethod() {
        value = 1
    }
    
    func sampleMethod() {
        value = 1   //Compile error
    }
}

class SampleClass: SampleProtocol {
    var value = 0
    
    func sampleMutatingMethod() {
        value = 1
    }
    
    func sampleMethod() {
        value = 1
    }
}

Associative type

In the previous explanation, it was necessary to decide all the types when defining the protocol.

By specifying the type in advance, it is difficult for unintended processing to be performed. It depends on one type and it is not convenient.

The associative type can be used there. It allows you to specify the type when the ** protocol is compliant. ** **

On the protocol side, the associative type acts as a placeholder (temporary value) and The compliant type specifies the actual associative type.

Associative types allow you to define a more abstract protocol that is independent of one type.

Definition method

Protocol associative names are defined using the associated type keyword. Associative types can be used as argument and return types for properties and methods within the same protocol.


protocol protocol name{
associatedtype associative type name

var property name:Associative type name
func method name(Argument name:Associative type name)
func method name() ->Associative type name
}

The actual type of associative type can be determined for each protocol-compliant type.

There are a total of three ways to determine the type. ** ① Use type alias and define typealias associative type name = specified type name (2) The associative type is automatically determined from the implementation. ③ Specify by nesting with the same name **

In the following sample code The SampleProtocol protocol defines an associative association. We also use associative types in properties and methods.

** Sample1 type uses a type alias to specify the Associative type. ** ** The definition method is typealias associative type name = specified type name.

All Associations of Sample1 type are treated as Int type.

** In Sample2 type, the type is specified from the implementation and the type alias is omitted. ** ** By describing the Int type in the place defined as Associative in the protocol, The Associative type is automatically determined to be the Int type.

** Sample3 type defines a nested type with the same name as the associative type. ** ** This type is an associative type.


protocol SampleProtocol {
    associatedtype Associative
    
    var value: Associative { get }
    func printValue(value: Associative) -> Associative
}

//① Define Associative
struct Sample1: SampleProtocol {
    typealias Associative = Int

    var value: Associative
    func printValue(value: Associative) -> Associative {
        return value
    }
}

//(2) Automatically determined from mounting
struct Sample2: SampleProtocol {
    
    var value: Int
    func printValue(value: Int) -> Int {
        return value
    }
}

//③ Define a nested type with the same name as the associative type
struct Sample3: SampleProtocol {
    struct Associative { }
    
    var value: Associative
    func printValue(value: Associative) -> Associative {
        return value
    }
}

Personally, the number of descriptions is reduced, and I think that ② is good because it can be understood at a glance. There may be rules in the actual business environment, so it may depend on it!

Add type constraint

Specify the protocol or superclass that the associative type of protocol should comply with, You can place constraints on the associative type.

To add a constraint, use : protocol name (or superclass name) after the associative declaration. The compiler checks if the associative type meets the type constraints, If it is not satisfied, a compilation error will occur.


protocol protocol name{
associatedtype associative type name:Protocol name or superclass name
}

The actual description looks like the following sample code.

In the associated type Associative: SuperSample part of the protocol, There are restrictions on the associative type.

The constraint is ** SuperSample type or a type that inherits SuperSample type. ** **

class Sample: SuperSample {} Since the Sample type inherits the SuperSample type, The type alias Associative = Sample of SampleStruct1 holds.

On the contrary, SampleStruct2 specifies Int type, so it is blocked by restrictions. I get a compile error.


class SuperSample {}

protocol SampleProtocol {
    //Must be of type SuperSample or inherit from it
    associatedtype Associative: SuperSample
}

//Inherits the SuperSample type
class Sample: SuperSample {}


struct SampleStruct1: SampleProtocol {
    //Sample type inherits SuperSample type
    typealias Associative = Sample
}

struct SampleSatuct2: SampleProtocol {
    //Int type does not inherit SuperSample type
    typealias Associative = Int   //Compile error
}

Error details: Type'SampleSatuct2' does not conform to protocol'SampleProtocol' Japanese translation: Type "SampleSatuct2" does not comply with protocol "SampleProtocol"

Specifying the default value

You can specify a default value for the protocol associative type at the same time as the declaration.

If you specify a default value ** Specifying an associative type on the type side that conforms to the protocol is optional. ** **


protocol SampleProtocol {
    associatedtype Associative = Int
}

struct Sample1: SampleProtocol {
    // Sample1.Associative becomes the default Int type
}

struct Sample2: SampleProtocol {
    // Sample2.Associative will be of type String
    typealias Associative = String
}

Protocol inheritance

Protocols can inherit from other protocols.

** Protocol inheritance is simply defined by the inheriting protocol It just inherits properties, methods, etc. to the protocol. ** **

There is no concept like overrides in classes.

Inheritance method is to inherit multiple inheritance by separating with , like when making the protocol conform to the type.


protocol Protocol name 1:Protocol name 2,Protocol name 3{
Protocol definition
}

I wrote the sample code based on the previous code.

ProtocolC inherits ProtocolA and ProtocolB, so It is a protocol that requires both id and title.

So when defining the Sample type, You need to define two things, id and title.


protocol ProtocolA {
    var id: Int { get }
}
protocol ProtocolB {
    var title: String { get }
}

//It is a protocol that requires both id and title.
protocol ProtocolC: ProtocolA, ProtocolB {}

struct Sample: ProtocolC {
    var id: Int
    var title: String
}

The above are the elements that make up the protocol.

I think it's quite complicated, so Try writing your own code to get a deeper understanding!

In addition to this article, there is an article about the protocol, so please have a look!

-[Swift] Protocol concept and definition method -[Swift] Protocol Extension

Thank you for watching until the end.

Recommended Posts

[Swift] Elements that make up the protocol
[Swift] API used for apps that passed the selection
[Swift] About protocol extension
[Swift] Lightly introduce the logic of the app that passed the selection
[Docker] The story that an error occurred in docker-compose up