Best practices
Platform-specific guidance for running CAMS in production apps on Android and iOS, including background sensing, iOS setup, permissions and build issues.
This page collects platform-specific guidance and tips for running CAMS in production apps.
Background sensing
From version 2.0, CAMS supports background sensing natively on Android using the flutter_background plugin. iOS does not allow apps to run in the background as such, but you can optimize data sampling on iOS.
Android
To keep the app running in the background, configure the SmartPhoneClientManager with background mode enabled and set the title and text of the background notification:
With background mode enabled, the app shows a battery optimization permission dialog and the background notification:
Battery optimization permission
Background notification
From Android 9, battery management has been significantly improved, which means that an app is put to sleep when it is in the background. This is a problem for apps that use CAMS for passive data collection. Some Android devices are repeatedly reported not to work in the background. Before you report a new issue, check whether your device model is on the Don't Kill My App list.
Don't Kill My App has suggestions for different phone vendors and OS versions. A general approach is to configure the settings for the app itself and for the battery:
In the Android Settings, optimize background mode for the app by allowing unrestricted battery usage and background data usage.
Unrestricted battery usage
Background data usage
In the Android Settings, optimize the battery settings for background mode.
Power saving off
Background usage limits off
Further reading:
- Issue #97: discussion of background sensing.
- This Reddit thread: how to keep an app running in the background on Samsung phones.
- Don't Kill My App: suggestions per vendor, for example for Samsung phones.
- Setup page for the Cardata app: simple guidelines for different phones.
iOS
On iOS, the best way to keep the app running in the background, to some degree, is to fetch location in the background or keep a BLE connection to an external device.
The carp_context_package uses the location plugin, which supports collecting location in the background. Follow the instructions of that package, including the changes to the Info.plist and AppDelegate.swift files. Also make sure that Allow location tracking always is enabled for the app on the phone.
iOS tends to aggressively kill apps, especially from iOS 15. Check these settings on the phone:
| Setting | Location | Value |
|---|---|---|
| Low Power Mode | Settings → Battery | Off |
| Background App Refresh | Settings → [Your app] → Background App Refresh | On |
If Background App Refresh is greyed out for the app, first enable it globally in Settings → General → Background App Refresh, then enable it for the app.
Other ways to keep the app running:
- Guided Access: the Guided Access feature in iOS forces the app to be the only app running, always in the foreground. It is therefore not useful for background sensing, but it can be useful when a phone is dedicated to one study, for example a study phone handed to a participant to use only for that study.
- Audio playback: configure the app as an audio player app. iOS keeps audio playing apps running in the background. This is a hack; see the sensors app for inspiration.
- Open BLE connection: as shown in this coverage study, iOS keeps the app running in the background if it samples data from a BLE device. This drains the battery significantly.
iOS setup
Podfile
In a new Flutter project, the iOS Podfile is auto-generated in the ios folder. Make these edits:
- Specify the platform version, for example
platform :ios, '12.0'. It should be later than 9.3.- You might also need to set this manually in Xcode. See this issue.
- In the
post_installsection, setconfig.build_settingsto the same platform version (see here and here). - Specify the Swift version by adding
ENV['SWIFT_VERSION'] = '5'. - In the
target 'RUNNER'section, adduse_frameworks!anduse_modular_headers!if Flutter has not already added them. - In the
target 'RUNNER'section, remove thetarget 'RunnerTests'section, which may cause build issues.
An edited Podfile looks like this:
File management
If you store data locally on the phone with the SQLiteDataManager or FileDataManager and want to give the app user (for example yourself, during development and debugging) access to it on the phone or in the macOS Finder, add these keys to Info.plist:
Wi-Fi
On iOS 12 and later, collection of Wi-Fi information is restricted. See:
- connectivity
- CNCopyCurrentNetworkInfo
- Access WiFi Information Entitlement
- How To Enable the WiFi Information Entitlement in iOS 13
App.framework compatibility
You may get this error, or the reverse:
This is a well-known issue with iOS / Xcode 11.4 for Flutter versions earlier than v1.15.3. See Flutter's archived Xcode migration page.
Notifications
The default notification controller in CAMS is FlutterLocalNotificationManager, which uses the flutter_local_notifications plugin. It supports scheduling and receiving notifications while the app is in the foreground, in the background or terminated, but requires platform-specific setup.
Note
Follow the flutter_local_notifications README and its platform setup guides for Android and iOS.
Initializing WidgetsFlutterBinding
To initialize the Flutter platform channels, call WidgetsFlutterBinding.ensureInitialized() in your main() method before you create the app. This is the main method of the CAMS demo app:
See What Does WidgetsFlutterBinding.ensureInitialized() do? for an explanation.
Android manifest merge
When you build your app, the AndroidManifest.xml files from all packages and their underlying plugins are merged.
An app that uses CARP packages and plugins can be rejected from the Google Play Store if it, for example, tries to collect SMS texts and call logs. Google considers these a restricted group of permissions that need a valid reason for access, and research is not on the list of exceptions. In that case you may want to remove collection of SMS texts and call logs while keeping collection of calendar information (all in the carp_communication_package).
To omit restricted permissions, specify that they must not be merged from a lower-level manifest. Add these lines to your app's AndroidManifest.xml:
See also issue #183.
Permissions
CAMS uses the permission_handler plugin to handle permissions. See its documentation for how to configure permissions on iOS and Android. On iOS, update both Info.plist and Podfile; otherwise you may get app review issues with Apple.
Build issues
If you have a build issue, work through these steps:
Clean the project
Run this in your project and try again:
On Android, you might also want to run
./gradlew clean. On iOS, you might want to remove the.symlinkfolder.Update your tools
Update Xcode, Android Studio, VS Code and the Android SDKs to the latest versions.
Compare with the example app
If the issue persists, clone
carp_mobile_sensingand run theexampleapp on the same platform. If that works, compare your project with the example setup.Open an issue
If the issue still persists and there is no existing GitHub issue, open a new issue. Use the issue template and provide a reproducible case based on the
exampleapp.Only open an issue after you have tried the
exampleapp. Build issues without reproducible example details may be closed.
MissingPluginException or PlatformException
You may get a MissingPluginException or PlatformException from plugins that CAMS uses, such as flutter_background. Even though pub get and pub upgrade fetch and update these transitive plugins and the app compiles, you may still need to depend on them explicitly in your app's pubspec.yaml file. See issue #530.