userIDやUUIDを同じパラメータとして扱い、サーバー側でよしなに判別するAPIを呼び出す時にどのように書けば良いかを考えていきます。
GET /v1/users/{user_identity}
このAPIのuser_identityは次のようにuserのidでもUUIDでも呼び出すことができます。
GET /v1/users/123456
GET /v1/users/AAAAA-BBBB-CCCCC-DDDD
このAPIを呼び出すために2つのメソッドを定義しました。
func fetchUser(byID id: Int) {
request(url: "/v1/users/\(id)")
}
func fetchUser(byUUID uuid: String) {
request(url: "/v1/users/\(uuid)")
}
内部の処理は同じなので、次のように一つのメソッドにまとめてみます。
func fetchUser(by id: Int? = nil, uuid: String? = nil) {
if id == nil && uuid == nil {
fatalError()
}
if id != nil && uuid != nil {
fatalError()
}
if let id = id {
request(url: "/v1/users/\(id)")
} else if let uuid = uuid {
request(url: "/v1/users/\(uuid)")
}
}
fetchUser
は、idかuuidを埋めれば適切にapiを呼ぶようになりました。
ただし、idとuuidはどちらか一つを与えるようにしなくてはfatalError
が発生します。
fetchUser(uuid: userUUID) //OK
fetchUser() // Crash
fetchUser(by: userID, uuid: userUUID) // Crash
あなたは実装者なのでこのパラメータの与え方のルールを知っていますが、他の開発者は中のコードを読むか、あなたが書いたコメントを読むか、はたまた間違えて呼んでクラッシュするのを確認しないと、気がつくことは出来ません。
不正な呼び出しをコンパイルの時点で気が付けない事も良くないですね。
そこで、Swiftの強力なenumを利用します。
enum UserIdentity {
case id(Int)
case uuid(String)
}
func fetchUser(by identity: UserIdentity) {
switch identity {
case .id(let id):
request(url: "/v1/users/\(id)")
case .uuid(let uuid):
request(url: "/v1/users/\(uuid)")
}
}
fetchUser(by .uuid(userUUID))
このようにenumで識別子を管理する事で、fetchUser
を呼ぶには必ず任意の識別子を一つ指定する制約が生まれました。
間違えた呼び方をしてビルドすることはもう出来ません。
おまけ
今回のケースでは、APIのパスをenumが生成することで次のように書く事もできます。
enum UserIdentity {
case id(Int)
case uuid(String)
}
extension UserIdentity {
var path: String {
switch identity {
case .id(let id):
return "/v1/users/\(id)"
case .uuid(let uuid):
return "/v1/users/\(uuid)"
}
}
}
func fetchUser(by identity: UserIdentity) {
request(url: identity.path)
}
fetchUser(by .uuid(userUUID))
メソッドの内部実装をシンプルにする事で、可読性が上がり処理が追いやすくなります。
これが正解かはプロジェクトの状況に寄りますが、可読性の高いコードを追求してみましょう。