Skip to content

Instantly share code, notes, and snippets.

@albertogiunta
Created November 27, 2019 18:14
Show Gist options
  • Save albertogiunta/1aee3e15247a64c9e393030a2da96843 to your computer and use it in GitHub Desktop.
Save albertogiunta/1aee3e15247a64c9e393030a2da96843 to your computer and use it in GitHub Desktop.
import Katana
import Ramen
import SBundle
import Storages
import Tempura
final class DependenciesContainer: RamenAppDelegate, SBundleProvider, Provider, <#Other app specific protocols#> {
// MARK: - Basic Dependencies Container requirements
let dispatch: StoreDispatch
let promisableDispatch: PromisableStoreDispatch
var getAppState: () -> AppState
var navigator: Navigator = Navigator()
var getState: () -> State { self.getAppState }
// MARK: - SBundle
var applicationBundle: Bundle = Bundle.main
let sbundle = SBundle(bundle: Bundle.main, decryptionKey: SBundle.key)
var appName: String {
(self.applicationBundle.localizedInfoDictionary?[kCFBundleNameKey as String] as? String)
?? (self.sbundle.infoDictionary[kCFBundleNameKey as String] as? String)
?? <#App Name#>
}
<#Add here other specific managers and providers#>
// MARK: - Ramen
var ramen: ManagersContainer = ManagersContainer()
init(dispatch: @escaping PromisableStoreDispatch, getAppState: @escaping () -> AppState) {
self.dispatch = { dispatchable in
_ = dispatch(dispatchable)
}
self.promisableDispatch = dispatch
self.getAppState = getAppState
super.init()
self.ramen.start(with: RamenManagerConfiguration(
analyticsConfiguration: self.analyticsConfiguration,
appSetupConfiguration: self.appSetupConfiguration,
customerSupportConfiguration: self.customerSupportConfiguration,
localizationConfiguration: self.localizationConfiguration,
monetizationConfiguration: self.monetizationConfiguration,
remoteContentConfiguration: self.remoteContentConfiguration,
reviewConfiguration: self.reviewConfiguration,
sbundle: self.sbundle,
secretMenuConfiguration: self.secretMenuConfiguration,
systemSingletons: self.systemSingletonsConfiguration,
storagesConfiguration: self.storagesConfiguration,
thirdPartiesConfiguration: self.thirdPartiesConfiguration
))
}
}
/// Analytics Configuration
extension DependenciesContainer: AnalyticsConfigurationDelegate {
func isPremium(state: State) -> [String: Bool] {
guard let state = state as? AppState else { return [:] }
return [
<#Custom Logic#>
// e.g.
// Metrics.UserInfo.Keys.isFreeUser.rawValue: state.isFreeUser,
// Metrics.UserInfo.Keys.hasSubscribed.rawValue: state.isSubscribed,
// Metrics.UserInfo.Keys.hasFreeAccess.rawValue: state.hasFreeAccess,
]
}
var analyticsConfiguration: RamenManagerConfiguration.AnalyticsConfiguration {
RamenManagerConfiguration.AnalyticsConfiguration(
installedBeforePico: false,
trackingServices: [ConsoleLoggerTrackingService(sbundle: self.sbundle)],
trackingTasks: Metrics.all,
delegate: self
)
}
}
/// AppSetup Configuration
extension DependenciesContainer {
var appSetupConfiguration: RamenManagerConfiguration.AppSetupConfiguration {
RamenManagerConfiguration.AppSetupConfiguration()
}
}
/// CustomerSupport Configuration
extension DependenciesContainer {
var customerSupportConfiguration: RamenManagerConfiguration.CustomerSupportConfiguration {
RamenManagerConfiguration.CustomerSupportConfiguration()
}
}
/// Localization Configuration
extension DependenciesContainer {
var localizationConfiguration: RamenManagerConfiguration.LocalizationConfiguration {
RamenManagerConfiguration.LocalizationConfiguration(availableLanguages: [], startingScreen: Screen.appSetup.rawValue)
}
}
/// Monetization Configuration
extension DependenciesContainer: MonetizationConfigurationDelegate {
func availableProductIDs(allIDs: [Models.Monetization.ProductID]) -> ([Models.Monetization.ProductID]) {
[]
}
var monetizationConfiguration: RamenManagerConfiguration.MonetizationConfiguration {
RamenManagerConfiguration.MonetizationConfiguration(delegate: self)
}
}
/// RemoteContent Configuration
extension DependenciesContainer: RemoteContentConfigurationDelegate {
var remoteContentConfiguration: RamenManagerConfiguration.RemoteContentConfiguration {
RamenManagerConfiguration.RemoteContentConfiguration(
minDeployVersionSupported: 11,
registeredResources: [
<#Custom App Resources#>
// e.g.
// MusicGenreResource.self,
// AudioTrackResource.self,
// StickerGroupResource.self,
// StickerResource.self,
],
delegate: self
)
}
}
/// ReviewConfiguration Configuration
extension DependenciesContainer {
var reviewConfiguration: RamenManagerConfiguration.ReviewConfiguration {
RamenManagerConfiguration.ReviewConfiguration()
}
}
/// SecretMenu Configuration
extension DependenciesContainer: SecretMenuConfigurationDelegate {
var window: UIWindow {
// It crashed at app start using keyWindow
return UIApplication.shared.delegate!.window!!
}
func publicItems(state: State) -> [Models.SecretMenu.MenuItem] {
[
<#Custom Public Items. Remove if not needed#>
]
}
func debugItems(state: State) -> [Models.SecretMenu.MenuItem] {
[
<#Custom Private Items. Remove if not needed#>
]
}
var secretMenuConfiguration: RamenManagerConfiguration.SecretMenuConfiguration {
RamenManagerConfiguration.SecretMenuConfiguration(delegate: self)
}
}
/// Storages Configuration
extension DependenciesContainer {
var storagesConfiguration: RamenManagerConfiguration.StoragesConfiguration {
RamenManagerConfiguration.StoragesConfiguration(secretsStorage: SecretsStorage(sbundle: self.sbundle))
}
}
/// SystemSingletons Configuration
extension DependenciesContainer {
var systemSingletonsConfiguration: RamenManagerConfiguration.SystemSingletons {
RamenManagerConfiguration.SystemSingletons()
}
}
/// ThirdParties Configuration
extension DependenciesContainer: ThirdPartiesConfigurationDelegate {
var thirdPartiesConfiguration: RamenManagerConfiguration.ThirdPartiesConfiguration {
RamenManagerConfiguration.ThirdPartiesConfiguration(delegate: self)
}
}
extension DependenciesContainer {
convenience init(dispatch: @escaping PromisableStoreDispatch, getState: @escaping () -> State) {
let getAppState: () -> AppState = {
guard let state = getState() as? AppState else {
fatalError("Wrong State Type")
}
return state
}
self.init(dispatch: dispatch, getAppState: getAppState)
}
}
// MARK: - Promoted IAPs
import StoreKit
extension DependenciesContainer: SideEffectDependencyContainer {
func transactionPaymentQueue(_ queue: SKPaymentQueue,
shouldAddStorePayment payment: SKPayment,
for product: SKProduct) -> Bool {
if self.getAppState().isSubscribed { return false }
_ = self.promisableDispatch(MonetizationActions.PromotedProductIdChanged(productIdentifier: product.productIdentifier))
return true
}
}

Update .xcode_version to your latest installed Xcde version

Update all podfile.lock versions to latest version as per the podfile

TARGET=AppName pod update

Resolve eventual conflicts regarding pod minor/major versions between what the app requires and what's required by ramen

TARGET=AppName pod insall

Add Ramen as a Development Pod in your podfile pod 'Ramen', :path => '/path-to-ramen/ramen-lib-swift'

Start your app project and check under Development Pods the presence of Ramen

ambrogio prepare AppName

  1. DependenciesContainer See the attached DependenciesContainer.swift template class. You'll need to basically remove every superflous manager delcaration, and only keep those that are app specific.

The previous manager/provider implementations that can be removed are:

  • Settings Manager
  • API Manager
  • TicketCounter
  • MartyMcFly
  • Monopoly
  • Emporium
  • SecretStorage
  • User Info Provider
  • Theirs
  • Concierge
  • Pico Katana
  • Storages
  • Secret Menu

The new interfaces the DependenciesContainer will have to conform to are:

  • MUST

    • RamenAppDelegate
    • SideEffectDependencyContainer
    • Provider
  • OPTIONAL (maybe required by other libraries):

    • SbundleProvider
  1. AppDelegate StandardAppDelegate becomes RamenAppDelegate

  2. From managers to side effects // TODO: explaing the rationale behind the switch from managers to side effects

NB All the managers methods that were being used before have now been converted to SideEffects. This means that what was previosly accessible through the dependencies container (e.g. context.dependencies.termsOfServiceManager.__clearTOSHistory()) is now namespaced as Ramen.Debug.ClearTermOfServiceHistory(). Also, the invocations will change from try await(context.depenencies...) to context.awaitDispatch(Ramen.Debug...)

  1. Remove pods and imports of these Libraries
  • AppSetup
  • BSPRosetta
  • Chocolate
  • Emporium
  • GimmeFive
  • Monopoly
  • Oracle
  • Pico
  • SecretMenu
  • Teleporter
  • TicketCounter
  • Theirs

4bis) change all references of

  • TPCodable to Codable

// not sure about this. check podfile: add FirebaseAnalytics to Theirs -> Ramen

  1. State / Settings / User

AppState will now only have to conform to StateWithRamen (except for app specific protocol conformances). Also, TPCodable is still required since KatanaPersistence requires it.

Remove the following states since they're now integrated into Ramen:

  • settings
  • me
  • monopoly
  • review
  • emporium

Settings will now only have to conform to Ramen.SettingsState, Codable. Remove the following properties since they're now integrated into Ramen

  • minIOSVersion
  • productIDs
  • softTriggersFactor
  • hardTriggersFactor
  • reviewMaxRequestsPerVersion
  • reviewMinTimeBetweenRequests
  • experiments
  • isAppseeEnabled
  • isFree
  • isBaseline
  • tosURL
  • tosVersion
  • privacyNoticeURL
  • privacyNoticeVersion

User or UserModel can be removed since it is now accessible through state.ramen.authentication.user

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment