実際のアプリを作るとき、多数のモデルが存在することになると思いますが、
APIから取得したデータはIDというプロパティを持つことが多いと思います。
それらはStringやIntなどの型で表現されると思いますが、
メソッドの引数名によっては誤ったモデルのIDを入れてしまうヒューマンエラーが起こる可能性があります。
それをSwiftの型を用いて解決したいと思います。
ヒューマンエラーの例
fetchLikes
というメソッドはStringのidを引数に取るため、LikeでもUserでもidがStringで宣言されているので、引数にLikeのIDを入れることができてしまいます。
final class LikeRepository {
/// UserのIDをもとにUserのお気に入り一覧を取得する
func fetchLikes(id: String) -> [Like] {
...
}
}
struct User {
var id: String
}
struct Like {
var id: String
}
let user = User(id: "id")
let repository = LikeRepository()
// 正しい使い方
repository.fetchLikes(id: user.id)
let like = Like(id: "id")
// 誤った使い方でもコンパイルできてしまうので、問題に気づけない
repository.fetchLikes(id: like.id)
解決策
ユーティリティを実装する
protocol Identifiable {
typealias ID = Identifier<Self>
var id: Identifier<Self> { get }
}
struct Identifier<Object>: Hashable {
var rawValue: String
}
/// User.IDなどのIdentifierをStringで初期化可能にする
extension Identifier: ExpressibleByStringLiteral {
init(stringLiteral value: StringLiteralType) {
self = Identifier(rawValue: value)
}
}
使い方
struct User: Identifiable, Equatable {
var id: ID
}
struct Like: Identifiable, Equatable {
var id: ID
}
final class LikeRepository {
func fetchLikes(id: User.ID) -> [Like] {
// id.rawValueでStringにアクセスできる
...
}
}
let user = User(id: "id")
let repository = LikeRepository()
// 正しい使い方
repository.fetchLikes(id: user.id)
let like = Like(id: "id")
// エラーになりコンパイルできないので、ミスに気づける
// Cannot convert value of type 'Like.ID' (aka 'Identifier<Like>') to expected argument type 'User.ID' (aka 'Identifier<User>')
repository.fetchLikes(id: like.id)