Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Local push notifications #76

Merged
merged 6 commits into from
Aug 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Stepic.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,7 @@
2C315E6F1F0A947D0039E4F0 /* LessonPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0899842B1ECDE194005C0B27 /* LessonPresenter.swift */; };
2C315E701F0A947D0039E4F0 /* LessonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0899842E1ECDE19E005C0B27 /* LessonView.swift */; };
2C315E711F0A947D0039E4F0 /* PagerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08C1943F1ED0A05D00A41B72 /* PagerController.swift */; };
2C4DA9E91F38729500E392FA /* LocalNotificationsHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C4DA9E81F38729500E392FA /* LocalNotificationsHelper.swift */; };
2C5F48D71F0F6FBD00C3A398 /* AdaptiveStepPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C5F48D51F0F6ED800C3A398 /* AdaptiveStepPresenter.swift */; };
2C5F48DB1F0FA0A500C3A398 /* AdaptiveStepsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C5F48D91F0FA04300C3A398 /* AdaptiveStepsPresenter.swift */; };
2C5F6BA91F20B2A900C251D3 /* CongratulationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C5F6BA71F20B2A900C251D3 /* CongratulationViewController.swift */; };
Expand Down Expand Up @@ -1843,6 +1844,7 @@
2C04C1091F16075A006B69B3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = en; path = en.lproj/step3.html; sourceTree = "<group>"; };
2C04C10A1F16075D006B69B3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = en; path = en.lproj/step2.html; sourceTree = "<group>"; };
2C04C10B1F16075F006B69B3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = en; path = en.lproj/step1.html; sourceTree = "<group>"; };
2C4DA9E81F38729500E392FA /* LocalNotificationsHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalNotificationsHelper.swift; sourceTree = "<group>"; };
2C5F48D51F0F6ED800C3A398 /* AdaptiveStepPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdaptiveStepPresenter.swift; sourceTree = "<group>"; };
2C5F48D91F0FA04300C3A398 /* AdaptiveStepsPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdaptiveStepsPresenter.swift; sourceTree = "<group>"; };
2C5F6BA71F20B2A900C251D3 /* CongratulationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CongratulationViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3484,6 +3486,7 @@
2C76ACCC1F16496C0077D9D7 /* RatingHelper.swift */,
2CCEB4941F27755800B45D63 /* AnalyticsEvents+Adaptive.swift */,
2C733C391F29E090000E7FAF /* StatsHelper.swift */,
2C4DA9E81F38729500E392FA /* LocalNotificationsHelper.swift */,
);
name = Helpers;
sourceTree = "<group>";
Expand Down Expand Up @@ -5370,6 +5373,7 @@
0805FE461F0D390B001226B4 /* CodePlaygroundManager.swift in Sources */,
864D66AB1E83DE03001E8D9E /* Scripts.swift in Sources */,
864D66AC1E83DE03001E8D9E /* Scripts.plist in Sources */,
2C4DA9E91F38729500E392FA /* LocalNotificationsHelper.swift in Sources */,
864D66AD1E83DE03001E8D9E /* Constants.swift in Sources */,
864D66AE1E83DE03001E8D9E /* Images.swift in Sources */,
864D66B01E83DE03001E8D9E /* ApplicationInfo.swift in Sources */,
Expand Down
4 changes: 4 additions & 0 deletions Stepic/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -249,4 +249,8 @@ NewLevelCongratulationShareText = "I've reached level %@ in %@ app. Join and bea
NewLevelCongratulationText = "You've reached level %@!";
RatingCongratulationText = "Correct! +%@ XP";
ShareAchievement = "Share achievement";
RetentionNotificationYesterday = "You scored %@ XP yesterday. Go back and improve your score!";
RetentionNotificationYesterdayZero = "You didn't score any points for yesterday. Improve your score today!";
RetentionNotificationYesterdayStreak = "You have been learning %@ in a row. Go back and keep learning!";
RetentionNotificationWeekly = "You haven't done it for a long time. Go back and keep learning!";
SocialSignupWithExistingEmail = "This email is already used. To log in using this social account you have to log in via email and password";
4 changes: 4 additions & 0 deletions Stepic/ru.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -250,4 +250,8 @@ NewLevelCongratulationShareText = "Я достиг %@ уровня в прило
NewLevelCongratulationText = "Вы достигли %@ уровня!";
RatingCongratulationText = "Правильно! +%@ опыта";
ShareAchievement = "Поделиться достижением";
RetentionNotificationYesterday = "Вчера Вы набрали %@ опыта. Возвращайтесь и улучшите свой результат!";
RetentionNotificationYesterdayZero = "За вчерашний день Вы ничего не решили. Улучшите свой результат сегодня!";
RetentionNotificationYesterdayStreak = "Вы занимаетесь уже %@ подряд. Возвращайтесь и продолжайте учиться!";
RetentionNotificationWeekly = "Вы давно не занимались. Возвращайтесь и продолжайте учиться!";
SocialSignupWithExistingEmail = "Указанный email привязан к другому аккаунту. Чтобы входить через эту социальную сеть, необходимо войти, используя email и пароль";
20 changes: 17 additions & 3 deletions StepicAdaptiveCourse/AdaptiveAppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,26 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
DefaultsContainer.launch.didLaunch = true
}

LocalNotificationsHelper.registerNotifications()

if let launchNotification = launchOptions?[UIApplicationLaunchOptionsKey.localNotification] as? UILocalNotification {
if let userInfo = launchNotification.userInfo as? [String: String], let notificationType = userInfo["type"] {
AnalyticsReporter.reportEvent(AnalyticsEvents.Adaptive.localNotification, parameters: ["type": notificationType])
}
}

return true
}

func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
LocalNotificationsHelper.schedule(notification: .tomorrow)
LocalNotificationsHelper.schedule(notification: .weekly)
}

func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
if let userInfo = notification.userInfo as? [String: String], let notificationType = userInfo["type"] {
AnalyticsReporter.reportEvent(AnalyticsEvents.Adaptive.localNotification, parameters: ["type": notificationType])
}
}

func applicationDidEnterBackground(_ application: UIApplication) {
Expand All @@ -46,7 +60,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}

func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
LocalNotificationsHelper.cancelAllNotifications()
}

func applicationWillTerminate(_ application: UIApplication) {
Expand Down
1 change: 1 addition & 0 deletions StepicAdaptiveCourse/AnalyticsEvents+Adaptive.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ extension AnalyticsEvents {
static let easy = "adaptive_reaction_easy"
static let hard = "adaptive_reaction_hard"
}
static let localNotification = "adaptive_opened_by_local_notification"
}
}
88 changes: 88 additions & 0 deletions StepicAdaptiveCourse/LocalNotificationsHelper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//
// LocalNotificationsHelper.swift
// Stepic
//
// Created by Vladislav Kiryukhin on 07.08.2017.
// Copyright © 2017 Alex Karpov. All rights reserved.
//

import UIKit

enum LocalNotification {
case tomorrow, weekly

var fireDate: Date {
switch self {
case .tomorrow:
return Date(timeIntervalSinceNow: 24 * 60 * 60)
case .weekly:
return Date(timeIntervalSinceNow: 2 * 24 * 60 * 60)
}
}

var repeatInterval: NSCalendar.Unit {
switch self {
case .tomorrow:
return NSCalendar.Unit(rawValue: 0)
case .weekly:
return NSCalendar.Unit.weekOfYear
}
}

var notification: UILocalNotification {
let localNotification = UILocalNotification()
localNotification.soundName = UILocalNotificationDefaultSoundName
localNotification.repeatInterval = self.repeatInterval
localNotification.fireDate = self.fireDate

switch self {
case .tomorrow:
let streak = StatsHelper.currentDayStreak
if streak == 0 {
// 0 points today, 0 points prev
localNotification.alertBody = NSLocalizedString("RetentionNotificationYesterdayZero", comment: "")
localNotification.userInfo = ["type": "yesterday_zero"]
} else if streak == 1 {
// X points today, 0 points prev
if let todayXP = StatsHelper.loadStats()?[StatsHelper.dayByDate(Date())] {
localNotification.alertBody = String(format: NSLocalizedString("RetentionNotificationYesterday", comment: ""), "\(todayXP)")
localNotification.userInfo = ["type": "yesterday"]
}
} else {
// X points today, X points prev
var streakDays = "\(streak) "
switch (streak % 10) {
case 1: streakDays += NSLocalizedString("days1", comment: "")
case 2, 3, 4: streakDays += NSLocalizedString("days234", comment: "")
default: streakDays += NSLocalizedString("days567890", comment: "")
}

localNotification.alertBody = String(format: NSLocalizedString("RetentionNotificationYesterdayStreak", comment: ""), "\(streakDays)")
localNotification.userInfo = ["type": "yesterday_streak"]
}
case .weekly:
localNotification.alertBody = NSLocalizedString("RetentionNotificationWeekly", comment: "")
localNotification.userInfo = ["type": "weekly"]
}

return localNotification
}
}

class LocalNotificationsHelper {
static func cancelAllNotifications() {
print("local notifications: cancelled all")
UIApplication.shared.cancelAllLocalNotifications()
}

static func schedule(notification: LocalNotification) {
print("local notifications: scheduled notification with fire date = \(notification.fireDate)")
UIApplication.shared.scheduleLocalNotification(notification.notification)
}

static func registerNotifications() {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
UIApplication.shared.registerUserNotificationSettings(settings)
}
}
18 changes: 16 additions & 2 deletions StepicAdaptiveCourse/StatsHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,27 @@ class StatsHelper {

private static let secondsInDay: TimeInterval = 24 * 60 * 60

private static func dayByDate(_ date: Date) -> Int {
static var currentDayStreak: Int {
get {
var curDay = StatsHelper.dayByDate(Date())
while curDay > 0 {
if let todayXP = StatsHelper.loadStats()?[curDay], todayXP != 0 {
curDay -= 1
} else {
break
}
}
return StatsHelper.dayByDate(Date()) - curDay
}
}

static func dayByDate(_ date: Date) -> Int {
// Day num (01.01.1970 - 0, 02.01.1970 - 1, ...)
let dayNum = Int(date.timeIntervalSince1970 / secondsInDay)
return dayNum
}

private static func dateByDay(_ day: Int) -> Date {
static func dateByDay(_ day: Int) -> Date {
// 00:00 am target day
let date = Date(timeIntervalSince1970: secondsInDay * Double(day))
return date
Expand Down