Il y a 4 mois -

Temps de lecture 3 minutes

Pépite 15 – Le saviez-vous ? Les secrets de Swift.Result !

Longtemps réclamé, déjà implémenté par de nombreuses bibliothèques tierces, Result fait enfin son apparition dans la standard library de Swift 5.0.

Si de nombreux articles se sont déjà attardés sur ses fonctionnalités, peu ont expliqué les ajouts qu’il a nécessité dans la standard library. Petit tour d’horizon des secrets d’Avengers: EndGame de Result.

Un Result peut en cacher un autre

Comme expliqué auparavant, Result était déjà implémenté dans plusieurs bibliothèques majeures. Dès lors, beaucoup de personnes avaient peur de voir leur code ne plus compiler avec Swift 5.0, Swift.Result risquant de rentrer en conflit avec les bibliothèques tierces.

C’est pour éviter cet écueil que la Core Team a introduit la notion de type shadowing : dorénavant, n’importe quel type ayant le même nom qu’un type de la standard library aura précédence sur celui-ci.

import Alamofire

// Equivalent à func doSomething() -> Alamofire.Result<Int>
func doSomething() -> Result<Int> { }

 

Le revers de la médaille est qu’il peut être parfois difficile de voir à la première lecture si le type utilisé est celui de la standard library ou d’une bibliothèque.

import Foundation
import RxSwift

// Pas d'import définissant un Result
// Donc ici Result == Swift.Result
func doSomething() -> Result<Int, Error> { }

Error en votre faveur

En Swift, il est (pour le moment) impossible d’utiliser un protocole comme type concret d’un générique ayant des contraintes.

struct Request<Body: Encodable> {
	let body: Encodable?
}

struct InfinityStone: Encodable {
...
}

// KO => Body == Encodable (protocole)
// Using 'Encodable' as a concrete type conforming to protocol 'Encodable' is not supported
let request: Request<Encodable> = Request<InfinityStone>()

// OK => Body == InfinityStone (type concret)
let request = Request<InfinityStone>()

 

Pourtant le code suivant compile :

// OK => Success == Int, Error == Error (protocole)
let result = Result<Int, Error>(2)

 

Et alors même que Result est défini comme suit dans la standard library : struct Result<Success, Failure: Error> { }.
La logique aurait donc voulu que nous ayons un message « Using ‘Error’ as a concrete type conforming to protocol ‘Error’ is not supported ».

(swift evolution 235) nous donne l’explication : Error conforme dorénavant à… Error !

Cet ajout à la standard library permet d’utiliser Result sans avoir à préciser des Error concrets, ce que l’on souhaite dans 90% des cas.

let snapResult: Result<Void, Error> = Result { throw SnapError.infinityStoneMissing }

 

Dans les 10% restants, le code à écrire sera quelque peu plus complexe :

// KO => 'Result<Void, SnapError>' is not convertible to 'Result<Void, Error>'
let snapResult = Result<Void, SnapError> { throw SnapError.infinityStoneMissing }

// OK
let snapResult = Result<Void, SnapError>.failure(.infinityStoneMissing)

// KO => cannot convert value of type 'Result<Void, SnapError>' to specified type 'Result<Void, Error>'
let anySnapResult: Result<Void, Error> = snapResult

// OK
let anySnapResult: Result<Void, Error> = snapResult.mapError { $0 as Error }

 

Voila, vous en savez un peu plus sur Result. Si c’est indéniablement un type utile, son insertion dans la standard library aura nécessité de procéder à quelques ajustements pour nous permettre de l’utiliser facilement. Pour les plus curieux d’entre vous, vous pouvez lire le thread [Revised] SE-0235: Add Result to the Standard Library pour connaître la genèse du projet, ou bien l’article The power of Result types in Swift pour apprendre à utiliser Result.

Publié par Jean-Christophe Pastant

Jean-Christophe est consultant iOS et partage régulièrement ses bonnes pratiques avec la communauté iOS.

Commentaire

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Xebian, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.