Enceladus is a Swift-based framework designed to stream cached and network results with a simple API.
- Local caching using SwiftData.
- Built-in polling. As long as a stream is active for a given query/ model, it is fetched and emitted at the interval specified by each model
- Stream models by unique identifier or as a singleton
- Stream lists of models based on query and sort descriptors. Queries are transformed into
SwiftData
predicates. - Fetch models asynchronously using
async/await
.
To integrate Enceladus into your Xcode project using Swift Package Manager, add the following to your Package.swift
:
dependencies: [
.package(url: "https://github.com/lekom/Enceladus.git", from: "1.0.0")
]
Enceladus provides the ModelProviding
protocol that outlines the methods for fetching models.
import Combine
import Foundation
public protocol ModelProviding {
func streamModel<T: BaseModel>(_ modelType: T.Type, id: String) -> AnyPublisher<ModelQueryResult<T>, Never>
func streamModel<T: SingletonModel>(modelType: T.Type) -> AnyPublisher<ModelQueryResult<T>, Never>
func streamFirstModel<T: ListModel>(_ modelType: T.Type, query: ModelQuery<T>?, sortDescriptors: [SortDescriptor<T>]?) -> AnyPublisher<ModelQueryResult<T>, Never>
func streamModels<T: ListModel>(_ modelType: T.Type, query: ModelQuery<T>?, limit: Int?, sortDescriptors: [SortDescriptor<T>]?) -> AnyPublisher<ListQueryResult<T>, Never>
func getModel<T: BaseModel>(_ modelType: T.Type, id: String) async -> Result<T, Error>
func getModel<T: SingletonModel>(_ modelType: T.Type) async -> Result<T, Error>
func getFirstModel<T: ListModel>(_ modelType: T.Type, query: ModelQuery<T>?, sortDescriptors: [SortDescriptor<T>]?) async -> Result<T, Error>
func getList<T: ListModel>(_ modelType: T.Type, query: ModelQuery<T>?, limit: Int?, sortDescriptors: [SortDescriptor<T>]?) async -> Result<[T], Error>
func configure(headersProvider: (() -> [String: String])?)
}
You can access the model provider using the getModelProvider()
function. This function returns an instance that conforms to the ModelProviding
protocol.
let modelProvider = getModelProvider()
Call getModelProvider().configure(headersProvider...
once at app launch to ensure any required headers are sent with each network request. The provided headersProvider
closure is called on each network request and the returned headers are not cached by this lib between network requests.
getModelProvider().configure(headersProvider: { ["authorization": "abc"] })
For unit testing, you can override the default model provider returned from getModelProvider()
by setting a global variable mockedModelProvider
. Set it to any mock instance that adopts the ModelProviding
protocol. MockModelProvider
is provided for convenience in the EnceladusMocks
target for use in testing.
var mockedModelProvider: ModelProviding?
func getModelProvider() -> ModelProviding {
return mockedModelProvider ?? DefaultModelProvider()
}
modelProvider.streamModel(MyModel.self, id: "123")
.sink { result in
switch result {
case .loading:
print("model is being fetched from the network")
case .loaded(let model)
print("Model: \(model) found as either a freshly cached item or result of a network request")
case .error(let error):
print("Error: \(error) occured while fetching the model")
}
}
.store(in: &cancellables)
let result = await modelProvider.getModel(MyModel.self, id: "123")
switch result {
case .success(let model):
print("Model: \(model)")
case .failure(let error):
print("Error: \(error)")
}
Contributions are welcome! Please open an issue or submit a pull request.