diegoRodicio

Está documentación está a túa disposición sin ningún custo económico. Sen embargo, para a súa elaboración dedico moito tempo e recursos, polo que agradecería unha colaboración co que consideres oportuno. Gracias.

Un protocolo en define métodos e propiedades que deben ser implementados de xeito obrigatorio nunha Class, Struct ou Enum.

Os protocolos tamén son chamados interfaces ou contratos.

Sintaxe dun Protocolo #

En Swift podemos crear un protocolo engadindo a keyword protocol seguido do nome que queremos dar ao protocolo:

protocol OMeuProtocolo {

}

Unha vez temos creado o noso protocolo, xa o podemos usar nun tipo. Para indicar que un tipo conforma un protocolo engadimos : e a continuación engadimos o nome do protocolo:

protocol OMeuProtocolo {

}

struct Usuario: OMeuProtocolo {
    var idade: Int
}

Neste caso o tipo Usuario conforma OMeuProtocolo.

Funcionamento #

Por defecto, non hai regras específicas ás que unha clase Swift deba cumprir. Con todo, nalgunhas situacións, unha clase terá que cumprir con certos criterios para poder traballar con outras clases.

  • Isto é particularmente común cando se escriben clases que necesitan traballar cos diversos frameworks que compoñen o SDK de iOS.
  • Un conxunto de regras que definen os requisitos mínimos que unha clase debe cumprir denomínase Protocolo.
  • Un protocolo declárase utilizando a palabra chave protocol e simplemente define os métodos e propiedades que debe conter obrigatoriamente unha clase.

Consideremos o seguinte protocolo:

protocol ConstrutorMensaxe {
    var nome: String{get}
    func construirMensaxe() -> String
}

Calquera clase que adopte este protocolo debe incluír de xeito obrigatorio:

  • Un valor de cadea lexible chamado nome
  • Un método chamado construirMensaxe() que non acepte parámetros e devolva unha cadea
protocol ConstrutorMensaxe {
    var nome: String{get}
    func construirMensaxe() -> String
}

class MiClase:ConstrutorMensaxe {
    var nome: String
    
    init(nome: String){
        self.nome = nome
    }
    func construirMensaxe() -> String {
        return "Ola \(nome)"
    }
}

var miObjeto = MiClase(nome: "Lola Mento")
print(miObjeto.construirMensaxe()) //Imprime: Ola Lola Mento

some – Tipos de devolución opacos #

En lugar de especificar un tipo de retorno específico (tamén coñecido como tipo concreto), os tipos de retorno opacos permiten que unha función devolva calquera tipo a condición de que se axuste a un protocolo determinado.

Os tipos de retorno opacos decláranse precedendo o nome do protocolo coa palabra chave some.

No seguinte exemplo, a función doblar(), declara que se devolverá un resultado de calquera tipo que se axuste ao protocolo Equatable:

func doblar(numero: Int) -> some Equatable {
    return numero * 2
}

print("Chamada a funcion doblar. Devolve o seguinte valor que cumple o protocolo Equatable: \(doblar(numero: 10))")
//Imprime: Chamada a funcion doblar. Devolve o seguinte valor que cumple o protocolo Equatable: 20

NOTA: O protocolo Equatable, que é un protocolo estándar proporcionado con Swift, un tipo debe permitir que os valores se podan comparar para a igualdade.

Dado que tanto os tipos concretos Int como String están en conformidade co protocolo Equatable, tamén é posible crear unha función que devolva un String:

func doblarCadea(cadea: String) -> some Equatable {
    return cadea + cadea
}

print("Chamada a funcion doblarCadea. Devolve o seguinte valor que cumple o protocolo Equatable: \(doblarCadea(cadea: "Lola Mento"))")
//Imprime: Chamada a funcion doblar. Devolve o seguinte valor que cumple o protocolo Equatable: Lola MentoLola Mento0

A maiores… #

Aínda que os dous métodos anteriores devolven tipos concretos completamente diferentes, o único que se sabe sobre estes tipos é que se axustan ao protocolo Equatable. Por tanto, coñecemos as capacidades do tipo, pero non o tipo real.

  • De feito, só coñecemos o tipo concreto devolto nestes exemplos porque temos acceso ao código fonte das funcións.
  • Se estas funcións residen nunha biblioteca ou framework de API para o que a fonte non está dispoñible para nós, non saberiamos o tipo exacto que se devolve.
  • Isto é intencional e está deseñado para ocultar o tipo de retorno subxacente utilizado dentro das API públicas.

Ao enmascarar o tipo de retorno de concreto, os programadores non confiarán nunha función que devolva un tipo concreto específico ou se arriscarán a acceder a obxectos internos aos que non se pretendía acceder. Isto tamén ten o beneficio de que o desarrollador da API pode facer cambios na implementación subxacente (incluída a devolución dun tipo de protocolo diferente) sen ter que preocuparse por romper as dependencias en calquera código que utilice a API.

Os tipos de retorno opacos son unha base fundamental da implementación das API de SwiftUI e utilízanse amplamente cando se desenvolven aplicacións en SwiftUI (a palabra some aparecerá con frecuencia nas declaracións de View en SwiftUl).

Extensións #

Cando creamos un tipo como o seguinte, podemos crear os métodos dentro do tipo, tal como vimos na sección anterior:

class Database {
    func connect() {
        print("Connected")
    }

    func addData() {
        print("Add Data")
    }

    func removeData() {
        print("Remove Data")
    }
}

En Swift podemos crear extensións dun tipo e mover a lóxica, é dicir, podemos usar a keyword extension seguido do tipo Database para mover algúns métodos:

class Database {

}

extension Database {
    func connect() {
        print("Connected")
    }

    func addData() {
        print("Add Data")
    }

    func removeData() {
        print("Remove Data")
    }
}

Desta maneira creamos unha extensión para agrupar certos métodos que visualizan por consola unha mensaxe. Agora podemos crear unha instancia de Database e usar os métodos que hai na extensión:

let database = Database()

database.connect()
database.addData()
database.removeData()

// RESULTADO 👇
// Connected
// Add Data
// Remove Data

O que acabamos de ver tamén o podemos implementar nos protocolos.

Podemos crear extensións dos protocolos e implementar un comportamento por defecto en todas aquelas funcións que un tipo deba implementar.

Imos velo paso a paso, a continuación creamos o seguinte protocolo:

protocol Visualizable {
    var información: String { get }
    func visualizarResultado()
}

Directamente podemos crear unha implementación por defecto do noso protocolo nunha extensión. Desta maneira non teremos que crear a mesma implementación en cada tipo que conforme o protocolo Visualizable.

A implementación que usarán os tipos é a que imos crear a continuación:

protocol Visualizable {
    var información: String { get }
    func visualizarResultado()
}

extension Visualizable {
    func visualizarResultado() {
        print("Mensaxe da extension do protocolo Visualizable: \(información)")
    }
}

Agora imos crear un tipo chamado Usuario que conformará o protocolo Visualizable.
Neste caso só debemos crear a propiedade información.

struct Usuario: Visualizable {
    var información: String { "Diego Rodicio" }
}

let drodicio = Usuario()
drodicio.visualizarResultado()

// RESULTADO
// Mensaxe da extension do protocolo Visualizable: "Diego Rodicio"

Vemos que usando as extensións en protocolos podemos evitar crear a mesma implementación en cada tipo.

Share Your Valuable Opinions