Slide 1

Slide 1 text

Firebase Cloud Messaging ೖ໳ฤ Firebase.Yebisu #2

Slide 2

Slide 2 text

Who am I ࡾӜ࿨໵(miup) • Cookpad, Komerco ࣄۀ෦ • iOS Engineer • ΍͍ͬͯΔ͜ͱ http://techlife.cookpad.com/entry/2018/02/09/102554

Slide 3

Slide 3 text

ࠓ೔࿩͢͜ͱ • Firebase Cloud Messaging (FCM) ʹ͍ͭͯ • ࣮૷ • σϞ

Slide 4

Slide 4 text

Firebase Cloud Messaging • APNs, GCM Λ͍͍ײ͡ʹ΍ͬͯ͘ΕΔ Firebase ͷ௨஌ج൫

Slide 5

Slide 5 text

͍͍ײ͡ʁ

Slide 6

Slide 6 text

۩ମతʹʢػೳ໘ʣ • OS ͝ͱɺΞϓϦ͝ͱͷૹ৴ʢίϯιʔϧʣ • Topic ͷ֓೦ ( ΧςΰϦΈ͍ͨͳ΋ͷ ) • Topic ୯ҐͰͷ௨஌ͷडऔ͕Մೳ • ϢʔβʔͷߦಈΛτϦΨʔʹϑΥϩϫʔʹ ௨஌

Slide 7

Slide 7 text

۩ମతʹʢ෼ੳʣ • ։෧཰Λऔͬͯ͘ΕΔ • ΠϕϯτΛࢦఆͯ͠ίϯόʔδϣϯ΋ग़ͯ͘͠ ΕΔ • ௨஌ͷ AB ςετ

Slide 8

Slide 8 text

Ͱ΋࣮૷͸೉͍͠ΜͰ͠ΐ͏ʁ

Slide 9

Slide 9 text

࣮૷ • ௨஌ڐ୚Λग़͢ • Token ؅ཧ • ઀ଓɺडऔ

Slide 10

Slide 10 text

࣮૷ (ೝূ) // ೝূ (iOS 10 Ҏ্Λ૝ఆ) Messaging.messaging().delegate = self UNUserNotificationCenter.current().delegate = self let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] UNUserNotificationCenter.current().requestAuthorization( options: authOptions, completionHandler: {_, _ in }) UIApplication.shared.registerForRemoteNotifications()

Slide 11

Slide 11 text

࣮૷ (Token औಘ) // implement MessagingDelegate extension AppDelegate: MessagingDelegate { public func messaging( _ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) { print(fcmToken) } }

Slide 12

Slide 12 text

࣮૷ (subscribe) guard Messaging.messaging().fcmToken != nil else { return } // subscribe Messaging.messaging().shouldEstablishDirectChannel = true // unsubscribe Messaging.messaging().shouldEstablishDirectChannel = false

Slide 13

Slide 13 text

࣮૷ (subscribe topic) // subscribe topic Messaging.messaging().subscribe(toTopic: “TopicName”) // unsubscribe topic Messaging.messaging().unsubscribe(toTopic: “TopicName")

Slide 14

Slide 14 text

࣮૷ (Message Handling) // ϑΥΞάϥ΢ϯυͰͷ௨஌ͷड৴ extension AppDelegate: UNUserNotificationCenterDelegate { public func userNotificationCenter( _ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { print(response.notification.request.content.userInfo) // call completion handler completionHandler([.alert, .badge, .sound]) } }

Slide 15

Slide 15 text

࣮૷ (Message Handling) // ௨஌։෧࣌ͷॲཧ (ϑΥΞάϥ΢ϯυɺόοΫάϥ΢ϯυڞ௨) extension AppDelegate: UNUserNotificationCenterDelegate { public func userNotificationCenter( _ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { print(response.notification.request.content.userInfo) // call completion handler completionHandler() } }

Slide 16

Slide 16 text

΍Δ͜ͱຖճಉ͡Ͱ͸ʁ

Slide 17

Slide 17 text

ϥΠϒϥϦԽ • Tsuchi (https://github.com/miup/Tsuchi) • TypeSafe ʹ Payload Λѻ͑Δ • τϐοΫʹ΋ରԠ

Slide 18

Slide 18 text

࢖͍ํʢPayload) struct PushNotification: PushNotificationPayload { let hoge: String let hige: String let aps: APS? }

Slide 19

Slide 19 text

࢖͍ํʢinitializeʣ class NotificationHandler { static let shared = NotificationHandler() private init() { // initialize your Tsushi settings (ىಈதͰ΋όφʔग़͔͢Ͳ͏͔) Tsuchi.shared.showsNotificationBannerOnPresenting = true Tsuchi.shared.didRefreshRegistrationTokenActionBlock = { token in // save token to your Database } Tsuchi.shared.subscribe(PushNotification.self) { result in switch result { case let .success((payload, mode)): print("reiceived: \(payload), mode: \(mode)") case let .failure(error): print("error: \(error)") } } } }

Slide 20

Slide 20 text

࢖͍ํʢRegistrationʣ extension NotificationHandler { func register() { Tsuchi.shared.register { granted in if granted { print("success registration") } else { print("failure registration") } } } func unregister() { Tsuchi.shared.unregister { print("unregister") } } }

Slide 21

Slide 21 text

࢖͍ํʢTopicʣ enum Topic: TopicType { case userAction(userID: String) var rawValue: String { switch self { case .userAction(let userID): return "user-action-\(userID)" } } } extension NotificationHandler { func subscribe(topic: Topic) { Tsuchi.shared.subscribe(toTopic: topic) } func unsubscribe(topic: Topic) { Tsuchi.shared.unsubscribe(fromTopic: topic) } }

Slide 22

Slide 22 text

۩ମతʹ • ΞϓϦىಈ࣌ʹ NotificationHandler Λ init • Ϣʔβʔ࡞੒ͨ͠Β Registration • ϑΥϩʔ࣌ʹ subscribe to topic • ΞϯϑΥϩʔ࣌ʹ unsubscribe from topic

Slide 23

Slide 23 text

࣮ࡍͷίʔυʢRegistrationʣ let user = Firebase.User() user.save { (ref, error) in guard let _ = ref else { return } NotificationHandler.shared.register() }

Slide 24

Slide 24 text

࣮ࡍͷίʔυʢTopicʣ extension Firebase { class User: RootObject { ... func follow(_ user: Firebase.User) { self.followee.insert(user) user.follower.insert(self) NotificationHandler.shared .subscribe(topic: .userAction(userID: user.id)) } func unfollow(_ user: Firebase.User) { self.followee.remove(user) user.follower.remove(self) NotificationHandler.shared .unsubscribe(topic: .userAction(userID: user.id)) } } }

Slide 25

Slide 25 text

࣮ࡍͷίʔυʢCloud Functionsʣ functions.database.ref(root/v1/post/{postID}).onCreate(async event => { const firPost: firebaseModel.Post = event.data.val() let user = await admin.database() .ref(`root/v1/user/${firPost.userID}`) .once(‘value’) .then(snap => snap.val()) const payload = { notification: { title: '৽͍͠౤ߘ͕͋Γ·ͨ͠', body: `${user.name}͕৽͍͠౤ߘΛ͠·ͨ͠` } } admin.messaging() .sendToTopic(`user-action-${firPost.userID}`, payload) }

Slide 26

Slide 26 text

σϞ