diff --git a/Examples/simple-fcm-client/android/app/build.gradle b/Examples/simple-fcm-client/android/app/build.gradle index d13dcafd04a77365f12a75954b00c5a4cabcfafa..c563f18aaef2cf08b0235a17beb5daabfe5969c3 100644 --- a/Examples/simple-fcm-client/android/app/build.gradle +++ b/Examples/simple-fcm-client/android/app/build.gradle @@ -131,6 +131,9 @@ android { } dependencies { + compile(project(':react-native-maps')) { + exclude group: 'com.google.android.gms', module: 'play-services-base' + } compile project(':react-native-fcm') compile fileTree(dir: "libs", include: ["*.jar"]) compile "com.facebook.react:react-native:+" // From node_modules diff --git a/Examples/simple-fcm-client/android/app/src/main/AndroidManifest.xml b/Examples/simple-fcm-client/android/app/src/main/AndroidManifest.xml index a0aba2877e579add45e89a746d888fbc3fe2d924..869cb8fefd4c80722074ea7936798c78034b9cd6 100644 --- a/Examples/simple-fcm-client/android/app/src/main/AndroidManifest.xml +++ b/Examples/simple-fcm-client/android/app/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ + getPackages() { return Arrays.asList( new MainReactPackage(), + new MapsPackage(), new FIRMessagingPackage() ); } diff --git a/Examples/simple-fcm-client/android/settings.gradle b/Examples/simple-fcm-client/android/settings.gradle index d75953b2ce301dc0e1eeee99427c6f34c1a60141..a8ad2f34040e848c6547cf6c982601d43f31c66d 100644 --- a/Examples/simple-fcm-client/android/settings.gradle +++ b/Examples/simple-fcm-client/android/settings.gradle @@ -1,4 +1,6 @@ rootProject.name = 'SimpleFcmClient' +include ':react-native-maps' +project(':react-native-maps').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-maps/lib/android') include ':app' include ':react-native-fcm' diff --git a/Examples/simple-fcm-client/app/App.js b/Examples/simple-fcm-client/app/App.js index 9f9f38243ba49e52988cb568847be180d8f5df64..55d60a7dc6dc36afcbc092d68d58c351ff3f4f82 100644 --- a/Examples/simple-fcm-client/app/App.js +++ b/Examples/simple-fcm-client/app/App.js @@ -11,17 +11,20 @@ import { TouchableOpacity, View, Clipboard, - Platform + Platform, + ScrollView } from 'react-native'; -import FCM from "react-native-fcm"; +import { StackNavigator } from 'react-navigation'; + +import FCM, {NotificationActionType} from "react-native-fcm"; import {registerKilledListener, registerAppListener} from "./Listeners"; import firebaseClient from "./FirebaseClient"; registerKilledListener(); -export default class App extends Component { +class MainPage extends Component { constructor(props) { super(props); @@ -38,11 +41,16 @@ export default class App extends Component { description: 'used for example', priority: 'high' }) - registerAppListener(); + registerAppListener(this.props.navigation); FCM.getInitialNotification().then(notif => { this.setState({ initNotif: notif }) + if(notif && notif.targetScreen === 'detail'){ + setTimeout(()=>{ + this.props.navigation.navigate('Detail') + }, 500) + } }); try{ @@ -65,16 +73,30 @@ export default class App extends Component { showLocalNotification() { FCM.presentLocalNotification({ - vibrate: 500, - title: 'Hello', channel: 'default', - body: 'Test Notification', - big_text: 'i am large, i am large, i am large, i am large, i am large, i am large, i am large, i am large, i am large, i am large, i am large, i am large, i am large, i am large, i am large, i am large, i am large, i am large, i am large, i am large, i am large, i am large, i am large, i am large, i am large, i am large, i am large', - priority: "high", - sound: "bell.mp3", - large_icon: "https://image.freepik.com/free-icon/small-boy-cartoon_318-38077.jpg", - show_in_foreground: true, - number: 10 + id: new Date().valueOf().toString(), // (optional for instant notification) + title: "Test Notification with action", // as FCM payload + body: "Force touch to reply", // as FCM payload (required) + sound: "bell.mp3", // "default" or filename + priority: "high", // as FCM payload + click_action: "com.myapp.MyCategory", // as FCM payload - this is used as category identifier on iOS. + badge: 10, // as FCM payload IOS only, set 0 to clear badges + number: 10, // Android only + ticker: "My Notification Ticker", // Android only + auto_cancel: true, // Android only (default true) + large_icon: "https://image.freepik.com/free-icon/small-boy-cartoon_318-38077.jpg", // Android only + icon: "ic_launcher", // as FCM payload, you can relace this with custom icon you put in mipmap + big_text: "Show when notification is expanded", // Android only + sub_text: "This is a subText", // Android only + color: "red", // Android only + vibrate: 300, // Android only default: 300, no vibration if you pass 0 + wake_screen: true, // Android only, wake up screen when notification arrives + group: "group", // Android only + picture: "https://google.png", // Android only bigPicture style + ongoing: true, // Android only + my_custom_data:'my_custom_field_value', // extra data you want to throw + lights: true, // Android only, LED blinking (default false) + show_in_foreground: true // notification when app is in foreground (local & remote) }); } @@ -89,7 +111,10 @@ export default class App extends Component { priority: "high", large_icon: "https://image.freepik.com/free-icon/small-boy-cartoon_318-38077.jpg", show_in_foreground: true, - picture: 'https://firebase.google.com/_static/af7ae4b3fc/images/firebase/lockup.png' + picture: 'https://firebase.google.com/_static/af7ae4b3fc/images/firebase/lockup.png', + wake_screen: true, + extra1: {a: 1}, + extra2: 1 }); } @@ -102,10 +127,11 @@ export default class App extends Component { "data":{ "custom_notification": { "title": "Simple FCM Client", - "body": "This is a notification with only NOTIFICATION.", + "body": "Click me to go to detail", "sound": "default", "priority": "high", - "show_in_foreground": true + "show_in_foreground": true, + targetScreen: 'detail' } }, "priority": 10 @@ -115,9 +141,12 @@ export default class App extends Component { "to": token, "notification":{ "title": "Simple FCM Client", - "body": "This is a notification with only NOTIFICATION.", + "body": "Click me to go to detail", "sound": "default" - }, + }, + data: { + targetScreen: 'detail' + }, "priority": 10 } } @@ -139,21 +168,21 @@ export default class App extends Component { firebaseClient.send(JSON.stringify(body), "data"); } - sendRemoteNotificationWithData(token) { - let body = { - "to": token, - "notification":{ - "title": "Simple FCM Client", - "body": "This is a notification with NOTIFICATION and DATA (NOTIF).", - "sound": "default" - }, - "data":{ - "hello": "there" - }, - "priority": "high" - } - - firebaseClient.send(JSON.stringify(body), "notification-data"); + showLocalNotificationWithAction() { + FCM.presentLocalNotification({ + title: 'Test Notification with action', + body: 'Force touch to reply', + priority: "high", + show_in_foreground: true, + click_action: "com.myidentifi.fcm.text", // for ios + android_actions: JSON.stringify([{ + id: "view", + title: 'view' + },{ + id: "dismiss", + title: 'dismiss' + }]) // for android, take syntax similar to ios's. only buttons are supported + }); } render() { @@ -161,19 +190,11 @@ export default class App extends Component { return ( + Welcome to Simple Fcm Client! - - Init notif: {JSON.stringify(this.state.initNotif)} - - - - this.setClipboardContent(this.state.token)} style={styles.instructions}> - Token: {this.state.token} - - {this.state.tokenCopyFeedback} @@ -190,17 +211,32 @@ export default class App extends Component { Send Remote Data - this.sendRemoteNotificationWithData(token)} style={styles.button}> - Send Remote Notification With Data + this.showLocalNotification()} style={styles.button}> + Show Local Notification - this.showLocalNotification()} style={styles.button}> - Send Local Notification + this.showLocalNotificationWithAction(token)} style={styles.button}> + Show Local Notification with Action this.scheduleLocalNotification()} style={styles.button}> Schedule Notification in 5s + + + Init notif: + + + {JSON.stringify(this.state.initNotif)} + + + + Token: + + this.setClipboardContent(this.state.token)}> + {this.state.token} + + ); } @@ -216,6 +252,25 @@ export default class App extends Component { } } +class DetailPage extends Component { + render(){ + return + Detail page + + } +} + +export default StackNavigator({ + Main: { + screen: MainPage, + }, + Detail: { + screen: DetailPage + } +}, { + initialRouteName: 'Main', +}); + const styles = StyleSheet.create({ container: { flex: 1, @@ -241,8 +296,8 @@ const styles = StyleSheet.create({ button: { backgroundColor: "teal", paddingHorizontal: 20, - paddingVertical: 10, - marginVertical: 15, + paddingVertical: 15, + marginVertical: 10, borderRadius: 10 }, buttonText: { diff --git a/Examples/simple-fcm-client/app/Listeners.js b/Examples/simple-fcm-client/app/Listeners.js index 0144e1f55ee357d441663b83e0c47f69ecf3300c..942e9f269ffb9a441d90ccf2e6a9cf60adb8c62c 100644 --- a/Examples/simple-fcm-client/app/Listeners.js +++ b/Examples/simple-fcm-client/app/Listeners.js @@ -1,6 +1,6 @@ -import { Platform, AsyncStorage } from 'react-native'; +import { Platform, AsyncStorage, AppState } from 'react-native'; -import FCM, {FCMEvent, RemoteNotificationResult, WillPresentNotificationResult, NotificationType} from "react-native-fcm"; +import FCM, {FCMEvent, RemoteNotificationResult, WillPresentNotificationResult, NotificationType, NotificationActionType, NotificationActionOption, NotificationCategoryOption} from "react-native-fcm"; AsyncStorage.getItem('lastNotification').then(data=>{ if(data){ @@ -10,22 +10,60 @@ AsyncStorage.getItem('lastNotification').then(data=>{ } }) +AsyncStorage.getItem('lastMessage').then(data=>{ + if(data){ + // if notification arrives when app is killed, it should still be logged here + console.log('last message', JSON.parse(data)); + AsyncStorage.removeItem('lastMessage'); + } +}) + export function registerKilledListener(){ // these callback will be triggered even when app is killed FCM.on(FCMEvent.Notification, notif => { AsyncStorage.setItem('lastNotification', JSON.stringify(notif)); + if(notif.opened_from_tray){ + setTimeout(()=>{ + if(notif._actionIdentifier === 'reply'){ + if(AppState.currentState !== 'background'){ + console.log('User replied '+ JSON.stringify(notif._userText)) + alert('User replied '+ JSON.stringify(notif._userText)); + } else { + AsyncStorage.setItem('lastMessage', JSON.stringify(notif._userText)); + } + } + if(notif._actionIdentifier === 'view'){ + alert("User clicked View in App"); + } + if(notif._actionIdentifier === 'dismiss'){ + alert("User clicked Dismiss"); + } + }, 1000) + } }); } // these callback will be triggered only when app is foreground or background -export function registerAppListener(){ +export function registerAppListener(navigation){ FCM.on(FCMEvent.Notification, notif => { console.log("Notification", notif); - if(notif.local_notification){ + + if(Platform.OS ==='ios' && notif._notificationType === NotificationType.WillPresent && !notif.local_notification){ + // this notification is only to decide if you want to show the notification when user if in forground. + // usually you can ignore it. just decide to show or not. + notif.finish(WillPresentNotificationResult.All) return; } + if(notif.opened_from_tray){ - return; + if(notif.targetScreen === 'detail'){ + setTimeout(()=>{ + navigation.navigate('Detail') + }, 500) + } + setTimeout(()=>{ + alert(`User tapped notification\n${JSON.stringify(notif)}`) + }, 500) } if(Platform.OS ==='ios'){ @@ -42,6 +80,8 @@ export function registerAppListener(){ break; case NotificationType.WillPresent: notif.finish(WillPresentNotificationResult.All) //other types available: WillPresentNotificationResult.None + // this type of notificaiton will be called only when you are in foreground. + // if it is a remote notification, don't do any app logic here. Another notification callback will be triggered with type NotificationType.Remote break; } } @@ -49,7 +89,6 @@ export function registerAppListener(){ FCM.on(FCMEvent.RefreshToken, token => { console.log("TOKEN (refreshUnsubscribe)", token); - this.props.onChangeToken(token); }); FCM.enableDirectChannel(); @@ -60,3 +99,35 @@ export function registerAppListener(){ FCM.isDirectChannelEstablished().then(d => console.log(d)); }, 1000); } + +FCM.setNotificationCategories([ + { + id: 'com.myidentifi.fcm.text', + actions: [ + { + type: NotificationActionType.TextInput, + id: 'reply', + title: 'Quick Reply', + textInputButtonTitle: 'Send', + textInputPlaceholder: 'Say something', + intentIdentifiers: [], + options: NotificationActionOption.AuthenticationRequired + }, + { + type: NotificationActionType.Default, + id: 'view', + title: 'View in App', + intentIdentifiers: [], + options: NotificationActionOption.Foreground + }, + { + type: NotificationActionType.Default, + id: 'dismiss', + title: 'Dismiss', + intentIdentifiers: [], + options: NotificationActionOption.Destructive + } + ], + options: [NotificationCategoryOption.CustomDismissAction, NotificationCategoryOption.PreviewsShowTitle] + } +]) diff --git a/Examples/simple-fcm-client/ios/Podfile.lock b/Examples/simple-fcm-client/ios/Podfile.lock index 5e427a7309e3434a3284c5a644de958204b5ab22..32fd9c5bbc5429d570d632415930c3904a0bfd9f 100644 --- a/Examples/simple-fcm-client/ios/Podfile.lock +++ b/Examples/simple-fcm-client/ios/Podfile.lock @@ -1,19 +1,20 @@ PODS: - - Firebase/Core (4.7.0): - - FirebaseAnalytics (= 4.0.5) - - FirebaseCore (= 4.0.12) - - Firebase/Messaging (4.7.0): + - Firebase/Core (4.9.0): + - FirebaseAnalytics (= 4.0.9) + - FirebaseCore (= 4.0.15) + - Firebase/Messaging (4.9.0): - Firebase/Core - - FirebaseMessaging (= 2.0.7) - - FirebaseAnalytics (4.0.5): + - FirebaseMessaging (= 2.1.0) + - FirebaseAnalytics (4.0.9): - FirebaseCore (~> 4.0) - FirebaseInstanceID (~> 2.0) - GoogleToolboxForMac/NSData+zlib (~> 2.1) - nanopb (~> 0.3) - - FirebaseCore (4.0.12): + - FirebaseCore (4.0.15): - GoogleToolboxForMac/NSData+zlib (~> 2.1) - - FirebaseInstanceID (2.0.7) - - FirebaseMessaging (2.0.7): + - FirebaseInstanceID (2.0.9): + - FirebaseCore (~> 4.0) + - FirebaseMessaging (2.1.0): - FirebaseAnalytics (~> 4.0) - FirebaseCore (~> 4.0) - FirebaseInstanceID (~> 2.0) @@ -35,15 +36,15 @@ DEPENDENCIES: - Firebase/Messaging SPEC CHECKSUMS: - Firebase: dbfb98ccec2dcfcd21ab9cc1b4981a3f3c8c5e26 - FirebaseAnalytics: 5b02a63ead2c3f0259cfc7f15e053e440587ecf8 - FirebaseCore: 6cf108b63997bc08c04a1ffa55a3ac0d71a59ffc - FirebaseInstanceID: 148c25c986c8699e67304b114e365713dce467f2 - FirebaseMessaging: 1a11d1c0a9ed9b3f75a0685bb0ae5932e1062f5f + Firebase: 632216af3ed7f31e3be34776947fdc7546cfb572 + FirebaseAnalytics: 388b630c15713f5dbf364071f5f3d6077fb52f4e + FirebaseCore: 3bd047463058fa6b5d312c97502c52e45401cdfb + FirebaseInstanceID: d2058a35e9bebda1b6dd42486b84917bde552a9d + FirebaseMessaging: 2bafab2d0f3ab3dfd753101c2c32995c2051b5da GoogleToolboxForMac: 2501e2ad72a52eb3dfe7bd9aee7dad11b858bd20 nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3 Protobuf: 8a9838fba8dae3389230e1b7f8c104aa32389c03 PODFILE CHECKSUM: 31f07bb14b00eef65c77cff51721f530ad6eb826 -COCOAPODS: 1.2.1 +COCOAPODS: 1.4.0 diff --git a/Examples/simple-fcm-client/ios/SimpleFcmClient.xcodeproj/project.pbxproj b/Examples/simple-fcm-client/ios/SimpleFcmClient.xcodeproj/project.pbxproj index 98b9dab434a31be0bbc44d4492e6767b2e5f2bfd..27945b50738bc766cfe7b72833d4e6c7d69223fb 100644 --- a/Examples/simple-fcm-client/ios/SimpleFcmClient.xcodeproj/project.pbxproj +++ b/Examples/simple-fcm-client/ios/SimpleFcmClient.xcodeproj/project.pbxproj @@ -23,11 +23,13 @@ 140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; 3A6D62911E2044AB00D0D2C7 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A6D62901E2044AB00D0D2C7 /* libz.tbd */; }; + 3A7531B520323B2400888478 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A7531B220323B0700888478 /* libRCTAnimation.a */; }; 3AC87E631F6C190900194883 /* bell.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 3AC87E621F6C190900194883 /* bell.mp3 */; }; 4339BFE81DAEE9D100F53B62 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 4339BFE71DAEE9D100F53B62 /* GoogleService-Info.plist */; }; 5FE70723D2AE04BF2D98342D /* libPods-SimpleFcmClientTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E8C044191FA88F1BCCF08BD /* libPods-SimpleFcmClientTests.a */; }; 6976C617E52062E3EE272128 /* libPods-SimpleFcmClient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A959965489E40CE19F2B06B4 /* libPods-SimpleFcmClient.a */; }; 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; + B5EEA6C40DAD43C696D4A7EE /* libAirMaps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 441E689027CF4924A5FEA8F6 /* libAirMaps.a */; }; FAE94A218EB64E38BF8D8E9B /* libRNFIRMessaging.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 692E216422234A4CB6A7A838 /* libRNFIRMessaging.a */; }; /* End PBXBuildFile section */ @@ -186,6 +188,27 @@ remoteGlobalIDString = 3D3CD9181DE5FBD800167DC4; remoteInfo = "jschelpers-tvOS"; }; + 3A7531B120323B0700888478 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 3A7531AC20323B0700888478 /* RCTAnimation.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTAnimation; + }; + 3A7531B320323B0700888478 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 3A7531AC20323B0700888478 /* RCTAnimation.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A28201D9B03D100D4039D; + remoteInfo = "RCTAnimation-tvOS"; + }; + 3AA3D5D4204DD40F00C3E8E6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 955F937AAFAB473EA79C80F7 /* AirMaps.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 11FA5C511C4A1296003AC2EE; + remoteInfo = AirMaps; + }; 3AAE7F501F55B50200E914A8 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; @@ -260,16 +283,19 @@ 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; }; 3A6D628E1E20449400D0D2C7 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; 3A6D62901E2044AB00D0D2C7 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; + 3A7531AC20323B0700888478 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = ""; }; 3AC87E621F6C190900194883 /* bell.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = bell.mp3; sourceTree = ""; }; 4339BFE31DAEBB4800F53B62 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; 4339BFE61DAED4D900F53B62 /* SimpleFcmClient.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = SimpleFcmClient.entitlements; path = SimpleFcmClient/SimpleFcmClient.entitlements; sourceTree = ""; }; 4339BFE71DAEE9D100F53B62 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 441E689027CF4924A5FEA8F6 /* libAirMaps.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libAirMaps.a; sourceTree = ""; }; 55A6E80F734FA3F596B96C04 /* Pods-SimpleFcmClientTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SimpleFcmClientTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SimpleFcmClientTests/Pods-SimpleFcmClientTests.debug.xcconfig"; sourceTree = ""; }; 64204739CA77D9B1EB1F0788 /* Pods-SimpleFcmClientTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SimpleFcmClientTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-SimpleFcmClientTests/Pods-SimpleFcmClientTests.release.xcconfig"; sourceTree = ""; }; 692E216422234A4CB6A7A838 /* libRNFIRMessaging.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFIRMessaging.a; sourceTree = ""; }; 73B4AFC1AC5C6373F8074CBC /* Pods-SimpleFcmClient.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SimpleFcmClient.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SimpleFcmClient/Pods-SimpleFcmClient.debug.xcconfig"; sourceTree = ""; }; 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; + 955F937AAFAB473EA79C80F7 /* AirMaps.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = AirMaps.xcodeproj; path = "../node_modules/react-native-maps/lib/ios/AirMaps.xcodeproj"; sourceTree = ""; }; A959965489E40CE19F2B06B4 /* libPods-SimpleFcmClient.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SimpleFcmClient.a"; sourceTree = BUILT_PRODUCTS_DIR; }; C7DEB70C413E484CBFA6AC45 /* RNFIRMessaging.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNFIRMessaging.xcodeproj; path = "../node_modules/react-native-fcm/ios/RNFIRMessaging.xcodeproj"; sourceTree = ""; }; CF6939675D5A4A68FBE567C0 /* Pods-SimpleFcmClient.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SimpleFcmClient.release.xcconfig"; path = "Pods/Target Support Files/Pods-SimpleFcmClient/Pods-SimpleFcmClient.release.xcconfig"; sourceTree = ""; }; @@ -289,6 +315,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 3A7531B520323B2400888478 /* libRCTAnimation.a in Frameworks */, 3A6D62911E2044AB00D0D2C7 /* libz.tbd in Frameworks */, 146834051AC3E58100842450 /* libReact.a in Frameworks */, 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */, @@ -302,6 +329,7 @@ 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */, FAE94A218EB64E38BF8D8E9B /* libRNFIRMessaging.a in Frameworks */, 6976C617E52062E3EE272128 /* libPods-SimpleFcmClient.a in Frameworks */, + B5EEA6C40DAD43C696D4A7EE /* libAirMaps.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -422,10 +450,28 @@ name = Products; sourceTree = ""; }; + 3A7531AD20323B0700888478 /* Products */ = { + isa = PBXGroup; + children = ( + 3A7531B220323B0700888478 /* libRCTAnimation.a */, + 3A7531B420323B0700888478 /* libRCTAnimation.a */, + ); + name = Products; + sourceTree = ""; + }; + 3AA3D5D1204DD40F00C3E8E6 /* Products */ = { + isa = PBXGroup; + children = ( + 3AA3D5D5204DD40F00C3E8E6 /* libAirMaps.a */, + ); + name = Products; + sourceTree = ""; + }; 3AF0A6071F7BE4DC004B899F /* Recovered References */ = { isa = PBXGroup; children = ( 692E216422234A4CB6A7A838 /* libRNFIRMessaging.a */, + 441E689027CF4924A5FEA8F6 /* libAirMaps.a */, ); name = "Recovered References"; sourceTree = ""; @@ -472,6 +518,7 @@ 832341AE1AAA6A7D00B99B32 /* Libraries */ = { isa = PBXGroup; children = ( + 3A7531AC20323B0700888478 /* RCTAnimation.xcodeproj */, 146833FF1AC3E56700842450 /* React.xcodeproj */, 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */, 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */, @@ -483,6 +530,7 @@ 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */, 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */, C7DEB70C413E484CBFA6AC45 /* RNFIRMessaging.xcodeproj */, + 955F937AAFAB473EA79C80F7 /* AirMaps.xcodeproj */, ); name = Libraries; sourceTree = ""; @@ -571,7 +619,7 @@ 83CBB9F71A601CBA00E9B192 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0800; + LastUpgradeCheck = 800; ORGANIZATIONNAME = Facebook; TargetAttributes = { 00E356ED1AD99517003FC87E = { @@ -605,10 +653,18 @@ productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; projectDirPath = ""; projectReferences = ( + { + ProductGroup = 3AA3D5D1204DD40F00C3E8E6 /* Products */; + ProjectRef = 955F937AAFAB473EA79C80F7 /* AirMaps.xcodeproj */; + }, { ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; }, + { + ProductGroup = 3A7531AD20323B0700888478 /* Products */; + ProjectRef = 3A7531AC20323B0700888478 /* RCTAnimation.xcodeproj */; + }, { ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */; ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; @@ -806,6 +862,27 @@ remoteRef = 3A6D627D1E20428000D0D2C7 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 3A7531B220323B0700888478 /* libRCTAnimation.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTAnimation.a; + remoteRef = 3A7531B120323B0700888478 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3A7531B420323B0700888478 /* libRCTAnimation.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTAnimation.a; + remoteRef = 3A7531B320323B0700888478 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3AA3D5D5204DD40F00C3E8E6 /* libAirMaps.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libAirMaps.a; + remoteRef = 3AA3D5D4204DD40F00C3E8E6 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 3AAE7F511F55B50200E914A8 /* libthird-party.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -899,13 +976,16 @@ files = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-SimpleFcmClientTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; 5FF00A260C7092D33989ED96 /* [CP] Embed Pods Frameworks */ = { @@ -974,13 +1054,16 @@ files = ( ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-SimpleFcmClient-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -1042,6 +1125,7 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1062,6 +1146,7 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", ); PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1089,6 +1174,7 @@ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, "$(SRCROOT)/../node_modules/react-native/React/**", "$(SRCROOT)/../node_modules/react-native-fcm/ios", + "$(SRCROOT)/../node_modules/react-native-maps/lib/ios/**", ); INFOPLIST_FILE = SimpleFcmClient/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; @@ -1123,6 +1209,7 @@ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, "$(SRCROOT)/../node_modules/react-native/React/**", "$(SRCROOT)/../node_modules/react-native-fcm/ios", + "$(SRCROOT)/../node_modules/react-native-maps/lib/ios/**", ); INFOPLIST_FILE = SimpleFcmClient/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; diff --git a/Examples/simple-fcm-client/ios/SimpleFcmClient.xcodeproj/xcshareddata/xcschemes/SimpleFcmClient.xcscheme b/Examples/simple-fcm-client/ios/SimpleFcmClient.xcodeproj/xcshareddata/xcschemes/SimpleFcmClient.xcscheme index 5f74c52d9416acdb85d93ae5e524be40596b5467..8432ca92e95c98f9968615a30d7f8c2c76a65702 100644 --- a/Examples/simple-fcm-client/ios/SimpleFcmClient.xcodeproj/xcshareddata/xcschemes/SimpleFcmClient.xcscheme +++ b/Examples/simple-fcm-client/ios/SimpleFcmClient.xcodeproj/xcshareddata/xcschemes/SimpleFcmClient.xcscheme @@ -54,6 +54,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" shouldUseLaunchSchemeArgsEnv = "YES"> =4.0.0 <4.1.0-0" yargs "^6.4.0" +react-navigation@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/react-navigation/-/react-navigation-1.2.1.tgz#06cb2c97eb1b2e20bdb4ff7aee1acfa218a1561b" + dependencies: + clamp "^1.0.1" + hoist-non-react-statics "^2.2.0" + path-to-regexp "^1.7.0" + prop-types "^15.5.10" + react-native-drawer-layout-polyfill "^1.3.2" + react-native-safe-area-view "^0.7.0" + react-native-tab-view "^0.0.74" + react-proxy@^1.1.7: version "1.1.8" resolved "https://registry.yarnpkg.com/react-proxy/-/react-proxy-1.1.8.tgz#9dbfd9d927528c3aa9f444e4558c37830ab8c26a" @@ -3785,13 +3932,13 @@ read-pkg@^1.0.0: util-deprecate "~1.0.1" readable-stream@^2.0.1, readable-stream@^2.1.4: - version "2.3.3" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" + version "2.3.4" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.4.tgz#c946c3f47fa7d8eabc0b6150f4a12f69a4574071" dependencies: core-util-is "~1.0.0" inherits "~2.0.3" isarray "~1.0.0" - process-nextick-args "~1.0.6" + process-nextick-args "~2.0.0" safe-buffer "~5.1.1" string_decoder "~1.0.3" util-deprecate "~1.0.1" @@ -3882,18 +4029,18 @@ replace-ext@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" -request@^2.55.0, request@^2.79.0: - version "2.79.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" +request@2.81.0: + version "2.81.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" dependencies: aws-sign2 "~0.6.0" aws4 "^1.2.1" - caseless "~0.11.0" + caseless "~0.12.0" combined-stream "~1.0.5" extend "~3.0.0" forever-agent "~0.6.1" form-data "~2.1.1" - har-validator "~2.0.6" + har-validator "~4.2.1" hawk "~3.1.3" http-signature "~1.1.0" is-typedarray "~1.0.0" @@ -3901,24 +4048,26 @@ request@^2.55.0, request@^2.79.0: json-stringify-safe "~5.0.1" mime-types "~2.1.7" oauth-sign "~0.8.1" - qs "~6.3.0" + performance-now "^0.2.0" + qs "~6.4.0" + safe-buffer "^5.0.1" stringstream "~0.0.4" tough-cookie "~2.3.0" - tunnel-agent "~0.4.1" + tunnel-agent "^0.6.0" uuid "^3.0.0" -request@^2.81.0: - version "2.81.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" +request@^2.55.0, request@^2.79.0: + version "2.79.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" dependencies: aws-sign2 "~0.6.0" aws4 "^1.2.1" - caseless "~0.12.0" + caseless "~0.11.0" combined-stream "~1.0.5" extend "~3.0.0" forever-agent "~0.6.1" form-data "~2.1.1" - har-validator "~4.2.1" + har-validator "~2.0.6" hawk "~3.1.3" http-signature "~1.1.0" is-typedarray "~1.0.0" @@ -3926,12 +4075,10 @@ request@^2.81.0: json-stringify-safe "~5.0.1" mime-types "~2.1.7" oauth-sign "~0.8.1" - performance-now "^0.2.0" - qs "~6.4.0" - safe-buffer "^5.0.1" + qs "~6.3.0" stringstream "~0.0.4" tough-cookie "~2.3.0" - tunnel-agent "^0.6.0" + tunnel-agent "~0.4.1" uuid "^3.0.0" require-directory@^2.1.1: @@ -3950,6 +4097,12 @@ resolve@^1.1.6: version "1.2.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.2.0.tgz#9589c3f2f6149d1417a40becc1663db6ec6bc26c" +resolve@^1.2.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36" + dependencies: + path-parse "^1.0.5" + response-time@~2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/response-time/-/response-time-2.3.2.tgz#ffa71bab952d62f7c1d49b7434355fbc68dffc5a" @@ -3971,8 +4124,8 @@ right-align@^0.1.1: align-text "^0.1.1" rimraf@2, rimraf@^2.5.1, rimraf@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" + version "2.6.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" dependencies: glob "^7.0.5" @@ -4015,8 +4168,8 @@ safe-buffer@~5.0.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7" sane@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/sane/-/sane-2.0.0.tgz#99cb79f21f4a53a69d4d0cd957c2db04024b8eb2" + version "2.4.1" + resolved "https://registry.yarnpkg.com/sane/-/sane-2.4.1.tgz#29f991208cf28636720efdc584293e7fd66663a5" dependencies: anymatch "^1.3.0" exec-sh "^0.2.0" @@ -4024,7 +4177,7 @@ sane@^2.0.0: minimatch "^3.0.2" minimist "^1.1.1" walker "~1.0.5" - watch "~0.10.0" + watch "~0.18.0" optionalDependencies: fsevents "^1.1.1" @@ -4174,6 +4327,10 @@ source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" +source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + source-map@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" @@ -4304,19 +4461,19 @@ supports-color@^3.1.0, supports-color@^3.1.2: dependencies: has-flag "^1.0.0" -supports-color@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.2.1.tgz#65a4bb2631e90e02420dba5554c375a4754bb836" +supports-color@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.2.0.tgz#b0d5333b1184dd3666cbe5aa0b45c5ac7ac17a4a" dependencies: - has-flag "^2.0.0" + has-flag "^3.0.0" "symbol-tree@>= 3.1.0 < 4.0.0": version "3.2.1" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.1.tgz#8549dd1d01fa9f893c18cc9ab0b106b4d9b168cb" tar-pack@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.0.tgz#23be2d7f671a8339376cbdb0b8fe3fdebf317984" + version "3.4.1" + resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.1.tgz#e1dbc03a9b9d3ba07e896ad027317eb679a10a1f" dependencies: debug "^2.2.0" fstream "^1.0.10" @@ -4375,11 +4532,11 @@ time-stamp@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.0.1.tgz#9f4bd23559c9365966f3302dbba2b07c6b99b151" -tmp@^0.0.31: - version "0.0.31" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" dependencies: - os-tmpdir "~1.0.1" + os-tmpdir "~1.0.2" tmpl@1.0.x: version "1.0.4" @@ -4543,6 +4700,13 @@ watch@~0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/watch/-/watch-0.10.0.tgz#77798b2da0f9910d595f1ace5b0c2258521f21dc" +watch@~0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/watch/-/watch-0.18.0.tgz#28095476c6df7c90c963138990c0a5423eb4b986" + dependencies: + exec-sh "^0.2.0" + minimist "^1.2.0" + webidl-conversions@^3.0.0, webidl-conversions@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" diff --git a/README.md b/README.md index 84bad419a229f6b9f840056faa0d90817e02779f..29a8139b8b24fd6fd8ad2ab4b98ce26cff525af5 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,14 @@ - An example working project is available at: https://github.com/evollu/react-native-fcm/tree/master/Examples/simple-fcm-client -- DO NOT change Android targetSdkVersion >= 26. The notification won't show up because of notification channel requirement. [Help is needed](https://github.com/evollu/react-native-fcm/issues/698) +- DO NOT change Android targetSdkVersion >= 26. The notification won't show up because of notification channel requirement. +If you have to upgrade, you can use sdk-26 branch and post feedback on [here](https://github.com/evollu/react-native-fcm/pull/699) ## Installation - Run `npm install react-native-fcm --save` -- Run `react-native link react-native-fcm` (RN 0.29.1+, otherwise `rnpm link react-native-fcm`) +- [Link libraries](https://facebook.github.io/react-native/docs/linking-libraries-ios.html) + Note: the auto link doesn't work with xcworkspace so CocoaPods user needs to do manual linking ## Configure Firebase Console ### FCM config file @@ -43,6 +45,7 @@ https://github.com/evollu/react-native-fcm/blob/master/Examples/simple-fcm-clien - Edit `android/app/build.gradle`. Add at the bottom of the file: ```diff apply plugin: "com.android.application" + ... + apply plugin: 'com.google.gms.google-services' ``` @@ -53,6 +56,8 @@ https://github.com/evollu/react-native-fcm/blob/master/Examples/simple-fcm-clien ... android:theme="@style/AppTheme"> ++ + + + + @@ -73,6 +78,7 @@ https://github.com/evollu/react-native-fcm/blob/master/Examples/simple-fcm-clien dependencies { + compile project(':react-native-fcm') + compile 'com.google.firebase:firebase-core:10.0.1' //this decides your firebase SDK version ++ compile 'com.google.firebase:firebase-messaging:10.0.1' compile fileTree(dir: "libs", include: ["*.jar"]) compile "com.android.support:appcompat-v7:23.0.1" compile "com.facebook.react:react-native:+" // From node_modules @@ -86,6 +92,19 @@ https://github.com/evollu/react-native-fcm/blob/master/Examples/simple-fcm-clien include ':app' ``` +- Edit `MainActivity.java`. This fixes [a bug](https://stackoverflow.com/questions/14853327/intent-not-restored-correctly-after-activity-is-killed-if-clear-top-and-single-t/18307360#18307360) +```diff ++ import android.content.Intent; +... +public class MainActivity extends ReactActivity { ++ @Override ++ public void onNewIntent(Intent intent) { ++ super.onNewIntent(intent); ++ setIntent(intent); ++ } +} +``` + ### Config for notification and `click_action` in Android To allow android to respond to `click_action`, you need to define Activities and filter on specific intent. Since all javascript is running in MainActivity, you can have MainActivity to handle actions: @@ -184,7 +203,7 @@ cd ios && pod init Edit the newly created `Podfile`: ```diff # Pods for YOURAPP -+ pod 'FirebaseMessaging' ++ pod 'Firebase/Messaging' ``` Install the `Firebase/Messaging` pod: @@ -192,13 +211,17 @@ Install the `Firebase/Messaging` pod: pod install ``` NOTE: you don't need to enable `use_frameworks!`. if you have to have `use_frameworks!` make sure you don't have `inherit! :search_paths` +NOTE: there is a working example in `master` branch ### Non Cocoapod approach -1. Download the Firebase SDK framework from [Integrate without CocoaPods](https://firebase.google.com/docs/ios/setup#frameworks). +1. Follow the instruction on [Integrate without CocoaPods](https://firebase.google.com/docs/ios/setup#frameworks). - Import libraries, add Capabilities (background running and push notification), upload APNS and etc etc etc... -2. Put frameworks under `ios/Pods` folder -2. Follow the `README` to link frameworks (Analytics+Messaging) +2. Put frameworks under `ios/Frameworks` folder, and drag those files into your xcode solution -> Frameworks +3. Put `firebase.h` and `module.modulemap` under `ios/Frameworks` folder, no need to drag into solution +4. Modify your project's `User Header Search Paths` and add `$(PROJECT_DIR)/Frameworks` +screen shot 2018-03-05 at 2 17 03 pm +NOTE: There is a working example in `no-pod` branch ### Shared steps @@ -273,8 +296,7 @@ Edit AndroidManifest.xml + + - + + + + @@ -290,138 +312,8 @@ NOTE: `com.evollu.react.fcm.FIRLocalMessagingPublisher` is required for presenti ## Usage +[Check example project](https://github.com/evollu/react-native-fcm/blob/master/Examples/simple-fcm-client/app/App.js#L68) -```javascript -import {Platform} from 'react-native'; -import FCM, {FCMEvent, RemoteNotificationResult, WillPresentNotificationResult, NotificationType} from 'react-native-fcm'; - -// this shall be called regardless of app state: running, background or not running. Won't be called when app is killed by user in iOS -FCM.on(FCMEvent.Notification, async (notif) => { - // there are two parts of notif. notif.notification contains the notification payload, notif.data contains data payload - if(notif.local_notification){ - //this is a local notification - } - if(notif.opened_from_tray){ - //iOS: app is open/resumed because user clicked banner - //Android: app is open/resumed because user clicked banner or tapped app icon - } - // await someAsyncCall(); - - if(Platform.OS ==='ios'){ - //optional - //iOS requires developers to call completionHandler to end notification process. If you do not call it your background remote notifications could be throttled, to read more about it see https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623013-application. - //This library handles it for you automatically with default behavior (for remote notification, finish with NoData; for WillPresent, finish depend on "show_in_foreground"). However if you want to return different result, follow the following code to override - //notif._notificationType is available for iOS platfrom - switch(notif._notificationType){ - case NotificationType.Remote: - notif.finish(RemoteNotificationResult.NewData) //other types available: RemoteNotificationResult.NewData, RemoteNotificationResult.ResultFailed - break; - case NotificationType.NotificationResponse: - notif.finish(); - break; - case NotificationType.WillPresent: - notif.finish(WillPresentNotificationResult.All) //other types available: WillPresentNotificationResult.None - break; - } - } -}); -FCM.on(FCMEvent.RefreshToken, (token) => { - console.log(token) - // fcm token may not be available on first load, catch it here -}); - -class App extends Component { - componentDidMount() { - // iOS: show permission prompt for the first call. later just check permission in user settings - // Android: check permission in user settings - FCM.requestPermissions().then(()=>console.log('granted')).catch(()=>console.log('notification permission rejected')); - - FCM.getFCMToken().then(token => { - console.log(token) - // store fcm token in your server - }); - - this.notificationListener = FCM.on(FCMEvent.Notification, async (notif) => { - // optional, do some component related stuff - }); - - // initial notification contains the notification that launchs the app. If user launchs app by clicking banner, the banner notification info will be here rather than through FCM.on event - // sometimes Android kills activity when app goes to background, and when resume it broadcasts notification before JS is run. You can use FCM.getInitialNotification() to capture those missed events. - // initial notification will be triggered all the time even when open app by icon so send some action identifier when you send notification - FCM.getInitialNotification().then(notif => { - console.log(notif) - }); - } - - componentWillUnmount() { - // stop listening for events - this.notificationListener.remove(); - } - - otherMethods(){ - - FCM.subscribeToTopic('/topics/foo-bar'); - FCM.unsubscribeFromTopic('/topics/foo-bar'); - FCM.presentLocalNotification({ - id: "UNIQ_ID_STRING", // (optional for instant notification) - title: "My Notification Title", // as FCM payload - body: "My Notification Message", // as FCM payload (required) - sound: "default", // as FCM payload - priority: "high", // as FCM payload - click_action: "ACTION", // as FCM payload - badge: 10, // as FCM payload IOS only, set 0 to clear badges - number: 10, // Android only - ticker: "My Notification Ticker", // Android only - auto_cancel: true, // Android only (default true) - large_icon: "ic_launcher", // Android only - icon: "ic_launcher", // as FCM payload, you can relace this with custom icon you put in mipmap - big_text: "Show when notification is expanded", // Android only - sub_text: "This is a subText", // Android only - color: "red", // Android only - vibrate: 300, // Android only default: 300, no vibration if you pass 0 - group: "group", // Android only - picture: "https://google.png", // Android only bigPicture style - ongoing: true, // Android only - my_custom_data:'my_custom_field_value', // extra data you want to throw - lights: true, // Android only, LED blinking (default false) - show_in_foreground // notification when app is in foreground (local & remote) - }); - - FCM.scheduleLocalNotification({ - fire_date: new Date().getTime(), //RN's converter is used, accept epoch time and whatever that converter supports - id: "UNIQ_ID_STRING", //REQUIRED! this is what you use to lookup and delete notification. In android notification with same ID will override each other - body: "from future past", - repeat_interval: "week" //day, hour - }) - - FCM.getScheduledLocalNotifications().then(notif=>console.log(notif)); - - //these clears notification from notification center/tray - FCM.removeAllDeliveredNotifications() - FCM.removeDeliveredNotification("UNIQ_ID_STRING") - - //these removes future local notifications - FCM.cancelAllLocalNotifications() - FCM.cancelLocalNotification("UNIQ_ID_STRING") - - FCM.setBadgeNumber(1); // iOS only and there's no way to set it in Android, yet. - FCM.getBadgeNumber().then(number=>console.log(number)); // iOS only and there's no way to get it in Android, yet. - FCM.send('984XXXXXXXXX', { - my_custom_data_1: 'my_custom_field_value_1', - my_custom_data_2: 'my_custom_field_value_2' - }); - - FCM.deleteInstanceId() - .then( () => { - //Deleted instance id successfully - //This will reset Instance ID and revokes all tokens. - }) - .catch(error => { - //Error while deleting instance id - }); - } -} -``` ### Build custom push notification for Android Firebase android misses important feature of android notification like `group`, `priority` and etc. As a work around you can send data message (no `notification` payload at all) and this repo will build a local notification for you. If you pass `custom_notification` in the payload, the repo will treat the content as a local notification config and shows immediately. @@ -538,6 +430,26 @@ FCM.send('984XXXXXXXXX', { The `Data Object` is message data comprising as many key-value pairs of the message's payload as are needed (ensure that the value of each pair in the data object is a `string`). Your `Sender ID` is a unique numerical value generated when you created your Firebase project, it is available in the `Cloud Messaging` tab of the Firebase console `Settings` pane. The sender ID is used to identify each app server that can send messages to the client app. +### Sending remote notifications with category on iOS +If you want to send notification which will have actions as you defined in app it's important to correctly set it's `category` (`click_action`) property. It's also good to set `"content-available" : 1` so app will gets enough time to handle actions in background. + +So the fcm payload should look like this: +```javascript +{ + "to": "some_device_token", + "content_available": true, + "notification": { + "title": "Alarm", + "subtitle": "First Alarm", + "body": "First Alarm", + "click_action": "com.myapp.MyCategory" // The id of notification category which you defined with FCM.setNotificationCategories + }, + "data": { + "extra": "juice" + } + } + ``` + ## Q & A #### Why do you build another local notification @@ -547,18 +459,12 @@ Yes there are `react-native-push-notification` and `react-native-system-notifica - The PushNotificationIOS by react native team is still missing features that recurring, so we are adding it here #### My Android build is failing -Try update your SDK and google play service. If you are having multiple plugins requiring different version of play-service sdk, use force to lock in version +Try update your SDK and google play service. +If you are having multiple plugins requiring different version of play-service sdk, skip conflicting group. The example project shows for how to colive with react-native-maps ``` -dependencies { - ... - compile ('com.android.support:appcompat-v7:25.0.1') { - exclude group: 'com.google.android', module: 'support-v4' - } - compile ('com.google.android.gms:play-services-gcm:10.0.1') { - force = true; + compile(project(':react-native-maps')) { + exclude group: 'com.google.android.gms', module: 'play-services-base' } - ... -} ``` #### My App throws FCM function undefined error @@ -640,3 +546,65 @@ Issues and pull requests are welcome. Let's make this thing better! #### Credits Local notification implementation is inspired by react-native-push-notification by zo0r + +## Sending remote notification + +How to send a push notification from your server? You should `POST` to this endpoint: + + https://fcm.googleapis.com/fcm/send + +You need to set the headers of `Content-Type` to `application/json` and `Authorization` to `key=******` where you replace `******` with the "Legacy server key" from here the Firebase dashbaord. Get this information by first going to: + +1. https://console.firebase.google.com/ +2. Click on "Gear" icon and click "Project Settingss". Screenshot: https://screenshotscdn.firefoxusercontent.com/images/35b93de8-44e1-49af-89d7-140b74c267c7.png +3. Click on "Cloud Message" tab and find "Legacy server key" here. Screenshot: https://screenshotscdn.firefoxusercontent.com/images/c52ec383-783d-47d3-a1e6-75249fb6f3fb.png + +The body should be json like this: + +``` +{ + "to":"FCM_DEVICE_TOKEN_GOES_HERE", + "data": { + "custom_notification": { + "body": "test body", + "title": "test title", + "color":"#00ACD4", + "priority":"high", + "icon":"ic_launcher", + "group": "GROUP", + "sound": "default", + "id": "id", + "show_in_foreground": true + } + } +} +``` + +Example: + +``` +fetch('https://fcm.googleapis.com/fcm/send', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + 'Authorization': 'key=EFefklefwef9efwefkejfwf' + }, + body: JSON.stringify({ + "to":"kajfsdf:efawefwe_fsdfdsf-asfawefwefwf_asdfsdfasd-asdfasdfsd9A_asdfsdf_asdf", + "data": { + "custom_notification": { + "body": "test body", + "title": "test title", + "color":"#00ACD4", + "priority":"high", + "icon":"ic_notif", + "group": "GROUP", + "sound": "default", + "id": "id", + "show_in_foreground": true + } + } + }) +}) +``` + diff --git a/android/src/main/java/com/evollu/react/fcm/FIRLocalMessagingHelper.java b/android/src/main/java/com/evollu/react/fcm/FIRLocalMessagingHelper.java index eca4004230c5523b11df61a72f219977069a3f74..a71173351f9065654d69f93c939af943176a7d02 100644 --- a/android/src/main/java/com/evollu/react/fcm/FIRLocalMessagingHelper.java +++ b/android/src/main/java/com/evollu/react/fcm/FIRLocalMessagingHelper.java @@ -58,7 +58,10 @@ public class FIRLocalMessagingHelper { return; } - Long fireDate = Math.round(bundle.getDouble("fire_date")); + long fireDate = Math.round(bundle.getDouble("fire_date")); + if(fireDate == 0){ + fireDate = Math.round(bundle.getLong("fire_date")); + } if (fireDate == 0) { Log.e(TAG, "failed to schedule notification because fire date is missing"); return; diff --git a/android/src/main/java/com/evollu/react/fcm/FIRMessagingModule.java b/android/src/main/java/com/evollu/react/fcm/FIRMessagingModule.java index ff426b828164d3377374f0731089d13fc425f38c..e68b759cd0e260b7713668dd6e6d9618318f444a 100644 --- a/android/src/main/java/com/evollu/react/fcm/FIRMessagingModule.java +++ b/android/src/main/java/com/evollu/react/fcm/FIRMessagingModule.java @@ -37,6 +37,7 @@ import java.util.ArrayList; import java.util.Map; import java.util.Set; import java.util.UUID; +import com.google.firebase.FirebaseApp; import static android.content.Context.NOTIFICATION_SERVICE; @@ -106,6 +107,7 @@ public class FIRMessagingModule extends ReactContextBaseJavaModule implements Li } if (mngr.getNotificationChannel(id) != null) { promise.resolve(null); + return; } // NotificationChannel channel = new NotificationChannel( @@ -132,6 +134,31 @@ public class FIRMessagingModule extends ReactContextBaseJavaModule implements Li } } + @ReactMethod + public void getEntityFCMToken(Promise promise) { + try { + String senderId = FirebaseApp.getInstance().getOptions().getGcmSenderId(); + String token = FirebaseInstanceId.getInstance().getToken(senderId, "FCM"); + Log.d(TAG, "Firebase token: " + token); + promise.resolve(token); + } catch (Throwable e) { + e.printStackTrace(); + promise.reject(null,e.getMessage()); + } + } + + @ReactMethod + public void deleteEntityFCMToken(Promise promise) { + try { + String senderId = FirebaseApp.getInstance().getOptions().getGcmSenderId(); + FirebaseInstanceId.getInstance().deleteToken(senderId, "FCM"); + promise.resolve(null); + } catch (Throwable e) { + e.printStackTrace(); + promise.reject(null,e.getMessage()); + } + } + @ReactMethod public void deleteInstanceId(Promise promise){ try { @@ -353,4 +380,3 @@ public class FIRMessagingModule extends ReactContextBaseJavaModule implements Li sendEvent("FCMNotificationReceived", parseIntent(intent)); } } - diff --git a/android/src/main/java/com/evollu/react/fcm/InstanceIdService.java b/android/src/main/java/com/evollu/react/fcm/InstanceIdService.java index 9ac9ef540dd7525158058ebdd1032cbaf6c65f7f..f01c160ceab4702d480c76a5290ab9f527b5224c 100644 --- a/android/src/main/java/com/evollu/react/fcm/InstanceIdService.java +++ b/android/src/main/java/com/evollu/react/fcm/InstanceIdService.java @@ -2,9 +2,14 @@ package com.evollu.react.fcm; import android.content.Intent; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; +import com.facebook.react.ReactApplication; +import com.facebook.react.ReactInstanceManager; +import com.facebook.react.bridge.ReactContext; import com.google.firebase.iid.FirebaseInstanceId; import com.google.firebase.iid.FirebaseInstanceIdService; @@ -25,11 +30,35 @@ public class InstanceIdService extends FirebaseInstanceIdService { Log.d(TAG, "Refreshed token: " + refreshedToken); // Broadcast refreshed token - Intent i = new Intent("com.evollu.react.fcm.FCMRefreshToken"); Bundle bundle = new Bundle(); bundle.putString("token", refreshedToken); i.putExtras(bundle); - LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(i); + + final Intent message = i; + + Handler handler = new Handler(Looper.getMainLooper()); + handler.post(new Runnable() { + public void run() { + // Construct and load our normal React JS code bundle + ReactInstanceManager mReactInstanceManager = ((ReactApplication) getApplication()).getReactNativeHost().getReactInstanceManager(); + ReactContext context = mReactInstanceManager.getCurrentReactContext(); + // If it's constructed, send a notification + if (context != null) { + LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(message); + } else { + // Otherwise wait for construction, then send the notification + mReactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() { + public void onReactContextInitialized(ReactContext context) { + LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(message); + } + }); + if (!mReactInstanceManager.hasStartedCreatingInitialContext()) { + // Construct it in the background + mReactInstanceManager.createReactContextInBackground(); + } + } + } + }); } } diff --git a/android/src/main/java/com/evollu/react/fcm/ReactNativeJson.java b/android/src/main/java/com/evollu/react/fcm/ReactNativeJson.java new file mode 100644 index 0000000000000000000000000000000000000000..e71f77adfc33c401c6735c545b5b6d861d6ff013 --- /dev/null +++ b/android/src/main/java/com/evollu/react/fcm/ReactNativeJson.java @@ -0,0 +1,122 @@ +package com.evollu.react.fcm; + +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.ReadableMapKeySetIterator; +import com.facebook.react.bridge.WritableArray; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.bridge.WritableNativeArray; +import com.facebook.react.bridge.WritableNativeMap; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.Iterator; + +public class ReactNativeJson { + public static WritableMap convertJsonToMap(JSONObject jsonObject) throws JSONException { + WritableMap map = new WritableNativeMap(); + + Iterator iterator = jsonObject.keys(); + while (iterator.hasNext()) { + String key = iterator.next(); + Object value = jsonObject.get(key); + if (value instanceof JSONObject) { + map.putMap(key, convertJsonToMap((JSONObject) value)); + } else if (value instanceof JSONArray) { + map.putArray(key, convertJsonToArray((JSONArray) value)); + } else if (value instanceof Boolean) { + map.putBoolean(key, (Boolean) value); + } else if (value instanceof Integer) { + map.putInt(key, (Integer) value); + } else if (value instanceof Double) { + map.putDouble(key, (Double) value); + } else if (value instanceof String) { + map.putString(key, (String) value); + } else { + map.putString(key, value.toString()); + } + } + return map; + } + + public static WritableArray convertJsonToArray(JSONArray jsonArray) throws JSONException { + WritableArray array = new WritableNativeArray(); + + for (int i = 0; i < jsonArray.length(); i++) { + Object value = jsonArray.get(i); + if (value instanceof JSONObject) { + array.pushMap(convertJsonToMap((JSONObject) value)); + } else if (value instanceof JSONArray) { + array.pushArray(convertJsonToArray((JSONArray) value)); + } else if (value instanceof Boolean) { + array.pushBoolean((Boolean) value); + } else if (value instanceof Integer) { + array.pushInt((Integer) value); + } else if (value instanceof Double) { + array.pushDouble((Double) value); + } else if (value instanceof String) { + array.pushString((String) value); + } else { + array.pushString(value.toString()); + } + } + return array; + } + + public static JSONObject convertMapToJson(ReadableMap readableMap) throws JSONException { + JSONObject object = new JSONObject(); + ReadableMapKeySetIterator iterator = readableMap.keySetIterator(); + while (iterator.hasNextKey()) { + String key = iterator.nextKey(); + switch (readableMap.getType(key)) { + case Null: + object.put(key, JSONObject.NULL); + break; + case Boolean: + object.put(key, readableMap.getBoolean(key)); + break; + case Number: + object.put(key, readableMap.getDouble(key)); + break; + case String: + object.put(key, readableMap.getString(key)); + break; + case Map: + object.put(key, convertMapToJson(readableMap.getMap(key))); + break; + case Array: + object.put(key, convertArrayToJson(readableMap.getArray(key))); + break; + } + } + return object; + } + + public static JSONArray convertArrayToJson(ReadableArray readableArray) throws JSONException { + JSONArray array = new JSONArray(); + for (int i = 0; i < readableArray.size(); i++) { + switch (readableArray.getType(i)) { + case Null: + break; + case Boolean: + array.put(readableArray.getBoolean(i)); + break; + case Number: + array.put(readableArray.getDouble(i)); + break; + case String: + array.put(readableArray.getString(i)); + break; + case Map: + array.put(convertMapToJson(readableArray.getMap(i))); + break; + case Array: + array.put(convertArrayToJson(readableArray.getArray(i))); + break; + } + } + return array; + } +} diff --git a/android/src/main/java/com/evollu/react/fcm/SendNotificationTask.java b/android/src/main/java/com/evollu/react/fcm/SendNotificationTask.java index 37496238df5f5842241d0b34132a3c7508027fa7..b15326caaabe56d8a56bb48a0c6768bae66be873 100644 --- a/android/src/main/java/com/evollu/react/fcm/SendNotificationTask.java +++ b/android/src/main/java/com/evollu/react/fcm/SendNotificationTask.java @@ -15,11 +15,17 @@ import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; +import android.os.PowerManager; import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationManagerCompat; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.WritableArray; + +import org.json.JSONArray; + import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; @@ -35,7 +41,7 @@ public class SendNotificationTask extends AsyncTask { private SharedPreferences sharedPreferences; private Boolean mIsForeground; - public SendNotificationTask(Context context, SharedPreferences sharedPreferences, Boolean mIsForeground, Bundle bundle){ + SendNotificationTask(Context context, SharedPreferences sharedPreferences, Boolean mIsForeground, Bundle bundle){ this.mContext = context; this.bundle = bundle; this.sharedPreferences = sharedPreferences; @@ -70,9 +76,12 @@ public class SendNotificationTask extends AsyncTask { .setAutoCancel(bundle.getBoolean("auto_cancel", true)) .setNumber((int)bundle.getDouble("number")) .setSubText(bundle.getString("sub_text")) - .setGroup(bundle.getString("group")) .setVibrate(new long[]{0, DEFAULT_VIBRATION}) .setExtras(bundle.getBundle("data")); + + if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){ + notification.setGroup(bundle.getString("group")); + } if (bundle.containsKey("ongoing") && bundle.getBoolean("ongoing")) { notification.setOngoing(bundle.getBoolean("ongoing")); @@ -207,11 +216,40 @@ public class SendNotificationTask extends AsyncTask { PendingIntent.FLAG_UPDATE_CURRENT); notification.setContentIntent(pendingIntent); + + if (bundle.containsKey("android_actions")) { + WritableArray actions = ReactNativeJson.convertJsonToArray(new JSONArray(bundle.getString("android_actions"))); + for (int a = 0; a < actions.size(); a++) { + ReadableMap action = actions.getMap(a); + String actionTitle = action.getString("title"); + String actionId = action.getString("id"); + Intent actionIntent = new Intent(); + actionIntent.setClassName(mContext, intentClassName); + actionIntent.setAction("com.evollu.react.fcm." + actionId + "_ACTION"); + actionIntent.putExtras(bundle); + actionIntent.putExtra("_actionIdentifier", actionId); + actionIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + PendingIntent pendingActionIntent = PendingIntent.getActivity(mContext, notificationID, actionIntent, + PendingIntent.FLAG_UPDATE_CURRENT); + + notification.addAction(0, actionTitle, pendingActionIntent); + } + } Notification info = notification.build(); NotificationManagerCompat.from(mContext).notify(notificationID, info); } + + if(bundle.getBoolean("wake_screen", false)){ + PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); + if(pm != null && !pm.isScreenOn()) + { + PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK |PowerManager.ACQUIRE_CAUSES_WAKEUP |PowerManager.ON_AFTER_RELEASE,"FCMLock"); + wl.acquire(5000); + } + } + //clear out one time scheduled notification once fired if(!bundle.containsKey("repeat_interval") && bundle.containsKey("fire_date")) { SharedPreferences.Editor editor = sharedPreferences.edit(); @@ -224,7 +262,7 @@ public class SendNotificationTask extends AsyncTask { return null; } - public Bitmap getBitmapFromURL(String strURL) { + private Bitmap getBitmapFromURL(String strURL) { try { URL url = new URL(strURL); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); @@ -238,11 +276,10 @@ public class SendNotificationTask extends AsyncTask { } } - public String getMainActivityClassName() { + protected String getMainActivityClassName() { String packageName = mContext.getPackageName(); Intent launchIntent = mContext.getPackageManager().getLaunchIntentForPackage(packageName); - String className = launchIntent.getComponent().getClassName(); - return className; + return launchIntent != null ? launchIntent.getComponent().getClassName() : null; } } diff --git a/index.d.ts b/index.d.ts index fce43d2dc21b246039043c14b97c75bae5d3ad07..ac9fa5a5ebc028eed2b77ad0fb5d8c25465a6e99 100644 --- a/index.d.ts +++ b/index.d.ts @@ -25,6 +25,26 @@ declare module "react-native-fcm" { const Local = "local_notification"; } + export enum NotificationCategoryOption { + CustomDismissAction = 'UNNotificationCategoryOptionCustomDismissAction', + AllowInCarPlay = 'UNNotificationCategoryOptionAllowInCarPlay', + PreviewsShowTitle = 'UNNotificationCategoryOptionHiddenPreviewsShowTitle', + PreviewsShowSubtitle = 'UNNotificationCategoryOptionHiddenPreviewsShowSubtitle', + None = 'UNNotificationCategoryOptionNone' + } + + export enum NotificationActionOption { + AuthenticationRequired = 'UNNotificationActionOptionAuthenticationRequired', + Destructive = 'UNNotificationActionOptionDestructive', + Foreground = 'UNNotificationActionOptionForeground', + None = 'UNNotificationActionOptionNone' + } + + export enum NotificationActionType { + Default = 'UNNotificationActionTypeDefault', + TextInput = 'UNNotificationActionTypeTextInput', + } + export interface Notification { collapse_key: string; opened_from_tray: boolean; @@ -44,6 +64,8 @@ declare module "react-native-fcm" { }; local_notification?: boolean; _notificationType: string; + _actionIdentifier?: string; + _userText?: string; finish(type?: string): void; [key: string]: any; } @@ -83,6 +105,23 @@ declare module "react-native-fcm" { remove(): void; } + export interface NotificationAction { + type: NotificationActionType; + id: string; + title?: string; + textInputButtonTitle?: string; + textInputPlaceholder?: string; + options: NotificationActionOption | NotificationActionOption[]; + } + + export interface NotificationCategory { + id: string; + actions: NotificationAction[]; + intentIdentifiers: string[]; + hiddenPreviewsBodyPlaceholder?: string; + options?: NotificationCategoryOption | NotificationCategoryOption[]; + } + export class FCM { static requestPermissions(): Promise; static getFCMToken(): Promise; @@ -109,6 +148,8 @@ declare module "react-native-fcm" { static enableDirectChannel(): void static isDirectChannelEstablished(): Promise static getAPNSToken(): Promise + + static setNotificationCategories(categories: NotificationCategory[]): void; } export default FCM; diff --git a/index.js b/index.js index 86088f5142000bf12953db6a7e45a908288f9a3f..c2781c400424d63e7f8a21da7c10e4343b549df1 100644 --- a/index.js +++ b/index.js @@ -26,6 +26,26 @@ export const NotificationType = { Local: 'local_notification' }; +export const NotificationCategoryOption = { + CustomDismissAction: 'UNNotificationCategoryOptionCustomDismissAction', + AllowInCarPlay: 'UNNotificationCategoryOptionAllowInCarPlay', + PreviewsShowTitle: 'UNNotificationCategoryOptionHiddenPreviewsShowTitle', + PreviewsShowSubtitle: 'UNNotificationCategoryOptionHiddenPreviewsShowSubtitle', + None: 'UNNotificationCategoryOptionNone' +}; + +export const NotificationActionOption = { + AuthenticationRequired: 'UNNotificationActionOptionAuthenticationRequired', + Destructive: 'UNNotificationActionOptionDestructive', + Foreground: 'UNNotificationActionOptionForeground', + None: 'UNNotificationActionOptionNone', +}; + +export const NotificationActionType = { + Default: 'UNNotificationActionTypeDefault', + TextInput: 'UNNotificationActionTypeTextInput', +}; + const RNFIRMessaging = NativeModules.RNFIRMessaging; const FCM = {}; @@ -48,6 +68,14 @@ FCM.getFCMToken = () => { return RNFIRMessaging.getFCMToken(); }; +FCM.getEntityFCMToken = () => { + return RNFIRMessaging.getEntityFCMToken(); +} + +FCM.deleteEntityFCMToken = () => { + return RNFIRMessaging.deleteEntityFCMToken(); +} + FCM.deleteInstanceId = () =>{ return RNFIRMessaging.deleteInstanceId(); }; @@ -64,7 +92,7 @@ FCM.requestPermissions = () => { FCM.createNotificationChannel = (channel) => { if (Platform.OS === 'android') { - return RNFIRMessaging.createNotificationChannel(); + return RNFIRMessaging.createNotificationChannel(channel); } } @@ -157,7 +185,7 @@ FCM.on = (event, callback) => { try { await callback(data); } catch (err) { - console.error('Notification handler err', err); + console.error('Notification handler err:\n'+err.stack); throw err; } if (!data._finishCalled) { @@ -180,4 +208,12 @@ FCM.send = (senderId, payload) => { RNFIRMessaging.send(senderId, payload); }; +FCM.setNotificationCategories = (categories) => { + if (Platform.OS === 'ios') { + RNFIRMessaging.setNotificationCategories(categories); + } +} + export default FCM; + +export {}; diff --git a/ios/RNFIRMessaging.h b/ios/RNFIRMessaging.h index 9813c4ff9264462ffb36c0a04b024cde924b22ab..3bb8d94bfc899d708973d91896dfac98f4348372 100644 --- a/ios/RNFIRMessaging.h +++ b/ios/RNFIRMessaging.h @@ -1,9 +1,14 @@ #import -@import FirebaseCore; -@import FirebaseMessaging; -@import FirebaseInstanceID; +#if __has_include() +#import +#import +#import +#else +@import Firebase; +#endif + #import @import UserNotifications; diff --git a/ios/RNFIRMessaging.m b/ios/RNFIRMessaging.m index 271dac8a5bbe7dc8729ef98b90f614968fe658e6..629589b442a9a5a7969d4f4a0db79e33be26807d 100644 --- a/ios/RNFIRMessaging.m +++ b/ios/RNFIRMessaging.m @@ -131,12 +131,105 @@ RCT_ENUM_CONVERTER(UNNotificationPresentationOptions, (@{ @end +@implementation RCTConvert (UNNotificationAction) + +typedef NS_ENUM(NSUInteger, UNNotificationActionType) { + UNNotificationActionTypeDefault, + UNNotificationActionTypeTextInput +}; + ++ (UNNotificationAction *) UNNotificationAction:(id)json { + NSDictionary *details = [self NSDictionary:json]; + + NSString *identifier = [RCTConvert NSString: details[@"id"]]; + NSString *title = [RCTConvert NSString: details[@"title"]]; + UNNotificationActionOptions options = [RCTConvert UNNotificationActionOptions: details[@"options"]]; + UNNotificationActionType type = [RCTConvert UNNotificationActionType:details[@"type"]]; + + if (type == UNNotificationActionTypeTextInput) { + NSString *textInputButtonTitle = [RCTConvert NSString: details[@"textInputButtonTitle"]]; + NSString *textInputPlaceholder = [RCTConvert NSString: details[@"textInputPlaceholder"]]; + + return [UNTextInputNotificationAction actionWithIdentifier:identifier title:title options:options textInputButtonTitle:textInputButtonTitle textInputPlaceholder:textInputPlaceholder]; + } + + return [UNNotificationAction actionWithIdentifier:identifier + title:title + options:options]; + +} + +RCT_ENUM_CONVERTER(UNNotificationActionType, (@{ + @"UNNotificationActionTypeDefault": @(UNNotificationActionTypeDefault), + @"UNNotificationActionTypeTextInput": @(UNNotificationActionTypeTextInput), + }), UNNotificationActionTypeDefault, integerValue) + + +RCT_MULTI_ENUM_CONVERTER(UNNotificationActionOptions, (@{ + @"UNNotificationActionOptionAuthenticationRequired": @(UNNotificationActionOptionAuthenticationRequired), + @"UNNotificationActionOptionDestructive": @(UNNotificationActionOptionDestructive), + @"UNNotificationActionOptionForeground": @(UNNotificationActionOptionForeground), + @"UNNotificationActionOptionNone": @(UNNotificationActionOptionNone), + }), UNNotificationActionOptionNone, integerValue) + + +@end + +@implementation RCTConvert (UNNotificationCategory) + + ++ (UNNotificationCategory *) UNNotificationCategory:(id)json { + NSDictionary *details = [self NSDictionary:json]; + + NSString *identifier = [RCTConvert NSString: details[@"id"]]; + + NSMutableArray *actions = [[NSMutableArray alloc] init]; + for (NSDictionary *actionDict in details[@"actions"]) { + [actions addObject:[RCTConvert UNNotificationAction:actionDict]]; + } + + NSArray *intentIdentifiers = [RCTConvert NSStringArray:details[@"intentIdentifiers"]]; + NSString *hiddenPreviewsBodyPlaceholder = [RCTConvert NSString:details[@"hiddenPreviewsBodyPlaceholder"]]; + UNNotificationCategoryOptions options = [RCTConvert UNNotificationCategoryOptions: details[@"options"]]; + + if (hiddenPreviewsBodyPlaceholder) { +#if defined(__IPHONE_11_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0 + return [UNNotificationCategory categoryWithIdentifier:identifier actions:actions intentIdentifiers:intentIdentifiers hiddenPreviewsBodyPlaceholder:hiddenPreviewsBodyPlaceholder options:options]; +#endif + } + + return [UNNotificationCategory categoryWithIdentifier:identifier actions:actions intentIdentifiers:intentIdentifiers options:options]; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpartial-availability" + +RCT_MULTI_ENUM_CONVERTER(UNNotificationCategoryOptions, (@{ + @"UNNotificationCategoryOptionNone": @(UNNotificationCategoryOptionNone), + @"UNNotificationCategoryOptionCustomDismissAction": @(UNNotificationCategoryOptionCustomDismissAction), + @"UNNotificationCategoryOptionAllowInCarPlay": @(UNNotificationCategoryOptionAllowInCarPlay), + @"UNNotificationCategoryOptionHiddenPreviewsShowTitle": @(UNNotificationCategoryOptionHiddenPreviewsShowTitle), + @"UNNotificationCategoryOptionHiddenPreviewsShowSubtitle": @(UNNotificationCategoryOptionHiddenPreviewsShowSubtitle), + }), UNNotificationCategoryOptionNone, integerValue) + +#pragma clang diagnostic pop + + +@end + +@interface RCTEventEmitter () +- (void) addListener:(NSString *)eventName; +@end + @interface RNFIRMessaging () @property (nonatomic, strong) NSMutableDictionary *notificationCallbacks; @end @implementation RNFIRMessaging +static bool jsHandlerRegistered; +static NSMutableArray* pendingNotifications; + RCT_EXPORT_MODULE(); - (NSArray *)supportedEvents { @@ -144,21 +237,21 @@ RCT_EXPORT_MODULE(); } + (BOOL)requiresMainQueueSetup { - return YES; + return YES; } + (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo fetchCompletionHandler:(nonnull RCTRemoteNotificationCallback)completionHandler { NSMutableDictionary* data = [[NSMutableDictionary alloc] initWithDictionary: userInfo]; [data setValue:@"remote_notification" forKey:@"_notificationType"]; [data setValue:@(RCTSharedApplication().applicationState == UIApplicationStateInactive) forKey:@"opened_from_tray"]; - [[NSNotificationCenter defaultCenter] postNotificationName:FCMNotificationReceived object:self userInfo:@{@"data": data, @"completionHandler": completionHandler}]; + [self sendNotificationEventWhenAvailable:@{@"data": data, @"completionHandler": completionHandler}]; } + (void)didReceiveLocalNotification:(UILocalNotification *)notification { NSMutableDictionary* data = [[NSMutableDictionary alloc] initWithDictionary: notification.userInfo]; [data setValue:@"local_notification" forKey:@"_notificationType"]; [data setValue:@(RCTSharedApplication().applicationState == UIApplicationStateInactive) forKey:@"opened_from_tray"]; - [[NSNotificationCenter defaultCenter] postNotificationName:FCMNotificationReceived object:self userInfo:@{@"data": data}]; + [self sendNotificationEventWhenAvailable:@{@"data": data}]; } + (void)didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(nonnull RCTNotificationResponseCallback)completionHandler @@ -169,14 +262,34 @@ RCT_EXPORT_MODULE(); if (response.actionIdentifier) { [data setValue:response.actionIdentifier forKey:@"_actionIdentifier"]; } - [[NSNotificationCenter defaultCenter] postNotificationName:FCMNotificationReceived object:self userInfo:@{@"data": data, @"completionHandler": completionHandler}]; + + if ([response isKindOfClass:UNTextInputNotificationResponse.class]) { + [data setValue:[(UNTextInputNotificationResponse *)response userText] forKey:@"_userText"]; + } + + NSDictionary *userInfo = @{@"data": data, @"completionHandler": completionHandler}; + [self sendNotificationEventWhenAvailable:userInfo]; + } + (void)willPresentNotification:(UNNotification *)notification withCompletionHandler:(nonnull RCTWillPresentNotificationCallback)completionHandler { NSMutableDictionary* data = [[NSMutableDictionary alloc] initWithDictionary: notification.request.content.userInfo]; [data setValue:@"will_present_notification" forKey:@"_notificationType"]; - [[NSNotificationCenter defaultCenter] postNotificationName:FCMNotificationReceived object:self userInfo:@{@"data": data, @"completionHandler": completionHandler}]; + [self sendNotificationEventWhenAvailable:@{@"data": data, @"completionHandler": completionHandler}]; +} + ++ (void)sendNotificationEventWhenAvailable:(NSDictionary*)data +{ + if(!jsHandlerRegistered){ + // JS hasn't registered callback yet. hold on that + if(!pendingNotifications){ + pendingNotifications = [NSMutableArray array]; + } + [pendingNotifications addObject:data]; + } else { + [[NSNotificationCenter defaultCenter] postNotificationName:FCMNotificationReceived object:self userInfo:data]; + } } - (void)dealloc @@ -186,10 +299,11 @@ RCT_EXPORT_MODULE(); - (instancetype)init { self = [super init]; + [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleNotificationReceived:) - name:FCMNotificationReceived - object:nil]; + selector:@selector(handleNotificationReceived:) + name:FCMNotificationReceived + object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sendDataMessageFailure:) @@ -207,9 +321,38 @@ RCT_EXPORT_MODULE(); dispatch_async(dispatch_get_main_queue(), ^{ [[FIRMessaging messaging] setDelegate:self]; }); + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + if(!jsHandlerRegistered){ + [self sendPendingNotifications]; + } + }); + return self; } +-(void) addListener:(NSString *)eventName { + [super addListener:eventName]; + + if([eventName isEqualToString:FCMNotificationReceived]) { + [self sendPendingNotifications]; + } +} + +-(void) sendPendingNotifications { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + jsHandlerRegistered = true; + + for (NSDictionary* data in pendingNotifications) { + [[NSNotificationCenter defaultCenter] postNotificationName:FCMNotificationReceived object:self userInfo:data]; + } + + [pendingNotifications removeAllObjects]; + + }); +} + RCT_EXPORT_METHOD(enableDirectChannel) { [[FIRMessaging messaging] setShouldEstablishDirectChannel:@YES]; @@ -222,14 +365,25 @@ RCT_EXPORT_METHOD(isDirectChannelEstablished:(RCTPromiseResolveBlock)resolve rej RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { - UILocalNotification *localUserInfo = self.bridge.launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]; - if (localUserInfo) { - resolve([[localUserInfo userInfo] copy]); - } else { - resolve([self.bridge.launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] copy]); - } + NSDictionary* initialNotif; + NSDictionary *localUserInfo = [[self.bridge.launchOptions[UIApplicationLaunchOptionsLocalNotificationKey] userInfo] mutableCopy]; + + NSDictionary *remoteUserInfo = [self.bridge.launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] mutableCopy]; + if(localUserInfo){ + initialNotif = localUserInfo; + } else if (remoteUserInfo) { + initialNotif = remoteUserInfo; + } + if (initialNotif) { + [initialNotif setValue:@YES forKey:@"opened_from_tray"]; + resolve(initialNotif); + } else { + resolve(nil); + } } + + RCT_EXPORT_METHOD(getAPNSToken:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { NSData * deviceToken = [FIRMessaging messaging].APNSToken; @@ -246,6 +400,42 @@ RCT_EXPORT_METHOD(getFCMToken:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromi resolve([FIRMessaging messaging].FCMToken); } +RCT_EXPORT_METHOD(getEntityFCMToken:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +{ + FIROptions *options = FIROptions.defaultOptions; + NSString *entity = options.GCMSenderID; + NSData * deviceToken = [FIRMessaging messaging].APNSToken; + + if (deviceToken == nil) { + resolve(nil); + return; + } + + [[FIRInstanceID instanceID]tokenWithAuthorizedEntity:entity scope:kFIRInstanceIDScopeFirebaseMessaging options:@{@"apns_token": deviceToken} handler:^(NSString * _Nullable token, NSError * _Nullable error) { + + if (error != nil) { + reject([NSString stringWithFormat:@"%ld",error.code],error.localizedDescription,nil); + } else { + resolve(token); + } + }]; +} + +RCT_EXPORT_METHOD(deleteEntityFCMToken:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +{ + FIROptions *options = FIROptions.defaultOptions;; + NSString *entity = options.GCMSenderID; + + [[FIRInstanceID instanceID]deleteTokenWithAuthorizedEntity:entity scope:kFIRInstanceIDScopeFirebaseMessaging handler:^(NSError * _Nullable error) { + + if (error != nil) { + reject([NSString stringWithFormat:@"%ld",error.code],error.localizedDescription,nil); + } else { + resolve(nil); + } + }]; +} + RCT_EXPORT_METHOD(deleteInstanceId:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { [[FIRInstanceID instanceID]deleteIDWithHandler:^(NSError * _Nullable error) { @@ -367,7 +557,9 @@ RCT_EXPORT_METHOD(removeAllDeliveredNotifications) if([UNUserNotificationCenter currentNotificationCenter] != nil){ [[UNUserNotificationCenter currentNotificationCenter] removeAllDeliveredNotifications]; } else { - [RCTSharedApplication() setApplicationIconBadgeNumber: 0]; + dispatch_async(dispatch_get_main_queue(), ^{ + [RCTSharedApplication() setApplicationIconBadgeNumber: 0]; + }); } } @@ -414,9 +606,25 @@ RCT_EXPORT_METHOD(getScheduledLocalNotifications:(RCTPromiseResolveBlock)resolve } } +RCT_EXPORT_METHOD(setNotificationCategories:(NSArray *)categories) +{ + if([UNUserNotificationCenter currentNotificationCenter] != nil) { + NSMutableSet *categoriesSet = [[NSMutableSet alloc] init]; + + for(NSDictionary *categoryDict in categories) { + UNNotificationCategory *category = [RCTConvert UNNotificationCategory:categoryDict]; + [categoriesSet addObject:category]; + } + + [[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:categoriesSet]; + } +} + RCT_EXPORT_METHOD(setBadgeNumber: (NSInteger) number) { - [RCTSharedApplication() setApplicationIconBadgeNumber:number]; + dispatch_async(dispatch_get_main_queue(), ^{ + [RCTSharedApplication() setApplicationIconBadgeNumber:number]; + }); } RCT_EXPORT_METHOD(getBadgeNumber: (RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) @@ -486,9 +694,7 @@ RCT_EXPORT_METHOD(finishNotificationResponse: (NSString *)completionHandlerId){ self.notificationCallbacks[completionHandlerId] = completionHandler; data[@"_completionHandlerId"] = completionHandlerId; } - [self sendEventWithName:FCMNotificationReceived body:data]; - } - (void)sendDataMessageFailure:(NSNotification *)notification diff --git a/ios/RNFIRMessaging.xcodeproj/project.pbxproj b/ios/RNFIRMessaging.xcodeproj/project.pbxproj index f662aef6d3e04e7e8d0eb4cc1b1d7819a90c8d0c..855a0128228cac8dff27daf8ac2fe92ff7733ed2 100644 --- a/ios/RNFIRMessaging.xcodeproj/project.pbxproj +++ b/ios/RNFIRMessaging.xcodeproj/project.pbxproj @@ -230,6 +230,7 @@ "$(PROJECT_DIR)/../../../ios/Frameworks/**", "$(SRCROOT)/../../../node_modules/react-native-firestack/ios/**", "$(PROJECT_DIR)/../../../ios/Pods/Firebase/**", + "$(PROJECT_DIR)/../../../ios/Pods/Headers/Public/**", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; @@ -257,6 +258,7 @@ "$(PROJECT_DIR)/../../../ios/Frameworks/**", "$(SRCROOT)/../../../node_modules/react-native-firestack/ios/**", "$(PROJECT_DIR)/../../../ios/Pods/Firebase/**", + "$(PROJECT_DIR)/../../../ios/Pods/Headers/Public/**", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; diff --git a/package.json b/package.json index a7eac5d9adffe9a837367d78bd58dad624c8c11f..138c264964773cbdbc11c1e8d1d7d9c2f8cad631 100644 --- a/package.json +++ b/package.json @@ -24,5 +24,5 @@ "type": "git", "url": "git+https://github.com/evollu/react-native-fcm.git" }, - "version": "11.0.1" + "version": "14.1.2" } diff --git a/react-native-fcm.podspec b/react-native-fcm.podspec index 4cc7ab62d92e2a30e90fc1869b38591b290c83d1..0eb8862a9be8189b3a758794e13494fa7be848fb 100644 --- a/react-native-fcm.podspec +++ b/react-native-fcm.podspec @@ -12,6 +12,8 @@ Pod::Spec.new do |s| s.source = { :git => 'https://github.com/evollu/react-native-fcm.git' } s.platform = :ios, '8.0' s.source_files = "ios/*.{h,m}" + s.public_header_files = ['ios/RNFIRMessaging.h'] + s.static_framework = true s.dependency "React" s.dependency "Firebase/Messaging"