Skip to content

Instantly share code, notes, and snippets.

@samrayner
Created October 17, 2021 20:27
Show Gist options
  • Save samrayner/d383bf2392b97227c1702c8c4eacd1e1 to your computer and use it in GitHub Desktop.
Save samrayner/d383bf2392b97227c1702c8c4eacd1e1 to your computer and use it in GitHub Desktop.
/*
Why?
- Lots of RawRepresentable boilerplate for every enum that needs a .other() case
- The model is polluted with an invalid case which we can't define behaviour for other than logging
- New method lets us handle the invalid case (e.g. log it) but then only pass on valid cases to the rest of the app
*/
import Foundation
enum RawExpectation<Expected: RawRepresentable> {
case expected(Expected)
case unexpected(Expected.RawValue)
func expected() -> Expected? {
switch self {
case .expected(let expected):
return expected
case .unexpected:
return nil
}
}
func expected(else fallback: Expected) -> Expected {
expected() ?? fallback
}
}
extension RawExpectation: RawRepresentable {
init(nonOptionalWithRawValue rawValue: Expected.RawValue) {
if let expected = Expected(rawValue: rawValue) {
self = .expected(expected)
} else {
self = .unexpected(rawValue)
}
}
init?(rawValue: Expected.RawValue) {
self.init(nonOptionalWithRawValue: rawValue)
}
var rawValue: Expected.RawValue {
switch self {
case .expected(let expected):
return expected.rawValue
case .unexpected(let rawValue):
return rawValue
}
}
}
extension RawExpectation: Decodable where Expected.RawValue: Decodable {
init(from decoder: Decoder) throws {
self.init(nonOptionalWithRawValue: try Expected.RawValue(from: decoder))
}
}
extension RawExpectation: Encodable where Expected.RawValue: Encodable {
func encode(to encoder: Encoder) throws {
try rawValue.encode(to: encoder)
}
}
// ------------------------
enum DocumentTypeOld: RawRepresentable, Codable {
private enum Keys {
static let photoId = "PHOTO_ID"
static let selfie = "SELFIE_PHOTO_ID"
static let bankStatement = "BANK_STATEMENT"
}
case photoId
case selfie
case bankStatement
case other(String)
init?(rawValue: String) {
switch rawValue {
case Keys.photoId:
self = .photoId
case Keys.selfie:
self = .selfie
case Keys.bankStatement:
self = .bankStatement
default:
self = .other(rawValue)
}
}
var rawValue: String {
switch self {
case .photoId:
return Keys.photoId
case .selfie:
return Keys.selfie
case .bankStatement:
return Keys.bankStatement
case .other(let string):
return string
}
}
}
enum DocumentTypeNew: String, Codable, CaseIterable {
case photoId = "PHOTO_ID"
case selfie = "SELFIE_PHOTO_ID"
case bankStatement = "BANK_STATEMENT"
}
struct Response: Codable {
let documentTypeOld: DocumentTypeOld
let documentTypeNew: RawExpectation<DocumentTypeNew>
}
let response = try JSONDecoder().decode(
Response.self,
from: """
{
"documentTypeOld": "PHOTO_ID",
"documentTypeNew": "BANK_STATEMENT"
}
""".data(using: .utf8)!
)
switch response.documentTypeOld {
case .selfie, .bankStatement, .photoId:
print("Expected doctype: \(response.documentTypeOld.rawValue)")
case .other(let rawValue):
print("Unexpected doctype: \(rawValue)")
}
switch response.documentTypeNew {
case .expected(let docType):
print("Expected doctype: \(docType.rawValue)")
case .unexpected(let rawValue):
print("Unexpected doctype: \(rawValue)")
}
func docTypeOldOrNil() -> DocumentTypeOld? {
switch response.documentTypeOld {
case .selfie, .bankStatement, .photoId:
return response.documentTypeOld
case .other(_):
return nil
}
}
func docTypeNewOrNil() -> DocumentTypeNew? {
response.documentTypeNew.expected()
}
print(docTypeOldOrNil())
print(docTypeNewOrNil())
print(String(data: try JSONEncoder().encode(response), encoding: .utf8))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment