Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:

- uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
with:
flutter-version: '3.24'
flutter-version: '3.41.9'
cache: true

# This step requires fetch of test_integration packages because flutter format and
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:

- uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
with:
flutter-version: '3.24'
flutter-version: '3.41.9'
cache: true

- name: Use Intro Blurb As Homepage
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/flutter_example_app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:

- uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
with:
flutter-version: "3.24"
flutter-version: "3.41.9"
cache: true

- name: "Build example app for iOS"
Expand All @@ -39,7 +39,7 @@ jobs:

- uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
with:
flutter-version: "3.29"
flutter-version: "3.41.9"
cache: true

- name: "Build example app for Android"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/flutter_integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:

- uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
with:
flutter-version: '3.29'
flutter-version: '3.41.9'
cache: true

# test_integration package depends on ably_flutter, so before we run integration
Expand Down Expand Up @@ -81,7 +81,7 @@ jobs:

- uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
with:
flutter-version: '3.29'
flutter-version: '3.41.9'
cache: true

- name: 'Run Flutter Driver tests'
Expand Down
11 changes: 8 additions & 3 deletions .github/workflows/ios_unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,24 @@ jobs:

- uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2.23.0
with:
flutter-version: '3.29'
flutter-version: '3.41.9'
cache: true

- name: Set up iOS test project
# --skip-tests sets up the CocoaPods workspace (downloads all deps, generates
# the ably_flutter-Unit-Tests scheme) without booting a simulator
# the ably_flutter-Unit-Tests scheme) without booting a simulator.
#
# --include-podspecs supplies a local Flutter pod backed by this SDK's engine
# artifacts; the one on CocoaPods trunk is pinned at 3.13.0 and predates the
# UIScene plugin APIs. See ios/Tests/Support/Flutter.podspec.
run: |
cd ios
pod lib lint ably_flutter.podspec \
--allow-warnings \
--skip-tests \
--validation-dir=/tmp/ably_test_build \
--no-clean
--no-clean \
--include-podspecs=Tests/Support/Flutter.podspec

- name: Pick simulator
run: |
Expand Down
3 changes: 3 additions & 0 deletions .pubignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
# See <https://github.com/ably/ably-flutter/issues/400#issuecomment-1129236627>
test/
test_integration/

# Development-only lint helper, of no use to consumers
ios/Tests/Support/
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
ruby 3.4.5
flutter 3.29.0
flutter 3.41.9
64 changes: 59 additions & 5 deletions PushNotifications.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ See [issue #226](https://github.com/ably/ably-flutter/issues/226).
- Android API level 19+ (Android 4.4+)
- Android devices
- Android emulator (with Google APIs)
- iOS 10+
- iOS 13+
- Physical devices only
- **Not supported:** iOS Simulator. Calling [`UIApplication:registerForRemoteNotifications`](https://developer.apple.com/documentation/uikit/uiapplication/1623078-registerforremotenotifications) will result in [`application:didFailToRegisterForRemoteNotificationsWithError`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622962-application) method being called in your AppDelegate with an error: `remote notifications are not supported in the simulator`). This is an iOS simulator limitation.

Expand Down Expand Up @@ -77,11 +77,65 @@ To get push notifications setup in your own app, read [Setting up your own app](
- Add `remote notification` Background mode:
- Under the **Signing & Capabilities** tab, click `+ Capability` and select `Background Modes`.
- Check `remote notifications`.
- In your `AppDelegate.swift` or `AppDelegate.m`, implement [`application:didFailToRegisterForRemoteNotificationsWithError:`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622962-application). An example is shown in the example app, [`AppDelegate.m`](./example/ios/Runner/AppDelegate.m).
- If your app uses the UIScene life cycle, register Ably's notification handlers from your `AppDelegate`. See [UIScene life cycle](#uiscene-life-cycle) below — this is required, and push notifications will not work correctly without it.
- In your `AppDelegate.swift` or `AppDelegate.m`, implement [`application:didFailToRegisterForRemoteNotificationsWithError:`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622962-application). An example is shown in the example app, [`AppDelegate.swift`](./example/ios/Runner/AppDelegate.swift).
- During development, place a breakpoint in this method to diagnose why your device cannot register with APNs. This method will be called when there is an error, for example, if entitlements are not configured or when registering for APNs on the iOS simulator. You can check the `error` argument. If an error occurs in this method, Ably will not get the APNs device token. [From the `application(_:didFailToRegisterForRemoteNotificationsWithError:)` documentation](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622962-application):
> UIKit calls this method if it was unable to register your app with APNs or if your app is not properly configured for remote notifications. During development, make sure your app has the proper entitlements and that its App ID is configured to support push notifications. You might use your implementation of this method to make a note of the failed registration so that you can try again later.
- Like the example app, you do not need to implement [`UIApplication:registerForRemoteNotifications`](https://developer.apple.com/documentation/uikit/uiapplication/1623078-registerforremotenotifications), as the Ably plugin does this for you at app launch.
- Handle messages received on your device by implementing the methods in your `AppDelegate`, such as `didReceiveRemoteNotification`, `didReceiveNotificationResponse` and `willPresentNotification`. This is shown in the example app, [`AppDelegate.m`](./example/ios/Runner/AppDelegate.m). For more information, have a look at the [receiving messages](#receiving-messages) section.
- Handle messages received on your device by implementing the methods in your `AppDelegate`, such as `didReceiveRemoteNotification`, `didReceiveNotificationResponse` and `willPresentNotification`. This is shown in the example app, [`AppDelegate.swift`](./example/ios/Runner/AppDelegate.swift). For more information, have a look at the [receiving messages](#receiving-messages) section.

### UIScene life cycle

Apple will require UIKit apps built against the SDK released after iOS 26 to adopt the [UIScene life cycle](https://developer.apple.com/documentation/uikit/scenes), and Flutter is [migrating accordingly](https://docs.flutter.dev/release/breaking-changes/uiscenedelegate). If your app has adopted it — that is, its `Info.plist` declares a `UIApplicationSceneManifest` — you **must** register Ably's push notification handlers yourself, from `application:didFinishLaunchingWithOptions:`:

```swift
import UIKit
import Flutter
import ably_flutter

@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
AblyFlutter.sharedInstance().registerPushNotificationHandlers()

GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
```

```objective-c
#import <ably_flutter/AblyFlutter.h>

@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[AblyFlutter sharedInstance] registerPushNotificationHandlers];

[GeneratedPluginRegistrant registerWithRegistry:self];
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
```

This is necessary because Apple requires [`UNUserNotificationCenter.delegate`](https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate) to be set before `application:didFinishLaunchingWithOptions:` returns, but for apps on the UIScene life cycle Flutter defers plugin registration until after it returns. The plugin therefore cannot install the delegate early enough on its own.

Apps still on the `UIApplicationDelegate` life cycle do not need to call this — plugin registration does it for them. Calling it anyway is safe: repeat calls are ignored rather than installing the delegate twice, so it is fine to add the call before migrating.

The example app ([`AppDelegate.swift`](./example/ios/Runner/AppDelegate.swift), [`Info.plist`](./example/ios/Runner/Info.plist)) runs on the UIScene life cycle and shows the whole setup.

### Opting out of Ably handling push notifications

By default the plugin installs its own `UNUserNotificationCenterDelegate` so it can forward notification events to Dart. To handle notifications entirely yourself, set `AblyFlutterHandlePushNotifications` to `NO` in your app's `Info.plist`:

```xml
<key>AblyFlutterHandlePushNotifications</key>
<false/>
```

With this set, the plugin installs no notification delegate and declines remote notifications so that your own `AppDelegate` methods handle them. `registerPushNotificationHandlers` also becomes a no-op. Note that the Dart-side push notification event APIs will not fire.

## Usage

Expand Down Expand Up @@ -446,7 +500,7 @@ Then, in your Android Manifest, disable Ably Flutter's broadcast receiver by rem

**iOS**: Implementing the [`didReceiveRemoteNotification` delegate method](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623013-application) declared in `UIApplicationDelegate`.

Take a look at the example app platform specific code to handle messages. For iOS, this is `AppDelegate.m`, and in Android, it is `PushMessagingService.java`. For further help on implementing the Platform specific message handlers, see "On Android" and "On iOS" sections on [Push Notifications - Device activation and subscription](https://ably.com/documentation/general/push/activate-subscribe).
Take a look at the example app platform specific code to handle messages. For iOS, this is `AppDelegate.swift`, and in Android, it is `PushMessagingService.java`. For further help on implementing the Platform specific message handlers, see "On Android" and "On iOS" sections on [Push Notifications - Device activation and subscription](https://ably.com/documentation/general/push/activate-subscribe).

### Additional considerations and resources
- For tips on how best to use push messaging on Android, read [Notifying your users with FCM](https://android-developers.googleblog.com/2018/09/notifying-your-users-with-fcm.html). For example:
Expand Down Expand Up @@ -563,7 +617,7 @@ For Android device registrations, the device push state error are errors passed

### Why are notifications not shown to the user when the app is open on Android?

When the app is in the foreground (open by the user), Firebase messaging ignores the message. You would need to send a data message and build a local notification instead. On iOS, you can specify this in your `UNUserNotificationCenterDelegate`'s `userNotificationCenter:_willPresentNotification:withCompletionHandler` method. In the example app, this is implemented in `AppDelegate.m`, where the notification is always shown. You can perform logic to decide if it should be shown or not based on the notification.
When the app is in the foreground (open by the user), Firebase messaging ignores the message. You would need to send a data message and build a local notification instead. On iOS, the plugin handles `userNotificationCenter:willPresentNotification:withCompletionHandler:` for you and asks Dart whether to show the notification, so decide this from [`PushNotificationEvents#setOnShowNotificationInForeground`](#showinghiding-foreground-notifications) rather than in your `AppDelegate`.

### Messaging generated from the "compose notification" in Firebase cloud messaging console are not received.

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ This SDK supports the following platforms:
| Platform | Support |
|----------|---------|
| Android | Android 4.4 (API level 19) or newer. Java 8 language features supported via [Desugaring](https://developer.android.com/studio/write/java8-support#library-desugaring). |
| iOS | iOS 10 or newer |
| Flutter | Flutter 2.5.0 or higher |
| iOS | iOS 13 or newer |
| Flutter | Flutter 3.38.0 or higher |

> [!NOTE]
> If your project needs support for SDK Version lower than 24, Android Gradle Plugin 4.0.0+ must be used.
Expand Down
30 changes: 30 additions & 0 deletions UPDATING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,36 @@

This guide lists the changes needed to upgrade from one version of Ably to a newer one when there are breaking changes.

## [Upgrading from v1.2.44]

This release adds support for Apple's [UIScene life cycle](https://developer.apple.com/documentation/uikit/scenes), which UIKit apps built against the SDK released after iOS 26 will be required to use.

- Updated Dart SDK constraint to `>=3.10.0 <4.0.0` and Flutter constraint to `>=3.38.0`. Flutter 3.38 is the first release exposing the plugin APIs needed for the UIScene life cycle.
- The minimum supported iOS version is now 13, up from 10. Raise `IPHONEOS_DEPLOYMENT_TARGET` and your Podfile's `platform :ios` to at least `13.0` if they are lower.
- **If your iOS app has adopted the UIScene life cycle** — that is, its `Info.plist` declares a `UIApplicationSceneManifest` — you must now register Ably's push notification handlers from your `AppDelegate`:

```swift
import ably_flutter

@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
AblyFlutter.sharedInstance().registerPushNotificationHandlers()

GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
```

Apple requires the `UNUserNotificationCenterDelegate` to be set before `application:didFinishLaunchingWithOptions:` returns, but Flutter defers plugin registration until after that for scene-based apps, so the plugin can no longer install it early enough by itself. See [Push notifications: UIScene life cycle](./PushNotifications.md#uiscene-life-cycle).

Apps still on the `UIApplicationDelegate` life cycle need no changes. Adding the call anyway is safe, so you can add it before migrating.
- `AblyFlutter` no longer declares conformance to `UNUserNotificationCenterDelegate`. It never implemented any of those methods, so installing it as the notification centre's delegate had no effect. Use `registerPushNotificationHandlers` instead.

## [Upgrading from v1.2.13]

- Updated SDK constraint to `>=2.14.0 <3.0.0`
Expand Down
1 change: 0 additions & 1 deletion analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ linter:
- no_runtimeType_toString
- omit_local_variable_types
- only_throw_errors
- package_api_docs
- prefer_adjacent_string_concatenation
- prefer_asserts_in_initializer_lists
- prefer_expression_function_bodies
Expand Down
Loading
Loading