Created
March 18, 2016 05:19
-
-
Save bobspryn/b97923d5893e058c64cb to your computer and use it in GitHub Desktop.
This file contains hidden or 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
// example json being mapped in | |
// { | |
// "pageName": "home", // string from a known set | |
// "slots": [ { | |
// "name": "homeTopSlot", // string from a known set, only found in the specific pageName | |
// "message": "Some message" | |
// }, | |
// { | |
// "name": "bottomTopSlot", // ditto | |
// "message": "Some message" | |
// }, | |
// ] | |
// } | |
// Could model this with just enums and associated values for the message | |
public enum PageEnum { | |
// spell out the slots in each page | |
public enum HomePageSlots { | |
case homeTopSlot(message: String) | |
case homeBottomSlot(message: String) | |
} | |
// maybe cart only has one slot right now | |
public enum CartPageSlots { | |
case defaultSlot(message: String) | |
} | |
// etc | |
// then associate the slots with the page case | |
case homePage([HomePageSlots]) | |
case cartPage([CartPageSlots]) | |
} | |
// feels good, but due to the oddities around instantiating an enum with an associated value | |
// each slot enum is going to need it's own custom initializer, extra boilerplatey when done in an extension | |
protocol PageSlot { | |
init?(slotIdentifier: String, message: String) | |
} | |
// pages with only one slot feel awful heavy | |
extension PageEnum.CartPageSlots: PageSlot { | |
init?(slotIdentifier: String, message: String) throws { | |
// guard against unknown values coming for slot names | |
guard(slotIdentifier == "cartSlot") else { return nil } | |
self = .defaultSlot(message) | |
} | |
} | |
extension PageEnum.HomePageSlots: PageSlot { | |
init?(slotIdentifier: String, message: String) throws { | |
switch slotIdentifier { | |
case "homeTopSlot": | |
self = .homeTopSlot(message) | |
case "homeBottomSlot": | |
self = .homeBottomSlot(message) | |
default: | |
return nil | |
} | |
} | |
} | |
// kick off the parsing | |
extension PageEnum: JSONDecodable { | |
// Extracts the valid info and uses the generic initializer | |
private static func initSlot<T:PageSlot>(json:JSON) throws -> T { | |
let slotIdentifier = try json.string("name") | |
let message = try json.string("message") | |
return try T.init(slotIdentifier: slotIdentifier, message: message) | |
} | |
// just a convenience wrapper | |
private static func mapSlots<T:PageSlot>(slots:[JSON]) throws -> [T] { | |
return try slots.map(PageEnum.initSlot) | |
} | |
public init(json value: JSON) throws { | |
let pageName = try value.string("response", "pageName") | |
let slots = try value.array("response", "slotResponseBean") | |
switch pageName { | |
case "homePage": | |
self = .homePage(try PageEnum.mapSlots(slots)) | |
case "cartPage": | |
self = .cartPage(try PageEnum.mapSlots(slots)) | |
default: | |
throw PageEnum.SomeErrorEnum.pageNotFound(pageName) | |
} | |
} | |
} | |
// Another thought is to use structs for the slots, and they own an enum that describes the slots available | |
// can somewhat be accomplished with associated types (typealias in 2.1), but with side effects | |
// ultimately doesn't limit boilerplate that much | |
// defines the interface all the slot enums adhere to for instantiation | |
public protocol PageSlotStruct { | |
// failable initializer | |
init?(slotIdentifier: String, message: String) | |
// typealias representing the enum concrete types will provide | |
typealias AvailableSlots: RawRepresentable | |
// define the storage for slot and message, settable unfortunately | |
var slot: AvailableSlots {set get} | |
var message: String {set get} | |
// we need a valid init to chain on in the extension | |
init() | |
} | |
extension PageSlotStruct where AvailableSlots.RawValue == String { | |
// get to use a default implementation initializer, but with some ugliness (settable properties in the protocol, weird init dance) | |
public init?(slotIdentifier: String, message: String) { | |
guard let theSlot = AvailableSlots(rawValue: slotIdentifier) else { | |
return nil | |
} | |
self.init() | |
self.slot = theSlot | |
self.message = message | |
} | |
} | |
// get to use an enum with a rawType, nice for matching the service | |
public enum HomePageSlots: String { | |
case homeTopSlot | |
case homeBottomSlot = "HomeBottomslot" | |
} | |
public struct HomePage: PageSlotStruct { | |
// need to satisy init and set slot to a default value | |
public var slot: HomePageSlots = .defaultSlot | |
public var message:String = "" | |
public init(){} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment