Last active
June 30, 2020 05:04
-
-
Save mattpolzin/1b98a22642e46387f6b867fd4e5e37c8 to your computer and use it in GitHub Desktop.
A simple template to follow for client side JSONAPI/ReSwift
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 Combine | |
import JSONAPI | |
import JSONAPIResourceCache | |
import JSONAPICombine | |
import ReSwift | |
import CombineReSwift | |
import CombineAPIRequest | |
// MARK: - Resources | |
enum DaySegment: String, Codable { case morning, afternoon, evening, night } | |
enum WidgetDescription: JSONAPI.ResourceObjectDescription { | |
static let jsonType: String = "widget" | |
struct Attributes: JSONAPI.Attributes { | |
let title: Attribute<String> | |
let color: Attribute<String> // hex starting with # | |
} | |
// ignore relationships for now: | |
typealias Relationships = NoRelationships | |
} | |
typealias Widget = JSONAPI.ResourceObject<WidgetDescription, NoMetadata, NoLinks, String> | |
// MARK: - Resource Documents | |
typealias ManyWidgets = JSONAPI.Document<ManyResourceBody<Widget>, NoMetadata, NoLinks, NoIncludes, NoAPIDescription, BasicJSONAPIError<String>> | |
// MARK: - Clientside Cache | |
struct EntityCache: ResourceCache, Equatable { | |
var widgets: ResourceHash<Widget> = [:] | |
mutating func merge(_ other: EntityCache) { | |
widgets.merge(other.widgets, uniquingKeysWith: { $1 }) | |
} | |
} | |
// Make resource Materializable | |
extension WidgetDescription: Materializable { | |
static var cachePath: WritableKeyPath<EntityCache, ResourceHash<Widget>> { \.widgets } | |
} | |
// MARK: - ReSwift Store | |
struct AppState: StateType, Equatable { | |
var entities: EntityCache = .init() { | |
didSet { | |
print("Now have \(entities.widgets.count) widgets.") | |
} | |
} | |
} | |
// Entity Updates from the server are just | |
// simple Actions | |
struct EntityUpdate: Action { | |
let entities: EntityCache | |
init(_ entities: EntityCache) { | |
self.entities = entities | |
} | |
} | |
let store = ReSwift.Store( | |
reducer: { action, state in | |
var state = state ?? AppState() | |
// only handles the entity update action | |
switch action { | |
case let update as EntityUpdate: | |
state.entities.merge(update.entities) | |
default: | |
break | |
} | |
return state | |
}, | |
state: AppState() | |
) | |
// MARK: - API | |
let decoder = JSONDecoder() | |
decoder.keyDecodingStrategy = .convertFromSnakeCase | |
// Make an API request and store entities in the cache. | |
try APIRequest( | |
.get, | |
host: URL(string: "https://website.com")!, | |
path: "/widgets", | |
responseType: ManyWidgets.self | |
) | |
.publisher(using: decoder) | |
.entities | |
.dispatch { EntityUpdate($0) } | |
//.print() // just prints debug info to the console | |
.subscribe(store) | |
RunLoop.current.run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment