Skip to content
Merged
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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ yarn-error.log
example/ios/
example/android/
example/google-services.json
.yarn/install-state.gz
Comment thread
jferrao-itrbl marked this conversation as resolved.
example/.yarn/install-state.gz

# Coverage
coverage/
coverage/
16 changes: 15 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,21 @@
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
and this project adheres to [Semantic
Versioning](https://semver.org/spec/v2.0.0.html).

## [1.1.0] - 2026-07-01

### Changed
- Updated `expo` to `^55.0.0` (Expo SDK 55)
- Updated `react` to `19.2.0`
- Updated `react-native` to `0.83.2`
- Updated `@iterable/react-native-sdk` peer dependency to `^3.0.0`
- Updated minimum Node.js version to `20.19.4`

### Notes
- Expo SDK 55 requires React Native's New Architecture
- Tested with Expo SDK 55

## [1.0.1] - 2025-07-16

Expand Down
7 changes: 7 additions & 0 deletions example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ npx expo run:android
- Check that `google-services.json` is properly placed (Android)
- Verify certificates and provisioning profiles (iOS)

4. **iOS build fails on `fmt` / `FMT_STRING` / `library 'fmt' not found` (Xcode 26.4+)**
- This is a known React Native + Xcode 26.4 compatibility issue
- `@iterable/expo-plugin` injects a Podfile workaround automatically during prebuild
- Rebuild native code: `npx expo prebuild --clean`, then `cd ios && pod install && cd ..`
- If you still see the error, confirm your generated `ios/Podfile` contains the
`@iterable/expo-plugin: fmt workaround for Xcode 26.4` comment inside `post_install`

### Development Tips

- Use `yarn start` to start the Metro bundler
Expand Down
2 changes: 1 addition & 1 deletion example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"open:android": "open -a \"Android Studio\" android"
},
"dependencies": {
"@iterable/react-native-sdk": "^2.0.2",
"@iterable/react-native-sdk": "^3.0.0",
"@react-navigation/native": "^7.0.19",
"@react-navigation/stack": "^7.2.3",
"expo": "^53.0.19",
Expand Down
11 changes: 5 additions & 6 deletions example/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1481,20 +1481,19 @@ __metadata:
languageName: node
linkType: hard

"@iterable/react-native-sdk@npm:^2.0.2":
version: 2.0.2
resolution: "@iterable/react-native-sdk@npm:2.0.2"
"@iterable/react-native-sdk@npm:^3.0.0":
version: 3.0.0
resolution: "@iterable/react-native-sdk@npm:3.0.0"
peerDependencies:
"@react-navigation/native": "*"
react: "*"
react-native: "*"
react-native-safe-area-context: "*"
react-native-vector-icons: "*"
react-native-webview: "*"
peerDependenciesMeta:
expo:
optional: true
checksum: 10c0/d1d6485dd3b982642df9569a4793aae0ff71191f8f6faffadbb580594db8e9cf2089f8a9b668d09c7b53e93ecbcd9d054833a8b82e0bb2a1d0d9a68a269d5967
checksum: 10c0/986244429e63646cd641a7f7be1a7c93be05f12000d80d1fa5cebce29bc4d87566e72766d1a3f57fe7f5c0a579a8c7d4b5b238cc815c8be161aef0a022314cfc
languageName: node
linkType: hard

Expand Down Expand Up @@ -3309,7 +3308,7 @@ __metadata:
resolution: "expo-plugin-example@workspace:."
dependencies:
"@babel/core": "npm:^7.25.2"
"@iterable/react-native-sdk": "npm:^2.0.2"
"@iterable/react-native-sdk": "npm:^3.0.0"
"@react-navigation/native": "npm:^7.0.19"
"@react-navigation/stack": "npm:^7.2.3"
"@types/react": "npm:~19.0.10"
Expand Down
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
"main": "build/withIterable.js",
"types": "build/withIterable.d.ts",
"scripts": {
"build": "expo-module build",
"build": "expo-module build && yarn build:plugin",
"build:plugin": "tsc -p plugin",
"clean": "expo-module clean",
"lint": "expo-module lint",
"test": "expo-module test",
"test:coverage": "jest test --coverage --config jest.config.js",
"prepare": "expo-module prepare",
"prepare": "expo-module prepare && yarn build:plugin",
"prepublishOnly": "expo-module prepublishOnly",
"expo-module": "expo-module",
"open:ios": "xed example/ios",
Expand All @@ -36,7 +37,7 @@
"devDependencies": {
"@commitlint/config-conventional": "^19.6.0",
"@evilmartians/lefthook": "^1.5.0",
"@iterable/react-native-sdk": "^2.0.2",
"@iterable/react-native-sdk": "^3.0.0",
"@react-native/eslint-config": "^0.79.5",
"@release-it/conventional-changelog": "^9.0.2",
"@types/jest": "^29.5.14",
Expand All @@ -60,7 +61,7 @@
"typescript": "^5.3.3"
},
"peerDependencies": {
"@iterable/react-native-sdk": "^2.0.2",
"@iterable/react-native-sdk": "^3.0.0",
"expo": "*",
"react": "*",
"react-native": "*"
Expand Down
55 changes: 55 additions & 0 deletions plugin/__tests__/withIosFmtWorkaround.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import {
createMockPodfileConfig,
createTestConfig,
type WithIterableResult,
} from '../__mocks__';
import { FMT_WORKAROUND_MARKER } from '../src/withIosFmtWorkaround';
import withIterable from '../src/withIterable';

const EXPO_PODFILE_SNIPPET = `
post_install do |installer|
react_native_post_install(
installer,
config[:reactNativePath],
:mac_catalyst_enabled => false,
:ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true',
)
end
`;

describe('withIosFmtWorkaround', () => {
it('injects the fmt workaround after react_native_post_install', async () => {
const result = withIterable(createTestConfig(), {}) as WithIterableResult;
const modifiedPodfile = await result.mods.ios.podfile(
createMockPodfileConfig({ contents: EXPO_PODFILE_SNIPPET })
);

expect(modifiedPodfile.modResults.contents).toContain(
FMT_WORKAROUND_MARKER
);
expect(modifiedPodfile.modResults.contents).toContain(
"config.build_settings['CLANG_CXX_LANGUAGE_STANDARD'] = 'c++17'"
);
});

it('does not inject the workaround twice', async () => {
const result = withIterable(createTestConfig(), {}) as WithIterableResult;
const firstPass = await result.mods.ios.podfile(
createMockPodfileConfig({ contents: EXPO_PODFILE_SNIPPET })
);
const secondPass = await result.mods.ios.podfile(
createMockPodfileConfig({ contents: firstPass.modResults.contents })
);

const markerCount = (
secondPass.modResults.contents.match(
new RegExp(
FMT_WORKAROUND_MARKER.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'),
'g'
)
) || []
).length;

expect(markerCount).toBe(1);
});
});
55 changes: 55 additions & 0 deletions plugin/src/withIosFmtWorkaround.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { ConfigPlugin, withPodfile } from 'expo/config-plugins';

/** Marker comment used to avoid injecting the workaround more than once. */
export const FMT_WORKAROUND_MARKER =
'# @iterable/expo-plugin: fmt workaround for Xcode 26.4';

/**
* Compiles the fmt pod in C++17 mode so FMT_USE_CONSTEVAL stays disabled.
* Required for Xcode 26.4+ when building React Native 0.79+.
*
* @see https://github.com/Iterable/react-native-sdk/blob/main/example/ios/Podfile
*/
const FMT_WORKAROUND_RUBY = `
${FMT_WORKAROUND_MARKER}
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
# fmt/base.h unconditionally redefines FMT_USE_CONSTEVAL based on __cplusplus,
# so preprocessor defines are overwritten. The reliable fix is to compile fmt
# in C++17 mode: FMT_CPLUSPLUS (201703L) < 201709L β†’ FMT_USE_CONSTEVAL = 0.
# All other pods stay in C++20 (React-perflogger needs std::unordered_map::contains).
if target.name == 'fmt'
config.build_settings['CLANG_CXX_LANGUAGE_STANDARD'] = 'c++17'
end
end
end`;

export const withIosFmtWorkaround: ConfigPlugin = (config) => {
return withPodfile(config, (newConfig) => {
const { contents } = newConfig.modResults;

if (contents.includes(FMT_WORKAROUND_MARKER)) {
return newConfig;
}

const postInstallMatch = contents.match(
/(react_native_post_install\([\s\S]*?\n\s*\))/
);

if (!postInstallMatch) {
console.warn(
'@iterable/expo-plugin: Could not inject fmt workaround into Podfile β€” react_native_post_install block not found'
);
return newConfig;
}

newConfig.modResults.contents = contents.replace(
postInstallMatch[0],
`${postInstallMatch[0]}${FMT_WORKAROUND_RUBY}`
);

return newConfig;
});
};

export default withIosFmtWorkaround;
2 changes: 2 additions & 0 deletions plugin/src/withIterable.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ConfigPlugin, withPlugins } from 'expo/config-plugins';

import { withDeepLinks } from './withDeepLinks';
import { withIosFmtWorkaround } from './withIosFmtWorkaround';
import {
type ConfigPluginProps,
type ConfigPluginPropsWithDefaults,
Expand All @@ -24,6 +25,7 @@ const withIterable: ConfigPlugin<ConfigPluginProps> = (config, props = {}) => {
[withStoreConfigValues, propsWithDefaults],
[withPushNotifications, propsWithDefaults],
[withDeepLinks, propsWithDefaults],
withIosFmtWorkaround,
]);
};

Expand Down
13 changes: 6 additions & 7 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2669,7 +2669,7 @@ __metadata:
dependencies:
"@commitlint/config-conventional": "npm:^19.6.0"
"@evilmartians/lefthook": "npm:^1.5.0"
"@iterable/react-native-sdk": "npm:^2.0.2"
"@iterable/react-native-sdk": "npm:^3.0.0"
"@react-native/eslint-config": "npm:^0.79.5"
"@release-it/conventional-changelog": "npm:^9.0.2"
"@types/jest": "npm:^29.5.14"
Expand All @@ -2692,7 +2692,7 @@ __metadata:
release-it: "npm:^17.10.0"
typescript: "npm:^5.3.3"
peerDependencies:
"@iterable/react-native-sdk": ^2.0.2
"@iterable/react-native-sdk": ^3.0.0
expo: "*"
react: "*"
react-native: "*"
Expand All @@ -2702,20 +2702,19 @@ __metadata:
languageName: unknown
linkType: soft

"@iterable/react-native-sdk@npm:^2.0.2":
version: 2.0.2
resolution: "@iterable/react-native-sdk@npm:2.0.2"
"@iterable/react-native-sdk@npm:^3.0.0":
version: 3.0.0
resolution: "@iterable/react-native-sdk@npm:3.0.0"
peerDependencies:
"@react-navigation/native": "*"
react: "*"
react-native: "*"
react-native-safe-area-context: "*"
react-native-vector-icons: "*"
react-native-webview: "*"
peerDependenciesMeta:
expo:
optional: true
checksum: 10c0/d1d6485dd3b982642df9569a4793aae0ff71191f8f6faffadbb580594db8e9cf2089f8a9b668d09c7b53e93ecbcd9d054833a8b82e0bb2a1d0d9a68a269d5967
checksum: 10c0/986244429e63646cd641a7f7be1a7c93be05f12000d80d1fa5cebce29bc4d87566e72766d1a3f57fe7f5c0a579a8c7d4b5b238cc815c8be161aef0a022314cfc
languageName: node
linkType: hard

Expand Down
Loading