Last active
November 25, 2017 17:18
-
-
Save MrAlek/5c47645558a0dc2883dd1619dc523d3c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import UIKit | |
enum SafeRawRepresentable<Wrapped: RawRepresentable>: RawRepresentable { | |
case known(Wrapped) | |
case unknown(Wrapped.RawValue) | |
var rawValue: Wrapped.RawValue { | |
switch self { | |
case .known(let wrapped): | |
return wrapped.rawValue | |
case .unknown(let rawValue): | |
return rawValue | |
} | |
} | |
init(rawValue: Wrapped.RawValue) { | |
if let wrapped = Wrapped(rawValue: rawValue) { | |
self = .known(wrapped) | |
} else { | |
self = .unknown(rawValue) | |
} | |
} | |
} | |
extension SafeRawRepresentable { | |
var value: Wrapped? { | |
switch self { | |
case .known(let wrapped): | |
return wrapped | |
case .unknown(_): | |
return nil | |
} | |
} | |
func flatMap<U>(_ transform: (Wrapped) throws -> U?) rethrows -> U? { | |
return try value.flatMap(transform) | |
} | |
} | |
// Example struct | |
struct Car { | |
enum Brand: String { | |
case volvo, bmw, koenigsegg | |
} | |
let registrationNumber: String | |
let brand: SafeRawRepresentable<Brand> | |
init?(dictionary: [String: Any]) { | |
guard | |
let registrationNumber = dictionary["registrationNumber"] as? String, | |
let rawBrand = dictionary["brand"] as? String | |
else { return nil } | |
self.registrationNumber = registrationNumber | |
self.brand = .init(rawValue: rawBrand) | |
} | |
} | |
// Example of computed property based on type safe enum | |
extension Car.Brand { | |
var isSwedish: Bool { | |
switch self { | |
case .volvo, .koenigsegg: | |
return true | |
case .bmw: | |
return false | |
} | |
} | |
} | |
// Example use (try in playground) | |
let knownBrandCar = Car(dictionary: ["registrationNumber": "ABC-123", "brand": "volvo"])! | |
let unknownBrandCar = Car(dictionary: ["registrationNumber": "CBA-321", "brand": "toyota"])! | |
[knownBrandCar, unknownBrandCar].forEach { car in | |
print("Car: \(car.registrationNumber)") | |
print("Made in Sweden 🇸🇪: " + (car.brand.flatMap { $0.isSwedish ? "Yep" : "Nope" } ?? "🤷")) | |
print("") | |
} | |
// Car: ABC-123 | |
// Made in Sweden 🇸🇪: Yep | |
// | |
// Car: CBA-321 | |
// Made in Sweden 🇸🇪: 🤷 | |
// | |
if let brand = knownBrandCar.brand.value { | |
// Do things with car brand | |
} | |
// You can also do unwrapping | |
let showSwedishFlag = unknownBrandCar.brand.value?.isSwedish ?? false | |
class CarBrandView: UIView { | |
init(brand: Car.Brand) { | |
super.init(frame: .zero) | |
// Customize view depending on car brand | |
} | |
required init?(coder aDecoder: NSCoder) { fatalError("Not implemented") } | |
} | |
// And pass type safe values to functions | |
let view = unknownBrandCar.brand.flatMap(CarBrandView.init) as? UIView ?? { | |
// Fallback view | |
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 400, height: 44)) | |
label.text = "We have no information about this brand" | |
return label | |
}() |
You get ==
for free by conforming to RawRepresentable
but Equatable
conformance for generic structs isn't currently doable I believe. Array
isn't Equatable
either. I'm having a hack night on Wednesday, could work on a PR after the group call.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Sorry for only responding now. This looks like a good alternative to the
.unknown
case to me, so curious what @JustinDSN thinks.It'll require some getting used to, but the semantics seem to make sense. We may want to add more convenience methods to this where possible, including
Equatable
conformance.