Commit 397522dc by Dmitriy Stepanets

Merge branch 'feature/IOS-133-onboarding' into develop

# Conflicts:
#	1Weather-Icons.sketch
#	1Weather.xcodeproj/project.pbxproj
#	1Weather/Resources/en.lproj/Localizable.strings
parents 4d6f4b51 e6b9d9dd
No preview for this file type
...@@ -109,6 +109,7 @@ ...@@ -109,6 +109,7 @@
CD6761842625B6A10079D273 /* RadarLayersCellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6761832625B6A10079D273 /* RadarLayersCellFactory.swift */; }; CD6761842625B6A10079D273 /* RadarLayersCellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6761832625B6A10079D273 /* RadarLayersCellFactory.swift */; };
CD6761882625C3360079D273 /* RadarViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6761872625C3360079D273 /* RadarViewModel.swift */; }; CD6761882625C3360079D273 /* RadarViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6761872625C3360079D273 /* RadarViewModel.swift */; };
CD678C6926B03F91001E0CB7 /* RadarWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD678C6826B03F91001E0CB7 /* RadarWidget.swift */; }; CD678C6926B03F91001E0CB7 /* RadarWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD678C6826B03F91001E0CB7 /* RadarWidget.swift */; };
CD6B2C8426C55F4600473B2D /* OnboardingPageControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6B2C8326C55F4600473B2D /* OnboardingPageControl.swift */; };
CD6B303B2572680C004B34B3 /* SelfSizingButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6B303A2572680C004B34B3 /* SelfSizingButton.swift */; }; CD6B303B2572680C004B34B3 /* SelfSizingButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6B303A2572680C004B34B3 /* SelfSizingButton.swift */; };
CD6B303E25726960004B34B3 /* ThemeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6B303D25726960004B34B3 /* ThemeProtocol.swift */; }; CD6B303E25726960004B34B3 /* ThemeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6B303D25726960004B34B3 /* ThemeProtocol.swift */; };
CD6B304325726AD1004B34B3 /* DefaultTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6B304225726AD1004B34B3 /* DefaultTheme.swift */; }; CD6B304325726AD1004B34B3 /* DefaultTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6B304225726AD1004B34B3 /* DefaultTheme.swift */; };
...@@ -184,6 +185,8 @@ ...@@ -184,6 +185,8 @@
CDC6126225E8DAB800188DA7 /* MoonPhaseCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC6126125E8DAB800188DA7 /* MoonPhaseCell.swift */; }; CDC6126225E8DAB800188DA7 /* MoonPhaseCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC6126125E8DAB800188DA7 /* MoonPhaseCell.swift */; };
CDC6126625E9085600188DA7 /* GraphLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC6126525E9085600188DA7 /* GraphLine.swift */; }; CDC6126625E9085600188DA7 /* GraphLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC6126525E9085600188DA7 /* GraphLine.swift */; };
CDC6126A25E90C8800188DA7 /* GraphLineSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC6126925E90C8800188DA7 /* GraphLineSettings.swift */; }; CDC6126A25E90C8800188DA7 /* GraphLineSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC6126925E90C8800188DA7 /* GraphLineSettings.swift */; };
CDC62C4526C13B9200156643 /* OnboardingPageController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC62C4426C13B9200156643 /* OnboardingPageController.swift */; };
CDC62C4726C13BE300156643 /* OnboardingContentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC62C4626C13BE300156643 /* OnboardingContentController.swift */; };
CDD0F1E52572425200CF5017 /* SF-Pro.ttf in Resources */ = {isa = PBXBuildFile; fileRef = CDD0F1E42572425200CF5017 /* SF-Pro.ttf */; }; CDD0F1E52572425200CF5017 /* SF-Pro.ttf in Resources */ = {isa = PBXBuildFile; fileRef = CDD0F1E42572425200CF5017 /* SF-Pro.ttf */; };
CDD0F1E82572429E00CF5017 /* AppFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDD0F1E72572429E00CF5017 /* AppFont.swift */; }; CDD0F1E82572429E00CF5017 /* AppFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDD0F1E72572429E00CF5017 /* AppFont.swift */; };
CDD0F1EE25725BCF00CF5017 /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDD0F1ED25725BCF00CF5017 /* ThemeManager.swift */; }; CDD0F1EE25725BCF00CF5017 /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDD0F1ED25725BCF00CF5017 /* ThemeManager.swift */; };
...@@ -406,6 +409,7 @@ ...@@ -406,6 +409,7 @@
CD6761832625B6A10079D273 /* RadarLayersCellFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadarLayersCellFactory.swift; sourceTree = "<group>"; }; CD6761832625B6A10079D273 /* RadarLayersCellFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadarLayersCellFactory.swift; sourceTree = "<group>"; };
CD6761872625C3360079D273 /* RadarViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadarViewModel.swift; sourceTree = "<group>"; }; CD6761872625C3360079D273 /* RadarViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadarViewModel.swift; sourceTree = "<group>"; };
CD678C6826B03F91001E0CB7 /* RadarWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadarWidget.swift; sourceTree = "<group>"; }; CD678C6826B03F91001E0CB7 /* RadarWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadarWidget.swift; sourceTree = "<group>"; };
CD6B2C8326C55F4600473B2D /* OnboardingPageControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingPageControl.swift; sourceTree = "<group>"; };
CD6B303A2572680C004B34B3 /* SelfSizingButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelfSizingButton.swift; sourceTree = "<group>"; }; CD6B303A2572680C004B34B3 /* SelfSizingButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelfSizingButton.swift; sourceTree = "<group>"; };
CD6B303D25726960004B34B3 /* ThemeProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeProtocol.swift; sourceTree = "<group>"; }; CD6B303D25726960004B34B3 /* ThemeProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeProtocol.swift; sourceTree = "<group>"; };
CD6B304225726AD1004B34B3 /* DefaultTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultTheme.swift; sourceTree = "<group>"; }; CD6B304225726AD1004B34B3 /* DefaultTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultTheme.swift; sourceTree = "<group>"; };
...@@ -481,6 +485,8 @@ ...@@ -481,6 +485,8 @@
CDC6126125E8DAB800188DA7 /* MoonPhaseCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoonPhaseCell.swift; sourceTree = "<group>"; }; CDC6126125E8DAB800188DA7 /* MoonPhaseCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoonPhaseCell.swift; sourceTree = "<group>"; };
CDC6126525E9085600188DA7 /* GraphLine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphLine.swift; sourceTree = "<group>"; }; CDC6126525E9085600188DA7 /* GraphLine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphLine.swift; sourceTree = "<group>"; };
CDC6126925E90C8800188DA7 /* GraphLineSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphLineSettings.swift; sourceTree = "<group>"; }; CDC6126925E90C8800188DA7 /* GraphLineSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphLineSettings.swift; sourceTree = "<group>"; };
CDC62C4426C13B9200156643 /* OnboardingPageController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingPageController.swift; sourceTree = "<group>"; };
CDC62C4626C13BE300156643 /* OnboardingContentController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingContentController.swift; sourceTree = "<group>"; };
CDD0F1E42572425200CF5017 /* SF-Pro.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-Pro.ttf"; sourceTree = "<group>"; }; CDD0F1E42572425200CF5017 /* SF-Pro.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-Pro.ttf"; sourceTree = "<group>"; };
CDD0F1E72572429E00CF5017 /* AppFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppFont.swift; sourceTree = "<group>"; }; CDD0F1E72572429E00CF5017 /* AppFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppFont.swift; sourceTree = "<group>"; };
CDD0F1ED25725BCF00CF5017 /* ThemeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeManager.swift; sourceTree = "<group>"; }; CDD0F1ED25725BCF00CF5017 /* ThemeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeManager.swift; sourceTree = "<group>"; };
...@@ -1197,6 +1203,16 @@ ...@@ -1197,6 +1203,16 @@
path = MoonPhaseCell; path = MoonPhaseCell;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
CDC62C4326C13AD100156643 /* InitialOnboarding */ = {
isa = PBXGroup;
children = (
CDC62C4426C13B9200156643 /* OnboardingPageController.swift */,
CDC62C4626C13BE300156643 /* OnboardingContentController.swift */,
CD6B2C8326C55F4600473B2D /* OnboardingPageControl.swift */,
);
path = InitialOnboarding;
sourceTree = "<group>";
};
CDD0F1DC2572400200CF5017 /* UI */ = { CDD0F1DC2572400200CF5017 /* UI */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
...@@ -1440,6 +1456,7 @@ ...@@ -1440,6 +1456,7 @@
CEC8FBAD263975170001A6BF /* Onboarding */ = { CEC8FBAD263975170001A6BF /* Onboarding */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
CDC62C4326C13AD100156643 /* InitialOnboarding */,
CEC8FBAE2639756A0001A6BF /* OnboardingViewController.swift */, CEC8FBAE2639756A0001A6BF /* OnboardingViewController.swift */,
); );
path = Onboarding; path = Onboarding;
...@@ -1838,6 +1855,7 @@ ...@@ -1838,6 +1855,7 @@
CD7BF15526203E6900A30DF5 /* RadarViewController.swift in Sources */, CD7BF15526203E6900A30DF5 /* RadarViewController.swift in Sources */,
CD82300325D69DE400A05501 /* TodayConditionsCell.swift in Sources */, CD82300325D69DE400A05501 /* TodayConditionsCell.swift in Sources */,
CD32CE08260C743B00235081 /* MenuViewModel.swift in Sources */, CD32CE08260C743B00235081 /* MenuViewModel.swift in Sources */,
CDC62C4526C13B9200156643 /* OnboardingPageController.swift in Sources */,
CD866A76260F77C500E96A5C /* SettingsDetailsCoordinator.swift in Sources */, CD866A76260F77C500E96A5C /* SettingsDetailsCoordinator.swift in Sources */,
CE0457902632B3BC00B3C19A /* NotificationsViewModel.swift in Sources */, CE0457902632B3BC00B3C19A /* NotificationsViewModel.swift in Sources */,
CE13B81A262480B3007CBD4D /* AdManager.swift in Sources */, CE13B81A262480B3007CBD4D /* AdManager.swift in Sources */,
...@@ -1936,6 +1954,8 @@ ...@@ -1936,6 +1954,8 @@
CD37D3FA260DF714002669D6 /* SettingsThemeCell.swift in Sources */, CD37D3FA260DF714002669D6 /* SettingsThemeCell.swift in Sources */,
CD6B303E25726960004B34B3 /* ThemeProtocol.swift in Sources */, CD6B303E25726960004B34B3 /* ThemeProtocol.swift in Sources */,
CD6B303B2572680C004B34B3 /* SelfSizingButton.swift in Sources */, CD6B303B2572680C004B34B3 /* SelfSizingButton.swift in Sources */,
CDC62C4726C13BE300156643 /* OnboardingContentController.swift in Sources */,
CD9B6B1125DBC723001D9B80 /* CubicCurveAlgorithm.swift in Sources */,
CD8B60B4263819790055CB3F /* NotificationsViewController.swift in Sources */, CD8B60B4263819790055CB3F /* NotificationsViewController.swift in Sources */,
CD67617026259D220079D273 /* RadarMapLayersController.swift in Sources */, CD67617026259D220079D273 /* RadarMapLayersController.swift in Sources */,
CEC8FBAF2639756A0001A6BF /* OnboardingViewController.swift in Sources */, CEC8FBAF2639756A0001A6BF /* OnboardingViewController.swift in Sources */,
...@@ -1994,6 +2014,7 @@ ...@@ -1994,6 +2014,7 @@
CD37D3F6260DF5BA002669D6 /* SettingsViewModel.swift in Sources */, CD37D3F6260DF5BA002669D6 /* SettingsViewModel.swift in Sources */,
CD17C5F625D15B4400EE884E /* TodayViewController.swift in Sources */, CD17C5F625D15B4400EE884E /* TodayViewController.swift in Sources */,
CD86245E25E646350097F3FB /* SunUvView.swift in Sources */, CD86245E25E646350097F3FB /* SunUvView.swift in Sources */,
CD6B2C8426C55F4600473B2D /* OnboardingPageControl.swift in Sources */,
CD427D2A266F86C600B4350A /* ShortsManager.swift in Sources */, CD427D2A266F86C600B4350A /* ShortsManager.swift in Sources */,
CDF8F12D26208E7B00DB384A /* MapCurrentTimeView.swift in Sources */, CDF8F12D26208E7B00DB384A /* MapCurrentTimeView.swift in Sources */,
CEC7D8EE2639FE2700B8836D /* OLInAppStoreManager.swift in Sources */, CEC7D8EE2639FE2700B8836D /* OLInAppStoreManager.swift in Sources */,
......
...@@ -82,6 +82,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ...@@ -82,6 +82,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
FirebaseApp.configure() FirebaseApp.configure()
ConfigManager.shared.updateConfig() ConfigManager.shared.updateConfig()
//App UI
let appCoordinator = AppCoordinator(window: self.window!) let appCoordinator = AppCoordinator(window: self.window!)
appCoordinator.start() appCoordinator.start()
ThemeManager.setBaseTheme() ThemeManager.setBaseTheme()
......
...@@ -98,11 +98,38 @@ class AppCoordinator: Coordinator { ...@@ -98,11 +98,38 @@ class AppCoordinator: Coordinator {
public func finishAnimation() { public func finishAnimation() {
DispatchQueue.main.async { DispatchQueue.main.async {
if ConfigManager.shared.config.showOnboarding {
if Settings.shared.initialOnboardingShowed {
self.finishInitialOnboarding()
}
else {
let initialOnboarding = OnboardingPageController(coordinator: self)
self.window.rootViewController = initialOnboarding
UIView.transition(with: self.window,
duration: 0.3,
options: .transitionCrossDissolve,
animations: nil)
}
}
else {
self.finishInitialOnboarding()
}
}
}
public func finishInitialOnboarding() {
DispatchQueue.main.async {
Settings.shared.initialOnboardingShowed = true
self.window.rootViewController = self.tabBarController self.window.rootViewController = self.tabBarController
UIView.transition(with: self.window,
//Search the TodayCoordinator duration: 0.3,
let todayCoordinator = (self.childCoordinators.first{$0 is TodayCoordinator} as? TodayCoordinator) options: .transitionCrossDissolve,
todayCoordinator?.showOnboardingOrPrivacyNoticeIfNeeded() animations: nil,
completion: {_ in
//Search the TodayCoordinator
let todayCoordinator = (self.childCoordinators.first{ $0 is TodayCoordinator } as? TodayCoordinator)
todayCoordinator?.showOnboardingOrPrivacyNoticeIfNeeded()
})
} }
} }
......
...@@ -21,8 +21,18 @@ public struct AppConfig: Codable { ...@@ -21,8 +21,18 @@ public struct AppConfig: Codable {
public let shortsLeftBelowCount: Int public let shortsLeftBelowCount: Int
public let shortsLastNudgeEnabled: Bool public let shortsLastNudgeEnabled: Bool
public let shortsSwipeUpNudgeCount: Int public let shortsSwipeUpNudgeCount: Int
public let showOnboarding: Bool
public init(popularCities: [GeoNamesPlace]?, adConfig: AdConfig, ccpaUpdateInterval: TimeInterval?, nwsAlertsViaMoEngageEnabled: Bool, showAttPrompt: Bool, shortsLeftBelowCountKey: Int, shortsLastNudgeEnabledKey: Bool, shortsSwipeUpNudgeCountKey: Int) { public init(popularCities: [GeoNamesPlace]?,
adConfig: AdConfig,
ccpaUpdateInterval: TimeInterval?,
nwsAlertsViaMoEngageEnabled: Bool,
showAttPrompt: Bool,
shortsLeftBelowCountKey: Int,
shortsLastNudgeEnabledKey: Bool,
shortsSwipeUpNudgeCountKey: Int,
showOnboarding: Bool
) {
self.popularCities = popularCities self.popularCities = popularCities
self.adConfig = adConfig self.adConfig = adConfig
self.ccpaUpdateInterval = ccpaUpdateInterval self.ccpaUpdateInterval = ccpaUpdateInterval
...@@ -31,6 +41,7 @@ public struct AppConfig: Codable { ...@@ -31,6 +41,7 @@ public struct AppConfig: Codable {
self.shortsLeftBelowCount = shortsLeftBelowCountKey self.shortsLeftBelowCount = shortsLeftBelowCountKey
self.shortsLastNudgeEnabled = shortsLastNudgeEnabledKey self.shortsLastNudgeEnabled = shortsLastNudgeEnabledKey
self.shortsSwipeUpNudgeCount = shortsSwipeUpNudgeCountKey self.shortsSwipeUpNudgeCount = shortsSwipeUpNudgeCountKey
self.showOnboarding = showOnboarding
} }
} }
...@@ -47,6 +58,7 @@ public class ConfigManager { ...@@ -47,6 +58,7 @@ public class ConfigManager {
private static let shortsLeftBelowCountKey = "shorts_left_below_nudge_every_x_cards" private static let shortsLeftBelowCountKey = "shorts_left_below_nudge_every_x_cards"
private static let shortsLastNudgeEnabledKey = "shorts_swipe_down_nudge_enabled" private static let shortsLastNudgeEnabledKey = "shorts_swipe_down_nudge_enabled"
private static let shortsSwipeUpNudgeCountKey = "shorts_swipe_up_nudge_on_x_cards" private static let shortsSwipeUpNudgeCountKey = "shorts_swipe_up_nudge_on_x_cards"
private static let showOnboardingKey = "ios_show_onboarding"
private let delegates = MulticastDelegate<ConfigManagerDelegate>() private let delegates = MulticastDelegate<ConfigManagerDelegate>()
...@@ -61,7 +73,7 @@ public class ConfigManager { ...@@ -61,7 +73,7 @@ public class ConfigManager {
}() }()
public static let shared = ConfigManager() public static let shared = ConfigManager()
public private(set) var isUpdating = false
public var config: AppConfig = AppConfig(popularCities: nil, public var config: AppConfig = AppConfig(popularCities: nil,
adConfig: AdConfig(), adConfig: AdConfig(),
ccpaUpdateInterval: nil, ccpaUpdateInterval: nil,
...@@ -69,12 +81,19 @@ public class ConfigManager { ...@@ -69,12 +81,19 @@ public class ConfigManager {
showAttPrompt: false, showAttPrompt: false,
shortsLeftBelowCountKey: 0, shortsLeftBelowCountKey: 0,
shortsLastNudgeEnabledKey: false, shortsLastNudgeEnabledKey: false,
shortsSwipeUpNudgeCountKey: 0) shortsSwipeUpNudgeCountKey: 0,
showOnboarding: false)
public func updateConfig() { public func updateConfig() {
log.info("update config") log.info("update config")
isUpdating = true
remoteConfig.fetchAndActivate { [weak self] (status, error) in remoteConfig.fetchAndActivate { [weak self] (status, error) in
guard let self = self else { return } guard let self = self else { return }
defer {
self.isUpdating = false
}
switch status { switch status {
case .successFetchedFromRemote: case .successFetchedFromRemote:
self.parseConfigFromFirebase(source: "remote") self.parseConfigFromFirebase(source: "remote")
...@@ -152,6 +171,9 @@ public class ConfigManager { ...@@ -152,6 +171,9 @@ public class ConfigManager {
let shortsSwipeNudgeCountValue = remoteConfig.configValue(forKey: ConfigManager.shortsSwipeUpNudgeCountKey) let shortsSwipeNudgeCountValue = remoteConfig.configValue(forKey: ConfigManager.shortsSwipeUpNudgeCountKey)
let shortsSwipeNudgeCount = shortsSwipeNudgeCountValue.numberValue.intValue let shortsSwipeNudgeCount = shortsSwipeNudgeCountValue.numberValue.intValue
let showOnboardingValue = remoteConfig.configValue(forKey: ConfigManager.showOnboardingKey)
let showOnboarding = showOnboardingValue.boolValue
DispatchQueue.main.async { DispatchQueue.main.async {
self.config = AppConfig(popularCities: popularCities, self.config = AppConfig(popularCities: popularCities,
adConfig: adConfig, adConfig: adConfig,
...@@ -160,7 +182,8 @@ public class ConfigManager { ...@@ -160,7 +182,8 @@ public class ConfigManager {
showAttPrompt: showAttPrompt, showAttPrompt: showAttPrompt,
shortsLeftBelowCountKey: shortsLeftBelowCount, shortsLeftBelowCountKey: shortsLeftBelowCount,
shortsLastNudgeEnabledKey: shortsLastNudgeEnabled, shortsLastNudgeEnabledKey: shortsLastNudgeEnabled,
shortsSwipeUpNudgeCountKey: shortsSwipeNudgeCount) shortsSwipeUpNudgeCountKey: shortsSwipeNudgeCount,
showOnboarding: showOnboarding)
self.notifyAboutConfigUpdate() self.notifyAboutConfigUpdate()
DispatchQueue.global().async { DispatchQueue.global().async {
let encoder = JSONEncoder() let encoder = JSONEncoder()
......
{
"images" : [
{
"filename" : "onboarding-alerts.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}
{
"images" : [
{
"filename" : "onboarding-close.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}
{
"images" : [
{
"filename" : "onboarding-forecast.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}
{
"images" : [
{
"filename" : "onboarding-next.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}
{
"images" : [
{
"filename" : "onboarding-radar.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}
...@@ -300,3 +300,14 @@ ...@@ -300,3 +300,14 @@
"widget.radar.title" = "Radar"; "widget.radar.title" = "Radar";
"widget.radar.description" = ""; "widget.radar.description" = "";
"widget.lastUpdatedTemplate" = "Last updated #LAST_UPDATED ago."; "widget.lastUpdatedTemplate" = "Last updated #LAST_UPDATED ago.";
"ads.placeholder" = "Advertisement";
//Onboarding
"onboarding.skip" = "Skip";
"onboarding.done" = "Done";
"onboarding.forecast.title" = "Extended forecast";
"onboarding.forecast.subtitle" = "<Description>";
"onboarding.alerts.title" = "Weather alerts";
"onboarding.alerts.subtitle" = "<Description>";
"onboarding.radar.title" = "Live doppler radar";
"onboarding.radar.subtitle" = "<Description>";
//
// OnboardingContentController.swift
// 1Weather
//
// Created by Dmitry Stepanets on 09.08.2021.
//
import UIKit
class OnboardingContentController: UIViewController {
//Private
private let mainImageView = UIImageView()
private let titleLabel = UILabel()
private let subtitleLabel = UILabel()
init(type: OnboardingControllerType) {
super.init(nibName: nil, bundle: nil)
mainImageView.image = type.image
titleLabel.text = type.title
subtitleLabel.text = type.subtitle
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
prepareImageView()
prepareLables()
updateUI()
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
updateUI()
}
private func updateUI() {
view.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor
subtitleLabel.textColor = ThemeManager.currentTheme.secondaryTextColor
}
}
private extension OnboardingContentController {
func prepareImageView() {
mainImageView.contentMode = .scaleAspectFit
view.addSubview(mainImageView)
mainImageView.snp.makeConstraints { make in
make.bottom.equalToSuperview().multipliedBy(0.56)
make.centerX.equalToSuperview()
make.left.right.equalToSuperview().inset(30)
}
}
func prepareLables() {
titleLabel.font = AppFont.SFPro.semibold(size: 24)
titleLabel.textColor = ThemeManager.currentTheme.graphTintColor
view.addSubview(titleLabel)
subtitleLabel.font = AppFont.SFPro.regular(size: 14)
view.addSubview(subtitleLabel)
//Constraints
titleLabel.snp.makeConstraints { make in
make.left.equalToSuperview().inset(30)
make.bottom.equalToSuperview().multipliedBy(0.7)
}
subtitleLabel.snp.makeConstraints { make in
make.left.equalToSuperview().inset(30)
make.top.equalTo(titleLabel.snp.bottom).offset(10)
}
}
}
//
// OnboardingPageControl.swift
// 1Weather
//
// Created by Dmitry Stepanets on 12.08.2021.
//
import UIKit
class OnboardingPageControl: UIView {
//Private
private let kSpacePerDot: CGFloat = 10
private let kDotSize: CGSize = .init(width: 9, height: 9)
private let kIndicatorSize: CGSize = .init(width: 23, height: 9)
private let kDotColor = UIColor(hex: 0xD0D0D0)
private let kIndicatorColor = ThemeManager.currentTheme.graphTintColor
private let totalPagesCount: Int
private var dots = [UIView]()
private lazy var dotColorComponents: (red: CGFloat, green: CGFloat, blue: CGFloat) = {
let components = kDotColor.cgColor.components
return (components?[0] ?? 0,
components?[1] ?? 0,
components?[2] ?? 0)
}()
private lazy var indicatorColorComponents: (red: CGFloat, green: CGFloat, blue: CGFloat) = {
let components = kIndicatorColor.cgColor.components
return (components?[0] ?? 0,
components?[1] ?? 0,
components?[2] ?? 0)
}()
init(totalPagesCount: Int) {
self.totalPagesCount = totalPagesCount
super.init(frame: .zero)
prepareView()
prepareDots()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func scroll(toPage nextPageIndex: Int, currentPageIndex: Int, screenTransitionProgress: CGFloat) {
guard screenTransitionProgress != 0 else { return }
var scrollProgress = max(0, screenTransitionProgress)
scrollProgress = min(screenTransitionProgress, 1)
let targetDot = dots[nextPageIndex]
let previousDot = dots[currentPageIndex]
//Width
let widthDelta = (kIndicatorSize.width - kDotSize.width) * scrollProgress
targetDot.snp.updateConstraints { update in
update.width.equalTo(kDotSize.width + widthDelta)
}
previousDot.snp.updateConstraints { update in
update.width.equalTo(kIndicatorSize.width - widthDelta)
}
//Color
//Blue to gray
let previousColorDelta: (red: CGFloat, green: CGFloat, blue: CGFloat) = (indicatorColorComponents.red - dotColorComponents.red,
indicatorColorComponents.green - dotColorComponents.green,
indicatorColorComponents.blue - dotColorComponents.blue)
previousDot.backgroundColor = UIColor(red: indicatorColorComponents.red - previousColorDelta.red * scrollProgress,
green: indicatorColorComponents.green - previousColorDelta.green * scrollProgress,
blue: indicatorColorComponents.blue - previousColorDelta.blue * scrollProgress,
alpha: 1)
//Gray to blue
let targetColorDelta: (red: CGFloat, green: CGFloat, blue: CGFloat) = (dotColorComponents.red - indicatorColorComponents.red,
dotColorComponents.green - indicatorColorComponents.green,
dotColorComponents.blue - indicatorColorComponents.blue)
targetDot.backgroundColor = UIColor(red: dotColorComponents.red - targetColorDelta.red * scrollProgress,
green: dotColorComponents.green - targetColorDelta.green * scrollProgress,
blue: dotColorComponents.blue - targetColorDelta.blue * scrollProgress,
alpha: 1)
}
}
//MARK:- Prepare
private extension OnboardingPageControl {
func prepareView() {
snp.makeConstraints { make in
make.height.equalTo(kDotSize.height)
}
}
func prepareDots() {
for index in 0..<totalPagesCount {
let dot = UIView()
dot.backgroundColor = index == 0 ? kIndicatorColor : kDotColor
dot.layer.cornerRadius = kDotSize.height / 2
addSubview(dot)
dots.append(dot)
dot.snp.makeConstraints { make in
make.size.equalTo(index == 0 ? kIndicatorSize : kDotSize)
make.centerY.equalToSuperview()
if index == 0 {
make.left.equalToSuperview()
}
else {
make.left.equalTo(dots[index - 1].snp.right).offset(kSpacePerDot)
}
if index == totalPagesCount - 1 {
make.right.equalToSuperview()
}
}
}
}
}
...@@ -11,11 +11,14 @@ import OneWeatherCore ...@@ -11,11 +11,14 @@ import OneWeatherCore
import OneWeatherAnalytics import OneWeatherAnalytics
class SplashAnimationViewController: UIViewController { class SplashAnimationViewController: UIViewController {
private let kAttemptsCount = 3
private let appCoordinator:AppCoordinator private let appCoordinator:AppCoordinator
private let backgroundImageView = UIImageView() private let backgroundImageView = UIImageView()
private let animation = Animation.named("splash") private let animation = Animation.named("splash")
private let logger = Logger(componentName: "SplashAnimationController")
private var animationView:AnimationView? private var animationView:AnimationView?
private let playButton = UIButton() private let playButton = UIButton()
private var currentAttempt = 0
init(appCoordinator:AppCoordinator) { init(appCoordinator:AppCoordinator) {
self.appCoordinator = appCoordinator self.appCoordinator = appCoordinator
...@@ -59,12 +62,36 @@ class SplashAnimationViewController: UIViewController { ...@@ -59,12 +62,36 @@ class SplashAnimationViewController: UIViewController {
} }
} }
private func finish() {
//We need to wait, until ConfigManager finishes updating configuration
//But this is given 3 attempts with a duration of 1 second
if ConfigManager.shared.isUpdating {
if currentAttempt < kAttemptsCount {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.logger.log(level: .debug, message: "Waiting for finish config update \(self.currentAttempt)/\(self.kAttemptsCount)")
self.currentAttempt += 1
self.finish()
return
}
}
else {
self.logger.log(level: .debug, message: "Finish animation")
self.appCoordinator.finishAnimation()
}
}
else {
self.logger.log(level: .debug, message: "Finish animation")
self.appCoordinator.finishAnimation()
}
}
private func noCityAnimation() { private func noCityAnimation() {
analytics(log: .ANALYTICS_FTUE_SPLASH_SEEN) analytics(log: .ANALYTICS_FTUE_SPLASH_SEEN)
analytics(set: .splashFTUE, to: true) analytics(set: .splashFTUE, to: true)
self.animationView?.play(completion: {[weak self] _ in self.animationView?.play(completion: {[weak self] _ in
DispatchQueue.global().asyncAfter(deadline: .now() + 0.5) { DispatchQueue.global().asyncAfter(deadline: .now() + 0.5) {
self?.appCoordinator.finishAnimation() self?.finish()
} }
}) })
} }
...@@ -76,7 +103,7 @@ class SplashAnimationViewController: UIViewController { ...@@ -76,7 +103,7 @@ class SplashAnimationViewController: UIViewController {
self.animationView?.alpha = 1 self.animationView?.alpha = 1
} completion: { _ in } completion: { _ in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
self.appCoordinator.finishAnimation() self.finish()
} }
} }
} }
......
...@@ -121,6 +121,12 @@ public enum AnalyticsEvent: String { ...@@ -121,6 +121,12 @@ public enum AnalyticsEvent: String {
case ANALYTICS_SHORTS_LIKE_BUTTON_CLICK = "LIKE_BUTTON_CLICK" case ANALYTICS_SHORTS_LIKE_BUTTON_CLICK = "LIKE_BUTTON_CLICK"
case ANALYTICS_SHORTS_EXIT_SHORTS_VIEW = "EXIT_SHORTS_VIEW" case ANALYTICS_SHORTS_EXIT_SHORTS_VIEW = "EXIT_SHORTS_VIEW"
case ANALYTICS_SHORTS_NUDGE_VIEW = "NUDGE_VIEW" case ANALYTICS_SHORTS_NUDGE_VIEW = "NUDGE_VIEW"
case ANALYTICS_ONBOARDING_FORECAST_SEEN = "ONBOARDING_FORECAST_SEEN"
case ANALYTICS_ONBOARDING_ALERT_SEEN = "ONBOARDING_ALERT_SEEN"
case ANALYTICS_ONBOARDING_RADAR_SEEN = "ONBOARDING_RADAR_SEEN"
case ANALYTICS_ONBOARDING_NEXT = "ONBOARDING_NEXT"
case ANALYTICS_ONBOARDING_SKIP = "ONBOARDING_SKIP"
case ANALYTICS_ONBOARDING_SWIPE = "ONBOARDING_SWIPE"
/// FTUE Funnel: User has saved his first city after installing the app. /// FTUE Funnel: User has saved his first city after installing the app.
case ANALYTICS_USER_QUALIFIED = "USER_QUALIFIED" case ANALYTICS_USER_QUALIFIED = "USER_QUALIFIED"
......
...@@ -98,6 +98,9 @@ public class Settings { ...@@ -98,6 +98,9 @@ public class Settings {
@UserDefaultsOptionalValue("widgetPromotionTriggerCount") @UserDefaultsOptionalValue("widgetPromotionTriggerCount")
public var widgetPromotionTriggerCount: Int? public var widgetPromotionTriggerCount: Int?
@UserDefaultsBasicValue(key: "initial_onboarding_showed")
public var initialOnboardingShowed = false
@UserDefaultsBasicValue(key: "shorts_showed_swipeUp_count") @UserDefaultsBasicValue(key: "shorts_showed_swipeUp_count")
public var shortsSwipeUpNudgeShowedCount = 0 public var shortsSwipeUpNudgeShowedCount = 0
......
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