added iOS source code
[wl-app.git] / iOS / WolneLektury / AppDelegate.swift
1 //
2 //  AppDelegate.swift
3 //  WolneLektury
4 //
5 //  Created by Pawel Dabrowski on 29/03/2018.
6 //  Copyright © 2018 Fundacja Nowoczesna Polska. All rights reserved.
7 //
8
9 import UIKit
10 import OAuthSwift
11 import SafariServices
12 import MBProgressHUD
13 import MatomoTracker
14 import Fabric
15 import Crashlytics
16 import Firebase
17 import UserNotifications
18
19 @UIApplicationMain
20 class AppDelegate: UIResponder, UIApplicationDelegate {
21     
22     var window: UIWindow?
23     var windowHud: MBProgressHUD?
24     var mainNavigator: MainNavigator!
25     var syncManager = SyncManager()
26     weak var sideMenuNavigationController: UINavigationController?
27     weak var safariParentViewController: UIViewController?
28     let gcmMessageIDKey = "gcm.message_id"
29
30     func showWindowHud(){
31         
32         guard let window = window else {return}
33         
34         if let windowHud = windowHud {
35             windowHud.show(animated: true)
36         }
37         else{
38             windowHud = MBProgressHUD.showAdded(to: window, animated: true)
39         }
40     }
41     
42     func hideWindowHud(){
43         windowHud?.hide(animated: true)
44     }
45     
46     var backgroundSessionCompletionHandler : (() -> Void)?    
47
48     func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
49         // Override point for customization after application launch.
50         
51         Fabric.with([Crashlytics.self])
52         FirebaseApp.configure()
53
54         mainNavigator = MainNavigator(window: window!)
55         setCustomAppearance()
56         
57         if syncManager.isLoggedIn(){
58             fetchUsername()
59         }
60         
61 //        configureMessaging(application: application)
62         
63         return true
64     }
65     
66     func configureMessaging(application: UIApplication) {
67         // [START set_messaging_delegate]
68         Messaging.messaging().delegate = self
69         // [END set_messaging_delegate]
70         
71         // Register for remote notifications. This shows a permission dialog on first run, to
72         // show the dialog at a more appropriate time move this registration accordingly.
73         // [START register_for_notifications]
74         if #available(iOS 10.0, *) {
75             // For iOS 10 display notification (sent via APNS)
76             UNUserNotificationCenter.current().delegate = self
77             
78             let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
79             UNUserNotificationCenter.current().requestAuthorization(
80                 options: authOptions,
81                 completionHandler: {_, _ in })
82         } else {
83             let settings: UIUserNotificationSettings =
84                 UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
85             application.registerUserNotificationSettings(settings)
86         }
87         
88         application.registerForRemoteNotifications()
89         
90         // [END register_for_notifications]
91     }
92     
93     func setCustomAppearance() {
94         
95         UINavigationBar.appearance().isTranslucent = false
96         UINavigationBar.appearance().tintColor = .white
97         UINavigationBar.appearance().titleTextAttributes = [NSAttributedStringKey.foregroundColor : UIColor.white]
98         UINavigationBar.appearance().barTintColor = Constants.Colors.navbarBgColor()
99         UINavigationBar.appearance().barStyle = .blackOpaque
100         UIApplication.shared.statusBarStyle = .lightContent
101     }
102     
103     func applicationWillResignActive(_ application: UIApplication) {
104         // 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.
105         // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
106     }
107     
108     func applicationDidEnterBackground(_ application: UIApplication) {
109         // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
110         // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
111     }
112
113     func applicationWillEnterForeground(_ application: UIApplication) {
114         // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
115     }
116
117     func applicationDidBecomeActive(_ application: UIApplication) {
118         // 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.
119         mainNavigator.updateSettingsViewIfPresented()
120     }
121
122     func applicationWillTerminate(_ application: UIApplication) {
123         // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
124     }
125     
126     func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
127         backgroundSessionCompletionHandler = completionHandler
128     }
129     
130     func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
131         
132         applicationHandle(url: url)
133         return true
134     }
135     
136     private func applicationHandle(url: URL) {
137         if url.host == Constants.callbackOauthHost {
138             showWindowHud()
139             syncManager.accessToken { (result) in
140                 self.hideWindowHud()
141                 switch result {
142                 case .success(let model):
143                     self.syncManager.updateUserCredentials(oAuthTokenModel: (model as! OAuthTokenModel))
144                     self.safariParentViewController?.presentedViewController?.dismiss(animated: true, completion: nil)
145                     self.mainNavigator.presentLibrary(dismissSideMenu: true)
146                     self.fetchUsername()
147                     break
148                 case .failure/*(let error)*/:
149                     break
150                 }
151             }
152         }
153         else if url.host == Constants.callbackPaypalSuccessHost {
154             self.safariParentViewController?.presentedViewController?.dismiss(animated: true, completion: nil)
155             safariParentViewController?.presentToast(message: "premium_purchase_succeeded".localized)
156             DatabaseManager.shared.setUserPremium()
157             mainNavigator.reset()
158         }
159         else if url.host == Constants.callbackPaypalErrorHost {
160             self.safariParentViewController?.presentedViewController?.dismiss(animated: true, completion: nil)
161             safariParentViewController?.presentToast(message: "premium_purchase_failed".localized)
162         }
163     }
164     
165     private func fetchUsername(){
166         showWindowHud()
167         self.syncManager.getUsername(completionHandler: { (result) in
168             self.hideWindowHud()
169             switch result {
170             case .success(let model):
171                 DatabaseManager.shared.updateUser(usernameModel: model as? UsernameModel)
172                 self.mainNavigator.setLoggedIn()
173                 break
174             case .failure/*(let error)*/:
175                 //We can download username later
176                 self.mainNavigator.setLoggedIn()
177                 break
178             }
179         })
180     }
181     
182     func login(fromViewController: UIViewController){
183         let oauthswift = OAuth1Swift(
184             consumerKey:    Config.CONSUMER_KEY,
185             consumerSecret: Config.CONSUMER_SECRET,
186             requestTokenUrl: Config.OAUTH_REQUEST_TOKEN,
187             authorizeUrl:    Config.OAUTH_AUTHORIZE,
188             accessTokenUrl:  Config.OAUTH_ACCESS_TOKEN
189         )
190         
191         safariParentViewController = fromViewController
192         
193         let handler = SafariURLHandler(viewController: fromViewController, oauthSwift: oauthswift)
194         handler.presentCompletion = {
195             print("Safari presented")
196         }
197         handler.dismissCompletion = {
198             print("Safari dismissed")
199         }
200         handler.factory = { url in
201             let controller = SFSafariViewController(url: url)
202             // Customize it, for instance
203             if #available(iOS 10.0, *) {
204                 //  controller.preferredBarTintColor = UIColor.red
205             }
206             return controller
207         }
208         
209         oauthswift.authorizeURLHandler = handler
210         showWindowHud()
211         syncManager.requestToken { (result) in
212             self.hideWindowHud()
213             switch result {
214             case .success(let model):
215                 let url = URL(string: String(format: Constants.authorizationUrlFormat, (model as! OAuthTokenModel).token))
216                 oauthswift.authorizeURLHandler.handle(url!)
217                 break
218             case .failure/*(let error)*/:
219                 break
220             }
221         }
222     }
223     
224     func presentPaypal(fromViewController: UIViewController) {
225         
226         let controller = SFSafariViewController(url: URL(string: Constants.webPaypalFormUrl)!)
227         safariParentViewController = fromViewController
228         fromViewController.present(controller, animated: true, completion: nil)
229     }
230     
231     
232     // ############################################
233     // MARK: - Messaging
234     // ############################################
235     
236     // [START receive_message]
237     func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
238         // If you are receiving a notification message while your app is in the background,
239         // this callback will not be fired till the user taps on the notification launching the application.
240         // TODO: Handle data of notification
241         
242         // With swizzling disabled you must let Messaging know about the message, for Analytics
243         // Messaging.messaging().appDidReceiveMessage(userInfo)
244         
245         // Print message ID.
246         if let messageID = userInfo[gcmMessageIDKey] {
247             print("Message ID: \(messageID)")
248         }
249         
250         // Print full message.
251         print(userInfo)
252     }
253     
254     func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
255                      fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
256         // If you are receiving a notification message while your app is in the background,
257         // this callback will not be fired till the user taps on the notification launching the application.
258         // TODO: Handle data of notification
259         
260         // With swizzling disabled you must let Messaging know about the message, for Analytics
261         // Messaging.messaging().appDidReceiveMessage(userInfo)
262         
263         // Print message ID.
264         if let messageID = userInfo[gcmMessageIDKey] {
265             print("Message ID: \(messageID)")
266         }
267         
268         // Print full message.
269         print(userInfo)
270         
271         completionHandler(UIBackgroundFetchResult.newData)
272     }
273     // [END receive_message]
274     
275     func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
276         print("Unable to register for remote notifications: \(error.localizedDescription)")
277     }
278     
279     // This function is added here only for debugging purposes, and can be removed if swizzling is enabled.
280     // If swizzling is disabled then this function must be implemented so that the APNs token can be paired to
281     // the FCM registration token.
282     func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
283         print("APNs token retrieved: \(deviceToken)")
284         
285         // With swizzling disabled you must set the APNs token here.
286         // Messaging.messaging().apnsToken = deviceToken
287     }
288 }
289
290 // [START ios_10_message_handling]
291 @available(iOS 10, *)
292 extension AppDelegate : UNUserNotificationCenterDelegate {
293     
294     // Receive displayed notifications for iOS 10 devices.
295     func userNotificationCenter(_ center: UNUserNotificationCenter,
296                                 willPresent notification: UNNotification,
297                                 withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
298         let userInfo = notification.request.content.userInfo
299         
300         // With swizzling disabled you must let Messaging know about the message, for Analytics
301         // Messaging.messaging().appDidReceiveMessage(userInfo)
302         
303         // Print message ID.
304         if let messageID = userInfo[gcmMessageIDKey] {
305             print("Message ID: \(messageID)")
306         }
307         
308         // Print full message.
309         print(userInfo)
310         
311         // Change this to your preferred presentation option
312         completionHandler([])
313     }
314     
315     func userNotificationCenter(_ center: UNUserNotificationCenter,
316                                 didReceive response: UNNotificationResponse,
317                                 withCompletionHandler completionHandler: @escaping () -> Void) {
318         let userInfo = response.notification.request.content.userInfo
319         // Print message ID.
320         if let messageID = userInfo[gcmMessageIDKey] {
321             print("Message ID: \(messageID)")
322         }
323         
324         // Print full message.
325         print(userInfo)
326         
327         completionHandler()
328     }
329 }
330 // [END ios_10_message_handling]
331
332
333 extension AppDelegate : MessagingDelegate {
334     // [START refresh_token]
335     func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
336         print("Firebase registration token: \(fcmToken)")
337         
338         let dataDict:[String: String] = ["token": fcmToken]
339         
340         Messaging.messaging().subscribe(toTopic: "wolnelektury")
341 //        NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
342         // TODO: If necessary send token to application server.
343         // Note: This callback is fired at each app startup and whenever a new token is generated.
344     }
345     // [END refresh_token]
346     
347     // [START ios_10_data_message]
348     // Receive data messages on iOS 10+ directly from FCM (bypassing APNs) when the app is in the foreground.
349     // To enable direct data messages, you can set Messaging.messaging().shouldEstablishDirectChannel to true.
350     func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
351         print("Received data message: \(remoteMessage.appData)")
352     }
353     // [END ios_10_data_message]
354 }