Skip to content

Instantly share code, notes, and snippets.

@WorldDownTown
Last active March 21, 2025 09:40
Show Gist options
  • Save WorldDownTown/b2a8fe00252678ffbf6e3db81f0aae53 to your computer and use it in GitHub Desktop.
Save WorldDownTown/b2a8fe00252678ffbf6e3db81f0aae53 to your computer and use it in GitHub Desktop.
EitherID protocol
import Foundation
enum Either<L, R> {
case left(L)
case right(R)
}
extension Either: Equatable where L: Equatable, R: Equatable {}
extension Either: Hashable where L: Hashable, R: Hashable {}
extension Either: Codable where L: Codable, R: Codable {
struct NeitherError: Error {}
init(from decoder: Decoder) throws {
let container: SingleValueDecodingContainer = try decoder.singleValueContainer()
if let l = try? container.decode(L.self) {
self = .left(l)
} else if let r = try? container.decode(R.self) {
self = .right(r)
} else {
throw NeitherError()
}
}
func encode(to encoder: Encoder) throws {
var container: SingleValueEncodingContainer = encoder.singleValueContainer()
switch self {
case .left(let l):
try container.encode(l)
case .right(let r):
try container.encode(r)
}
}
}
protocol IDProtocol: RawRepresentable, Codable, Hashable {}
extension IDProtocol {
init(_ value: RawValue) {
self.init(rawValue: value)!
}
}
protocol EitherID: IDProtocol where RawValue == Either<L, R> {
associatedtype L: Codable, Hashable
associatedtype R: Codable, Hashable
}
extension EitherID {
init(from decoder: Decoder) throws {
let container: SingleValueDecodingContainer = try decoder.singleValueContainer()
self.init(try container.decode(Either<L, R>.self))
}
func encode(to encoder: Encoder) throws {
var container: SingleValueEncodingContainer = encoder.singleValueContainer()
switch rawValue {
case .left(let l):
try container.encode(l)
case .right(let r):
try container.encode(r)
}
}
}
extension EitherID where Self: ExpressibleByIntegerLiteral, L == Int {
init(integerLiteral value: Int) {
self.init(.left(value))
}
}
extension EitherID where Self: ExpressibleByIntegerLiteral, R == Int {
init(integerLiteral value: Int) {
self.init(.right(value))
}
}
extension EitherID where Self: ExpressibleByStringLiteral, L == String {
init(stringLiteral value: String) {
self.init(.left(value))
}
}
extension EitherID where Self: ExpressibleByStringLiteral, R == String {
init(stringLiteral value: String) {
self.init(.right(value))
}
}
protocol IntOrStringID: EitherID, ExpressibleByIntegerLiteral, ExpressibleByStringLiteral where L == Int, R == String {}
struct User: Codable {
let id: ID
struct ID: IntOrStringID {
let rawValue: Either<Int, String>
/// APIリクエストの値とかに使う
var stringValue: String {
switch rawValue {
case .left(let v): "\(v)"
case .right(let v): v
}
}
}
}
do {
// id: number
let user1: User = try JSONDecoder().decode(User.self, from: Data(#"{"id":1}"#.utf8))
print(user1)
// User(id: __lldb_expr_19.User.ID(rawValue: __lldb_expr_19.Either<Swift.Int, Swift.String>.left(1)))
let data1: Data = try JSONEncoder().encode(user1)
print(String(data: data1, encoding: .utf8)!)
// {"id":1}
// id: string
let user2: User = try JSONDecoder().decode(User.self, from: Data(#"{"id":"1"}"#.utf8))
print(user2)
// User(id: __lldb_expr_19.User.ID(rawValue: __lldb_expr_19.Either<Swift.Int, Swift.String>.right("1")))
let data2: Data = try JSONEncoder().encode(user2)
print(String(data: data2, encoding: .utf8)!)
// {"id":"1"}
} catch {
print(error)
print(error.localizedDescription)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment