Commit f916bb10 by Demid Merzlyakov

IOS-175: implement the feature availability layer.

Breaks the code. Requires the next commit to work.
parent c3ad571c
//
// AppFeature.swift
// OneWeatherCore
//
// Created by Demid Merzlyakov on 24.08.2021.
//
import Foundation
public enum AppFeature: String, Codable, CaseIterable {
case airQualityIndex
case attPrompt
case ads
case nwsAlertsViaMoEngage
case onboarding
case minutelyForecast
case shorts
case shortsLastNudge
}
//
// FeatureAvailabilityChecker.swift
// OneWeatherCore
//
// Created by Demid Merzlyakov on 24.08.2021.
//
import Foundation
public protocol FeatureAvailabilityChecker {
var isAvailable: Bool { get }
}
//
// FeatureAvailabilityCheckerHelpers.swift
// OneWeatherCore
//
// Created by Demid Merzlyakov on 25.08.2021.
//
import Foundation
public extension FeatureAvailabilityChecker {
static prefix func ! (checker: Self) -> FeatureAvailabilityChecker {
return FeatureAvailabilityNot(decoratedChecker: checker)
}
}
public func && (first: FeatureAvailabilityChecker, second: FeatureAvailabilityChecker) -> FeatureAvailabilityChecker {
return FeatureAvaillabilityAnd(first: first, second: second)
}
public func || (first: FeatureAvailabilityChecker, second: FeatureAvailabilityChecker) -> FeatureAvailabilityChecker {
return FeatureAvaillabilityOr(first: first, second: second)
}
fileprivate struct FeatureAvailabilityNot: FeatureAvailabilityChecker {
private let decorated: FeatureAvailabilityChecker
public init(decoratedChecker: FeatureAvailabilityChecker) {
decorated = decoratedChecker
}
var isAvailable: Bool {
!decorated.isAvailable
}
}
fileprivate struct FeatureAvaillabilityAnd: FeatureAvailabilityChecker {
private let firstChecker: FeatureAvailabilityChecker
private let secondChecker: FeatureAvailabilityChecker
public init(first: FeatureAvailabilityChecker, second: FeatureAvailabilityChecker) {
firstChecker = first
secondChecker = second
}
var isAvailable: Bool {
firstChecker.isAvailable && secondChecker.isAvailable
}
}
fileprivate struct FeatureAvaillabilityOr: FeatureAvailabilityChecker {
private let firstChecker: FeatureAvailabilityChecker
private let secondChecker: FeatureAvailabilityChecker
public init(first: FeatureAvailabilityChecker, second: FeatureAvailabilityChecker) {
firstChecker = first
secondChecker = second
}
var isAvailable: Bool {
firstChecker.isAvailable || secondChecker.isAvailable
}
}
//
// PremiumInAppFeatureAvailabilityChecker.swift
// OneWeatherCore
//
// Created by Demid Merzlyakov on 25.08.2021.
//
import Foundation
public struct PremiumInAppFeatureAvailabilityChecker: FeatureAvailabilityChecker {
public init() {}
public var isAvailable: Bool {
if let metricsLog = UserDefaults.standard.dictionary(forKey: kOLAppMetricsKey) {
return metricsLog[kEventInAppPurchasedCompleted] != nil
}
return false
}
}
//
// ProSubscriptionAvailabilityChecker.swift
// OneWeatherCore
//
// Created by Demid Merzlyakov on 25.08.2021.
//
import Foundation
public struct ProSubscriptionAvailabilityChecker: FeatureAvailabilityChecker {
public init() {}
public var isAvailable: Bool {
#warning("Not implemented!")
//TODO: Implement!
// IOS-155
#if DEBUG
return true
#else
return false
#endif
}
}
//
// USOnlyFeatureAvailabilityChecker.swift
// OneWeatherCore
//
// Created by Demid Merzlyakov on 25.08.2021.
//
import Foundation
public struct USOnlyFeatureAvailabilityChecker: FeatureAvailabilityChecker {
public typealias LocationFetcher = () -> Location?
private let locationFetcher: LocationFetcher
public init(locationFetcher: @escaping LocationFetcher) {
self.locationFetcher = locationFetcher
}
public var isAvailable: Bool {
if let location = locationFetcher() {
return location.countryCode == "US"
}
return false
}
}
//
// FeatureAvailabilityManager.swift
// OneWeatherCore
//
// Created by Demid Merzlyakov on 24.08.2021.
//
import Foundation
public class FeatureAvailabilityManager {
public typealias ConfigFetcher = () -> AppConfig?
private let checkers: [AppFeature: FeatureAvailabilityChecker]
private let configFetcher: ConfigFetcher
public static var shared: FeatureAvailabilityManager!
public init(configFetcher: @escaping ConfigFetcher, checkers: [AppFeature: FeatureAvailabilityChecker]) {
self.configFetcher = configFetcher
self.checkers = checkers
}
public func isAvailable(feature: AppFeature) -> Bool {
guard let config = configFetcher() else {
// TODO: log error
return false
}
guard config.isEnabled(feature: feature) else {
return false
}
if let avaiabilityChecker = checkers[feature] {
return avaiabilityChecker.isAvailable
}
return true
}
}
// TODO: make FeatureAvailabilityCheckers observe the values and notify the FeatureAvailabilityManager that the value has been changed. Move from observing different things across the app (ConfigManager, LocationManager, etc.) to observing just the FeatureAvailabilityManager
//public protocol FeatureAvailabilityObserver {
// func manager(_ manager: FeatureAvailabilityManager, updatedAvailability isAvailable: Bool, for feature: AppFeature)
//}
......@@ -11,31 +11,29 @@ public struct AppConfig: Codable {
public let popularCities: [GeoNamesPlace]?
public let adConfig: AdConfig
public let ccpaUpdateInterval: TimeInterval?
public let nwsAlertsViaMoEngageEnabled: Bool
public let showAttPrompt: Bool
public let shortsLeftBelowCount: Int
public let shortsLastNudgeEnabled: Bool
public let shortsSwipeUpNudgeCount: Int
public let showOnboarding: Bool
private let explicitFeatureAvailability: [AppFeature: Bool]
public init(popularCities: [GeoNamesPlace]?,
adConfig: AdConfig,
ccpaUpdateInterval: TimeInterval?,
nwsAlertsViaMoEngageEnabled: Bool,
showAttPrompt: Bool,
shortsLeftBelowCountKey: Int,
shortsLastNudgeEnabledKey: Bool,
shortsSwipeUpNudgeCountKey: Int,
showOnboarding: Bool
explicitFeatureAvailability: [AppFeature: Bool]
) {
self.popularCities = popularCities
self.adConfig = adConfig
self.ccpaUpdateInterval = ccpaUpdateInterval
self.nwsAlertsViaMoEngageEnabled = nwsAlertsViaMoEngageEnabled
self.showAttPrompt = showAttPrompt
self.shortsLeftBelowCount = shortsLeftBelowCountKey
self.shortsLastNudgeEnabled = shortsLastNudgeEnabledKey
self.shortsSwipeUpNudgeCount = shortsSwipeUpNudgeCountKey
self.showOnboarding = showOnboarding
self.explicitFeatureAvailability = explicitFeatureAvailability
}
public func isEnabled(feature: AppFeature) -> Bool {
if feature == .ads {
return adConfig.adsEnabled
}
return explicitFeatureAvailability[feature] ?? true
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment