Issue #18
A single singleton
There are many classes that designed to be used as singleton, like UserDefaults.standard
, FileManager.default
, NotificationCenter.default
or even our own classes like UserManager
, Storage
, … Singleton is a design patter and has its own use case, sometimes we still need to use it. But if we are to use singleton, we should just use 1, and group all other singleton under this single singleton. Thanks to Vadym for showing this to me
Swift makes it extremely easy to make singleton, let name it App
then we have a single point of control for all the singletons
struct App {
static let model = AppModel()
static let realmProvider = RealmProvider()
static let networkingProvider = NetworkingProvider()
static var navigator = Navigator()
static let config = AppConfig()
static let pushNotificationCenter = PushNotificationCenter()
static let lifeCycle = LifeCycle()
}
These are use cases where a single instance is needed
AppModel
This is where we store model for an app, that can be
- is onboarding shown
- organization name
Session
that encapsulates token, current profile
LifeCycle
This is where we listen to app life cycle, I use rx
to make it easy, see https://github.com/onmyway133/blog/issues/12
RealmProvider
I prefer Realm
for storing and caching, usually 1 Realm
is enough. This is where we return the a certain Realm
instance
class RealmProvider {
static func realm() -> Realm {
let configuration = Realm.Configuration(schemaVersion: App.config.schemaVersion)
return try! Realm(configuration: configuration)
}
}
AppConfig
This is where we have configurations for staging and production environment, those can be client key, Firebase
configuration, analytics keys, …
Navigator
I use Compass to do central navigation, and there should be 1 Navigator
that does the job
Inject a singleton
Sometime we rely on a singleton to do our job, to make dependencies clear and testing easier, we need to inject this singleton, and leverage Swift default parameter, thanks to John for showing this to me
Here is an example of a ViewModel
that relies on networking
class ProfileViewModel {
let networking: Networking<APIEndpoint>
init(networking: Networking<APIEndpoint> = App.networking) {
self.networking = networking
networking.rxRequest(APIEndpoint.profile)
.bindNext({ profile in
print(profile)
})
}
}