diff --git a/packages/config/src/android/Permissions.ts b/packages/config/src/android/Permissions.ts index a995eff3ae..a49a3fc11c 100644 --- a/packages/config/src/android/Permissions.ts +++ b/packages/config/src/android/Permissions.ts @@ -1,5 +1,6 @@ import { ExpoConfig } from '../Config.types'; -import { createAndroidManifestPlugin } from '../plugins/android-plugins'; +import { ConfigPlugin } from '../Plugin.types'; +import { withAndroidManifest } from '../plugins/android-plugins'; import { AndroidManifest, ManifestUsesPermission } from './Manifest'; const USES_PERMISSION = 'uses-permission'; @@ -43,7 +44,21 @@ export const allPermissions = [ 'com.sonyericsson.home.permission.BROADCAST_BADGE', ]; -export const withPermissions = createAndroidManifestPlugin(setAndroidPermissions); +export const withPermissions: ConfigPlugin = (config, permissions) => { + if (Array.isArray(permissions)) { + permissions = permissions.filter(Boolean); + if (!config.android) config.android = {}; + if (!config.android.permissions) config.android.permissions = []; + config.android.permissions = [ + // @ts-ignore + ...new Set(config.android.permissions.concat(permissions)), + ]; + } + return withAndroidManifest(config, async config => { + config.modResults = await setAndroidPermissions(config, config.modResults); + return config; + }); +}; function prefixAndroidPermissionsIfNecessary(permissions: string[]): string[] { return permissions.map(permission => { diff --git a/packages/config/src/ios/Permissions.ts b/packages/config/src/ios/Permissions.ts new file mode 100644 index 0000000000..82e0a22ef1 --- /dev/null +++ b/packages/config/src/ios/Permissions.ts @@ -0,0 +1,46 @@ +import { ConfigPlugin } from '../Plugin.types'; +import { withInfoPlist } from '../plugins/ios-plugins'; +import { InfoPlist } from './IosConfig.types'; + +/** + * Apply permissions and their respective descriptions to the iOS Info.plist. + * Providing a null description will remove the permission from the Info.plist. + * + * @param config + * @param permissions record of strings where the key matches Info.plist permissions and the values are the permission descriptions. + */ +export const withPermissions: ConfigPlugin> = ( + config, + permissions +) => { + return withInfoPlist(config, async config => { + config.modResults = applyPermissions(permissions, config.modResults); + return config; + }); +}; + +export function applyPermissions( + permissions: Record, + infoPlist: Record +): InfoPlist { + const entries = Object.entries(permissions); + if (entries.length === 0) { + // TODO: Debug warn + // console.warn('[withPermissions] no permissions were provided'); + } + for (const [permission, description] of entries) { + if (description == null) { + delete infoPlist[permission]; + } else { + const existingPermission = infoPlist[permission]; + if (existingPermission && existingPermission !== description) { + // TODO: Debug warn + // console.warn( + // `[withPermissionsIos][conflict] permission "${permission}" is already defined in the Info.plist with description "${existingPermission}"` + // ); + } + infoPlist[permission] = description; + } + } + return infoPlist; +} diff --git a/packages/config/src/ios/__tests__/Permissions-test.ts b/packages/config/src/ios/__tests__/Permissions-test.ts new file mode 100644 index 0000000000..026cb6f0dd --- /dev/null +++ b/packages/config/src/ios/__tests__/Permissions-test.ts @@ -0,0 +1,15 @@ +import { applyPermissions } from '../Permissions'; + +describe(applyPermissions, () => { + it(`applies permissions`, () => { + expect( + applyPermissions( + { baz: 'new', foo: null, echo: 'delta' }, + { + foo: 'bar', + baz: 'fox', + } + ) + ).toStrictEqual({ baz: 'new', echo: 'delta' }); + }); +}); diff --git a/packages/config/src/ios/index.ts b/packages/config/src/ios/index.ts index 5436b90098..61805d976a 100644 --- a/packages/config/src/ios/index.ts +++ b/packages/config/src/ios/index.ts @@ -11,6 +11,7 @@ import * as Locales from './Locales'; import * as Name from './Name'; import * as Orientation from './Orientation'; import * as Paths from './Paths'; +import * as Permissions from './Permissions'; import * as ProvisioningProfile from './ProvisioningProfile'; import * as RequiresFullScreen from './RequiresFullScreen'; import * as Scheme from './Scheme'; @@ -41,6 +42,7 @@ export { Orientation, Paths, ProvisioningProfile, + Permissions, RequiresFullScreen, Scheme, Updates,