Use foreground service with Expo and React Native
Asked Answered
F

2

8

I am using React Native together with Expo to build an app for tracking workouts. I want to implement a foreground service for ongoing workouts but also to keep Expo in my project.

I tried to use this approach: https://github.com/cristianoccazinsp/react-native-foreground-service

For this, permissions are needed to be added in Android Manifest file, in order to do that, I added the permissions in the app.json file:

"android": {
  "adaptiveIcon": {
    "foregroundImage": "./assets/adaptive-icon.png",
    "backgroundColor": "#FFFFFF",
    "permissions": ["FOREGROUND_SERVICE", "WAKE_LOCK"]
  }
},

the next step would be to add the service into the Android Manifest file:

<meta-data android:name="com.zinspector.foregroundservice.notification_channel_name"
            android:value="zInspector Service"/>
<meta-data  android:name="com.zinspector.foregroundservice.notification_channel_description"
            android:value="zInspector Service."/>
<meta-data  android:name="com.zinspector.foregroundservice.notification_color"
            android:resource="@color/orange"/>

<service android:name="com.zinspector.foregroundservice.ForegroundService"></service>
<service android:name="com.zinspector.foregroundservice.ForegroundServiceTask"></service>
```

The problem is that I did not find a solution to use expo and add those service properties using app.json

Could anyone help me with this issue or provide another solution for having a foreground service and keep using Expo ? Thank you

Fin answered 21/5, 2022 at 14:39 Comment(1)
Basically it relies upon the android capability which isn't built-in in the official SDK react-native of the expo app. So everything like this, you have to build your own app.Maccarthy
G
7

Looks like you need to create an expo config plugin to add meta-data and service values to the AndroidManifest.xml, also you need to copy one file.

I'll use the mentioned https://github.com/cristianoccazinsp/react-native-foreground-service library installation steps as an example.

First, you will need to start using expo prebuild in your project, it exposes the native files without ejecting from expo and lets us keep all of the expo tooling, you can ignore the native folders in your repo because they will be re-generated on every build.

I usually end up having something like this in the package.json scripts for easier access:

"eas:build": "npx expo prebuild && eas build --platform=android",
"prebuild": "npx expo prebuild",

You can test the prebuild separately also by just running npx expo prebuild and check if the native ios and android folders are created.

Next, let's create a configPlugins folder for the files we need into your project root.

Next, we need to create an expo config plugin, which allows us to edit and copy native files during the expo prebuild step.

configPlugins/withForegroundService.ts

import { AndroidConfig, ConfigPlugin, withAndroidManifest } from '@expo/config-plugins'
import path from 'path'
import { Paths } from '@expo/config-plugins/build/android'
import fs from 'fs'

export const withForegroundService: ConfigPlugin = (config) => {
    config = withAndroidManifest(config, async (config) => {
        const mainApplication = AndroidConfig.Manifest.getMainApplicationOrThrow(config.modResults)

        // set the metadata
        AndroidConfig.Manifest.addMetaDataItemToMainApplication(
            mainApplication,
            'com.zinspector.foregroundservice.notification_channel_name',
            'zInspector Service',
            'value',
        )
        AndroidConfig.Manifest.addMetaDataItemToMainApplication(
            mainApplication,
            'com.zinspector.foregroundservice.notification_channel_description',
            'zInspector Service.',
            'value',
        )
        AndroidConfig.Manifest.addMetaDataItemToMainApplication(
            mainApplication,
            'com.zinspector.foregroundservice.notification_color',
            '@color/orange',
            'resource',
        )

        // set the services
        mainApplication.service = []
        mainApplication.service.push({
            $: {
                'android:name': 'com.zinspector.foregroundservice.ForegroundService',
            },
        })
        mainApplication.service.push({
            $: {
                'android:name': 'com.zinspector.foregroundservice.ForegroundServiceTask',
            },
        })

        // copy the color.xml
        const srcFilePath = path.join(__dirname, 'color.xml')
        const resFilePath = path.join(
            await Paths.getResourceFolderAsync(config.modRequest.projectRoot),
            'values',
            'color.xml',
        )

        const res_dir = path.resolve(resFilePath, '..')

        if (!fs.existsSync(res_dir)) {
            await fs.promises.mkdir(res_dir)
        }

        try {
            await fs.promises.copyFile(srcFilePath, resFilePath)
        } catch (e) {
            throw e
        }

        return config
    })

    return config
}

Next, create the color.xml as the library requires

configPlugins/color.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item  name="orange"  type="color">#FF4500
    </item>
    <integer-array  name="androidcolors">
        <item>@color/orange</item>
    </integer-array>
</resources>

Now we need to tell our app.config.ts about the plugin. You can use app.json also, but then the withForegroundService.ts needs to be a .js file and will be registered like this: plugins: [['./configPlugins/withForegroundService']].

app.config.ts

import { ExpoConfig, ConfigContext } from '@expo/config'
import { withForegroundService } from './configPlugins/withForegroundService'

export default ({ config }: ConfigContext): ExpoConfig => ({
    ...config,
    ...
    ...
    plugins: [
        withForegroundService,
    ],
})

And to test it out just run npx expo prebuild, if everything is set up correctly, you should see the changes in android/app/src/main/AndroidManifest.xml and color.xml should be copied to android/app/src/main/res/values/color.xml

Now only thing to do is configure your foreground task and run npx expo prebuild && eas build --platform=android

I tested this approach and got the foreground service notification to appear. foreground service notification

Hope this helps!

Gloaming answered 14/3, 2023 at 22:41 Comment(0)
V
1

Not a sufficient answer, but anything is better than nothing. I used to create a foreground service. The challenge was to keep expo in the project. I'm using Expo Managed (EAS), but was (so far) unable to get it built, using the instructions here. Without Expo this does work however.

https://github.com/Raja0sama/rn-foreground-service

If there is someone more experienced able to explain how to get a foreground service running, preferably fully integrated with Expo, but if not, usable in a through EAS built project, please help.

Vinasse answered 5/1, 2023 at 14:30 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Penultimate

© 2022 - 2024 — McMap. All rights reserved.