Skip to content

Commit

Permalink
feat(ios): add getToken and clean project to be publish
Browse files Browse the repository at this point in the history
  • Loading branch information
stewones committed Mar 25, 2019
1 parent d873c91 commit ddec39b
Show file tree
Hide file tree
Showing 13 changed files with 165 additions and 351 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
dist/
node_modules/
node_modules/


Pods
3 changes: 2 additions & 1 deletion CapacitorFCM.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

Pod::Spec.new do |s|
s.name = 'CapacitorFCM'
s.name = 'CapacitorFcm'
s.version = '0.0.1'
s.summary = 'bring firebase cloud messaging feature to capacitor'
s.license = 'MIT'
Expand All @@ -10,6 +10,7 @@
s.source_files = 'ios/Plugin/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}'
s.ios.deployment_target = '11.0'
s.dependency 'Capacitor'
s.dependency 'Firebase'
s.dependency 'FirebaseMessaging'
s.dependency 'FirebaseCore'
s.dependency 'FirebaseAnalytics'
Expand Down
318 changes: 64 additions & 254 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,271 +1,79 @@
# capacitor-fcm

Capacitor plugin to enable Firebase Cloud Messaging

## Notice

> This plugin is currently under active development and has not yet been published.
## iOS

- [x] subscribe to topic
- [x] unsubscribe from topic
- [ ] get token

## Android

- [ ] subscribe to topic
- [ ] unsubscribe from topic
- [ ] get token

## Steps

**Important**

For now I'm using a custom capacitor build (locally), so you have to change the path at [podfile](https://github.com/stewwan/capacitor-fcm/blob/master/ios/Plugin/Podfile).

- git clone this repo
- npm install
- npm run build
- npm link
- cd `ios/Plugin`
- pod install
- go back to your capacitor powered app and `npm link capacitor-fcm`
- cd to `ios/App/Podfile` and add
```
pod 'Firebase/Core'
pod 'Firebase/Messaging'
pod 'CapacitorFCM', :path => '/path_to_this_repo/capacitor-fcm'
```
- pod install

## Important step (iOS)

open your client `AppDelegate.swift` and make the necessary changes to reflect the following

```swift
import UIKit
import UserNotifications
import Capacitor
import Firebase

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

let gcmMessageIDKey = "gcm.message_id"


func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FirebaseApp.configure()
// [START set_messaging_delegate]
Messaging.messaging().delegate = self
// [END set_messaging_delegate]


// Register for remote notifications. This shows a permission dialog on first run, to
// show the dialog at a more appropriate time move this registration accordingly.
// [START register_for_notifications]
if #available(iOS 10.0, *) {
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self

let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {_, _ in })
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}

application.registerForRemoteNotifications()

// [END register_for_notifications]

//
// lines below is a workaround for webview input's issue
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)

return true
}



//
// webview workaround
@objc func keyboardWillHide(notification: NSNotification) {
if #available(iOS 12.0, *) {
let vc = self.window?.rootViewController as! CAPBridgeViewController
for v in vc.bridgedWebView!.subviews {
if v is UIScrollView {
let scrollView = v as! UIScrollView
scrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: false)
}
}
}
}


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.
}

func applicationDidEnterBackground(_ application: UIApplication) {
// 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.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}

func applicationWillEnterForeground(_ application: UIApplication) {
// 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.
}

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.
}

func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
// Called when the app was launched with a url. Feel free to add additional processing here,
// but if you want the App API to support tracking app url opens, make sure to keep this call
return CAPBridge.handleOpenUrl(url, options)
}

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {
// Called when the app was launched with an activity, including Universal Links.
// Feel free to add additional processing here, but if you want the App API to support
// tracking app url opens, make sure to keep this call
return CAPBridge.handleContinueActivity(userActivity, restorationHandler)
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)

let statusBarRect = UIApplication.shared.statusBarFrame
guard let touchPoint = event?.allTouches?.first?.location(in: self.window) else { return }

if statusBarRect.contains(touchPoint) {
NotificationCenter.default.post(CAPBridge.statusBarTappedNotification)
}
}

// [START receive_message]
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// TODO: Handle data of notification
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}

// Print full message.
print(userInfo)
}

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// TODO: Handle data of notification
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}

// Print full message.
print(userInfo)

completionHandler(UIBackgroundFetchResult.newData)
}
// [END receive_message]
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Unable to register for remote notifications: \(error.localizedDescription)")
}

// This function is added here only for debugging purposes, and can be removed if swizzling is enabled.
// If swizzling is disabled then this function must be implemented so that the APNs token can be paired to
// the FCM registration token.
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("APNs token retrieved: \(deviceToken)")
Capacitor plugin to enable features from Firebase Cloud Messaging

## API

- subscribeTo
- unsubscribeFrom
- getToken

## Usage

```js
import { FCM } from "capacitor-fcm";
const fcm = new FCM();

import { Plugins } from "@capacitor/core";
const { PushNotifications } = Plugins;

//
// Subscribe to a specific topic
PushNotifications.register()
.then(_ => {
fcm
.subscribeTo({ topic: "test" })
.then(r => alert(`subscribed to topic`))
.catch(err => console.log(err));
})
.catch(err => alert(JSON.stringify(err)));

//
// Unsubscribe from a specific topic
fcm
.unsubscribeFrom({ topic: "test" })
.then(r => alert(`unsubscribed from topic`))
.catch(err => console.log(err));
}

// With swizzling disabled you must set the APNs token here.
// Messaging.messaging().apnsToken = deviceToken
}
//
// get remote token
fcm
.getToken()
.then(r => alert(`Token ${r.token}`))
.catch(err => console.log(err));
}
```

// [START ios_10_message_handling]
@available(iOS 10, *)
extension AppDelegate : UNUserNotificationCenterDelegate {
## iOS setup

// Receive displayed notifications for iOS 10 devices.
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
let userInfo = notification.request.content.userInfo
- `ionic start my-cap-app --capacitor`
- `cd my-cap-app`
- `npm install —-save capacitor-fcm`
- `mkdir www && touch www/index.html`
- `npx cap add ios`
- npx cap open ios
- sign your app at xcode (general tab)
- enable remote notification capabilities
- add `GoogleService-Info.plist` to your app in xcode

// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
Then you should be set to go. Run `ionic cap run ios --livereload` to start the server and play it through xcode

// Print full message.
print(userInfo)
> Important Notice: every time you change a native code you may need to clean the cache (Product > Clean build folder) and then run the app again.
// Change this to your preferred presentation option
completionHandler([])
}
## Android setup

func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
(soon)

// Print full message.
print(userInfo)
## Add Google config files

completionHandler()
}
}
// [END ios_10_message_handling]
Navigate to the project settings page for your application on Firebase.

extension AppDelegate : MessagingDelegate {
// [START refresh_token]
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
print("Firebase registration token: \(fcmToken)")
Android: Download the google-services.json file and copy it to the android/app/ directory of your capacitor project. You will also need to add the Firebase SDK to your gradle files. More info can be found here: https://firebase.google.com/docs/android/setup#manually_add_firebase

let dataDict:[String: String] = ["token": fcmToken]
NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
// TODO: If necessary send token to application server.
// Note: This callback is fired at each app startup and whenever a new token is generated.
}
// [END refresh_token]
// [START ios_10_data_message]
// Receive data messages on iOS 10+ directly from FCM (bypassing APNs) when the app is in the foreground.
// To enable direct data messages, you can set Messaging.messaging().shouldEstablishDirectChannel to true.
func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
print("Received data message: \(remoteMessage.appData)")
}
// [END ios_10_data_message]
}
```
iOS: Download the GoogleService-Info.plist file. In Xcode right-click on the yellow folder named, "App" and select the 'Add files to "App"'. tip: if you drag and drop your file to this location, Xcode may not be able to find it.

That's all folks.

## Don't forget

Expand All @@ -275,6 +83,8 @@ extension AppDelegate : MessagingDelegate {

After all of these steps you should be set to go. Try to run your client using `ionic cap run ios`.

That's all folks.

Follow me [@Twitter](https://twitter.com/StewanSilva)

## License
Expand Down
2 changes: 1 addition & 1 deletion android/capacitor-fcm/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.stewan.capacitor-fcm.capacitorfcm">
package="io.stewan.capacitor-fcm">
</manifest>
2 changes: 2 additions & 0 deletions ios/Plugin/Plugin.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@
"${SRCROOT}/Pods/Target Support Files/Pods-PluginTests/Pods-PluginTests-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/Capacitor/Capacitor.framework",
"${BUILT_PRODUCTS_DIR}/CapacitorCordova/Cordova.framework",
"${BUILT_PRODUCTS_DIR}/GCDWebServer/GCDWebServer.framework",
"${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework",
"${BUILT_PRODUCTS_DIR}/Protobuf/Protobuf.framework",
"${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework",
Expand All @@ -299,6 +300,7 @@
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Capacitor.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Cordova.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GCDWebServer.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Protobuf.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework",
Expand Down
Binary file not shown.
1 change: 1 addition & 0 deletions ios/Plugin/Plugin/Plugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
CAP_PLUGIN(FCM, "FCMPlugin",
CAP_PLUGIN_METHOD(subscribeTo, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(unsubscribeFrom, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(getToken, CAPPluginReturnPromise);
)
Loading

0 comments on commit ddec39b

Please sign in to comment.