High-level plugins for modifying iOS project files including Info.plist, entitlements, Xcode project configuration, and AppDelegate files. These plugins provide convenient interfaces for common iOS development tasks.
Modify the Info.plist file with structured property list manipulation.
function withInfoPlist(
config: ExpoConfig,
action: Mod<InfoPlist>
): ExpoConfig;Usage:
config = withInfoPlist(config, (config) => {
const infoPlist = config.modResults;
// Set app display name
infoPlist.CFBundleDisplayName = "My Custom App";
// Add URL schemes
infoPlist.CFBundleURLTypes = [
{
CFBundleURLName: "com.example.myapp",
CFBundleURLSchemes: ["myapp", "mycustomscheme"]
}
];
return config;
});Helper for creating Info.plist modification plugins.
function createInfoPlistPlugin(
action: (expo: ExpoConfig, infoPlist: InfoPlist) => Promise<InfoPlist> | InfoPlist,
name?: string
): ConfigPlugin;Create an Info.plist plugin with property conflict detection.
function createInfoPlistPluginWithPropertyGuard(
action: (expo: ExpoConfig, infoPlist: InfoPlist) => Promise<InfoPlist> | InfoPlist,
settings: {
infoPlistProperty: string;
expoConfigProperty: string;
expoPropertyGetter?: (config: ExpoConfig) => string;
},
name?: string
): ConfigPlugin;Modify the app's entitlements file for capabilities and permissions.
function withEntitlementsPlist(
config: ExpoConfig,
action: Mod<Plist>
): ExpoConfig;Usage:
config = withEntitlementsPlist(config, (config) => {
const entitlements = config.modResults;
// Enable associated domains
entitlements["com.apple.developer.associated-domains"] = [
"applinks:example.com",
"webcredentials:example.com"
];
// Enable keychain access groups
entitlements["keychain-access-groups"] = [
"$(AppIdentifierPrefix)com.example.myapp"
];
return config;
});Modify the Expo.plist file for Expo Updates configuration.
function withExpoPlist(
config: ExpoConfig,
action: Mod<Plist>
): ExpoConfig;Modify the Xcode project file (.pbxproj) for build settings and configurations.
function withXcodeProject(
config: ExpoConfig,
action: Mod<XcodeProject>
): ExpoConfig;Usage:
config = withXcodeProject(config, (config) => {
const xcodeProject = config.modResults;
// Add build setting
const configurations = xcodeProject.pbxXCBuildConfigurationSection();
Object.keys(configurations).forEach(key => {
const configuration = configurations[key];
if (configuration.buildSettings) {
configuration.buildSettings.CUSTOM_FLAG = "YES";
configuration.buildSettings.OTHER_LDFLAGS = [
"$(inherited)",
"-framework CustomFramework"
];
}
});
return config;
});Modify the AppDelegate.m or AppDelegate.swift file.
function withAppDelegate(
config: ExpoConfig,
action: Mod<AppDelegateProjectFile>
): ExpoConfig;Usage:
config = withAppDelegate(config, (config) => {
const appDelegate = config.modResults;
// Add import
if (!appDelegate.contents.includes("#import <CustomSDK/CustomSDK.h>")) {
appDelegate.contents = appDelegate.contents.replace(
/#import "AppDelegate\.h"/,
`#import "AppDelegate.h"
#import <CustomSDK/CustomSDK.h>`
);
}
// Add initialization code
const initCode = ` [CustomSDK initializeWithKey:@"your-key"];`;
if (!appDelegate.contents.includes(initCode)) {
appDelegate.contents = appDelegate.contents.replace(
/- \(BOOL\)application:\(UIApplication \*\)application didFinishLaunchingWithOptions:\(NSDictionary \*\)launchOptions\s*{/,
`- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
${initCode}`
);
}
return config;
});Modify Podfile properties for CocoaPods configuration.
function withPodfileProperties(
config: ExpoConfig,
action: Mod<Record<string, string>>
): ExpoConfig;Usage:
config = withPodfileProperties(config, (config) => {
const properties = config.modResults;
// Set iOS deployment target
properties["ios.deploymentTarget"] = "12.0";
// Enable modular headers
properties["ios.useModularHeaders"] = "true";
return config;
});Legacy Podfile modification plugin - use withPodfileProperties instead.
function withPodfile(
config: ExpoConfig,
action: Mod<PodfileProjectFile>
): ExpoConfig;interface InfoPlist extends Record<string, JSONValue | undefined> {
UIStatusBarHidden?: boolean;
UIStatusBarStyle?: string;
UILaunchStoryboardName?: string | "SplashScreen";
CFBundleShortVersionString?: string;
CFBundleVersion?: string;
CFBundleDisplayName?: string;
CFBundleIdentifier?: string;
CFBundleName?: string;
CFBundleURLTypes?: URLScheme[];
CFBundleDevelopmentRegion?: string;
ITSAppUsesNonExemptEncryption?: boolean;
LSApplicationQueriesSchemes?: string[];
UIBackgroundModes?: string[];
UISupportedInterfaceOrientations?: InterfaceOrientation[];
UISupportedInterfaceOrientationsIPhone?: InterfaceOrientation[];
UISupportedInterfaceOrientationsIPad?: InterfaceOrientation[];
UIRequiredDeviceCapabilities?: string[];
NSAppTransportSecurity?: Record<string, any>;
NSLocationWhenInUseUsageDescription?: string;
NSLocationAlwaysAndWhenInUseUsageDescription?: string;
NSCameraUsageDescription?: string;
NSMicrophoneUsageDescription?: string;
NSPhotoLibraryUsageDescription?: string;
}
interface URLScheme {
CFBundleURLName?: string;
CFBundleURLSchemes: string[];
}
type InterfaceOrientation =
| "UIInterfaceOrientationPortrait"
| "UIInterfaceOrientationPortraitUpsideDown"
| "UIInterfaceOrientationLandscapeLeft"
| "UIInterfaceOrientationLandscapeRight";
interface AppDelegateProjectFile {
path: string;
contents: string;
language: "objc" | "swift";
}
interface PodfileProjectFile {
path: string;
contents: string;
}
type Plist = Record<string, any>;
type XcodeProject = any; // From 'xcode' packageimport {
withInfoPlist,
withEntitlementsPlist,
withXcodeProject,
withAppDelegate,
IOSConfig,
ConfigPlugin
} from "@expo/config-plugins";
const withCompleteiOSSetup: ConfigPlugin<{
appName: string;
bundleId: string;
urlScheme: string;
framework: string;
}> = (config, { appName, bundleId, urlScheme, framework }) => {
// Update Info.plist
config = withInfoPlist(config, (config) => {
const infoPlist = config.modResults;
infoPlist.CFBundleDisplayName = appName;
infoPlist.CFBundleIdentifier = bundleId;
infoPlist.CFBundleURLTypes = [
{
CFBundleURLName: bundleId,
CFBundleURLSchemes: [urlScheme]
}
];
return config;
});
// Setup entitlements
config = withEntitlementsPlist(config, (config) => {
const entitlements = config.modResults;
entitlements["com.apple.developer.associated-domains"] = [
`applinks:${urlScheme}.com`
];
return config;
});
// Configure Xcode project
config = withXcodeProject(config, (config) => {
const xcodeProject = config.modResults;
// Add framework
xcodeProject.addFramework(`${framework}.framework`);
return config;
});
// Modify AppDelegate
config = withAppDelegate(config, (config) => {
const appDelegate = config.modResults;
// Add import
const importLine = `#import <${framework}/${framework}.h>`;
if (!appDelegate.contents.includes(importLine)) {
appDelegate.contents = appDelegate.contents.replace(
/#import "AppDelegate\.h"/,
`#import "AppDelegate.h"\n${importLine}`
);
}
return config;
});
return config;
};const withPrivacyPermissions: ConfigPlugin<{
locationUsage?: string;
cameraUsage?: string;
microphoneUsage?: string;
}> = (config, { locationUsage, cameraUsage, microphoneUsage }) => {
return withInfoPlist(config, (config) => {
const infoPlist = config.modResults;
if (locationUsage) {
infoPlist.NSLocationWhenInUseUsageDescription = locationUsage;
infoPlist.NSLocationAlwaysAndWhenInUseUsageDescription = locationUsage;
}
if (cameraUsage) {
infoPlist.NSCameraUsageDescription = cameraUsage;
}
if (microphoneUsage) {
infoPlist.NSMicrophoneUsageDescription = microphoneUsage;
}
return config;
});
};const withAdvancedXcodeConfig: ConfigPlugin = (config) => {
return withXcodeProject(config, (config) => {
const xcodeProject = config.modResults;
// Get build configurations
const configurations = xcodeProject.pbxXCBuildConfigurationSection();
Object.keys(configurations).forEach(key => {
const configuration = configurations[key];
if (configuration.buildSettings) {
// Set deployment target
configuration.buildSettings.IPHONEOS_DEPLOYMENT_TARGET = "12.0";
// Enable bitcode
configuration.buildSettings.ENABLE_BITCODE = "YES";
// Add compiler flags
configuration.buildSettings.OTHER_CFLAGS = [
"$(inherited)",
"-DCUSTOM_FLAG=1"
];
// Add linker flags
configuration.buildSettings.OTHER_LDFLAGS = [
"$(inherited)",
"-framework CustomFramework",
"-lc++"
];
}
});
return config;
});
};