Skip to content

Commit

Permalink
Subscribe to notifications on launch AB (#416)
Browse files Browse the repository at this point in the history
* Create group with folder

* Split test

* Fix

* Respect current group

* Do not request authorization before onboarding

* Request authorization after onboarding

* Request authorization for all users if they participants

* Make decisions based on current group

* Refactor rename isParticipant -> shouldShowOnFirstLaunch

* Suggest streak
  • Loading branch information
ivan-magda authored Nov 30, 2018
1 parent 59cff2d commit c37e2d5
Show file tree
Hide file tree
Showing 17 changed files with 199 additions and 46 deletions.
46 changes: 25 additions & 21 deletions Stepic.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -4012,6 +4012,7 @@
62E9826D2C6AABDF608C2B57 /* ProfileDescriptionPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E98AFF3B8AC3CB8374445B /* ProfileDescriptionPresenter.swift */; };
62E982769C19C7852BCD59C4 /* TooltipStorageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E98117E7E58E81E4BF6E24 /* TooltipStorageManager.swift */; };
62E9829488F863D10CF5544C /* UITableView+TableHeaderLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E98D9D45E6DA6099AF1C0B /* UITableView+TableHeaderLayout.swift */; };
62E982C316E409B90DC3AA21 /* SubscribeNotificationsOnLaunchSplitTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E988F198B6ED19738CB0B5 /* SubscribeNotificationsOnLaunchSplitTest.swift */; };
62E982CD54C2C86F14DF84EB /* ProfileHeaderInfoView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 62E98801330B94B34FA0D7B9 /* ProfileHeaderInfoView.xib */; };
62E982F7B4EDC6C94D1283FE /* ProgressesNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E982F6B023E2C827407B1B /* ProgressesNetworkService.swift */; };
62E983397736699787D8DD35 /* UIView+fromNib.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E982BB5246C2BBC5E88D06 /* UIView+fromNib.swift */; };
Expand Down Expand Up @@ -6044,6 +6045,7 @@
62E9888C0199CE31C6B4E91D /* ProfileViewController+StreakNotificationsControlView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ProfileViewController+StreakNotificationsControlView.swift"; sourceTree = "<group>"; };
62E988EACCEC4FB37B0567A7 /* VerticalCourseListFlowLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VerticalCourseListFlowLayout.swift; sourceTree = "<group>"; };
62E988EC03C89C26C42DE55E /* CourseListCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CourseListCollectionViewCell.swift; sourceTree = "<group>"; };
62E988F198B6ED19738CB0B5 /* SubscribeNotificationsOnLaunchSplitTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscribeNotificationsOnLaunchSplitTest.swift; sourceTree = "<group>"; };
62E989338C0834107DD43B60 /* CourseReviewSummariesNetworkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CourseReviewSummariesNetworkService.swift; sourceTree = "<group>"; };
62E989579A70D89D559F7520 /* CodeEditorSettingsPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CodeEditorSettingsPresenter.swift; sourceTree = "<group>"; };
62E9897F76726E9D4EF78C1A /* StepsPagerRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StepsPagerRouter.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -7107,17 +7109,6 @@
name = Explore;
sourceTree = "<group>";
};
0853A3DE213D8F6100931F72 /* Split tests */ = {
isa = PBXGroup;
children = (
089877B4214061EC0065DFA2 /* Active Tests */,
0898779D214047640065DFA2 /* SplitTestGroupProtocol.swift */,
0898779F214047650065DFA2 /* SplitTestingService.swift */,
0898779E214047640065DFA2 /* SplitTestProtocol.swift */,
);
name = "Split tests";
sourceTree = "<group>";
};
085514E91CFB09170080CB88 /* WebViewHelpers */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -7514,14 +7505,6 @@
path = "Deep Links";
sourceTree = "<group>";
};
089877B4214061EC0065DFA2 /* Active Tests */ = {
isa = PBXGroup;
children = (
08421BCA21764FC400E8A81B /* ActiveSplitTestsContainer.swift */,
);
name = "Active Tests";
sourceTree = "<group>";
};
089F58871D22BD44000CD540 /* DiscussionCountView */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -7802,7 +7785,7 @@
62E986DB2768602666465180 /* AppDelegate.swift */,
08E43E95214C277600E3CB50 /* Transition Routers */,
2C89089E216F460F00083341 /* Services */,
0853A3DE213D8F6100931F72 /* Split tests */,
62E98F39D7A7B4486E71DF3E /* SplitTests */,
08484EDF211AF41A0006266F /* Stories */,
2CE664DE20F5204D0082F3FE /* Downloader */,
088E73E92060124B00D458E3 /* ApiRequestRetrier.swift */,
Expand Down Expand Up @@ -9055,6 +9038,15 @@
path = View;
sourceTree = "<group>";
};
2C37968421ABED9500BC7E62 /* ActiveTests */ = {
isa = PBXGroup;
children = (
08421BCA21764FC400E8A81B /* ActiveSplitTestsContainer.swift */,
62E988F198B6ED19738CB0B5 /* SubscribeNotificationsOnLaunchSplitTest.swift */,
);
path = ActiveTests;
sourceTree = "<group>";
};
2C3E42B92101DE34009D76DF /* KnowledgeGraph */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -10867,6 +10859,17 @@
path = Presenter;
sourceTree = "<group>";
};
62E98F39D7A7B4486E71DF3E /* SplitTests */ = {
isa = PBXGroup;
children = (
2C37968421ABED9500BC7E62 /* ActiveTests */,
0898779D214047640065DFA2 /* SplitTestGroupProtocol.swift */,
0898779F214047650065DFA2 /* SplitTestingService.swift */,
0898779E214047640065DFA2 /* SplitTestProtocol.swift */,
);
path = SplitTests;
sourceTree = "<group>";
};
62E98F6778240E9688E5282D /* StreakActivityView */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -12845,7 +12848,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "${SRCROOT}/lint.sh";
shellScript = "${SRCROOT}/lint.sh\n";
};
08983F251F3E4131008199B1 /* SwiftLint Script */ = {
isa = PBXShellScriptBuildPhase;
Expand Down Expand Up @@ -15035,6 +15038,7 @@
62E982769C19C7852BCD59C4 /* TooltipStorageManager.swift in Sources */,
62E985DD90444821A835C02F /* CourseListsCollectionSkeletonView.swift in Sources */,
62E98A53567525055BCCA089 /* AppDelegate.swift in Sources */,
62E982C316E409B90DC3AA21 /* SubscribeNotificationsOnLaunchSplitTest.swift in Sources */,
62E9874D1EAE49639F81C916 /* URL+AppendQueryParameters.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
3 changes: 3 additions & 0 deletions Stepic/Analytics/NotificationAlertsAnalytics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ struct NotificationAlertsAnalytics {
case streakAfterLogin
case streakAfterSubmission(shownCount: Int)
case personalDeadline
case abAppLaunch

var description: String {
switch self {
Expand All @@ -63,6 +64,8 @@ struct NotificationAlertsAnalytics {
return "streak after submission - \(shownCount)"
case .personalDeadline:
return "create personal deadline"
case .abAppLaunch:
return "ab subscribe on app launch"
}
}
}
Expand Down
25 changes: 21 additions & 4 deletions Stepic/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?

private let userNotificationsCenterDelegate = UserNotificationsCenterDelegate()
private let notificationsRegistrationService: NotificationsRegistrationServiceProtocol = NotificationsRegistrationService()
private let notificationsRegistrationService: NotificationsRegistrationServiceProtocol = NotificationsRegistrationService(
presenter: NotificationsRequestOnlySettingsAlertPresenter(),
analytics: .init(source: .abAppLaunch)
)
private let branchService = BranchService(deepLinkRoutingService: DeepLinkRoutingService())
private let splitTestingService = SplitTestingService(
analyticsService: AnalyticsUserProperties(),
storage: UserDefaults.standard
)
private var didShowOnboarding = true

deinit {
NotificationCenter.default.removeObserver(self)
Expand Down Expand Up @@ -85,6 +93,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
IQKeyboardManager.sharedManager().enableAutoToolbar = false

if !DefaultsContainer.launch.didLaunch {
self.didShowOnboarding = false
DefaultsContainer.launch.initStartVersion()
ActiveSplitTestsContainer.setActiveTestsGroups()
AnalyticsUserProperties.shared.setPushPermissionStatus(.notDetermined)
Expand All @@ -96,7 +105,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
self.checkForUpdates()
}

self.notificationsRegistrationService.renewDeviceToken()
let subscribeSplitTest = self.splitTestingService.fetchSplitTest(SubscribeNotificationsOnLaunchSplitTest.self)
let shouldParticipate = SubscribeNotificationsOnLaunchSplitTest.shouldParticipate
&& subscribeSplitTest.currentGroup.shouldShowOnFirstLaunch
if shouldParticipate && self.didShowOnboarding {
self.notificationsRegistrationService.registerForRemoteNotifications()
} else {
self.notificationsRegistrationService.renewDeviceToken()
}

LocalNotificationsMigrator().migrateIfNeeded()
NotificationsService().handleLaunchOptions(launchOptions)
self.userNotificationsCenterDelegate.attachNotificationDelegate()
Expand Down Expand Up @@ -227,7 +244,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
return true
}
if url.scheme == "vk\(StepicApplicationsInfo.SocialInfo.AppIds.vk)"
|| url.scheme == "fb\(StepicApplicationsInfo.SocialInfo.AppIds.facebook)" {
|| url.scheme == "fb\(StepicApplicationsInfo.SocialInfo.AppIds.facebook)" {
return true
}

Expand All @@ -249,7 +266,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
if branchService.canOpenWithBranch(url: url) {
branchService.openURL(app: app, open: url, options: options)
} else {
// Other actions
// Other actions
self.handleOpenedFromDeepLink(url)
}
}
Expand Down
11 changes: 8 additions & 3 deletions Stepic/AuthNavigationViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,14 @@ final class AuthNavigationViewController: UINavigationController {
}

self.userActivitiesAPI.retrieve(user: userId).done { userActivity in
if userActivity.didSolveThisWeek
&& self.notificationSuggestionManager.canShowAlert(context: .streak, after: .login) {
self.streaksAlertPresentationManager.suggestStreak(streak: userActivity.currentStreak)
let canShowAlert = self.notificationSuggestionManager.canShowAlert(
context: .streak,
after: .login
)
if canShowAlert && userActivity.didSolveThisWeek {
self.streaksAlertPresentationManager.suggestStreak(
streak: userActivity.currentStreak
)
}
}.catch { error in
print("\(#file) \(#function) \(error)")
Expand Down
6 changes: 5 additions & 1 deletion Stepic/BaseCardsStepsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,11 @@ class BaseCardsStepsViewController: CardsStepsViewController {
analytics: .init(source: .courseSubscription)
),
course: course,
view: self
view: self,
splitTestingService: SplitTestingService(
analyticsService: AnalyticsUserProperties(),
storage: UserDefaults.standard
)
)
presenter?.refresh()
}
Expand Down
15 changes: 13 additions & 2 deletions Stepic/CardsStepsPresenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class BaseCardsStepsPresenter: CardsStepsPresenter, StepCardViewDelegate {
internal var lastViewedUpdater: LocalProgressLastViewedUpdater
internal var notificationSuggestionManager: NotificationSuggestionManager
internal var notificationsRegistrationService: NotificationsRegistrationServiceProtocol
var splitTestingService: SplitTestingServiceProtocol

// FIXME: incapsulate/remove this
var state: CardsStepsPresenterState = .loaded
Expand Down Expand Up @@ -118,7 +119,7 @@ class BaseCardsStepsPresenter: CardsStepsPresenter, StepCardViewDelegate {
return true
}

init(stepsAPI: StepsAPI, lessonsAPI: LessonsAPI, recommendationsAPI: RecommendationsAPI, unitsAPI: UnitsAPI, viewsAPI: ViewsAPI, ratingsAPI: AdaptiveRatingsAPI, ratingManager: AdaptiveRatingManager, statsManager: AdaptiveStatsManager, storageManager: AdaptiveStorageManager, lastViewedUpdater: LocalProgressLastViewedUpdater, notificationSuggestionManager: NotificationSuggestionManager, notificationsRegistrationService: NotificationsRegistrationServiceProtocol, course: Course?, view: CardsStepsView) {
init(stepsAPI: StepsAPI, lessonsAPI: LessonsAPI, recommendationsAPI: RecommendationsAPI, unitsAPI: UnitsAPI, viewsAPI: ViewsAPI, ratingsAPI: AdaptiveRatingsAPI, ratingManager: AdaptiveRatingManager, statsManager: AdaptiveStatsManager, storageManager: AdaptiveStorageManager, lastViewedUpdater: LocalProgressLastViewedUpdater, notificationSuggestionManager: NotificationSuggestionManager, notificationsRegistrationService: NotificationsRegistrationServiceProtocol, course: Course?, view: CardsStepsView, splitTestingService: SplitTestingServiceProtocol) {
self.stepsAPI = stepsAPI
self.lessonsAPI = lessonsAPI
self.recommendationsAPI = recommendationsAPI
Expand All @@ -131,6 +132,7 @@ class BaseCardsStepsPresenter: CardsStepsPresenter, StepCardViewDelegate {
self.lastViewedUpdater = lastViewedUpdater
self.notificationSuggestionManager = notificationSuggestionManager
self.notificationsRegistrationService = notificationsRegistrationService
self.splitTestingService = splitTestingService

self.course = course
self.view = view
Expand All @@ -152,7 +154,16 @@ class BaseCardsStepsPresenter: CardsStepsPresenter, StepCardViewDelegate {
}

func appearedAfterSubscription() {
self.notificationsRegistrationService.registerForRemoteNotifications()
if SubscribeNotificationsOnLaunchSplitTest.shouldParticipate {
let subscribeSplitTest = self.splitTestingService.fetchSplitTest(
SubscribeNotificationsOnLaunchSplitTest.self
)
if !subscribeSplitTest.currentGroup.shouldShowOnFirstLaunch {
self.notificationsRegistrationService.registerForRemoteNotifications()
}
} else {
self.notificationsRegistrationService.registerForRemoteNotifications()
}
}

private func refreshTopCardForOnboarding(stepIndex: Int) {
Expand Down
17 changes: 15 additions & 2 deletions Stepic/NotificationsPresenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,17 @@ final class NotificationsPresenter {
// Store unread notifications count to pass it to analytics
private var badgeUnreadCount = 0

private let splitTestingService: SplitTestingServiceProtocol

init(
section: NotificationsSection,
notificationsAPI: NotificationsAPI,
usersAPI: UsersAPI,
notificationsStatusAPI: NotificationStatusesAPI,
notificationsRegistrationService: NotificationsRegistrationServiceProtocol,
notificationSuggestionManager: NotificationSuggestionManager,
view: NotificationsView
view: NotificationsView,
splitTestingService: SplitTestingServiceProtocol
) {
self.section = section
self.notificationsAPI = notificationsAPI
Expand All @@ -71,6 +74,7 @@ final class NotificationsPresenter {
self.notificationsRegistrationService = notificationsRegistrationService
self.notificationSuggestionManager = notificationSuggestionManager
self.view = view
self.splitTestingService = splitTestingService

self.notificationsRegistrationService.delegate = self

Expand Down Expand Up @@ -139,7 +143,16 @@ final class NotificationsPresenter {
}

func didAppear() {
self.notificationsRegistrationService.registerForRemoteNotifications()
if SubscribeNotificationsOnLaunchSplitTest.shouldParticipate {
let subscribeSplitTest = self.splitTestingService.fetchSplitTest(
SubscribeNotificationsOnLaunchSplitTest.self
)
if !subscribeSplitTest.currentGroup.shouldShowOnFirstLaunch {
self.notificationsRegistrationService.registerForRemoteNotifications()
}
} else {
self.notificationsRegistrationService.registerForRemoteNotifications()
}
}

func refresh() {
Expand Down
6 changes: 5 additions & 1 deletion Stepic/NotificationsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,11 @@ class NotificationsViewController: UIViewController, NotificationsView {
analytics: .init(source: .notificationsTab)
),
notificationSuggestionManager: NotificationSuggestionManager(),
view: self
view: self,
splitTestingService: SplitTestingService(
analyticsService: AnalyticsUserProperties(),
storage: UserDefaults.standard
)
)

tableView.register(UINib(nibName: "NotificationsTableViewCell", bundle: nil), forCellReuseIdentifier: NotificationsTableViewCell.reuseId)
Expand Down
10 changes: 9 additions & 1 deletion Stepic/OnboardingViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,16 @@ class OnboardingViewController: UIViewController {
return DeviceInfo.current.orientation.interface.isLandscape
}

private let notificationsRegistrationService: NotificationsRegistrationServiceProtocol = NotificationsRegistrationService(
presenter: NotificationsRequestOnlySettingsAlertPresenter(),
analytics: .init(source: .abAppLaunch)
)

@IBAction func onCloseButtonClick(_ sender: Any) {
dismiss(animated: true) {
AnalyticsReporter.reportEvent(AnalyticsEvents.Onboarding.onboardingClosed, parameters: ["screen": self.currentPageIndex + 1])
AmplitudeAnalyticsEvents.Onboarding.closed(screen: self.currentPageIndex + 1).send()
self.notificationsRegistrationService.registerForRemoteNotifications()
}
}

Expand Down Expand Up @@ -194,7 +200,9 @@ class OnboardingViewController: UIViewController {
} else {
AnalyticsReporter.reportEvent(AnalyticsEvents.Onboarding.onboardingComplete, parameters: ["screen": currentPageIndex + 1])
AmplitudeAnalyticsEvents.Onboarding.completed.send()
dismiss(animated: true, completion: nil)
self.dismiss(animated: true, completion: {
self.notificationsRegistrationService.registerForRemoteNotifications()
})

if let authSource = authSource {
RoutingManager.auth.routeFrom(controller: authSource, success: nil, cancel: nil)
Expand Down
22 changes: 20 additions & 2 deletions Stepic/QuizViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ class QuizViewController: UIViewController, QuizView, QuizControllerDataSource,
}
}

private let splitTestingService: SplitTestingServiceProtocol = SplitTestingService(
analyticsService: AnalyticsUserProperties(),
storage: UserDefaults.standard
)

private func submissionsLeftLocalizable(count: Int) -> String {
func triesLocalizableFor(count: Int) -> String {
switch (abs(count) % 10) {
Expand Down Expand Up @@ -343,8 +348,21 @@ class QuizViewController: UIViewController, QuizView, QuizControllerDataSource,
}

func suggestStreak(streak: Int) {
self.streaksAlertPresentationManager.controller = self
self.streaksAlertPresentationManager.suggestStreak(streak: streak)
if self.shouldSuggestStreak() {
self.streaksAlertPresentationManager.controller = self
self.streaksAlertPresentationManager.suggestStreak(streak: streak)
}
}

private func shouldSuggestStreak() -> Bool {
if SubscribeNotificationsOnLaunchSplitTest.shouldParticipate {
let subscribeSplitTest = self.splitTestingService.fetchSplitTest(
SubscribeNotificationsOnLaunchSplitTest.self
)
return !subscribeSplitTest.currentGroup.shouldShowOnFirstLaunch
} else {
return true
}
}

func showRateAlert() {
Expand Down
Loading

0 comments on commit c37e2d5

Please sign in to comment.