Topics

#224: Standard Architecture 🏣

Topics

After Bite #223's "Standard Setup", many readers asked for more detail about the actual architecture involved. Today we'll take a look at an example request, model, and a bit of how data "flows" from the web in to views in the app.

Who? Again it's me, Jake Marsh! I write this thing. Writing in the first person feels quite strange at this point.

Let's start with a Moya setup for one endpoint. I love this convention. It standardizes "what goes where" for HTTP verbs, params, etc. I'm never trying invent, just define it, and move on.

public enum StewardAPI : RxMoya.TargetType {
  case Checkins(start: Int)
}

extension StewardAPI {
  public var method: RxMoya.Method { return .GET }
}

extension StewardAPI {
  public var path: String {
    switch self {
      case .Checkins: return "/checkins"
    }
  }
}

extension StewardAPI {
  public var parameters: [String: AnyObject]? {
    switch self {
    case .Checkins(let start):
      return ["start": start, "maxResults" : 50]

Next, to actually use this API, I have an small extension I add with a global function called fetch. This returns an Observable<Decodable>.

This "calls" the HTTP endpoint, and expects some JSON that can be transformed into one of my defined Decodable types:

fetch(.Checkins(start: start), type: [Checkin].self).subscribe { event in
  switch event {
    case .Next(let checkins): print(checkins)
    case .Error(let error): print(error)
    default: break
  }
}.addDisposableTo(rx_disposeBag)

Normally, I wouldn't switch here, I'd map this into a type I use called a "Presenter" (people also call it a "Decorator" or a "View Model").

It is immutable and contains boring business logic. This is what actually gets handed to my view controllers and views:

fetch(.Checkins(start: start), type: [Checkin].self)
  .map { ListItemPresenter(item: $0) }

Please send any questions about any of this to hello@littlebitesofcocoa.com. Happy to go in to further detail if there is interest here.