├── .dart_tool ├── package_config.json ├── package_config_subset └── version ├── .gitignore ├── .vscode └── launch.json ├── LICENSE.txt ├── README.md ├── example ├── .gitignore ├── .metadata ├── README.md ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ ├── google-services.json │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── mwaysolutions │ │ │ │ │ └── flutter │ │ │ │ │ └── apns_example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── assets │ ├── Xcode_setup.png │ ├── keychain.png │ ├── push_notification_setup.png │ ├── register_app.png │ └── select_capability.png ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Podfile │ ├── Podfile.lock │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ ├── Runner-Bridging-Header.h │ │ └── Runner.entitlements ├── lib │ ├── main.dart │ └── storage.dart ├── payloads │ └── action.apns ├── pubspec.lock └── pubspec.yaml ├── flutter_apns ├── .gitignore ├── .metadata ├── CHANGELOG.md ├── README.md ├── flutter_apns.iml ├── lib │ ├── apns.dart │ ├── flutter_apns.dart │ └── src │ │ ├── apns_connector.dart │ │ ├── connector.dart │ │ └── firebase_connector.dart ├── pubspec.lock └── pubspec.yaml └── flutter_apns_only ├── .flutter-plugins ├── .flutter-plugins-dependencies ├── .gitignore ├── .idea ├── libraries │ └── Dart_SDK.xml ├── modules.xml ├── runConfigurations │ └── example_lib_main_dart.xml └── workspace.xml ├── .metadata ├── CHANGELOG.md ├── README.md ├── flutter_apns_only.iml ├── ios ├── .gitignore ├── Assets │ └── .gitkeep ├── Classes │ ├── FlutterApnsPlugin.swift │ ├── FlutterApnsSerialization.h │ ├── FlutterApnsSerialization.m │ ├── FlutterApnsSwizzler.h │ └── FlutterApnsSwizzler.m └── flutter_apns_only.podspec ├── lib └── flutter_apns_only.dart ├── pubspec.lock └── pubspec.yaml /.dart_tool/package_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "configVersion": 2, 3 | "packages": [ 4 | { 5 | "name": "async", 6 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/async-2.5.0", 7 | "packageUri": "lib/", 8 | "languageVersion": "2.12" 9 | }, 10 | { 11 | "name": "boolean_selector", 12 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0", 13 | "packageUri": "lib/", 14 | "languageVersion": "2.12" 15 | }, 16 | { 17 | "name": "characters", 18 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/characters-1.1.0", 19 | "packageUri": "lib/", 20 | "languageVersion": "2.12" 21 | }, 22 | { 23 | "name": "charcode", 24 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/charcode-1.2.0", 25 | "packageUri": "lib/", 26 | "languageVersion": "2.12" 27 | }, 28 | { 29 | "name": "clock", 30 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0", 31 | "packageUri": "lib/", 32 | "languageVersion": "2.12" 33 | }, 34 | { 35 | "name": "collection", 36 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0", 37 | "packageUri": "lib/", 38 | "languageVersion": "2.12" 39 | }, 40 | { 41 | "name": "fake_async", 42 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0", 43 | "packageUri": "lib/", 44 | "languageVersion": "2.12" 45 | }, 46 | { 47 | "name": "firebase_core", 48 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_core-1.0.1", 49 | "packageUri": "lib/", 50 | "languageVersion": "2.12" 51 | }, 52 | { 53 | "name": "firebase_core_platform_interface", 54 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_core_platform_interface-4.0.0", 55 | "packageUri": "lib/", 56 | "languageVersion": "2.12" 57 | }, 58 | { 59 | "name": "firebase_core_web", 60 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_core_web-1.0.1", 61 | "packageUri": "lib/", 62 | "languageVersion": "2.12" 63 | }, 64 | { 65 | "name": "firebase_messaging", 66 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_messaging-9.0.0", 67 | "packageUri": "lib/", 68 | "languageVersion": "2.12" 69 | }, 70 | { 71 | "name": "firebase_messaging_platform_interface", 72 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_messaging_platform_interface-2.0.0", 73 | "packageUri": "lib/", 74 | "languageVersion": "2.12" 75 | }, 76 | { 77 | "name": "firebase_messaging_web", 78 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_messaging_web-1.0.1", 79 | "packageUri": "lib/", 80 | "languageVersion": "2.12" 81 | }, 82 | { 83 | "name": "flutter", 84 | "rootUri": "file:///Users/work/flutter/packages/flutter", 85 | "packageUri": "lib/", 86 | "languageVersion": "2.12" 87 | }, 88 | { 89 | "name": "flutter_test", 90 | "rootUri": "file:///Users/work/flutter/packages/flutter_test", 91 | "packageUri": "lib/", 92 | "languageVersion": "2.12" 93 | }, 94 | { 95 | "name": "flutter_web_plugins", 96 | "rootUri": "file:///Users/work/flutter/packages/flutter_web_plugins", 97 | "packageUri": "lib/", 98 | "languageVersion": "2.12" 99 | }, 100 | { 101 | "name": "js", 102 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/js-0.6.3", 103 | "packageUri": "lib/", 104 | "languageVersion": "2.12" 105 | }, 106 | { 107 | "name": "matcher", 108 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.10", 109 | "packageUri": "lib/", 110 | "languageVersion": "2.12" 111 | }, 112 | { 113 | "name": "meta", 114 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/meta-1.3.0", 115 | "packageUri": "lib/", 116 | "languageVersion": "2.12" 117 | }, 118 | { 119 | "name": "path", 120 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/path-1.8.0", 121 | "packageUri": "lib/", 122 | "languageVersion": "2.12" 123 | }, 124 | { 125 | "name": "plugin_platform_interface", 126 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/plugin_platform_interface-2.0.0", 127 | "packageUri": "lib/", 128 | "languageVersion": "2.12" 129 | }, 130 | { 131 | "name": "sky_engine", 132 | "rootUri": "file:///Users/work/flutter/bin/cache/pkg/sky_engine", 133 | "packageUri": "lib/", 134 | "languageVersion": "2.12" 135 | }, 136 | { 137 | "name": "source_span", 138 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.0", 139 | "packageUri": "lib/", 140 | "languageVersion": "2.12" 141 | }, 142 | { 143 | "name": "stack_trace", 144 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0", 145 | "packageUri": "lib/", 146 | "languageVersion": "2.12" 147 | }, 148 | { 149 | "name": "stream_channel", 150 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0", 151 | "packageUri": "lib/", 152 | "languageVersion": "2.12" 153 | }, 154 | { 155 | "name": "string_scanner", 156 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0", 157 | "packageUri": "lib/", 158 | "languageVersion": "2.12" 159 | }, 160 | { 161 | "name": "term_glyph", 162 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0", 163 | "packageUri": "lib/", 164 | "languageVersion": "2.12" 165 | }, 166 | { 167 | "name": "test_api", 168 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/test_api-0.2.19", 169 | "packageUri": "lib/", 170 | "languageVersion": "2.12" 171 | }, 172 | { 173 | "name": "typed_data", 174 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.0", 175 | "packageUri": "lib/", 176 | "languageVersion": "2.12" 177 | }, 178 | { 179 | "name": "vector_math", 180 | "rootUri": "file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.0", 181 | "packageUri": "lib/", 182 | "languageVersion": "2.12" 183 | }, 184 | { 185 | "name": "flutter_apns", 186 | "rootUri": "../", 187 | "packageUri": "lib/", 188 | "languageVersion": "2.12" 189 | } 190 | ], 191 | "generated": "2021-03-18T12:53:18.727924Z", 192 | "generator": "pub", 193 | "generatorVersion": "2.12.1" 194 | } 195 | -------------------------------------------------------------------------------- /.dart_tool/package_config_subset: -------------------------------------------------------------------------------- 1 | flutter_apns 2 | 2.12 3 | file:///Users/work/Documents/Projects/flutter-apns/ 4 | file:///Users/work/Documents/Projects/flutter-apns/lib/ 5 | async 6 | 2.12 7 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/async-2.5.0/ 8 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/async-2.5.0/lib/ 9 | boolean_selector 10 | 2.12 11 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/ 12 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/lib/ 13 | characters 14 | 2.12 15 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/characters-1.1.0/ 16 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/characters-1.1.0/lib/ 17 | charcode 18 | 2.12 19 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/charcode-1.2.0/ 20 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/charcode-1.2.0/lib/ 21 | clock 22 | 2.12 23 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0/ 24 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0/lib/ 25 | collection 26 | 2.12 27 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0/ 28 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0/lib/ 29 | fake_async 30 | 2.12 31 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0/ 32 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0/lib/ 33 | firebase_core 34 | 2.12 35 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_core-1.0.1/ 36 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_core-1.0.1/lib/ 37 | firebase_core_platform_interface 38 | 2.12 39 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_core_platform_interface-4.0.0/ 40 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_core_platform_interface-4.0.0/lib/ 41 | firebase_core_web 42 | 2.12 43 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_core_web-1.0.1/ 44 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_core_web-1.0.1/lib/ 45 | firebase_messaging 46 | 2.12 47 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_messaging-9.0.0/ 48 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_messaging-9.0.0/lib/ 49 | firebase_messaging_platform_interface 50 | 2.12 51 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_messaging_platform_interface-2.0.0/ 52 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_messaging_platform_interface-2.0.0/lib/ 53 | firebase_messaging_web 54 | 2.12 55 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_messaging_web-1.0.1/ 56 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_messaging_web-1.0.1/lib/ 57 | js 58 | 2.12 59 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/js-0.6.3/ 60 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/js-0.6.3/lib/ 61 | matcher 62 | 2.12 63 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.10/ 64 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.10/lib/ 65 | meta 66 | 2.12 67 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/meta-1.3.0/ 68 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/meta-1.3.0/lib/ 69 | path 70 | 2.12 71 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/path-1.8.0/ 72 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/path-1.8.0/lib/ 73 | plugin_platform_interface 74 | 2.12 75 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/plugin_platform_interface-2.0.0/ 76 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/plugin_platform_interface-2.0.0/lib/ 77 | source_span 78 | 2.12 79 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.0/ 80 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.0/lib/ 81 | stack_trace 82 | 2.12 83 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/ 84 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib/ 85 | stream_channel 86 | 2.12 87 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/ 88 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/lib/ 89 | string_scanner 90 | 2.12 91 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0/ 92 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0/lib/ 93 | term_glyph 94 | 2.12 95 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0/ 96 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0/lib/ 97 | test_api 98 | 2.12 99 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/test_api-0.2.19/ 100 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/test_api-0.2.19/lib/ 101 | typed_data 102 | 2.12 103 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.0/ 104 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.0/lib/ 105 | vector_math 106 | 2.12 107 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.0/ 108 | file:///Users/work/flutter/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.0/lib/ 109 | sky_engine 110 | 2.12 111 | file:///Users/work/flutter/bin/cache/pkg/sky_engine/ 112 | file:///Users/work/flutter/bin/cache/pkg/sky_engine/lib/ 113 | flutter 114 | 2.12 115 | file:///Users/work/flutter/packages/flutter/ 116 | file:///Users/work/flutter/packages/flutter/lib/ 117 | flutter_test 118 | 2.12 119 | file:///Users/work/flutter/packages/flutter_test/ 120 | file:///Users/work/flutter/packages/flutter_test/lib/ 121 | flutter_web_plugins 122 | 2.12 123 | file:///Users/work/flutter/packages/flutter_web_plugins/ 124 | file:///Users/work/flutter/packages/flutter_web_plugins/lib/ 125 | 2 126 | -------------------------------------------------------------------------------- /.dart_tool/version: -------------------------------------------------------------------------------- 1 | 2.0.2 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .packages 2 | .fvm 3 | .idea 4 | 5 | # Flutter/Dart/Pub related 6 | **/doc/api/ 7 | */.dart_tool/ 8 | */.flutter-plugins 9 | */.flutter-plugins-dependencies 10 | */.packages 11 | */.pub-cache/ 12 | */.pub/ 13 | */build/ 14 | */coverage/ 15 | 16 | # Android related 17 | **/android/**/gradle-wrapper.jar 18 | **/android/.gradle 19 | **/android/captures/ 20 | **/android/gradlew 21 | **/android/gradlew.bat 22 | **/android/local.properties 23 | **/android/**/GeneratedPluginRegistrant.java 24 | **/android/**/gen/ 25 | 26 | # iOS/XCode related 27 | **/ios/**/*.mode1v3 28 | **/ios/**/*.mode2v3 29 | **/ios/**/*.moved-aside 30 | **/ios/**/*.pbxuser 31 | **/ios/**/*.perspectivev3 32 | **/ios/**/*sync/ 33 | **/ios/**/.sconsign.dblite 34 | **/ios/**/.tags* 35 | **/ios/**/.vagrant/ 36 | **/ios/**/DerivedData/ 37 | **/ios/**/Icon? 38 | **/ios/**/Pods/ 39 | **/ios/**/.symlinks/ 40 | **/ios/**/profile 41 | **/ios/**/xcuserdata 42 | **/ios/.generated/ 43 | **/ios/Flutter/App.framework 44 | **/ios/Flutter/Flutter.framework 45 | **/ios/Flutter/Flutter.podspec 46 | **/ios/Flutter/Generated.xcconfig 47 | **/ios/Flutter/app.flx 48 | **/ios/Flutter/app.zip 49 | **/ios/Flutter/flutter_assets/ 50 | **/ios/Flutter/flutter_export_environment.sh 51 | **/ios/ServiceDefinitions.json 52 | **/ios/Runner/GeneratedPluginRegistrant.* 53 | 54 | # Exceptions to above rules. 55 | !**/ios/**/default.mode1v3 56 | !**/ios/**/default.mode2v3 57 | !**/ios/**/default.pbxuser 58 | !**/ios/**/default.perspectivev3 59 | 60 | */.fvm/flutter_sdk 61 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "example", 9 | "request": "launch", 10 | "type": "dart", 11 | "program": "example/lib/main.dart" 12 | }, 13 | { 14 | "name": "example profile", 15 | "request": "launch", 16 | "type": "dart", 17 | "flutterMode": "profile", 18 | "program": "example/lib/main.dart" 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2023 M-Way Solutions GmbH 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # apns 2 | 3 | ⛔️ DEPRECATED ⛔️ 4 | 5 | This package is not longer maintained. 6 | Please write message or GitHub issue if you want to take it over and end deprecated state. 7 | 8 | Plugin to implement APNS push notifications on iOS and Firebase on Android. 9 | 10 | ## Why this plugin was made? 11 | 12 | Currently, the only available push notification plugin is `firebase_messaging`. This means that, even on iOS, you will need to setup firebase and communicate with Google to send push notification. This plugin solves the problem by providing native APNS implementation while leaving configured Firebase for Android. 13 | 14 | ## Usage 15 | 1. Configure firebase on Android according to instructions: https://pub.dartlang.org/packages/firebase_messaging. 16 | 2. On iOS, make sure you have correctly configured your app to support push notifications, and that you have generated certificate/token for sending pushes. For more infos see section [How to run example app on iOS](#how-to-run-example-app-on-ios) 17 | 18 | 3. Add the following lines to the `didFinishLaunchingWithOptions` method in the AppDelegate.m/AppDelegate.swift file of your iOS project 19 | 20 | Objective-C: 21 | ```objc 22 | if (@available(iOS 10.0, *)) { 23 | [UNUserNotificationCenter currentNotificationCenter].delegate = (id) self; 24 | } 25 | ``` 26 | 27 | Swift: 28 | ```swift 29 | if #available(iOS 10.0, *) { 30 | UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate 31 | } 32 | ``` 33 | 34 | 4. Add `flutter_apns` as a [dependency in your pubspec.yaml file](https://flutter.io/platform-plugins/). 35 | 5. Using `createPushConnector()` method, configure push service according to your needs. `PushConnector` closely resembles `FirebaseMessaging`, so Firebase samples may be useful during implementation. You should create the connector as soon as possible to get the onLaunch callback working on closed app launch. 36 | ```dart 37 | import 'package:flutter_apns/apns.dart'; 38 | 39 | final connector = createPushConnector(); 40 | connector.configure( 41 | onLaunch: _onLaunch, 42 | onResume: _onResume, 43 | onMessage: _onMessage, 44 | ); 45 | connector.requestNotificationPermissions() 46 | ``` 47 | 6. Build on device and test your solution using Firebase Console (Android) and CURL (iOS, see [How to run example app on iOS](#how-to-run-example-app-on-ios)). 48 | 49 | ## Additional APNS features: 50 | ### Displaying notification while in foreground 51 | 52 | ```dart 53 | final connector = createPushConnector(); 54 | if (connector is ApnsPushConnector) { 55 | connector.shouldPresent = (x) => Future.value(true); 56 | } 57 | ``` 58 | 59 | ### Handling predefined actions 60 | 61 | Firstly, configure supported actions: 62 | ```dart 63 | final connector = createPushConnector(); 64 | if (connector is ApnsPushConnector) { 65 | connector.setNotificationCategories([ 66 | UNNotificationCategory( 67 | identifier: 'MEETING_INVITATION', 68 | actions: [ 69 | UNNotificationAction( 70 | identifier: 'ACCEPT_ACTION', 71 | title: 'Accept', 72 | options: [], 73 | ), 74 | UNNotificationAction( 75 | identifier: 'DECLINE_ACTION', 76 | title: 'Decline', 77 | options: [], 78 | ), 79 | ], 80 | intentIdentifiers: [], 81 | options: [], 82 | ), 83 | ]); 84 | } 85 | ``` 86 | 87 | Then, handle possible actions in your push handler: 88 | ```dart 89 | Future onPush(String name, RemoteMessage payload) { 90 | final action = UNNotificationAction.getIdentifier(payload.data); 91 | 92 | if (action == 'MEETING_INVITATION') { 93 | // do something 94 | } 95 | 96 | return Future.value(true); 97 | } 98 | ``` 99 | 100 | Note: if user clickes your notification while app is in the background, push will be delivered through onResume without actually waking up the app. Make sure your handling of given action is quick and error free, as execution time in for apps running in the background is very limited. 101 | 102 | Check the example project for fully working code. 103 | 104 | ## Enabling FirebaseCore 105 | If you want to use firebase, but not firebase messaging, add this configuration entry in your Info.plist (to avoid MissingPluginException): 106 | 107 | ``` 108 | flutter_apns.disable_firebase_core 109 | 110 | ``` 111 | 112 | ## flutter_apns_only - APNS without firebase 113 | If only care about apns - use flutter_apns_only plugin. It does not depend on firebase. To ensure no swizzling (which is needed by original plugin to disable firebase) takes place, add this configuration entry in your Info.plist: 114 | 115 | ```plist 116 | flutter_apns.disable_swizzling 117 | 118 | ``` 119 | 120 | ## Troubleshooting 121 | 122 | 1. Ensure that you are testing on actual device. NOTE: this may not be needed from 11.4: https://ohmyswift.com/blog/2020/02/13/simulating-remote-push-notifications-in-a-simulator/ 123 | 2. If onToken method is not being called, add error logging to your AppDelegate, see code below. 124 | 3. Open Console app for macOS, connect your device, and run your app. Search for "PUSH registration failed" string in logs. The error message will tell you what was wrong. 125 | 126 | *swift* 127 | ```swift 128 | import UIKit 129 | import Flutter 130 | 131 | @UIApplicationMain 132 | @objc class AppDelegate: FlutterAppDelegate { 133 | override func application( 134 | _ application: UIApplication, 135 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 136 | ) -> Bool { 137 | GeneratedPluginRegistrant.register(with: self) 138 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 139 | } 140 | 141 | func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { 142 | NSLog("PUSH registration failed: \(error)") 143 | } 144 | } 145 | 146 | ``` 147 | 148 | *objc* 149 | ```objc 150 | #include "AppDelegate.h" 151 | #include "GeneratedPluginRegistrant.h" 152 | 153 | @implementation AppDelegate 154 | 155 | - (BOOL)application:(UIApplication *)application 156 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 157 | [GeneratedPluginRegistrant registerWithRegistry:self]; 158 | // Override point for customization after application launch. 159 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 160 | } 161 | 162 | -(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { 163 | NSLog(@"%@", error); 164 | } 165 | 166 | @end 167 | ``` 168 | ## How to run example app on iOS 169 | Setting up push notifications on iOS can be tricky since there is no way to permit Apple Push Notification Service (APNS) which requires a complicated certificate setup. The following guide describes a step by step approach to send push notifications from your Mac to an iPhone utilizing the example app of this package. This guide only describes debug environment setup. 170 | 171 | 1. Open example ios folder with Xcode 172 | 2. Select Runner -> Signing & Capabilities 173 | 3. Select your development team and add a globally unique bundle identifier. The one on the picture is already occupied: 174 | ![](example/assets/Xcode_setup.png?raw=true) 175 | 4. Go to https://developer.apple.com/account/resources/identifiers/list/bundleId and press on the plus button 176 | 5. Select "App IDs" and press continue 177 | ![](example/assets/register_app.png?raw=true) 178 | 6. Select type "App" 179 | 7. Select "App ID Prefix" which should be same as "Team ID" 180 | 8. Enter description and bundle ID. The latter one needs to be the same as the bundle ID specified in 3. 181 | 9. Select push notification capability 182 | ![](example/assets/select_capability.png?raw=true) 183 | 11. Press on "Continue" and then on "Register" 184 | 12. Go to https://developer.apple.com/account/resources/certificates and add a new certificate by pressing on the plus-button. 185 | 13. Select 'Apple Push Notification service SSL (Sandbox & Production)' 186 | ![](example/assets/push_notification_setup.png?raw=true) 187 | 14. Select the app ID that you hav defined in point 4.-10. 188 | 15. Select a Certificate Signing Request (CSR) file. See https://help.apple.com/developer-account/#/devbfa00fef7 on how to create this certificate 189 | 16. When having finished, download the newly created Apple Push Services certificate 190 | 17. Add certificate to your local keychain by opening the newly downloaded file 191 | 18. Press on "login" on the upper left corner of your keychain window and select the tab "My Certificates" 192 | ![](example/assets/keychain.png?raw=true) 193 | 19. Right click on the Apple-Push-Services-certificate and export it as .p12-file 194 | 20. Convert p12-file to pem-file by following command. Please consider that "flutterApns" needs to be replaced by your respective certificate name.
195 | [More info](https://stackoverflow.com/questions/1762555/creating-pem-file-for-apns) 196 | ``` 197 | openssl pkcs12 -in flutterApns.p12 -out flutterApns.pem -nodes -clcerts 198 | ``` 199 | 21. Start example app on physical iPhone device from Xcode or your favorite IDE. 200 | 22. Device token gets automatically printed when application was able to retrieve push token from APNS. This happens after accepting notification permission prompt. 201 | 23. Send the following CURL from you development Mac. You can execute CURLs by copy-pasting them into Terminal and hit enter.
202 | [More info](https://gist.github.com/greencoder/16d1f8d7b0fed5b49cf64312ce2b72cc) 203 | ```curl 204 | curl -v \ 205 | -d '{"aps":{"alert":"","badge":2}}' \ 206 | -H "apns-topic: " \ 207 | -H "apns-priority: 10" \ 208 | --http2 \ 209 | --cert .pem \ 210 | https://api.development.push.apple.com/3/device/ 211 | ``` 212 | 24. A push notification does appear if the example app is in background. 213 | 214 | When not utilizing the example app, you need to additionally [setup push notification capability inside Xcode](https://developer.apple.com/documentation/usernotifications/registering_your_app_with_apns) and add the code mentioned in [usage](#usage). 215 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # Visual Studio Code related 19 | .vscode/ 20 | 21 | # Flutter/Dart/Pub related 22 | **/doc/api/ 23 | .dart_tool/ 24 | .flutter-plugins 25 | .packages 26 | .pub-cache/ 27 | .pub/ 28 | /build/ 29 | 30 | # Android related 31 | **/android/**/gradle-wrapper.jar 32 | **/android/.gradle 33 | **/android/captures/ 34 | **/android/gradlew 35 | **/android/gradlew.bat 36 | **/android/local.properties 37 | **/android/**/GeneratedPluginRegistrant.java 38 | 39 | # iOS/XCode related 40 | **/ios/**/*.mode1v3 41 | **/ios/**/*.mode2v3 42 | **/ios/**/*.moved-aside 43 | **/ios/**/*.pbxuser 44 | **/ios/**/*.perspectivev3 45 | **/ios/**/*sync/ 46 | **/ios/**/.sconsign.dblite 47 | **/ios/**/.tags* 48 | **/ios/**/.vagrant/ 49 | **/ios/**/DerivedData/ 50 | **/ios/**/Icon? 51 | **/ios/**/Pods/ 52 | **/ios/**/.symlinks/ 53 | **/ios/**/profile 54 | **/ios/**/xcuserdata 55 | **/ios/.generated/ 56 | **/ios/Flutter/App.framework 57 | **/ios/Flutter/Flutter.framework 58 | **/ios/Flutter/Generated.xcconfig 59 | **/ios/Flutter/app.flx 60 | **/ios/Flutter/app.zip 61 | **/ios/Flutter/flutter_assets/ 62 | **/ios/ServiceDefinitions.json 63 | **/ios/Runner/GeneratedPluginRegistrant.* 64 | 65 | # Exceptions to above rules. 66 | !**/ios/**/default.mode1v3 67 | !**/ios/**/default.mode2v3 68 | !**/ios/**/default.pbxuser 69 | !**/ios/**/default.perspectivev3 70 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 71 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 204eceea93694aa081e0132c8281b76d3b3d6b4a 8 | channel: dev 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # flutter_apns_example 2 | 3 | Demonstrates how to use the flutter_apns plugin. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.io/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.io/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.io/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | apply plugin: 'com.google.gms.google-services' 28 | 29 | android { 30 | compileSdkVersion 31 31 | 32 | sourceSets { 33 | main.java.srcDirs += 'src/main/kotlin' 34 | } 35 | 36 | lintOptions { 37 | disable 'InvalidPackage' 38 | } 39 | 40 | defaultConfig { 41 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 42 | applicationId "com.mwaysolutions.flutter.apns_example" 43 | minSdkVersion 19 44 | targetSdkVersion 29 45 | versionCode flutterVersionCode.toInteger() 46 | versionName flutterVersionName 47 | } 48 | 49 | buildTypes { 50 | release { 51 | // TODO: Add your own signing config for the release build. 52 | // Signing with the debug keys for now, so `flutter run --release` works. 53 | signingConfig signingConfigs.debug 54 | } 55 | } 56 | } 57 | 58 | flutter { 59 | source '../..' 60 | } 61 | 62 | dependencies { 63 | implementation platform('com.google.firebase:firebase-bom:29.0.3') 64 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 65 | implementation 'com.google.firebase:firebase-messaging' 66 | } 67 | 68 | 69 | -------------------------------------------------------------------------------- /example/android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "510862848685", 4 | "project_id": "flutter-apns-example-56d2e", 5 | "storage_bucket": "flutter-apns-example-56d2e.appspot.com" 6 | }, 7 | "client": [ 8 | { 9 | "client_info": { 10 | "mobilesdk_app_id": "1:510862848685:android:bdb37bc4f479a30bae4c24", 11 | "android_client_info": { 12 | "package_name": "com.mwaysolutions.flutter.apns_example" 13 | } 14 | }, 15 | "oauth_client": [ 16 | { 17 | "client_id": "510862848685-0u0g93oua205k5hqj4u2ouk5s8vlk2h0.apps.googleusercontent.com", 18 | "client_type": 3 19 | } 20 | ], 21 | "api_key": [ 22 | { 23 | "current_key": "AIzaSyD5AHt74hylUX3w5Q6fo7aANFtbpw1j4z8" 24 | } 25 | ], 26 | "services": { 27 | "appinvite_service": { 28 | "other_platform_oauth_client": [ 29 | { 30 | "client_id": "510862848685-0u0g93oua205k5hqj4u2ouk5s8vlk2h0.apps.googleusercontent.com", 31 | "client_type": 3 32 | }, 33 | { 34 | "client_id": "510862848685-spi3uj38b7fkj0c9gm6kev67mlcjb312.apps.googleusercontent.com", 35 | "client_type": 2, 36 | "ios_info": { 37 | "bundle_id": "com.mwaysolutions.flutterApns" 38 | } 39 | } 40 | ] 41 | } 42 | } 43 | } 44 | ], 45 | "configuration_version": "1" 46 | } -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 11 | 18 | 22 | 26 | 31 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 44 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/mwaysolutions/flutter/apns_example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.mwaysolutions.flutter.apns_example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.7.10' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.1.0-rc01' 10 | classpath 'com.google.gms:google-services:4.3.10' 11 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | google() 18 | jcenter() 19 | } 20 | } 21 | 22 | rootProject.buildDir = '../build' 23 | subprojects { 24 | project.buildDir = "${rootProject.buildDir}/${project.name}" 25 | } 26 | subprojects { 27 | project.evaluationDependsOn(':app') 28 | } 29 | 30 | task clean(type: Delete) { 31 | delete rootProject.buildDir 32 | } 33 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | android.enableR8=true 5 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-all.zip 7 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /example/assets/Xcode_setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/assets/Xcode_setup.png -------------------------------------------------------------------------------- /example/assets/keychain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/assets/keychain.png -------------------------------------------------------------------------------- /example/assets/push_notification_setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/assets/push_notification_setup.png -------------------------------------------------------------------------------- /example/assets/register_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/assets/register_app.png -------------------------------------------------------------------------------- /example/assets/select_capability.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/assets/select_capability.png -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '10.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Firebase/CoreOnly (9.3.0): 3 | - FirebaseCore (= 9.3.0) 4 | - Firebase/Messaging (9.3.0): 5 | - Firebase/CoreOnly 6 | - FirebaseMessaging (~> 9.3.0) 7 | - firebase_core (1.20.0): 8 | - Firebase/CoreOnly (= 9.3.0) 9 | - Flutter 10 | - firebase_messaging (12.0.1): 11 | - Firebase/Messaging (= 9.3.0) 12 | - firebase_core 13 | - Flutter 14 | - FirebaseCore (9.3.0): 15 | - FirebaseCoreDiagnostics (~> 9.0) 16 | - FirebaseCoreInternal (~> 9.0) 17 | - GoogleUtilities/Environment (~> 7.7) 18 | - GoogleUtilities/Logger (~> 7.7) 19 | - FirebaseCoreDiagnostics (9.4.0): 20 | - GoogleDataTransport (< 10.0.0, >= 9.1.4) 21 | - GoogleUtilities/Environment (~> 7.7) 22 | - GoogleUtilities/Logger (~> 7.7) 23 | - nanopb (< 2.30910.0, >= 2.30908.0) 24 | - FirebaseCoreInternal (9.4.0): 25 | - "GoogleUtilities/NSData+zlib (~> 7.7)" 26 | - FirebaseInstallations (9.4.0): 27 | - FirebaseCore (~> 9.0) 28 | - GoogleUtilities/Environment (~> 7.7) 29 | - GoogleUtilities/UserDefaults (~> 7.7) 30 | - PromisesObjC (~> 2.1) 31 | - FirebaseMessaging (9.3.0): 32 | - FirebaseCore (~> 9.0) 33 | - FirebaseInstallations (~> 9.0) 34 | - GoogleDataTransport (< 10.0.0, >= 9.1.4) 35 | - GoogleUtilities/AppDelegateSwizzler (~> 7.7) 36 | - GoogleUtilities/Environment (~> 7.7) 37 | - GoogleUtilities/Reachability (~> 7.7) 38 | - GoogleUtilities/UserDefaults (~> 7.7) 39 | - nanopb (< 2.30910.0, >= 2.30908.0) 40 | - Flutter (1.0.0) 41 | - flutter_apns_only (0.0.1): 42 | - Flutter 43 | - flutter_local_notifications (0.0.1): 44 | - Flutter 45 | - GoogleDataTransport (9.2.0): 46 | - GoogleUtilities/Environment (~> 7.7) 47 | - nanopb (< 2.30910.0, >= 2.30908.0) 48 | - PromisesObjC (< 3.0, >= 1.2) 49 | - GoogleUtilities/AppDelegateSwizzler (7.7.0): 50 | - GoogleUtilities/Environment 51 | - GoogleUtilities/Logger 52 | - GoogleUtilities/Network 53 | - GoogleUtilities/Environment (7.7.0): 54 | - PromisesObjC (< 3.0, >= 1.2) 55 | - GoogleUtilities/Logger (7.7.0): 56 | - GoogleUtilities/Environment 57 | - GoogleUtilities/Network (7.7.0): 58 | - GoogleUtilities/Logger 59 | - "GoogleUtilities/NSData+zlib" 60 | - GoogleUtilities/Reachability 61 | - "GoogleUtilities/NSData+zlib (7.7.0)" 62 | - GoogleUtilities/Reachability (7.7.0): 63 | - GoogleUtilities/Logger 64 | - GoogleUtilities/UserDefaults (7.7.0): 65 | - GoogleUtilities/Logger 66 | - nanopb (2.30909.0): 67 | - nanopb/decode (= 2.30909.0) 68 | - nanopb/encode (= 2.30909.0) 69 | - nanopb/decode (2.30909.0) 70 | - nanopb/encode (2.30909.0) 71 | - path_provider_ios (0.0.1): 72 | - Flutter 73 | - PromisesObjC (2.1.1) 74 | 75 | DEPENDENCIES: 76 | - firebase_core (from `.symlinks/plugins/firebase_core/ios`) 77 | - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) 78 | - Flutter (from `Flutter`) 79 | - flutter_apns_only (from `.symlinks/plugins/flutter_apns_only/ios`) 80 | - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) 81 | - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) 82 | 83 | SPEC REPOS: 84 | trunk: 85 | - Firebase 86 | - FirebaseCore 87 | - FirebaseCoreDiagnostics 88 | - FirebaseCoreInternal 89 | - FirebaseInstallations 90 | - FirebaseMessaging 91 | - GoogleDataTransport 92 | - GoogleUtilities 93 | - nanopb 94 | - PromisesObjC 95 | 96 | EXTERNAL SOURCES: 97 | firebase_core: 98 | :path: ".symlinks/plugins/firebase_core/ios" 99 | firebase_messaging: 100 | :path: ".symlinks/plugins/firebase_messaging/ios" 101 | Flutter: 102 | :path: Flutter 103 | flutter_apns_only: 104 | :path: ".symlinks/plugins/flutter_apns_only/ios" 105 | flutter_local_notifications: 106 | :path: ".symlinks/plugins/flutter_local_notifications/ios" 107 | path_provider_ios: 108 | :path: ".symlinks/plugins/path_provider_ios/ios" 109 | 110 | SPEC CHECKSUMS: 111 | Firebase: ef75abb1cdbc746d5a38f4e26c422c807b189b8c 112 | firebase_core: 96214f90497b808a2cf2a24517084c5f6de37b53 113 | firebase_messaging: a2cd2c6dd7b5430bb9e4e50e9a474413ae3bce09 114 | FirebaseCore: c088995ece701a021a48a1348ea0174877de2a6a 115 | FirebaseCoreDiagnostics: aaa87098082c4d4bdd1a9557b1186d18ca85ce8c 116 | FirebaseCoreInternal: a13302b0088fbf5f38b79b6ece49c2af7d3e05d6 117 | FirebaseInstallations: 61db1054e688d2bdc4e2b3f744c1b086e913b742 118 | FirebaseMessaging: 2f6e38b6133059eb796ec224104f09379298a8c3 119 | Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a 120 | flutter_apns_only: 3d91c0ca9dbef4439874858590909a19f8ed06a4 121 | flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743 122 | GoogleDataTransport: 1c8145da7117bd68bbbed00cf304edb6a24de00f 123 | GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1 124 | nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431 125 | path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 126 | PromisesObjC: ab77feca74fa2823e7af4249b8326368e61014cb 127 | 128 | PODFILE CHECKSUM: fe0e1ee7f3d1f7d00b11b474b62dd62134535aea 129 | 130 | COCOAPODS: 1.11.3 131 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 51; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 16 | CC1D83AC835782508F710CD2 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3BCD3DB631646ECC081B9E8 /* Pods_Runner.framework */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXCopyFilesBuildPhase section */ 20 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 21 | isa = PBXCopyFilesBuildPhase; 22 | buildActionMask = 2147483647; 23 | dstPath = ""; 24 | dstSubfolderSpec = 10; 25 | files = ( 26 | ); 27 | name = "Embed Frameworks"; 28 | runOnlyForDeploymentPostprocessing = 0; 29 | }; 30 | /* End PBXCopyFilesBuildPhase section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 34 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 35 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 36 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 37 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 38 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 39 | 849488397CB6738A37C81C0E /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 40 | 962780442535DBFD002E578E /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; 41 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 42 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 43 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 45 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 46 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 47 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 48 | A20DE472B009BF65164F5A14 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 49 | B05EE8A8DCA9D01EBACAD207 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 50 | C3BCD3DB631646ECC081B9E8 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | /* End PBXFileReference section */ 52 | 53 | /* Begin PBXFrameworksBuildPhase section */ 54 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | CC1D83AC835782508F710CD2 /* Pods_Runner.framework in Frameworks */, 59 | ); 60 | runOnlyForDeploymentPostprocessing = 0; 61 | }; 62 | /* End PBXFrameworksBuildPhase section */ 63 | 64 | /* Begin PBXGroup section */ 65 | 0B3D5C43758AE6016C407DE0 /* Frameworks */ = { 66 | isa = PBXGroup; 67 | children = ( 68 | C3BCD3DB631646ECC081B9E8 /* Pods_Runner.framework */, 69 | ); 70 | name = Frameworks; 71 | sourceTree = ""; 72 | }; 73 | 9740EEB11CF90186004384FC /* Flutter */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 77 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 78 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 79 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 80 | ); 81 | name = Flutter; 82 | sourceTree = ""; 83 | }; 84 | 97C146E51CF9000F007C117D = { 85 | isa = PBXGroup; 86 | children = ( 87 | 9740EEB11CF90186004384FC /* Flutter */, 88 | 97C146F01CF9000F007C117D /* Runner */, 89 | 97C146EF1CF9000F007C117D /* Products */, 90 | EDAC0C689CD7CA0F764BF8F3 /* Pods */, 91 | 0B3D5C43758AE6016C407DE0 /* Frameworks */, 92 | ); 93 | sourceTree = ""; 94 | }; 95 | 97C146EF1CF9000F007C117D /* Products */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 97C146EE1CF9000F007C117D /* Runner.app */, 99 | ); 100 | name = Products; 101 | sourceTree = ""; 102 | }; 103 | 97C146F01CF9000F007C117D /* Runner */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 962780442535DBFD002E578E /* Runner.entitlements */, 107 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 108 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 109 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 110 | 97C147021CF9000F007C117D /* Info.plist */, 111 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 112 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 113 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 114 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 115 | ); 116 | path = Runner; 117 | sourceTree = ""; 118 | }; 119 | EDAC0C689CD7CA0F764BF8F3 /* Pods */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | A20DE472B009BF65164F5A14 /* Pods-Runner.debug.xcconfig */, 123 | B05EE8A8DCA9D01EBACAD207 /* Pods-Runner.release.xcconfig */, 124 | 849488397CB6738A37C81C0E /* Pods-Runner.profile.xcconfig */, 125 | ); 126 | path = Pods; 127 | sourceTree = ""; 128 | }; 129 | /* End PBXGroup section */ 130 | 131 | /* Begin PBXNativeTarget section */ 132 | 97C146ED1CF9000F007C117D /* Runner */ = { 133 | isa = PBXNativeTarget; 134 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 135 | buildPhases = ( 136 | 720BB1016EDD9FF7C923CD32 /* [CP] Check Pods Manifest.lock */, 137 | 9740EEB61CF901F6004384FC /* Run Script */, 138 | 97C146EA1CF9000F007C117D /* Sources */, 139 | 97C146EB1CF9000F007C117D /* Frameworks */, 140 | 97C146EC1CF9000F007C117D /* Resources */, 141 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 142 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 143 | 5FC337143C6C208FF39C4D21 /* [CP] Embed Pods Frameworks */, 144 | ); 145 | buildRules = ( 146 | ); 147 | dependencies = ( 148 | ); 149 | name = Runner; 150 | productName = Runner; 151 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 152 | productType = "com.apple.product-type.application"; 153 | }; 154 | /* End PBXNativeTarget section */ 155 | 156 | /* Begin PBXProject section */ 157 | 97C146E61CF9000F007C117D /* Project object */ = { 158 | isa = PBXProject; 159 | attributes = { 160 | LastUpgradeCheck = 1300; 161 | ORGANIZATIONNAME = ""; 162 | TargetAttributes = { 163 | 97C146ED1CF9000F007C117D = { 164 | CreatedOnToolsVersion = 7.3.1; 165 | LastSwiftMigration = 1100; 166 | }; 167 | }; 168 | }; 169 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 170 | compatibilityVersion = "Xcode 9.3"; 171 | developmentRegion = en; 172 | hasScannedForEncodings = 0; 173 | knownRegions = ( 174 | en, 175 | Base, 176 | ); 177 | mainGroup = 97C146E51CF9000F007C117D; 178 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 179 | projectDirPath = ""; 180 | projectRoot = ""; 181 | targets = ( 182 | 97C146ED1CF9000F007C117D /* Runner */, 183 | ); 184 | }; 185 | /* End PBXProject section */ 186 | 187 | /* Begin PBXResourcesBuildPhase section */ 188 | 97C146EC1CF9000F007C117D /* Resources */ = { 189 | isa = PBXResourcesBuildPhase; 190 | buildActionMask = 2147483647; 191 | files = ( 192 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 193 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 194 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 195 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 196 | ); 197 | runOnlyForDeploymentPostprocessing = 0; 198 | }; 199 | /* End PBXResourcesBuildPhase section */ 200 | 201 | /* Begin PBXShellScriptBuildPhase section */ 202 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 203 | isa = PBXShellScriptBuildPhase; 204 | buildActionMask = 2147483647; 205 | files = ( 206 | ); 207 | inputPaths = ( 208 | ); 209 | name = "Thin Binary"; 210 | outputPaths = ( 211 | ); 212 | runOnlyForDeploymentPostprocessing = 0; 213 | shellPath = /bin/sh; 214 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 215 | }; 216 | 5FC337143C6C208FF39C4D21 /* [CP] Embed Pods Frameworks */ = { 217 | isa = PBXShellScriptBuildPhase; 218 | buildActionMask = 2147483647; 219 | files = ( 220 | ); 221 | inputFileListPaths = ( 222 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", 223 | ); 224 | name = "[CP] Embed Pods Frameworks"; 225 | outputFileListPaths = ( 226 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", 227 | ); 228 | runOnlyForDeploymentPostprocessing = 0; 229 | shellPath = /bin/sh; 230 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 231 | showEnvVarsInLog = 0; 232 | }; 233 | 720BB1016EDD9FF7C923CD32 /* [CP] Check Pods Manifest.lock */ = { 234 | isa = PBXShellScriptBuildPhase; 235 | buildActionMask = 2147483647; 236 | files = ( 237 | ); 238 | inputFileListPaths = ( 239 | ); 240 | inputPaths = ( 241 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 242 | "${PODS_ROOT}/Manifest.lock", 243 | ); 244 | name = "[CP] Check Pods Manifest.lock"; 245 | outputFileListPaths = ( 246 | ); 247 | outputPaths = ( 248 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 249 | ); 250 | runOnlyForDeploymentPostprocessing = 0; 251 | shellPath = /bin/sh; 252 | 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"; 253 | showEnvVarsInLog = 0; 254 | }; 255 | 9740EEB61CF901F6004384FC /* Run Script */ = { 256 | isa = PBXShellScriptBuildPhase; 257 | buildActionMask = 2147483647; 258 | files = ( 259 | ); 260 | inputPaths = ( 261 | ); 262 | name = "Run Script"; 263 | outputPaths = ( 264 | ); 265 | runOnlyForDeploymentPostprocessing = 0; 266 | shellPath = /bin/sh; 267 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; 268 | }; 269 | /* End PBXShellScriptBuildPhase section */ 270 | 271 | /* Begin PBXSourcesBuildPhase section */ 272 | 97C146EA1CF9000F007C117D /* Sources */ = { 273 | isa = PBXSourcesBuildPhase; 274 | buildActionMask = 2147483647; 275 | files = ( 276 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 277 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 278 | ); 279 | runOnlyForDeploymentPostprocessing = 0; 280 | }; 281 | /* End PBXSourcesBuildPhase section */ 282 | 283 | /* Begin PBXVariantGroup section */ 284 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 285 | isa = PBXVariantGroup; 286 | children = ( 287 | 97C146FB1CF9000F007C117D /* Base */, 288 | ); 289 | name = Main.storyboard; 290 | sourceTree = ""; 291 | }; 292 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 293 | isa = PBXVariantGroup; 294 | children = ( 295 | 97C147001CF9000F007C117D /* Base */, 296 | ); 297 | name = LaunchScreen.storyboard; 298 | sourceTree = ""; 299 | }; 300 | /* End PBXVariantGroup section */ 301 | 302 | /* Begin XCBuildConfiguration section */ 303 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 304 | isa = XCBuildConfiguration; 305 | buildSettings = { 306 | ALWAYS_SEARCH_USER_PATHS = NO; 307 | CLANG_ANALYZER_NONNULL = YES; 308 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 309 | CLANG_CXX_LIBRARY = "libc++"; 310 | CLANG_ENABLE_MODULES = YES; 311 | CLANG_ENABLE_OBJC_ARC = YES; 312 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 313 | CLANG_WARN_BOOL_CONVERSION = YES; 314 | CLANG_WARN_COMMA = YES; 315 | CLANG_WARN_CONSTANT_CONVERSION = YES; 316 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 317 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 318 | CLANG_WARN_EMPTY_BODY = YES; 319 | CLANG_WARN_ENUM_CONVERSION = YES; 320 | CLANG_WARN_INFINITE_RECURSION = YES; 321 | CLANG_WARN_INT_CONVERSION = YES; 322 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 323 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 324 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 325 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 326 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 327 | CLANG_WARN_STRICT_PROTOTYPES = YES; 328 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 329 | CLANG_WARN_UNREACHABLE_CODE = YES; 330 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 331 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 332 | COPY_PHASE_STRIP = NO; 333 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 334 | ENABLE_NS_ASSERTIONS = NO; 335 | ENABLE_STRICT_OBJC_MSGSEND = YES; 336 | GCC_C_LANGUAGE_STANDARD = gnu99; 337 | GCC_NO_COMMON_BLOCKS = YES; 338 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 339 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 340 | GCC_WARN_UNDECLARED_SELECTOR = YES; 341 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 342 | GCC_WARN_UNUSED_FUNCTION = YES; 343 | GCC_WARN_UNUSED_VARIABLE = YES; 344 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 345 | MTL_ENABLE_DEBUG_INFO = NO; 346 | SDKROOT = iphoneos; 347 | SUPPORTED_PLATFORMS = iphoneos; 348 | TARGETED_DEVICE_FAMILY = "1,2"; 349 | VALIDATE_PRODUCT = YES; 350 | }; 351 | name = Profile; 352 | }; 353 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 354 | isa = XCBuildConfiguration; 355 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 356 | buildSettings = { 357 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 358 | CLANG_ENABLE_MODULES = YES; 359 | CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; 360 | CODE_SIGN_IDENTITY = "Apple Development"; 361 | CODE_SIGN_STYLE = Automatic; 362 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 363 | DEVELOPMENT_TEAM = ""; 364 | ENABLE_BITCODE = NO; 365 | FRAMEWORK_SEARCH_PATHS = ( 366 | "$(inherited)", 367 | "$(PROJECT_DIR)/Flutter", 368 | ); 369 | INFOPLIST_FILE = Runner/Info.plist; 370 | LD_RUNPATH_SEARCH_PATHS = ( 371 | "$(inherited)", 372 | "@executable_path/Frameworks", 373 | ); 374 | LIBRARY_SEARCH_PATHS = ( 375 | "$(inherited)", 376 | "$(PROJECT_DIR)/Flutter", 377 | ); 378 | PRODUCT_NAME = "$(TARGET_NAME)"; 379 | PROVISIONING_PROFILE_SPECIFIER = ""; 380 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 381 | SWIFT_VERSION = 5.0; 382 | VERSIONING_SYSTEM = "apple-generic"; 383 | }; 384 | name = Profile; 385 | }; 386 | 97C147031CF9000F007C117D /* Debug */ = { 387 | isa = XCBuildConfiguration; 388 | buildSettings = { 389 | ALWAYS_SEARCH_USER_PATHS = NO; 390 | CLANG_ANALYZER_NONNULL = YES; 391 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 392 | CLANG_CXX_LIBRARY = "libc++"; 393 | CLANG_ENABLE_MODULES = YES; 394 | CLANG_ENABLE_OBJC_ARC = YES; 395 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 396 | CLANG_WARN_BOOL_CONVERSION = YES; 397 | CLANG_WARN_COMMA = YES; 398 | CLANG_WARN_CONSTANT_CONVERSION = YES; 399 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 400 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 401 | CLANG_WARN_EMPTY_BODY = YES; 402 | CLANG_WARN_ENUM_CONVERSION = YES; 403 | CLANG_WARN_INFINITE_RECURSION = YES; 404 | CLANG_WARN_INT_CONVERSION = YES; 405 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 406 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 407 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 408 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 409 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 410 | CLANG_WARN_STRICT_PROTOTYPES = YES; 411 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 412 | CLANG_WARN_UNREACHABLE_CODE = YES; 413 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 414 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 415 | COPY_PHASE_STRIP = NO; 416 | DEBUG_INFORMATION_FORMAT = dwarf; 417 | ENABLE_STRICT_OBJC_MSGSEND = YES; 418 | ENABLE_TESTABILITY = YES; 419 | GCC_C_LANGUAGE_STANDARD = gnu99; 420 | GCC_DYNAMIC_NO_PIC = NO; 421 | GCC_NO_COMMON_BLOCKS = YES; 422 | GCC_OPTIMIZATION_LEVEL = 0; 423 | GCC_PREPROCESSOR_DEFINITIONS = ( 424 | "DEBUG=1", 425 | "$(inherited)", 426 | ); 427 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 428 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 429 | GCC_WARN_UNDECLARED_SELECTOR = YES; 430 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 431 | GCC_WARN_UNUSED_FUNCTION = YES; 432 | GCC_WARN_UNUSED_VARIABLE = YES; 433 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 434 | MTL_ENABLE_DEBUG_INFO = YES; 435 | ONLY_ACTIVE_ARCH = YES; 436 | SDKROOT = iphoneos; 437 | TARGETED_DEVICE_FAMILY = "1,2"; 438 | }; 439 | name = Debug; 440 | }; 441 | 97C147041CF9000F007C117D /* Release */ = { 442 | isa = XCBuildConfiguration; 443 | buildSettings = { 444 | ALWAYS_SEARCH_USER_PATHS = NO; 445 | CLANG_ANALYZER_NONNULL = YES; 446 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 447 | CLANG_CXX_LIBRARY = "libc++"; 448 | CLANG_ENABLE_MODULES = YES; 449 | CLANG_ENABLE_OBJC_ARC = YES; 450 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 451 | CLANG_WARN_BOOL_CONVERSION = YES; 452 | CLANG_WARN_COMMA = YES; 453 | CLANG_WARN_CONSTANT_CONVERSION = YES; 454 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 455 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 456 | CLANG_WARN_EMPTY_BODY = YES; 457 | CLANG_WARN_ENUM_CONVERSION = YES; 458 | CLANG_WARN_INFINITE_RECURSION = YES; 459 | CLANG_WARN_INT_CONVERSION = YES; 460 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 461 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 462 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 463 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 464 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 465 | CLANG_WARN_STRICT_PROTOTYPES = YES; 466 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 467 | CLANG_WARN_UNREACHABLE_CODE = YES; 468 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 469 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 470 | COPY_PHASE_STRIP = NO; 471 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 472 | ENABLE_NS_ASSERTIONS = NO; 473 | ENABLE_STRICT_OBJC_MSGSEND = YES; 474 | GCC_C_LANGUAGE_STANDARD = gnu99; 475 | GCC_NO_COMMON_BLOCKS = YES; 476 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 477 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 478 | GCC_WARN_UNDECLARED_SELECTOR = YES; 479 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 480 | GCC_WARN_UNUSED_FUNCTION = YES; 481 | GCC_WARN_UNUSED_VARIABLE = YES; 482 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 483 | MTL_ENABLE_DEBUG_INFO = NO; 484 | SDKROOT = iphoneos; 485 | SUPPORTED_PLATFORMS = iphoneos; 486 | SWIFT_COMPILATION_MODE = wholemodule; 487 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 488 | TARGETED_DEVICE_FAMILY = "1,2"; 489 | VALIDATE_PRODUCT = YES; 490 | }; 491 | name = Release; 492 | }; 493 | 97C147061CF9000F007C117D /* Debug */ = { 494 | isa = XCBuildConfiguration; 495 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 496 | buildSettings = { 497 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 498 | CLANG_ENABLE_MODULES = YES; 499 | CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; 500 | CODE_SIGN_IDENTITY = "Apple Development"; 501 | CODE_SIGN_STYLE = Automatic; 502 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 503 | DEVELOPMENT_TEAM = ""; 504 | ENABLE_BITCODE = NO; 505 | FRAMEWORK_SEARCH_PATHS = ( 506 | "$(inherited)", 507 | "$(PROJECT_DIR)/Flutter", 508 | ); 509 | INFOPLIST_FILE = Runner/Info.plist; 510 | LD_RUNPATH_SEARCH_PATHS = ( 511 | "$(inherited)", 512 | "@executable_path/Frameworks", 513 | ); 514 | LIBRARY_SEARCH_PATHS = ( 515 | "$(inherited)", 516 | "$(PROJECT_DIR)/Flutter", 517 | ); 518 | PRODUCT_NAME = "$(TARGET_NAME)"; 519 | PROVISIONING_PROFILE_SPECIFIER = ""; 520 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 521 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 522 | SWIFT_VERSION = 5.0; 523 | VERSIONING_SYSTEM = "apple-generic"; 524 | }; 525 | name = Debug; 526 | }; 527 | 97C147071CF9000F007C117D /* Release */ = { 528 | isa = XCBuildConfiguration; 529 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 530 | buildSettings = { 531 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 532 | CLANG_ENABLE_MODULES = YES; 533 | CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; 534 | CODE_SIGN_IDENTITY = "Apple Development"; 535 | CODE_SIGN_STYLE = Automatic; 536 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 537 | DEVELOPMENT_TEAM = ""; 538 | ENABLE_BITCODE = NO; 539 | FRAMEWORK_SEARCH_PATHS = ( 540 | "$(inherited)", 541 | "$(PROJECT_DIR)/Flutter", 542 | ); 543 | INFOPLIST_FILE = Runner/Info.plist; 544 | LD_RUNPATH_SEARCH_PATHS = ( 545 | "$(inherited)", 546 | "@executable_path/Frameworks", 547 | ); 548 | LIBRARY_SEARCH_PATHS = ( 549 | "$(inherited)", 550 | "$(PROJECT_DIR)/Flutter", 551 | ); 552 | PRODUCT_NAME = "$(TARGET_NAME)"; 553 | PROVISIONING_PROFILE_SPECIFIER = ""; 554 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 555 | SWIFT_VERSION = 5.0; 556 | VERSIONING_SYSTEM = "apple-generic"; 557 | }; 558 | name = Release; 559 | }; 560 | /* End XCBuildConfiguration section */ 561 | 562 | /* Begin XCConfigurationList section */ 563 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 564 | isa = XCConfigurationList; 565 | buildConfigurations = ( 566 | 97C147031CF9000F007C117D /* Debug */, 567 | 97C147041CF9000F007C117D /* Release */, 568 | 249021D3217E4FDB00AE95B9 /* Profile */, 569 | ); 570 | defaultConfigurationIsVisible = 0; 571 | defaultConfigurationName = Release; 572 | }; 573 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 574 | isa = XCConfigurationList; 575 | buildConfigurations = ( 576 | 97C147061CF9000F007C117D /* Debug */, 577 | 97C147071CF9000F007C117D /* Release */, 578 | 249021D4217E4FDB00AE95B9 /* Profile */, 579 | ); 580 | defaultConfigurationIsVisible = 0; 581 | defaultConfigurationName = Release; 582 | }; 583 | /* End XCConfigurationList section */ 584 | }; 585 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 586 | } 587 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | import UserNotifications 4 | 5 | @UIApplicationMain 6 | @objc class AppDelegate: FlutterAppDelegate { 7 | override func application( 8 | _ application: UIApplication, 9 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 10 | ) -> Bool { 11 | 12 | UNUserNotificationCenter.current().delegate = self 13 | 14 | GeneratedPluginRegistrant.register(with: self) 15 | 16 | 17 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 18 | } 19 | 20 | override func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { 21 | print(error) 22 | } 23 | } 24 | 25 | 26 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CADisableMinimumFrameDurationOnPhone 6 | 7 | CFBundleDevelopmentRegion 8 | $(DEVELOPMENT_LANGUAGE) 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | apns_example 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | flutter_apns.disable_swizzling 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | aps-environment 6 | development 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_apns/flutter_apns.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'storage.dart'; 5 | 6 | Future main() async { 7 | WidgetsFlutterBinding.ensureInitialized(); 8 | await storage.setup(); 9 | runApp(MyApp()); 10 | } 11 | 12 | class MyApp extends StatefulWidget { 13 | @override 14 | _MyAppState createState() => _MyAppState(); 15 | } 16 | 17 | class _MyAppState extends State { 18 | final PushConnector connector = createPushConnector(); 19 | final _messengerKey = GlobalKey(); 20 | final mySnackbar = SnackBar( 21 | content: Text("the token was added to the clipboard"), 22 | duration: Duration(seconds: 3), 23 | ); 24 | String _token = ""; 25 | 26 | Future _register() async { 27 | final connector = this.connector; 28 | connector.configure( 29 | onLaunch: (data) => onPush('onLaunch', data), 30 | onResume: (data) => onPush('onResume', data), 31 | onMessage: (data) => onPush('onMessage', data), 32 | onBackgroundMessage: _onBackgroundMessage, 33 | ); 34 | connector.token.addListener(() { 35 | print('Token ${connector.token.value}'); 36 | }); 37 | 38 | connector.requestNotificationPermissions(); 39 | 40 | if (connector is ApnsPushConnector) { 41 | connector.shouldPresent = (x) async { 42 | final remote = RemoteMessage.fromMap(x.payload); 43 | return remote.category == 'MEETING_INVITATION'; 44 | }; 45 | connector.setNotificationCategories([ 46 | UNNotificationCategory( 47 | identifier: 'MEETING_INVITATION', 48 | actions: [ 49 | UNNotificationAction( 50 | identifier: 'ACCEPT_ACTION', 51 | title: 'Accept', 52 | options: UNNotificationActionOptions.values, 53 | ), 54 | UNNotificationAction( 55 | identifier: 'DECLINE_ACTION', 56 | title: 'Decline', 57 | options: [], 58 | ), 59 | ], 60 | intentIdentifiers: [], 61 | options: UNNotificationCategoryOptions.values, 62 | ), 63 | ]); 64 | } 65 | } 66 | 67 | @override 68 | void initState() { 69 | storage.append('restart'); 70 | _register(); 71 | super.initState(); 72 | } 73 | 74 | @override 75 | Widget build(BuildContext context) { 76 | return MaterialApp( 77 | scaffoldMessengerKey: _messengerKey, 78 | home: Scaffold( 79 | appBar: AppBar( 80 | title: const Text('Plugin example app'), 81 | ), 82 | body: Column( 83 | children: [ 84 | Expanded( 85 | flex: 2, 86 | child: Column( 87 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 88 | children: [ 89 | Text( 90 | 'Token:', 91 | style: TextStyle(fontSize: 20), 92 | ), 93 | ValueListenableBuilder( 94 | valueListenable: connector.token, 95 | builder: (context, dynamic data, __) { 96 | if (data == null) { 97 | _token = ""; 98 | return Padding( 99 | padding: const EdgeInsets.all(8.0), 100 | child: SelectableText( 101 | "Status: Currently unregistered"), 102 | ); 103 | } else { 104 | _token = data; 105 | return Padding( 106 | padding: const EdgeInsets.all(8.0), 107 | child: SelectableText('$_token'), 108 | ); 109 | } 110 | }, 111 | ), 112 | TextButton.icon( 113 | onPressed: () { 114 | Clipboard.setData(ClipboardData(text: _token)) 115 | .then((value) { 116 | _messengerKey.currentState 117 | ?.showSnackBar(mySnackbar); 118 | }); 119 | }, 120 | icon: Icon(Icons.content_paste), 121 | label: Text("Add token to clipboard")), 122 | ValueListenableBuilder( 123 | valueListenable: connector.isDisabledByUser, 124 | builder: (context, dynamic data, __) { 125 | if (data == null) { 126 | return Text("Push notifications are not defined"); 127 | } else { 128 | return TextButton.icon( 129 | onPressed: () { 130 | connector.requestNotificationPermissions(); 131 | }, 132 | icon: Icon(Icons.replay_outlined), 133 | label: Text(data 134 | ? "The push notifications are rejected \n please enable them in the settings" 135 | : "Push notifications are authorized")); 136 | } 137 | }), 138 | ElevatedButton.icon( 139 | icon: Icon(Icons.app_registration), 140 | label: Text('Reset token'), 141 | onPressed: () { 142 | connector.unregister().then((_) => _register()); 143 | }, 144 | ), 145 | ], 146 | )), 147 | Expanded( 148 | child: AnimatedBuilder( 149 | animation: storage, 150 | builder: (context, _) { 151 | return Container( 152 | padding: EdgeInsets.all(10), 153 | decoration: BoxDecoration(border: Border.all()), 154 | child: 155 | SingleChildScrollView(child: Text(storage.content))); 156 | }, 157 | ), 158 | ) 159 | ], 160 | ), 161 | ), 162 | ); 163 | } 164 | } 165 | 166 | Future onPush(String name, RemoteMessage payload) { 167 | storage.append('$name: ${payload.notification?.title}'); 168 | 169 | final action = UNNotificationAction.getIdentifier(payload.data); 170 | 171 | if (action != null && action != UNNotificationAction.defaultIdentifier) { 172 | storage.append('Action: $action'); 173 | } 174 | 175 | return Future.value(true); 176 | } 177 | 178 | Future _onBackgroundMessage(RemoteMessage data) => 179 | onPush('onBackgroundMessage', data); 180 | -------------------------------------------------------------------------------- /example/lib/storage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:path_provider/path_provider.dart'; 5 | 6 | class Storage extends ChangeNotifier { 7 | late File storage; 8 | 9 | String content = 'not initialized'; 10 | 11 | Future setup() async { 12 | final path = await getApplicationDocumentsDirectory(); 13 | final file = File(path.path + '/storage.json'); 14 | 15 | if (!file.existsSync()) { 16 | await file.create(); 17 | } 18 | 19 | storage = file; 20 | content = await storage.readAsString(); 21 | } 22 | 23 | Future append(String data) async { 24 | final entry = '${DateTime.now()}: $data\n'; 25 | await storage.writeAsString(entry, mode: FileMode.append); 26 | content = await storage.readAsString(); 27 | notifyListeners(); 28 | print(data); 29 | } 30 | } 31 | 32 | final storage = Storage(); 33 | -------------------------------------------------------------------------------- /example/payloads/action.apns: -------------------------------------------------------------------------------- 1 | { 2 | "aps": { 3 | "category": "MEETING_INVITATION", 4 | "alert": { 5 | "title": "Weekly Staff Meeting", 6 | "body": "Every Tuesday at 2pm" 7 | } 8 | }, 9 | "MEETING_ID": "123456789", 10 | "USER_ID": "ABCD1234", 11 | "Simulator Target Bundle": "com.pgs-soft.pszot.Sample" 12 | } 13 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | args: 5 | dependency: transitive 6 | description: 7 | name: args 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.3.1" 11 | async: 12 | dependency: transitive 13 | description: 14 | name: async 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.8.2" 18 | boolean_selector: 19 | dependency: transitive 20 | description: 21 | name: boolean_selector 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "2.1.0" 25 | characters: 26 | dependency: transitive 27 | description: 28 | name: characters 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.2.0" 32 | charcode: 33 | dependency: transitive 34 | description: 35 | name: charcode 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.3.1" 39 | clock: 40 | dependency: transitive 41 | description: 42 | name: clock 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.1.0" 46 | collection: 47 | dependency: transitive 48 | description: 49 | name: collection 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.16.0" 53 | cupertino_icons: 54 | dependency: "direct main" 55 | description: 56 | name: cupertino_icons 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.0.5" 60 | dbus: 61 | dependency: transitive 62 | description: 63 | name: dbus 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "0.7.7" 67 | fake_async: 68 | dependency: transitive 69 | description: 70 | name: fake_async 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "1.3.0" 74 | ffi: 75 | dependency: transitive 76 | description: 77 | name: ffi 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "2.0.1" 81 | file: 82 | dependency: transitive 83 | description: 84 | name: file 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "6.1.2" 88 | firebase_core: 89 | dependency: transitive 90 | description: 91 | name: firebase_core 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "1.20.0" 95 | firebase_core_platform_interface: 96 | dependency: transitive 97 | description: 98 | name: firebase_core_platform_interface 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "4.5.0" 102 | firebase_core_web: 103 | dependency: transitive 104 | description: 105 | name: firebase_core_web 106 | url: "https://pub.dartlang.org" 107 | source: hosted 108 | version: "1.7.1" 109 | firebase_messaging: 110 | dependency: transitive 111 | description: 112 | name: firebase_messaging 113 | url: "https://pub.dartlang.org" 114 | source: hosted 115 | version: "12.0.1" 116 | firebase_messaging_platform_interface: 117 | dependency: transitive 118 | description: 119 | name: firebase_messaging_platform_interface 120 | url: "https://pub.dartlang.org" 121 | source: hosted 122 | version: "4.1.0" 123 | firebase_messaging_web: 124 | dependency: transitive 125 | description: 126 | name: firebase_messaging_web 127 | url: "https://pub.dartlang.org" 128 | source: hosted 129 | version: "3.1.0" 130 | flutter: 131 | dependency: "direct main" 132 | description: flutter 133 | source: sdk 134 | version: "0.0.0" 135 | flutter_apns: 136 | dependency: "direct main" 137 | description: 138 | path: "../flutter_apns" 139 | relative: true 140 | source: path 141 | version: "1.5.4" 142 | flutter_apns_only: 143 | dependency: transitive 144 | description: 145 | name: flutter_apns_only 146 | url: "https://pub.dartlang.org" 147 | source: hosted 148 | version: "1.5.2" 149 | flutter_local_notifications: 150 | dependency: "direct main" 151 | description: 152 | name: flutter_local_notifications 153 | url: "https://pub.dartlang.org" 154 | source: hosted 155 | version: "9.7.0" 156 | flutter_local_notifications_linux: 157 | dependency: transitive 158 | description: 159 | name: flutter_local_notifications_linux 160 | url: "https://pub.dartlang.org" 161 | source: hosted 162 | version: "0.5.1" 163 | flutter_local_notifications_platform_interface: 164 | dependency: transitive 165 | description: 166 | name: flutter_local_notifications_platform_interface 167 | url: "https://pub.dartlang.org" 168 | source: hosted 169 | version: "5.0.0" 170 | flutter_test: 171 | dependency: "direct dev" 172 | description: flutter 173 | source: sdk 174 | version: "0.0.0" 175 | flutter_web_plugins: 176 | dependency: transitive 177 | description: flutter 178 | source: sdk 179 | version: "0.0.0" 180 | js: 181 | dependency: transitive 182 | description: 183 | name: js 184 | url: "https://pub.dartlang.org" 185 | source: hosted 186 | version: "0.6.4" 187 | matcher: 188 | dependency: transitive 189 | description: 190 | name: matcher 191 | url: "https://pub.dartlang.org" 192 | source: hosted 193 | version: "0.12.11" 194 | material_color_utilities: 195 | dependency: transitive 196 | description: 197 | name: material_color_utilities 198 | url: "https://pub.dartlang.org" 199 | source: hosted 200 | version: "0.1.4" 201 | meta: 202 | dependency: transitive 203 | description: 204 | name: meta 205 | url: "https://pub.dartlang.org" 206 | source: hosted 207 | version: "1.7.0" 208 | path: 209 | dependency: transitive 210 | description: 211 | name: path 212 | url: "https://pub.dartlang.org" 213 | source: hosted 214 | version: "1.8.1" 215 | path_provider: 216 | dependency: "direct main" 217 | description: 218 | name: path_provider 219 | url: "https://pub.dartlang.org" 220 | source: hosted 221 | version: "2.0.11" 222 | path_provider_android: 223 | dependency: transitive 224 | description: 225 | name: path_provider_android 226 | url: "https://pub.dartlang.org" 227 | source: hosted 228 | version: "2.0.17" 229 | path_provider_ios: 230 | dependency: transitive 231 | description: 232 | name: path_provider_ios 233 | url: "https://pub.dartlang.org" 234 | source: hosted 235 | version: "2.0.11" 236 | path_provider_linux: 237 | dependency: transitive 238 | description: 239 | name: path_provider_linux 240 | url: "https://pub.dartlang.org" 241 | source: hosted 242 | version: "2.1.7" 243 | path_provider_macos: 244 | dependency: transitive 245 | description: 246 | name: path_provider_macos 247 | url: "https://pub.dartlang.org" 248 | source: hosted 249 | version: "2.0.6" 250 | path_provider_platform_interface: 251 | dependency: transitive 252 | description: 253 | name: path_provider_platform_interface 254 | url: "https://pub.dartlang.org" 255 | source: hosted 256 | version: "2.0.4" 257 | path_provider_windows: 258 | dependency: transitive 259 | description: 260 | name: path_provider_windows 261 | url: "https://pub.dartlang.org" 262 | source: hosted 263 | version: "2.1.1" 264 | petitparser: 265 | dependency: transitive 266 | description: 267 | name: petitparser 268 | url: "https://pub.dartlang.org" 269 | source: hosted 270 | version: "5.0.0" 271 | platform: 272 | dependency: transitive 273 | description: 274 | name: platform 275 | url: "https://pub.dartlang.org" 276 | source: hosted 277 | version: "3.1.0" 278 | plugin_platform_interface: 279 | dependency: transitive 280 | description: 281 | name: plugin_platform_interface 282 | url: "https://pub.dartlang.org" 283 | source: hosted 284 | version: "2.1.2" 285 | process: 286 | dependency: transitive 287 | description: 288 | name: process 289 | url: "https://pub.dartlang.org" 290 | source: hosted 291 | version: "4.2.4" 292 | sky_engine: 293 | dependency: transitive 294 | description: flutter 295 | source: sdk 296 | version: "0.0.99" 297 | source_span: 298 | dependency: transitive 299 | description: 300 | name: source_span 301 | url: "https://pub.dartlang.org" 302 | source: hosted 303 | version: "1.8.2" 304 | stack_trace: 305 | dependency: transitive 306 | description: 307 | name: stack_trace 308 | url: "https://pub.dartlang.org" 309 | source: hosted 310 | version: "1.10.0" 311 | stream_channel: 312 | dependency: transitive 313 | description: 314 | name: stream_channel 315 | url: "https://pub.dartlang.org" 316 | source: hosted 317 | version: "2.1.0" 318 | string_scanner: 319 | dependency: transitive 320 | description: 321 | name: string_scanner 322 | url: "https://pub.dartlang.org" 323 | source: hosted 324 | version: "1.1.0" 325 | term_glyph: 326 | dependency: transitive 327 | description: 328 | name: term_glyph 329 | url: "https://pub.dartlang.org" 330 | source: hosted 331 | version: "1.2.0" 332 | test_api: 333 | dependency: transitive 334 | description: 335 | name: test_api 336 | url: "https://pub.dartlang.org" 337 | source: hosted 338 | version: "0.4.9" 339 | timezone: 340 | dependency: transitive 341 | description: 342 | name: timezone 343 | url: "https://pub.dartlang.org" 344 | source: hosted 345 | version: "0.8.0" 346 | vector_math: 347 | dependency: transitive 348 | description: 349 | name: vector_math 350 | url: "https://pub.dartlang.org" 351 | source: hosted 352 | version: "2.1.2" 353 | win32: 354 | dependency: transitive 355 | description: 356 | name: win32 357 | url: "https://pub.dartlang.org" 358 | source: hosted 359 | version: "2.7.0" 360 | xdg_directories: 361 | dependency: transitive 362 | description: 363 | name: xdg_directories 364 | url: "https://pub.dartlang.org" 365 | source: hosted 366 | version: "0.2.0+1" 367 | xml: 368 | dependency: transitive 369 | description: 370 | name: xml 371 | url: "https://pub.dartlang.org" 372 | source: hosted 373 | version: "6.1.0" 374 | sdks: 375 | dart: ">=2.17.0 <3.0.0" 376 | flutter: ">=3.0.0" 377 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_apns_example 2 | description: Demonstrates how to use the flutter_apns plugin. 3 | publish_to: "none" 4 | version: 1.0.0+1 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | 13 | cupertino_icons: ^1.0.5 14 | path_provider: ^2.0.11 15 | 16 | flutter_local_notifications: ^9.7.0 17 | flutter_apns: 18 | 19 | dev_dependencies: 20 | flutter_test: 21 | sdk: flutter 22 | 23 | flutter: 24 | uses-material-design: true 25 | 26 | dependency_overrides: 27 | # flutter_apns_only: 28 | # path: ../flutter_apns_only 29 | flutter_apns: 30 | path: ../flutter_apns 31 | -------------------------------------------------------------------------------- /flutter_apns/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | idea/ 4 | 5 | .packages 6 | .pub/ 7 | 8 | build/ 9 | flutter_export_environment.sh 10 | .flutter-plugins-dependencies 11 | -------------------------------------------------------------------------------- /flutter_apns/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 204eceea93694aa081e0132c8281b76d3b3d6b4a 8 | channel: dev 9 | 10 | project_type: plugin 11 | -------------------------------------------------------------------------------- /flutter_apns/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 1.6.0 2 | * (#97) updated dependencies 3 | * enhanced example app 4 | ### 1.5.4 5 | * explicitly mentioned supported platforms in pubspec.yaml 6 | ### 1.5.3 7 | * update firebase dependencies 8 | * Add readme: how to run example app in iOS 9 | ### 1.5.1 10 | * (#58) fix unable to build android (update firebase dependencies) 11 | ### 1.5.0-dev.1 12 | * (#51) Update firebase push connector to support the firebase_messaging 9.0.0. API 13 | * (#43) Fix onLaunch not working with closed app 14 | * (#41) Add authorization status for iOS 15 | * (#48) Upgrade to null safety 16 | * (#48) separate flutter_apns and flutter_apns_only 17 | 18 | ### 1.4.1 19 | * (#31) fix duplicated onLaunch onResume 20 | * (#37) add possibility to disable firebase core 21 | 22 | ### 1.4.0 23 | * (#32) add unregister method 24 | * (#24) add support for foreground alerts 25 | * (#15) add support for actions 26 | 27 | ### 1.3.1 28 | * documentation & project structure cleanup 29 | 30 | ### 1.3.0 31 | * upgraded firebase to 7.0.0 32 | 33 | ### 1.2.0 34 | * upgraded firebase to 6.0.16 35 | IMPORTANT! New version requires setting UNUserNotificationCenterDelegate. Check the readme. 36 | 37 | ### 1.1.0 38 | * merged https://github.com/mwaylabs/flutter-apns/pull/11 - make sure that you are handling isDisabledByUser == null 39 | * upgraded firebase to 6.0.9 40 | 41 | ### 1.0.5 42 | * fixed https://github.com/mwaylabs/flutter-apns/issues/9 43 | 44 | ### 1.0.4 45 | * fixed iOS 13 bug with decoding token from NSData 46 | 47 | ### 1.0.3 48 | * added background messaging support oon Android 49 | * added workaround for flutter 1.7 50 | 51 | ### 1.0.2 52 | * fixed compilation error when using dynamic frameworks 53 | * upgraded dependencies 54 | 55 | ## 1.0.1 56 | * fixed compilation error with newer flutter version 57 | * implemented method swizzling which will prevent firebase plugin from registering 58 | 59 | ## 1.0.0 60 | * First release. 61 | -------------------------------------------------------------------------------- /flutter_apns/README.md: -------------------------------------------------------------------------------- 1 | # apns 2 | 3 | Plugin to implement APNS push notifications on iOS and Firebase on Android. 4 | 5 | ## Why this plugin was made? 6 | 7 | Currently, the only available push notification plugin is `firebase_messaging`. This means that, even on iOS, you will need to setup firebase and communicate with Google to send push notification. This plugin solves the problem by providing native APNS implementation while leaving configured Firebase for Android. 8 | 9 | ## Usage 10 | 1. Configure firebase on Android according to instructions: https://pub.dartlang.org/packages/firebase_messaging. 11 | 2. On iOS, make sure you have correctly configured your app to support push notifications, and that you have generated certificate/token for sending pushes. For more infos see section [How to run example app on iOS](#how-to-run-example-app-on-ios) 12 | 13 | 3. Add the following lines to the `didFinishLaunchingWithOptions` method in the AppDelegate.m/AppDelegate.swift file of your iOS project 14 | 15 | Objective-C: 16 | ```objc 17 | if (@available(iOS 10.0, *)) { 18 | [UNUserNotificationCenter currentNotificationCenter].delegate = (id) self; 19 | } 20 | ``` 21 | 22 | Swift: 23 | ```swift 24 | if #available(iOS 10.0, *) { 25 | UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate 26 | } 27 | ``` 28 | 29 | 4. Add `flutter_apns` as a [dependency in your pubspec.yaml file](https://flutter.io/platform-plugins/). 30 | 5. Using `createPushConnector()` method, configure push service according to your needs. `PushConnector` closely resembles `FirebaseMessaging`, so Firebase samples may be useful during implementation. You should create the connector as soon as possible to get the onLaunch callback working on closed app launch. 31 | ```dart 32 | import 'package:flutter_apns/apns.dart'; 33 | 34 | final connector = createPushConnector(); 35 | connector.configure( 36 | onLaunch: _onLaunch, 37 | onResume: _onResume, 38 | onMessage: _onMessage, 39 | ); 40 | connector.requestNotificationPermissions() 41 | ``` 42 | 6. Build on device and test your solution using Firebase Console (Android) and CURL (iOS, see [How to run example app on iOS](#how-to-run-example-app-on-ios)). 43 | 44 | ## Additional APNS features: 45 | ### Displaying notification while in foreground 46 | 47 | ```dart 48 | final connector = createPushConnector(); 49 | if (connector is ApnsPushConnector) { 50 | connector.shouldPresent = (x) => Future.value(true); 51 | } 52 | ``` 53 | 54 | ### Handling predefined actions 55 | 56 | Firstly, configure supported actions: 57 | ```dart 58 | final connector = createPushConnector(); 59 | if (connector is ApnsPushConnector) { 60 | connector.setNotificationCategories([ 61 | UNNotificationCategory( 62 | identifier: 'MEETING_INVITATION', 63 | actions: [ 64 | UNNotificationAction( 65 | identifier: 'ACCEPT_ACTION', 66 | title: 'Accept', 67 | options: [], 68 | ), 69 | UNNotificationAction( 70 | identifier: 'DECLINE_ACTION', 71 | title: 'Decline', 72 | options: [], 73 | ), 74 | ], 75 | intentIdentifiers: [], 76 | options: [], 77 | ), 78 | ]); 79 | } 80 | ``` 81 | 82 | Then, handle possible actions in your push handler: 83 | ```dart 84 | Future onPush(String name, RemoteMessage payload) { 85 | final action = UNNotificationAction.getIdentifier(payload.data); 86 | 87 | if (action == 'MEETING_INVITATION') { 88 | // do something 89 | } 90 | 91 | return Future.value(true); 92 | } 93 | ``` 94 | 95 | Note: if user clickes your notification while app is in the background, push will be delivered through onResume without actually waking up the app. Make sure your handling of given action is quick and error free, as execution time in for apps running in the background is very limited. 96 | 97 | Check the example project for fully working code. 98 | 99 | ## Enabling FirebaseCore 100 | If you want to use firebase, but not firebase messaging, add this configuration entry in your Info.plist (to avoid MissingPluginException): 101 | 102 | ``` 103 | flutter_apns.disable_firebase_core 104 | 105 | ``` 106 | 107 | ## flutter_apns_only - APNS without firebase 108 | If only care about apns - use flutter_apns_only plugin. It does not depend on firebase. To ensure no swizzling (which is needed by original plugin to disable firebase) takes place, add this configuration entry in your Info.plist: 109 | 110 | ```plist 111 | flutter_apns.disable_swizzling 112 | 113 | ``` 114 | 115 | ## Troubleshooting 116 | 117 | 1. Ensure that you are testing on actual device. NOTE: this may not be needed from 11.4: https://ohmyswift.com/blog/2020/02/13/simulating-remote-push-notifications-in-a-simulator/ 118 | 2. If onToken method is not being called, add error logging to your AppDelegate, see code below. 119 | 3. Open Console app for macOS, connect your device, and run your app. Search for "PUSH registration failed" string in logs. The error message will tell you what was wrong. 120 | 121 | *swift* 122 | ```swift 123 | import UIKit 124 | import Flutter 125 | 126 | @UIApplicationMain 127 | @objc class AppDelegate: FlutterAppDelegate { 128 | override func application( 129 | _ application: UIApplication, 130 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 131 | ) -> Bool { 132 | GeneratedPluginRegistrant.register(with: self) 133 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 134 | } 135 | 136 | func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { 137 | NSLog("PUSH registration failed: \(error)") 138 | } 139 | } 140 | 141 | ``` 142 | 143 | *objc* 144 | ```objc 145 | #include "AppDelegate.h" 146 | #include "GeneratedPluginRegistrant.h" 147 | 148 | @implementation AppDelegate 149 | 150 | - (BOOL)application:(UIApplication *)application 151 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 152 | [GeneratedPluginRegistrant registerWithRegistry:self]; 153 | // Override point for customization after application launch. 154 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 155 | } 156 | 157 | -(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { 158 | NSLog(@"%@", error); 159 | } 160 | 161 | @end 162 | ``` 163 | ## How to run example app on iOS 164 | Setting up push notifications on iOS can be tricky since there is no way to permit Apple Push Notification Service (APNS) which requires a complicated certificate setup. The following guide describes a step by step approach to send push notifications from your Mac to an iPhone utilizing the example app of this package. This guide only describes debug environment setup. 165 | 166 | 1. Open example ios folder with Xcode 167 | 2. Select Runner -> Signing & Capabilities 168 | 3. Select your development team and add a globally unique bundle identifier. The one on the picture is already occupied: 169 | ![](example/assets/Xcode_setup.png?raw=true) 170 | 4. Go to https://developer.apple.com/account/resources/identifiers/list/bundleId and press on the plus button 171 | 5. Select "App IDs" and press continue 172 | ![](example/assets/register_app.png?raw=true) 173 | 6. Select type "App" 174 | 7. Select "App ID Prefix" which should be same as "Team ID" 175 | 8. Enter description and bundle ID. The latter one needs to be the same as the bundle ID specified in 3. 176 | 9. Select push notification capability 177 | ![](example/assets/select_capability.png?raw=true) 178 | 11. Press on "Continue" and then on "Register" 179 | 12. Go to https://developer.apple.com/account/resources/certificates and add a new certificate by pressing on the plus-button. 180 | 13. Select 'Apple Push Notification service SSL (Sandbox & Production)' 181 | ![](example/assets/push_notification_setup.png?raw=true) 182 | 14. Select the app ID that you hav defined in point 4.-10. 183 | 15. Select a Certificate Signing Request (CSR) file. See https://help.apple.com/developer-account/#/devbfa00fef7 on how to create this certificate 184 | 16. When having finished, download the newly created Apple Push Services certificate 185 | 17. Add certificate to your local keychain by opening the newly downloaded file 186 | 18. Press on "login" on the upper left corner of your keychain window and select the tab "My Certificates" 187 | ![](example/assets/keychain.png?raw=true) 188 | 19. Right click on the Apple-Push-Services-certificate and export it as .p12-file 189 | 20. Convert p12-file to pem-file by following command. Please consider that "flutterApns" needs to be replaced by your respective certificate name.
190 | [More info](https://stackoverflow.com/questions/1762555/creating-pem-file-for-apns) 191 | ``` 192 | openssl pkcs12 -in flutterApns.p12 -out flutterApns.pem -nodes -clcerts 193 | ``` 194 | 21. Start example app on physical iPhone device from Xcode or your favorite IDE. 195 | 22. Device token gets automatically printed when application was able to retrieve push token from APNS. This happens after accepting notification permission prompt. 196 | 23. Send the following CURL from you development Mac. You can execute CURLs by copy-pasting them into Terminal and hit enter.
197 | [More info](https://gist.github.com/greencoder/16d1f8d7b0fed5b49cf64312ce2b72cc) 198 | ```curl 199 | curl -v \ 200 | -d '{"aps":{"alert":"","badge":2}}' \ 201 | -H "apns-topic: " \ 202 | -H "apns-priority: 10" \ 203 | --http2 \ 204 | --cert .pem \ 205 | https://api.development.push.apple.com/3/device/ 206 | ``` 207 | 24. A push notification does appear if the example app is in background. 208 | 209 | When not utilizing the example app, you need to additionally [setup push notification capability inside Xcode](https://developer.apple.com/documentation/usernotifications/registering_your_app_with_apns) and add the code mentioned in [usage](#usage). 210 | -------------------------------------------------------------------------------- /flutter_apns/flutter_apns.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /flutter_apns/lib/apns.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter_apns/src/apns_connector.dart'; 4 | import 'package:flutter_apns/src/connector.dart'; 5 | import 'package:flutter_apns/src/firebase_connector.dart'; 6 | 7 | export 'package:flutter_apns/src/connector.dart'; 8 | export 'package:flutter_apns/src/apns_connector.dart'; 9 | export 'package:flutter_apns/src/firebase_connector.dart'; 10 | 11 | /// Creates either APNS or Firebase connector to manage the push notification registration. 12 | PushConnector createPushConnector() { 13 | if (Platform.isIOS) { 14 | return ApnsPushConnector(); 15 | } else { 16 | return FirebasePushConnector(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /flutter_apns/lib/flutter_apns.dart: -------------------------------------------------------------------------------- 1 | export 'package:flutter_apns/src/connector.dart'; 2 | export 'package:flutter_apns/apns.dart'; 3 | 4 | export 'package:firebase_messaging/firebase_messaging.dart' show RemoteMessage; 5 | -------------------------------------------------------------------------------- /flutter_apns/lib/src/apns_connector.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_messaging/firebase_messaging.dart'; 2 | import 'package:flutter_apns_only/flutter_apns_only.dart'; 3 | export 'package:flutter_apns_only/flutter_apns_only.dart'; 4 | 5 | import 'connector.dart'; 6 | 7 | class ApnsPushConnector extends ApnsPushConnectorOnly implements PushConnector { 8 | @override 9 | Future configure({onMessage, onLaunch, onResume, onBackgroundMessage, options}) { 10 | ApnsMessageHandler? mapHandler(MessageHandler? input) { 11 | if (input == null) { 12 | return null; 13 | } 14 | 15 | return (apnsMessage) => input(RemoteMessage.fromMap(apnsMessage.payload)); 16 | } 17 | 18 | configureApns( 19 | onMessage: mapHandler(onMessage), 20 | onLaunch: mapHandler(onLaunch), 21 | onResume: mapHandler(onResume), 22 | onBackgroundMessage: mapHandler(onBackgroundMessage)); 23 | 24 | return Future.value(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /flutter_apns/lib/src/connector.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_messaging/firebase_messaging.dart'; 2 | import 'package:flutter/foundation.dart'; 3 | import 'package:firebase_core/firebase_core.dart'; 4 | 5 | /// Function signature for callbacks executed when push message is available; 6 | typedef Future MessageHandler(RemoteMessage message); 7 | 8 | /// Interface for either APNS or Firebase connector, implementing common features. 9 | abstract class PushConnector { 10 | /// User declined to allow for push messages. 11 | /// initially nil 12 | ValueNotifier get isDisabledByUser; 13 | 14 | /// Value of registered token 15 | /// initially nil 16 | ValueNotifier get token; 17 | 18 | /// Either GCM or APNS 19 | String get providerType; 20 | 21 | /// Configures callbacks for supported message situations. 22 | /// It should be called as soon as app is launch or you won't get the `onLaunch` callback 23 | Future configure({ 24 | /// iOS only: return true to display notification while app is in foreground 25 | MessageHandler? onMessage, 26 | MessageHandler? onLaunch, 27 | MessageHandler? onResume, 28 | MessageHandler? onBackgroundMessage, 29 | FirebaseOptions? options, 30 | }); 31 | 32 | /// Prompts (if need) the user to enable push notifications. 33 | /// After user makes their choice, isDisabledByUser will become either true or false. 34 | /// If accepted, token.value will be set 35 | void requestNotificationPermissions(); 36 | 37 | /// Unregisters from the service and clears the token. 38 | Future unregister(); 39 | 40 | /// Deletes used resources. 41 | void dispose() {} 42 | } 43 | -------------------------------------------------------------------------------- /flutter_apns/lib/src/firebase_connector.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_apns/src/connector.dart'; 2 | import 'package:firebase_messaging/firebase_messaging.dart'; 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:firebase_core/firebase_core.dart'; 5 | 6 | class FirebasePushConnector extends PushConnector { 7 | late final firebase = FirebaseMessaging.instance; 8 | 9 | @override 10 | final isDisabledByUser = ValueNotifier(null); 11 | 12 | bool didInitialize = false; 13 | 14 | @override 15 | Future configure({ 16 | MessageHandler? onMessage, 17 | MessageHandler? onLaunch, 18 | MessageHandler? onResume, 19 | MessageHandler? onBackgroundMessage, 20 | FirebaseOptions? options, 21 | }) async { 22 | if (!didInitialize) { 23 | await Firebase.initializeApp( 24 | options: options, 25 | ); 26 | didInitialize = true; 27 | } 28 | 29 | firebase.onTokenRefresh.listen((value) { 30 | token.value = value; 31 | }); 32 | 33 | FirebaseMessaging.onMessage.listen(onMessage); 34 | FirebaseMessaging.onMessageOpenedApp.listen(onResume); 35 | 36 | if (onBackgroundMessage != null) { 37 | FirebaseMessaging.onBackgroundMessage(onBackgroundMessage); 38 | } 39 | 40 | final initial = await FirebaseMessaging.instance.getInitialMessage(); 41 | if (initial != null) { 42 | onLaunch?.call(initial); 43 | } 44 | 45 | token.value = await firebase.getToken(); 46 | } 47 | 48 | @override 49 | final token = ValueNotifier(null); 50 | 51 | @override 52 | void requestNotificationPermissions() async { 53 | if (!didInitialize) { 54 | await Firebase.initializeApp(); 55 | didInitialize = true; 56 | } 57 | 58 | NotificationSettings permissions = await firebase.requestPermission( 59 | alert: true, 60 | announcement: false, 61 | badge: true, 62 | carPlay: false, 63 | criticalAlert: false, 64 | provisional: false, 65 | sound: true, 66 | ); 67 | 68 | if (permissions.authorizationStatus.name == 'authorized') { 69 | isDisabledByUser.value = false; 70 | } else if (permissions.authorizationStatus.name == 'denied') { 71 | isDisabledByUser.value = true; 72 | } 73 | } 74 | 75 | @override 76 | String get providerType => 'GCM'; 77 | 78 | @override 79 | Future unregister() async { 80 | await firebase.setAutoInitEnabled(false); 81 | await firebase.deleteToken(); 82 | 83 | token.value = null; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /flutter_apns/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.8.2" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.1.0" 18 | characters: 19 | dependency: transitive 20 | description: 21 | name: characters 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.2.0" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.3.1" 32 | clock: 33 | dependency: transitive 34 | description: 35 | name: clock 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.1.0" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.16.0" 46 | fake_async: 47 | dependency: transitive 48 | description: 49 | name: fake_async 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.3.0" 53 | firebase_core: 54 | dependency: "direct main" 55 | description: 56 | name: firebase_core 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.20.0" 60 | firebase_core_platform_interface: 61 | dependency: transitive 62 | description: 63 | name: firebase_core_platform_interface 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "4.5.0" 67 | firebase_core_web: 68 | dependency: transitive 69 | description: 70 | name: firebase_core_web 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "1.7.1" 74 | firebase_messaging: 75 | dependency: "direct main" 76 | description: 77 | name: firebase_messaging 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "12.0.1" 81 | firebase_messaging_platform_interface: 82 | dependency: transitive 83 | description: 84 | name: firebase_messaging_platform_interface 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "4.1.0" 88 | firebase_messaging_web: 89 | dependency: transitive 90 | description: 91 | name: firebase_messaging_web 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "3.1.0" 95 | flutter: 96 | dependency: "direct main" 97 | description: flutter 98 | source: sdk 99 | version: "0.0.0" 100 | flutter_apns_only: 101 | dependency: "direct main" 102 | description: 103 | name: flutter_apns_only 104 | url: "https://pub.dartlang.org" 105 | source: hosted 106 | version: "1.5.2" 107 | flutter_test: 108 | dependency: "direct dev" 109 | description: flutter 110 | source: sdk 111 | version: "0.0.0" 112 | flutter_web_plugins: 113 | dependency: transitive 114 | description: flutter 115 | source: sdk 116 | version: "0.0.0" 117 | js: 118 | dependency: transitive 119 | description: 120 | name: js 121 | url: "https://pub.dartlang.org" 122 | source: hosted 123 | version: "0.6.4" 124 | matcher: 125 | dependency: transitive 126 | description: 127 | name: matcher 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "0.12.11" 131 | material_color_utilities: 132 | dependency: transitive 133 | description: 134 | name: material_color_utilities 135 | url: "https://pub.dartlang.org" 136 | source: hosted 137 | version: "0.1.4" 138 | meta: 139 | dependency: transitive 140 | description: 141 | name: meta 142 | url: "https://pub.dartlang.org" 143 | source: hosted 144 | version: "1.7.0" 145 | path: 146 | dependency: transitive 147 | description: 148 | name: path 149 | url: "https://pub.dartlang.org" 150 | source: hosted 151 | version: "1.8.1" 152 | plugin_platform_interface: 153 | dependency: transitive 154 | description: 155 | name: plugin_platform_interface 156 | url: "https://pub.dartlang.org" 157 | source: hosted 158 | version: "2.1.2" 159 | sky_engine: 160 | dependency: transitive 161 | description: flutter 162 | source: sdk 163 | version: "0.0.99" 164 | source_span: 165 | dependency: transitive 166 | description: 167 | name: source_span 168 | url: "https://pub.dartlang.org" 169 | source: hosted 170 | version: "1.8.2" 171 | stack_trace: 172 | dependency: transitive 173 | description: 174 | name: stack_trace 175 | url: "https://pub.dartlang.org" 176 | source: hosted 177 | version: "1.10.0" 178 | stream_channel: 179 | dependency: transitive 180 | description: 181 | name: stream_channel 182 | url: "https://pub.dartlang.org" 183 | source: hosted 184 | version: "2.1.0" 185 | string_scanner: 186 | dependency: transitive 187 | description: 188 | name: string_scanner 189 | url: "https://pub.dartlang.org" 190 | source: hosted 191 | version: "1.1.0" 192 | term_glyph: 193 | dependency: transitive 194 | description: 195 | name: term_glyph 196 | url: "https://pub.dartlang.org" 197 | source: hosted 198 | version: "1.2.0" 199 | test_api: 200 | dependency: transitive 201 | description: 202 | name: test_api 203 | url: "https://pub.dartlang.org" 204 | source: hosted 205 | version: "0.4.9" 206 | vector_math: 207 | dependency: transitive 208 | description: 209 | name: vector_math 210 | url: "https://pub.dartlang.org" 211 | source: hosted 212 | version: "2.1.2" 213 | sdks: 214 | dart: ">=2.17.0-0 <3.0.0" 215 | flutter: ">=2.2.0" 216 | -------------------------------------------------------------------------------- /flutter_apns/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_apns 2 | description: APNS push notification plugin. Uses firebase_messaging on Android, but replaces it on iOS with custom implementation. 3 | version: 1.6.0 4 | homepage: https://github.com/mwaylabs/flutter-apns 5 | 6 | plugin: 7 | platforms: 8 | android: 9 | pluginClass: FirebasePushConnector 10 | ios: 11 | pluginClass: ApnsPushConnector 12 | 13 | environment: 14 | sdk: ">=2.12.0 <3.0.0" 15 | flutter: ">=2.2.0" 16 | 17 | dependencies: 18 | flutter: 19 | sdk: flutter 20 | firebase_core: ^2.1.1 21 | firebase_messaging: ^14.0.3 22 | flutter_apns_only: ^1.5.2 23 | 24 | dev_dependencies: 25 | flutter_test: 26 | sdk: flutter 27 | -------------------------------------------------------------------------------- /flutter_apns_only/.flutter-plugins: -------------------------------------------------------------------------------- 1 | # This is a generated file; do not edit or check into version control. 2 | flutter_apns_only=/Users/work/Documents/Projects/flutter-apns/flutter_apns_only/ 3 | -------------------------------------------------------------------------------- /flutter_apns_only/.flutter-plugins-dependencies: -------------------------------------------------------------------------------- 1 | {"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_apns_only","path":"/Users/work/Documents/Projects/flutter-apns/flutter_apns_only/","dependencies":[]}],"android":[],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"flutter_apns_only","dependencies":[]}],"date_created":"2021-03-23 15:32:25.763986","version":"2.0.3"} -------------------------------------------------------------------------------- /flutter_apns_only/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | -------------------------------------------------------------------------------- /flutter_apns_only/.idea/libraries/Dart_SDK.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /flutter_apns_only/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /flutter_apns_only/.idea/runConfigurations/example_lib_main_dart.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /flutter_apns_only/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /flutter_apns_only/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 8962f6dc68ec8e2206ac2fa874da4a453856c7d3 8 | channel: stable 9 | 10 | project_type: plugin 11 | -------------------------------------------------------------------------------- /flutter_apns_only/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.6.0 2 | * upgraded together with flutter_apns main package 3 | * unify version number 4 | 5 | ## 1.5.2 6 | * upgraded together with flutter_apns 7 | * (#78) Do not hide push notification when opening app 8 | 9 | ## 1.5.0-dev.1 10 | * separated apns code from flutter_apns 11 | -------------------------------------------------------------------------------- /flutter_apns_only/README.md: -------------------------------------------------------------------------------- 1 | # flutter_apns_only 2 | 3 | APNS push notification plugin. Works only on iOS. See flutter_apns for apns & firebase combo. 4 | -------------------------------------------------------------------------------- /flutter_apns_only/flutter_apns_only.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /flutter_apns_only/ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/Generated.xcconfig 37 | /Flutter/flutter_export_environment.sh -------------------------------------------------------------------------------- /flutter_apns_only/ios/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mway-io/flutter-apns/2af82d6335807bd7b3d142022e13e740131a617a/flutter_apns_only/ios/Assets/.gitkeep -------------------------------------------------------------------------------- /flutter_apns_only/ios/Classes/FlutterApnsPlugin.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UserNotifications 3 | 4 | func getFlutterError(_ error: Error) -> FlutterError { 5 | let e = error as NSError 6 | return FlutterError(code: "Error: \(e.code)", message: e.domain, details: error.localizedDescription) 7 | } 8 | 9 | @objc public class FlutterApnsPlugin: NSObject, FlutterPlugin, UNUserNotificationCenterDelegate { 10 | internal init(channel: FlutterMethodChannel) { 11 | self.channel = channel 12 | } 13 | 14 | public static func register(with registrar: FlutterPluginRegistrar) { 15 | let channel = FlutterMethodChannel(name: "flutter_apns", binaryMessenger: registrar.messenger()) 16 | let instance = FlutterApnsPlugin(channel: channel) 17 | registrar.addApplicationDelegate(instance) 18 | registrar.addMethodCallDelegate(instance, channel: channel) 19 | } 20 | 21 | let channel: FlutterMethodChannel 22 | var launchNotification: [String: Any]? 23 | var resumingFromBackground = false 24 | 25 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 26 | switch call.method { 27 | case "requestNotificationPermissions": 28 | requestNotificationPermissions(call, result: result) 29 | case "configure": 30 | assert( 31 | UNUserNotificationCenter.current().delegate != nil, 32 | "UNUserNotificationCenter.current().delegate is not set. Check readme at https://pub.dev/packages/flutter_apns." 33 | ) 34 | UIApplication.shared.registerForRemoteNotifications() 35 | 36 | // check for onLaunch notification *after* configure has been ran 37 | if let launchNotification = launchNotification { 38 | channel.invokeMethod("onLaunch", arguments: launchNotification) 39 | self.launchNotification = nil 40 | return 41 | } 42 | result(nil) 43 | case "getAuthorizationStatus": 44 | getAuthorizationStatus(result) 45 | case "unregister": 46 | UIApplication.shared.unregisterForRemoteNotifications() 47 | result(nil) 48 | case "setNotificationCategories": 49 | setNotificationCategories(arguments: call.arguments!) 50 | result(nil) 51 | default: 52 | assertionFailure(call.method) 53 | result(FlutterMethodNotImplemented) 54 | } 55 | } 56 | 57 | func setNotificationCategories(arguments: Any) { 58 | let arguments = arguments as! [[String: Any]] 59 | func decodeCategory(map: [String: Any]) -> UNNotificationCategory { 60 | return UNNotificationCategory( 61 | identifier: map["identifier"] as! String, 62 | actions: (map["actions"] as! [[String: Any]]).map(decodeAction), 63 | intentIdentifiers: map["intentIdentifiers"] as! [String], 64 | options: decodeCategoryOptions(data: map["options"] as! [String]) 65 | ) 66 | } 67 | func decodeCategoryOptions(data: [String]) -> UNNotificationCategoryOptions { 68 | let mapped = data.compactMap { 69 | UNNotificationCategoryOptions.stringToValue[$0] 70 | } 71 | return .init(mapped) 72 | } 73 | 74 | func decodeAction(map: [String: Any]) -> UNNotificationAction { 75 | return UNNotificationAction( 76 | identifier: map["identifier"] as! String, 77 | title: map["title"] as! String, 78 | options: decodeActionOptions(data: map["options"] as! [String]) 79 | ) 80 | } 81 | 82 | func decodeActionOptions(data: [String]) -> UNNotificationActionOptions { 83 | let mapped = data.compactMap { 84 | UNNotificationActionOptions.stringToValue[$0] 85 | } 86 | return .init(mapped) 87 | } 88 | 89 | let categories = arguments.map(decodeCategory) 90 | UNUserNotificationCenter.current().setNotificationCategories(Set(categories)) 91 | } 92 | 93 | func getAuthorizationStatus(_ result: @escaping FlutterResult) { 94 | UNUserNotificationCenter.current().getNotificationSettings { (settings) in 95 | switch settings.authorizationStatus { 96 | case .authorized: 97 | result("authorized") 98 | case .denied: 99 | result("denied") 100 | case .notDetermined: 101 | result("notDetermined") 102 | default: 103 | result("unsupported") 104 | } 105 | } 106 | } 107 | 108 | func requestNotificationPermissions(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 109 | let center = UNUserNotificationCenter.current() 110 | let application = UIApplication.shared 111 | 112 | func readBool(_ key: String) -> Bool { 113 | (call.arguments as? [String: Any])?[key] as? Bool ?? false 114 | } 115 | 116 | assert(center.delegate != nil) 117 | 118 | var options = [UNAuthorizationOptions]() 119 | 120 | if readBool("sound") { 121 | options.append(.sound) 122 | } 123 | if readBool("badge") { 124 | options.append(.badge) 125 | } 126 | if readBool("alert") { 127 | options.append(.alert) 128 | } 129 | 130 | var provisionalRequested = false 131 | if #available(iOS 12.0, *) { 132 | if readBool("provisional") { 133 | options.append(.provisional) 134 | provisionalRequested = true 135 | } 136 | } 137 | 138 | 139 | let optionsUnion = UNAuthorizationOptions(options) 140 | 141 | center.requestAuthorization(options: optionsUnion) { (granted, error) in 142 | if let error = error { 143 | result(getFlutterError(error)) 144 | return 145 | } 146 | 147 | center.getNotificationSettings { (settings) in 148 | let map = [ 149 | "sound": settings.soundSetting == .enabled, 150 | "badge": settings.badgeSetting == .enabled, 151 | "alert": settings.alertSetting == .enabled, 152 | "provisional": granted && provisionalRequested 153 | ] 154 | 155 | self.channel.invokeMethod("onIosSettingsRegistered", arguments: map) 156 | } 157 | 158 | result(granted) 159 | } 160 | 161 | application.registerForRemoteNotifications() 162 | } 163 | 164 | //MARK: - AppDelegate 165 | 166 | public func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [AnyHashable : Any] = [:]) -> Bool { 167 | if let launchNotification = launchOptions[UIApplication.LaunchOptionsKey.remoteNotification] as? [String: Any] { 168 | self.launchNotification = FlutterApnsSerialization.remoteMessageUserInfo(toDict: launchNotification) 169 | } 170 | return true 171 | } 172 | 173 | public func applicationDidEnterBackground(_ application: UIApplication) { 174 | resumingFromBackground = true 175 | } 176 | 177 | public func applicationDidBecomeActive(_ application: UIApplication) { 178 | resumingFromBackground = false 179 | UIApplication.shared.applicationIconBadgeNumber = -1; 180 | } 181 | 182 | public func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { 183 | channel.invokeMethod("onToken", arguments: deviceToken.hexString) 184 | } 185 | 186 | 187 | public func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) -> Bool { 188 | let userInfo = FlutterApnsSerialization.remoteMessageUserInfo(toDict: userInfo) 189 | 190 | if resumingFromBackground { 191 | onResume(userInfo: userInfo) 192 | } else { 193 | channel.invokeMethod("onMessage", arguments: userInfo) 194 | } 195 | 196 | completionHandler(.noData) 197 | return true 198 | } 199 | 200 | public func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { 201 | let userInfo = notification.request.content.userInfo 202 | 203 | guard userInfo["aps"] != nil else { 204 | return 205 | } 206 | 207 | let dict = FlutterApnsSerialization.remoteMessageUserInfo(toDict: userInfo) 208 | 209 | channel.invokeMethod("willPresent", arguments: dict) { (result) in 210 | let shouldShow = (result as? Bool) ?? false 211 | if shouldShow { 212 | completionHandler([.alert, .sound]) 213 | } else { 214 | completionHandler([]) 215 | let userInfo = FlutterApnsSerialization.remoteMessageUserInfo(toDict: userInfo) 216 | self.channel.invokeMethod("onMessage", arguments: userInfo) 217 | } 218 | } 219 | } 220 | 221 | public func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { 222 | var userInfo = response.notification.request.content.userInfo 223 | guard userInfo["aps"] != nil else { 224 | return 225 | } 226 | 227 | userInfo["actionIdentifier"] = response.actionIdentifier 228 | let dict = FlutterApnsSerialization.remoteMessageUserInfo(toDict: userInfo) 229 | 230 | if launchNotification != nil { 231 | launchNotification = dict 232 | return 233 | } 234 | 235 | onResume(userInfo: dict) 236 | completionHandler() 237 | } 238 | 239 | func onResume(userInfo: [AnyHashable: Any]) { 240 | channel.invokeMethod("onResume", arguments: userInfo) 241 | } 242 | } 243 | 244 | extension UNNotificationCategoryOptions { 245 | static let stringToValue: [String: UNNotificationCategoryOptions] = { 246 | var r: [String: UNNotificationCategoryOptions] = [:] 247 | r["UNNotificationCategoryOptions.customDismissAction"] = .customDismissAction 248 | r["UNNotificationCategoryOptions.allowInCarPlay"] = .allowInCarPlay 249 | if #available(iOS 11.0, *) { 250 | r["UNNotificationCategoryOptions.hiddenPreviewsShowTitle"] = .hiddenPreviewsShowTitle 251 | } 252 | if #available(iOS 11.0, *) { 253 | r["UNNotificationCategoryOptions.hiddenPreviewsShowSubtitle"] = .hiddenPreviewsShowSubtitle 254 | } 255 | if #available(iOS 13.0, *) { 256 | r["UNNotificationCategoryOptions.allowAnnouncement"] = .allowAnnouncement 257 | } 258 | return r 259 | }() 260 | } 261 | 262 | extension UNNotificationActionOptions { 263 | static let stringToValue: [String: UNNotificationActionOptions] = { 264 | var r: [String: UNNotificationActionOptions] = [:] 265 | r["UNNotificationActionOptions.authenticationRequired"] = .authenticationRequired 266 | r["UNNotificationActionOptions.destructive"] = .destructive 267 | r["UNNotificationActionOptions.foreground"] = .foreground 268 | return r 269 | }() 270 | } 271 | 272 | extension Data { 273 | var hexString: String { 274 | let hexString = map { String(format: "%02.2hhx", $0) }.joined() 275 | return hexString 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /flutter_apns_only/ios/Classes/FlutterApnsSerialization.h: -------------------------------------------------------------------------------- 1 | // 2 | // Serialization.h 3 | // flutter_apns 4 | // 5 | // Created by Pawel Szot on 19/03/2021. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | /// Copied from https://github.com/FirebaseExtended/flutterfire/blob/master/packages/firebase_messaging/firebase_messaging/ios/Classes/FLTFirebaseMessagingPlugin.m#L759 13 | @interface FlutterApnsSerialization : NSObject 14 | + (NSDictionary *)remoteMessageUserInfoToDict:(NSDictionary *)userInfo; 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /flutter_apns_only/ios/Classes/FlutterApnsSerialization.m: -------------------------------------------------------------------------------- 1 | // 2 | // Serialization.m 3 | // flutter_apns 4 | // 5 | // Created by Pawel Szot on 19/03/2021. 6 | // 7 | 8 | #import "FlutterApnsSerialization.h" 9 | #import 10 | 11 | @implementation FlutterApnsSerialization 12 | 13 | + (NSDictionary *)remoteMessageUserInfoToDict:(NSDictionary *)userInfo { 14 | NSMutableDictionary *message = [[NSMutableDictionary alloc] init]; 15 | NSMutableDictionary *data = [[NSMutableDictionary alloc] init]; 16 | NSMutableDictionary *notification = [[NSMutableDictionary alloc] init]; 17 | NSMutableDictionary *notificationIOS = [[NSMutableDictionary alloc] init]; 18 | 19 | // message.data 20 | for (id key in userInfo) { 21 | // message.messageId 22 | if ([key isEqualToString:@"gcm.message_id"] || [key isEqualToString:@"google.message_id"] || 23 | [key isEqualToString:@"message_id"]) { 24 | message[@"messageId"] = userInfo[key]; 25 | continue; 26 | } 27 | 28 | // message.messageType 29 | if ([key isEqualToString:@"message_type"]) { 30 | message[@"messageType"] = userInfo[key]; 31 | continue; 32 | } 33 | 34 | // message.collapseKey 35 | if ([key isEqualToString:@"collapse_key"]) { 36 | message[@"collapseKey"] = userInfo[key]; 37 | continue; 38 | } 39 | 40 | // message.from 41 | if ([key isEqualToString:@"from"]) { 42 | message[@"from"] = userInfo[key]; 43 | continue; 44 | } 45 | 46 | // message.sentTime 47 | if ([key isEqualToString:@"google.c.a.ts"]) { 48 | message[@"sentTime"] = userInfo[key]; 49 | continue; 50 | } 51 | 52 | // message.to 53 | if ([key isEqualToString:@"to"] || [key isEqualToString:@"google.to"]) { 54 | message[@"to"] = userInfo[key]; 55 | continue; 56 | } 57 | 58 | // build data dict from remaining keys but skip keys that shouldn't be included in data 59 | if ([key isEqualToString:@"aps"] || [key hasPrefix:@"gcm."] || [key hasPrefix:@"google."]) { 60 | continue; 61 | } 62 | 63 | // message.apple.imageUrl 64 | if ([key isEqualToString:@"fcm_options"]) { 65 | if (userInfo[key] != nil && userInfo[key][@"image"] != nil) { 66 | notificationIOS[@"imageUrl"] = userInfo[key][@"image"]; 67 | } 68 | continue; 69 | } 70 | 71 | data[key] = userInfo[key]; 72 | } 73 | message[@"data"] = data; 74 | 75 | if (userInfo[@"aps"] != nil) { 76 | NSDictionary *apsDict = userInfo[@"aps"]; 77 | // message.category 78 | if (apsDict[@"category"] != nil) { 79 | message[@"category"] = apsDict[@"category"]; 80 | } 81 | 82 | // message.threadId 83 | if (apsDict[@"thread-id"] != nil) { 84 | message[@"threadId"] = apsDict[@"thread-id"]; 85 | } 86 | 87 | // message.contentAvailable 88 | if (apsDict[@"content-available"] != nil) { 89 | message[@"contentAvailable"] = @([apsDict[@"content-available"] boolValue]); 90 | } 91 | 92 | // message.mutableContent 93 | if (apsDict[@"mutable-content"] != nil && [apsDict[@"mutable-content"] intValue] == 1) { 94 | message[@"mutableContent"] = @([apsDict[@"mutable-content"] boolValue]); 95 | } 96 | 97 | // message.notification.* 98 | if (apsDict[@"alert"] != nil) { 99 | // can be a string or dictionary 100 | if ([apsDict[@"alert"] isKindOfClass:[NSString class]]) { 101 | // message.notification.title 102 | notification[@"title"] = apsDict[@"alert"]; 103 | } else if ([apsDict[@"alert"] isKindOfClass:[NSDictionary class]]) { 104 | NSDictionary *apsAlertDict = apsDict[@"alert"]; 105 | 106 | // message.notification.title 107 | if (apsAlertDict[@"title"] != nil) { 108 | notification[@"title"] = apsAlertDict[@"title"]; 109 | } 110 | 111 | // message.notification.titleLocKey 112 | if (apsAlertDict[@"title-loc-key"] != nil) { 113 | notification[@"titleLocKey"] = apsAlertDict[@"title-loc-key"]; 114 | } 115 | 116 | // message.notification.titleLocArgs 117 | if (apsAlertDict[@"title-loc-args"] != nil) { 118 | notification[@"titleLocArgs"] = apsAlertDict[@"title-loc-args"]; 119 | } 120 | 121 | // message.notification.body 122 | if (apsAlertDict[@"body"] != nil) { 123 | notification[@"body"] = apsAlertDict[@"body"]; 124 | } 125 | 126 | // message.notification.bodyLocKey 127 | if (apsAlertDict[@"loc-key"] != nil) { 128 | notification[@"bodyLocKey"] = apsAlertDict[@"loc-key"]; 129 | } 130 | 131 | // message.notification.bodyLocArgs 132 | if (apsAlertDict[@"loc-args"] != nil) { 133 | notification[@"bodyLocArgs"] = apsAlertDict[@"loc-args"]; 134 | } 135 | 136 | // Apple only 137 | // message.notification.apple.subtitle 138 | if (apsAlertDict[@"subtitle"] != nil) { 139 | notificationIOS[@"subtitle"] = apsAlertDict[@"subtitle"]; 140 | } 141 | 142 | // Apple only 143 | // message.notification.apple.subtitleLocKey 144 | if (apsAlertDict[@"subtitle-loc-key"] != nil) { 145 | notificationIOS[@"subtitleLocKey"] = apsAlertDict[@"subtitle-loc-key"]; 146 | } 147 | 148 | // Apple only 149 | // message.notification.apple.subtitleLocArgs 150 | if (apsAlertDict[@"subtitle-loc-args"] != nil) { 151 | notificationIOS[@"subtitleLocArgs"] = apsAlertDict[@"subtitle-loc-args"]; 152 | } 153 | 154 | // Apple only 155 | // message.notification.apple.badge 156 | if (apsAlertDict[@"badge"] != nil) { 157 | notificationIOS[@"badge"] = apsAlertDict[@"badge"]; 158 | } 159 | } 160 | 161 | notification[@"apple"] = notificationIOS; 162 | message[@"notification"] = notification; 163 | } 164 | 165 | // message.notification.apple.sound 166 | if (apsDict[@"sound"] != nil) { 167 | if ([apsDict[@"sound"] isKindOfClass:[NSString class]]) { 168 | // message.notification.apple.sound 169 | notification[@"sound"] = @{ 170 | @"name" : apsDict[@"sound"], 171 | @"critical" : @NO, 172 | @"volume" : @1, 173 | }; 174 | } else if ([apsDict[@"sound"] isKindOfClass:[NSDictionary class]]) { 175 | NSDictionary *apsSoundDict = apsDict[@"sound"]; 176 | NSMutableDictionary *notificationIOSSound = [[NSMutableDictionary alloc] init]; 177 | 178 | // message.notification.apple.sound.name String 179 | if (apsSoundDict[@"name"] != nil) { 180 | notificationIOSSound[@"name"] = apsSoundDict[@"name"]; 181 | } 182 | 183 | // message.notification.apple.sound.critical Boolean 184 | if (apsSoundDict[@"critical"] != nil) { 185 | notificationIOSSound[@"critical"] = @([apsSoundDict[@"critical"] boolValue]); 186 | } 187 | 188 | // message.notification.apple.sound.volume Number 189 | if (apsSoundDict[@"volume"] != nil) { 190 | notificationIOSSound[@"volume"] = apsSoundDict[@"volume"]; 191 | } 192 | 193 | // message.notification.apple.sound 194 | notificationIOS[@"sound"] = notificationIOSSound; 195 | } 196 | 197 | notification[@"apple"] = notificationIOS; 198 | message[@"notification"] = notification; 199 | } 200 | } 201 | 202 | return message; 203 | } 204 | 205 | @end 206 | -------------------------------------------------------------------------------- /flutter_apns_only/ios/Classes/FlutterApnsSwizzler.h: -------------------------------------------------------------------------------- 1 | @interface FlutterApnsSwizzler : NSObject 2 | 3 | @property (class, readonly) BOOL didSwizzle; 4 | 5 | @end 6 | -------------------------------------------------------------------------------- /flutter_apns_only/ios/Classes/FlutterApnsSwizzler.m: -------------------------------------------------------------------------------- 1 | #import "FlutterApnsSwizzler.h" 2 | #import 3 | #import 4 | 5 | static int swizzleCounter; 6 | 7 | @interface FlutterApnsSwizzler () 8 | @end 9 | 10 | @implementation FlutterApnsSwizzler 11 | 12 | + (BOOL)didSwizzle { 13 | return swizzleCounter > 0; 14 | } 15 | 16 | + (void)apns_registerWithRegistrar:(NSObject *)registrar { 17 | swizzleCounter++; 18 | } 19 | 20 | + (void)disablePluginNamed:(NSString *)name { 21 | Class c = NSClassFromString(name); 22 | 23 | if (!c) { 24 | return; 25 | } 26 | 27 | SEL orig = @selector(registerWithRegistrar:); 28 | SEL new = @selector(apns_registerWithRegistrar:); 29 | 30 | Method origMethod = class_getClassMethod(c, orig); 31 | Method newMethod = class_getClassMethod(self, new); 32 | 33 | c = object_getClass((id)c); 34 | 35 | if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) 36 | class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); 37 | else 38 | method_setImplementation(origMethod, method_getImplementation(newMethod)); 39 | } 40 | 41 | /// Returns flag stored in Info.plist. True if value is not present. 42 | + (BOOL)getFlag:(NSString *)key defaultValue:(BOOL)defaultValue { 43 | NSString *keyWithSuffix = [NSString stringWithFormat:@"flutter_apns.%@", key]; 44 | NSObject *disable = [NSBundle mainBundle].infoDictionary[keyWithSuffix]; 45 | 46 | if (disable) { 47 | if ([disable isKindOfClass:[NSNumber class]]) { 48 | return ((NSNumber *)disable).boolValue; 49 | } 50 | NSAssert(false, @"flutter_apns: invalid value of flutter_apns.%@", key); 51 | } 52 | 53 | return defaultValue; 54 | } 55 | 56 | + (void)load { 57 | if ([self getFlag:@"disable_swizzling" defaultValue: NO]) { 58 | return; 59 | } 60 | 61 | if ([self getFlag:@"disable_firebase_core" defaultValue: YES]) { 62 | [self disablePluginNamed:@"FLTFirebaseCorePlugin"]; 63 | } 64 | 65 | [self disablePluginNamed:@"FLTFirebaseMessagingPlugin"]; 66 | } 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /flutter_apns_only/ios/flutter_apns_only.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. 3 | # Run `pod lib lint flutter_apns_only.podspec` to validate before publishing. 4 | # 5 | Pod::Spec.new do |s| 6 | s.name = 'flutter_apns_only' 7 | s.version = '0.0.1' 8 | s.summary = 'A new flutter plugin project.' 9 | s.description = <<-DESC 10 | A new flutter plugin project. 11 | DESC 12 | s.homepage = 'http://example.com' 13 | s.license = { :file => '../LICENSE' } 14 | s.author = { 'Your Company' => 'email@example.com' } 15 | s.source = { :path => '.' } 16 | s.source_files = 'Classes/**/*' 17 | s.dependency 'Flutter' 18 | s.platform = :ios, '8.0' 19 | 20 | # Flutter.framework does not contain a i386 slice. 21 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } 22 | s.swift_version = '5.0' 23 | end 24 | -------------------------------------------------------------------------------- /flutter_apns_only/lib/flutter_apns_only.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:flutter/foundation.dart'; 5 | import 'package:flutter/services.dart' hide MessageHandler; 6 | 7 | class ApnsRemoteMessage { 8 | ApnsRemoteMessage.fromMap(this.payload); 9 | 10 | final Map payload; 11 | 12 | String? get actionIdentifier => UNNotificationAction.getIdentifier(payload); 13 | } 14 | 15 | typedef ApnsMessageHandler = Future Function(ApnsRemoteMessage); 16 | typedef WillPresentHandler = Future Function(ApnsRemoteMessage); 17 | 18 | enum ApnsAuthorizationStatus { 19 | authorized, 20 | denied, 21 | notDetermined, 22 | unsupported, 23 | } 24 | 25 | class ApnsPushConnectorOnly { 26 | final MethodChannel _channel = () { 27 | assert(Platform.isIOS, 28 | 'ApnsPushConnectorOnly can only be created on iOS platform!'); 29 | return const MethodChannel('flutter_apns'); 30 | }(); 31 | ApnsMessageHandler? _onMessage; 32 | ApnsMessageHandler? _onLaunch; 33 | ApnsMessageHandler? _onResume; 34 | 35 | Future requestNotificationPermissions( 36 | [IosNotificationSettings iosSettings = const IosNotificationSettings()]) async { 37 | final bool? result = await _channel.invokeMethod( 38 | 'requestNotificationPermissions', iosSettings.toMap()); 39 | return result ?? false; 40 | } 41 | 42 | Future getAuthorizationStatus() async { 43 | return _authorizationStatusForString(await _channel.invokeMethod('getAuthorizationStatus', [])); 44 | } 45 | 46 | final StreamController _iosSettingsStreamController = 47 | StreamController.broadcast(); 48 | 49 | Stream get onIosSettingsRegistered { 50 | return _iosSettingsStreamController.stream; 51 | } 52 | 53 | /// Sets up [MessageHandler] for incoming messages. 54 | void configureApns({ 55 | ApnsMessageHandler? onMessage, 56 | ApnsMessageHandler? onLaunch, 57 | ApnsMessageHandler? onResume, 58 | ApnsMessageHandler? onBackgroundMessage, 59 | }) { 60 | _onMessage = onMessage; 61 | _onLaunch = onLaunch; 62 | _onResume = onResume; 63 | _channel.setMethodCallHandler(_handleMethod); 64 | _channel.invokeMethod('configure'); 65 | } 66 | 67 | Future _handleMethod(MethodCall call) async { 68 | switch (call.method) { 69 | case 'onToken': 70 | token.value = call.arguments; 71 | return null; 72 | case 'onIosSettingsRegistered': 73 | final obj = IosNotificationSettings._fromMap( 74 | call.arguments.cast()); 75 | 76 | isDisabledByUser.value = obj.alert == false; 77 | return null; 78 | case 'onMessage': 79 | return _onMessage?.call(_extractMessage(call)); 80 | case 'onLaunch': 81 | return _onLaunch?.call(_extractMessage(call)); 82 | case 'onResume': 83 | return _onResume?.call(_extractMessage(call)); 84 | case 'willPresent': 85 | return shouldPresent?.call(_extractMessage(call)) ?? 86 | Future.value(false); 87 | 88 | default: 89 | throw UnsupportedError('Unrecognized JSON message'); 90 | } 91 | } 92 | 93 | ApnsRemoteMessage _extractMessage(MethodCall call) { 94 | final map = call.arguments as Map; 95 | // fix null safety errors 96 | map.putIfAbsent('contentAvailable', () => false); 97 | map.putIfAbsent('mutableContent', () => false); 98 | return ApnsRemoteMessage.fromMap(map.cast()); 99 | } 100 | 101 | ApnsAuthorizationStatus _authorizationStatusForString(String? value) { 102 | switch (value) { 103 | case 'authorized': 104 | return ApnsAuthorizationStatus.authorized; 105 | case 'denied': 106 | return ApnsAuthorizationStatus.denied; 107 | case 'notDetermined': 108 | return ApnsAuthorizationStatus.notDetermined; 109 | case 'unsupported': 110 | default: 111 | return ApnsAuthorizationStatus.unsupported; 112 | } 113 | } 114 | 115 | /// Handler that returns true/false to decide if push alert should be displayed when in foreground. 116 | /// Returning true will delay onMessage callback until user actually clicks on it 117 | WillPresentHandler? shouldPresent; 118 | 119 | final isDisabledByUser = ValueNotifier(null); 120 | 121 | final token = ValueNotifier(null); 122 | 123 | String get providerType => "APNS"; 124 | 125 | void dispose() { 126 | _iosSettingsStreamController.close(); 127 | } 128 | 129 | /// https://developer.apple.com/documentation/usernotifications/declaring_your_actionable_notification_types 130 | Future setNotificationCategories( 131 | List categories) { 132 | return _channel.invokeMethod( 133 | 'setNotificationCategories', 134 | categories.map((e) => e.toJson()).toList(), 135 | ); 136 | } 137 | 138 | Future unregister() async { 139 | await _channel.invokeMethod('unregister'); 140 | token.value = null; 141 | } 142 | } 143 | 144 | class IosNotificationSettings { 145 | const IosNotificationSettings({ 146 | this.sound = true, 147 | this.alert = true, 148 | this.badge = true, 149 | }); 150 | 151 | IosNotificationSettings._fromMap(Map settings) 152 | : sound = settings['sound'], 153 | alert = settings['alert'], 154 | badge = settings['badge']; 155 | 156 | final bool? sound; 157 | final bool? alert; 158 | final bool? badge; 159 | 160 | Map toMap() { 161 | return {'sound': sound, 'alert': alert, 'badge': badge}; 162 | } 163 | 164 | @override 165 | String toString() => 'PushNotificationSettings ${toMap()}'; 166 | } 167 | 168 | /// https://developer.apple.com/documentation/usernotifications/unnotificationcategory 169 | class UNNotificationCategory { 170 | final String identifier; 171 | final List actions; 172 | final List intentIdentifiers; 173 | final List options; 174 | 175 | Map toJson() { 176 | return { 177 | 'identifier': identifier, 178 | 'actions': actions.map((e) => e.toJson()).toList(), 179 | 'intentIdentifiers': intentIdentifiers, 180 | 'options': _optionsToJson(options), 181 | }; 182 | } 183 | 184 | UNNotificationCategory({ 185 | required this.identifier, 186 | required this.actions, 187 | required this.intentIdentifiers, 188 | required this.options, 189 | }); 190 | } 191 | 192 | /// https://developer.apple.com/documentation/usernotifications/UNNotificationAction 193 | class UNNotificationAction { 194 | final String identifier; 195 | final String title; 196 | final List options; 197 | 198 | static const defaultIdentifier = 199 | 'com.apple.UNNotificationDefaultActionIdentifier'; 200 | 201 | /// Returns action identifier associated with this push. 202 | /// May be null, UNNotificationAction.defaultIdentifier, or value declared in setNotificationCategories 203 | static String? getIdentifier(Map payload) { 204 | final data = payload['data'] as Map?; 205 | return data?['actionIdentifier'] ?? payload['actionIdentifier']; 206 | } 207 | 208 | UNNotificationAction({ 209 | required this.identifier, 210 | required this.title, 211 | required this.options, 212 | }); 213 | 214 | dynamic toJson() { 215 | return { 216 | 'identifier': identifier, 217 | 'title': title, 218 | 'options': _optionsToJson(options), 219 | }; 220 | } 221 | } 222 | 223 | /// https://developer.apple.com/documentation/usernotifications/unnotificationactionoptions 224 | enum UNNotificationActionOptions { 225 | authenticationRequired, 226 | destructive, 227 | foreground, 228 | } 229 | 230 | /// https://developer.apple.com/documentation/usernotifications/unnotificationcategoryoptions 231 | enum UNNotificationCategoryOptions { 232 | customDismissAction, 233 | allowInCarPlay, 234 | hiddenPreviewsShowTitle, 235 | hiddenPreviewsShowSubtitle, 236 | allowAnnouncement, 237 | } 238 | 239 | List _optionsToJson(List values) { 240 | return values.map((e) => e.toString()).toList(); 241 | } 242 | -------------------------------------------------------------------------------- /flutter_apns_only/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.8.2" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.1.0" 18 | characters: 19 | dependency: transitive 20 | description: 21 | name: characters 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.2.0" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.3.1" 32 | clock: 33 | dependency: transitive 34 | description: 35 | name: clock 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.1.0" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.16.0" 46 | fake_async: 47 | dependency: transitive 48 | description: 49 | name: fake_async 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.3.0" 53 | flutter: 54 | dependency: "direct main" 55 | description: flutter 56 | source: sdk 57 | version: "0.0.0" 58 | flutter_test: 59 | dependency: "direct dev" 60 | description: flutter 61 | source: sdk 62 | version: "0.0.0" 63 | matcher: 64 | dependency: transitive 65 | description: 66 | name: matcher 67 | url: "https://pub.dartlang.org" 68 | source: hosted 69 | version: "0.12.11" 70 | material_color_utilities: 71 | dependency: transitive 72 | description: 73 | name: material_color_utilities 74 | url: "https://pub.dartlang.org" 75 | source: hosted 76 | version: "0.1.4" 77 | meta: 78 | dependency: transitive 79 | description: 80 | name: meta 81 | url: "https://pub.dartlang.org" 82 | source: hosted 83 | version: "1.7.0" 84 | path: 85 | dependency: transitive 86 | description: 87 | name: path 88 | url: "https://pub.dartlang.org" 89 | source: hosted 90 | version: "1.8.1" 91 | sky_engine: 92 | dependency: transitive 93 | description: flutter 94 | source: sdk 95 | version: "0.0.99" 96 | source_span: 97 | dependency: transitive 98 | description: 99 | name: source_span 100 | url: "https://pub.dartlang.org" 101 | source: hosted 102 | version: "1.8.2" 103 | stack_trace: 104 | dependency: transitive 105 | description: 106 | name: stack_trace 107 | url: "https://pub.dartlang.org" 108 | source: hosted 109 | version: "1.10.0" 110 | stream_channel: 111 | dependency: transitive 112 | description: 113 | name: stream_channel 114 | url: "https://pub.dartlang.org" 115 | source: hosted 116 | version: "2.1.0" 117 | string_scanner: 118 | dependency: transitive 119 | description: 120 | name: string_scanner 121 | url: "https://pub.dartlang.org" 122 | source: hosted 123 | version: "1.1.0" 124 | term_glyph: 125 | dependency: transitive 126 | description: 127 | name: term_glyph 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "1.2.0" 131 | test_api: 132 | dependency: transitive 133 | description: 134 | name: test_api 135 | url: "https://pub.dartlang.org" 136 | source: hosted 137 | version: "0.4.9" 138 | vector_math: 139 | dependency: transitive 140 | description: 141 | name: vector_math 142 | url: "https://pub.dartlang.org" 143 | source: hosted 144 | version: "2.1.2" 145 | sdks: 146 | dart: ">=2.17.0-0 <3.0.0" 147 | flutter: ">=1.12.13+hotfix.5" 148 | -------------------------------------------------------------------------------- /flutter_apns_only/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_apns_only 2 | description: APNS push notification plugin. Works only on iOS. See flutter_apns for apns & firebase combo 3 | version: 1.6.0 4 | homepage: https://github.com/mwaylabs/flutter-apns 5 | 6 | environment: 7 | sdk: ">=2.12.0-0 <3.0.0" 8 | flutter: ">=1.12.13+hotfix.5" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | 14 | dev_dependencies: 15 | flutter_test: 16 | sdk: flutter 17 | 18 | flutter: 19 | plugin: 20 | platforms: 21 | ios: 22 | pluginClass: FlutterApnsPlugin 23 | --------------------------------------------------------------------------------