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
27 changes: 27 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,29 @@ jobs:
command: |
yarn prepare

pod-install:
<<: *macos_config
# React Native 0.81's pod install requires Xcode 16.1 or newer.
macos:
xcode: 26.4.0
steps:
- attach_project
- node/install:
node-version: 22.14.0
- run:
# The Xcode 26 image does not ship the Ruby pinned in .ruby-version.
name: Install Ruby and gems
command: |
rbenv install --skip-existing
bundle install
- run:
name: Install example pods with an empty Intercom iOS SDK cache
command: |
export INTERCOM_IOS_SDK_CACHE_DIR="$(mktemp -d)"
cd examples/example/ios
bundle exec pod install
ls "$INTERCOM_IOS_SDK_CACHE_DIR"/*/Intercom.xcframework.zip

workflows:
version: 2.1
build-and-test:
Expand All @@ -120,3 +143,7 @@ workflows:
<<: *run_always
requires:
- install-dependencies
- pod-install:
<<: *run_always
requires:
- install-dependencies
25 changes: 21 additions & 4 deletions .github/workflows/update-dependencies.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
id: current-versions
run: |
# Extract current iOS version from podspec
IOS_VERSION=$(grep -E "s\.dependency.*Intercom" intercom-react-native.podspec | sed -E "s/.*'([^']+)'.*/\1/" | sed 's/~> //')
IOS_VERSION=$(sed -nE 's/^intercom_ios_sdk_version = "([^"]+)"$/\1/p' intercom-react-native.podspec)

# Extract current Android version from build.gradle
ANDROID_VERSION=$(grep -E "io\.intercom\.android:intercom-sdk:" android/build.gradle | head -1 | sed -E "s/.*:([^']+)'.*/\1/")
Expand All @@ -56,6 +56,7 @@ jobs:
IOS_RELEASE=$(curl -s https://api.github.com/repos/intercom/intercom-ios/releases/latest)
IOS_VERSION=$(echo "$IOS_RELEASE" | jq -r '.tag_name // "null"')
IOS_CHANGELOG=$(echo "$IOS_RELEASE" | jq -r '.body // "No changelog available"')
IOS_ZIP_DIGEST=$(echo "$IOS_RELEASE" | jq -r '[.assets[]? | select(.name == "Intercom.xcframework.zip") | .digest][0] // ""')

# Get Android release
ANDROID_RELEASE=$(curl -s https://api.github.com/repos/intercom/intercom-android/releases/latest)
Expand All @@ -66,6 +67,7 @@ jobs:
echo "🤖 Latest Android SDK: $ANDROID_VERSION"

echo "ios_version=$IOS_VERSION" >> $GITHUB_OUTPUT
echo "ios_zip_digest=$IOS_ZIP_DIGEST" >> $GITHUB_OUTPUT
echo "android_version=$ANDROID_VERSION" >> $GITHUB_OUTPUT
echo "ios_changelog<<EOF" >> $GITHUB_OUTPUT
echo "$IOS_CHANGELOG" >> $GITHUB_OUTPUT
Expand Down Expand Up @@ -150,7 +152,7 @@ jobs:
echo " Target: iOS=$TARGET_IOS, Android=$TARGET_ANDROID"

# Find existing PRs
EXISTING_PRS=$(gh pr list --state open --search "update Intercom SDK dependencies" --json number,title,headRefName 2>/dev/null || echo "[]")
EXISTING_PRS=$(gh pr list --state open --search "update Intercom SDK dependencies in:title" --json number,title,headRefName 2>/dev/null || echo "[]")

if [ "$EXISTING_PRS" = "[]" ] || [ "$(echo "$EXISTING_PRS" | jq 'length')" -eq 0 ]; then
echo "✅ No existing PRs found - proceeding with update"
Expand Down Expand Up @@ -252,6 +254,7 @@ jobs:
IOS_NEEDS_UPDATE: ${{ steps.update-analysis.outputs.ios_needs_update }}
IOS_NEW_VERSION: ${{ steps.update-analysis.outputs.ios_new_version }}
IOS_OLD_VERSION: ${{ steps.current-versions.outputs.ios_version }}
IOS_RELEASE_DIGEST: ${{ steps.latest-versions.outputs.ios_zip_digest }}
ANDROID_NEEDS_UPDATE: ${{ steps.update-analysis.outputs.android_needs_update }}
ANDROID_NEW_VERSION: ${{ steps.update-analysis.outputs.android_new_version }}
ANDROID_OLD_VERSION: ${{ steps.current-versions.outputs.android_version }}
Expand All @@ -262,7 +265,21 @@ jobs:
# Update iOS if needed
if [ "$IOS_NEEDS_UPDATE" = "true" ]; then
echo "📱 Updating iOS SDK: $IOS_OLD_VERSION → $IOS_NEW_VERSION"
sed -i '' "s/s\.dependency \"Intercom\", '~> [^']*'/s.dependency \"Intercom\", '~> $IOS_NEW_VERSION'/" intercom-react-native.podspec
IOS_ZIP="$RUNNER_TEMP/Intercom.xcframework.zip"
curl --fail --silent --show-error --location --retry 3 --output "$IOS_ZIP" \
"https://github.com/intercom/intercom-ios/releases/download/$IOS_NEW_VERSION/Intercom.xcframework.zip"
IOS_SHA256=$(shasum -a 256 "$IOS_ZIP" | cut -d ' ' -f 1)
if [ "$IOS_RELEASE_DIGEST" != "sha256:$IOS_SHA256" ]; then
echo "❌ Downloaded Intercom.xcframework.zip ($IOS_SHA256) does not match the release digest ($IOS_RELEASE_DIGEST)"
exit 1
fi
sed -i '' -E "s/^intercom_ios_sdk_version = \"[^\"]*\"/intercom_ios_sdk_version = \"$IOS_NEW_VERSION\"/" intercom-react-native.podspec
sed -i '' -E "s/^intercom_ios_sdk_sha256 = \"[^\"]*\"/intercom_ios_sdk_sha256 = \"$IOS_SHA256\"/" intercom-react-native.podspec
Comment thread
niamh-coleman marked this conversation as resolved.
Resolved
if ! grep -qxF "intercom_ios_sdk_version = \"$IOS_NEW_VERSION\"" intercom-react-native.podspec \
|| ! grep -qxF "intercom_ios_sdk_sha256 = \"$IOS_SHA256\"" intercom-react-native.podspec; then
echo "❌ Could not update the iOS SDK version and checksum in intercom-react-native.podspec"
exit 1
fi

CHANGES="$CHANGES\\n- Updated iOS SDK from $IOS_OLD_VERSION to $IOS_NEW_VERSION"
PR_TITLE_PARTS="iOS: $IOS_OLD_VERSION → $IOS_NEW_VERSION"
Expand Down Expand Up @@ -343,7 +360,7 @@ jobs:
if [ -f "$dir/Podfile" ]; then
echo "📱 Updating pods in $dir"
cd "$dir"
bundle exec pod install --repo-update || pod install --repo-update || pod update Intercom || echo "Pod install failed in $dir"
bundle exec pod install --repo-update || pod install --repo-update || echo "Pod install failed in $dir"
cd - > /dev/null

# Add any updated Podfile.lock files
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ examples/*/vendor/
# Cocoapods
#
examples/*/ios/Pods
intercom-ios-sdk/

# node.js
#
Expand Down
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -738,11 +738,13 @@ The Expo plugin automatically generates a `FirebaseMessagingService` for Android

### Versioning in dependencies

We define the iOS and Android versions used in React Native with optimistic operators
i.e. `~> for iOS` and `+ for Android`, ensuring compatibility while allowing for non-breaking updates:

iOS (~> in CocoaPods):
We use a version constraint e.g.`~> 18.6.1` which allows updates from `18.6.1 to 18.6.x` (up to but not including 18.7.0).
iOS (exact version):
The Intercom iOS SDK is no longer published to the CocoaPods trunk. During `pod install`, the podspec downloads
`Intercom.xcframework.zip` for the exact iOS SDK version this release was built against from the
[intercom-ios GitHub release](https://github.com/intercom/intercom-ios/releases), verifies its SHA-256 checksum
and links it. You don't need to add the `Intercom` pod to your Podfile (remove it if you added it yourself).
The download is cached in `~/Library/Caches/intercom-react-native`; set `INTERCOM_IOS_SDK_CACHE_DIR` to use
another directory, for example one your CI caches or one you pre-fill for offline builds.

Android (+ in Gradle):
We use a version constraint e.g. `18.6.+` which allows updates to any patch version within 18.6.x
Expand Down
6 changes: 1 addition & 5 deletions examples/example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@ PODS:
- hermes-engine (0.81.1):
- hermes-engine/Pre-built (= 0.81.1)
- hermes-engine/Pre-built (0.81.1)
- Intercom (19.9.2)
- intercom-react-native (10.9.0):
- boost
- DoubleConversion
- fast_float
- fmt
- glog
- hermes-engine
- Intercom (~> 19.9.2)
- RCT-Folly
- RCT-Folly/Fabric
- RCTRequired
Expand Down Expand Up @@ -2398,7 +2396,6 @@ DEPENDENCIES:

SPEC REPOS:
trunk:
- Intercom
- SocketRocket

EXTERNAL SOURCES:
Expand Down Expand Up @@ -2560,8 +2557,7 @@ SPEC CHECKSUMS:
fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd
glog: 5683914934d5b6e4240e497e0f4a3b42d1854183
hermes-engine: 4f8246b1f6d79f625e0d99472d1f3a71da4d28ca
Intercom: c69d3e6909b6ef64b1bfbac5ca746bcdf2f67a1f
intercom-react-native: 2cbdb91ae67222232817745b3fcdeb2379e8ec76
intercom-react-native: 4972c5f28ed080e890474dd446c8040c4a9e08e8
RCT-Folly: 59ec0ac1f2f39672a0c6e6cecdd39383b764646f
RCTDeprecation: c4b9e2fd0ab200e3af72b013ed6113187c607077
RCTRequired: e97dd5dafc1db8094e63bc5031e0371f092ae92a
Expand Down
6 changes: 1 addition & 5 deletions examples/with-notifications/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@ PODS:
- hermes-engine (0.81.1):
- hermes-engine/Pre-built (= 0.81.1)
- hermes-engine/Pre-built (0.81.1)
- Intercom (19.9.2)
- intercom-react-native (10.9.0):
- boost
- DoubleConversion
- fast_float
- fmt
- glog
- hermes-engine
- Intercom (~> 19.9.2)
- RCT-Folly
- RCT-Folly/Fabric
- RCTRequired
Expand Down Expand Up @@ -2603,7 +2601,6 @@ DEPENDENCIES:

SPEC REPOS:
trunk:
- Intercom
- MMKV
- MMKVCore
- SocketRocket
Expand Down Expand Up @@ -2773,8 +2770,7 @@ SPEC CHECKSUMS:
fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd
glog: 5683914934d5b6e4240e497e0f4a3b42d1854183
hermes-engine: 4f8246b1f6d79f625e0d99472d1f3a71da4d28ca
Intercom: c69d3e6909b6ef64b1bfbac5ca746bcdf2f67a1f
intercom-react-native: 2cbdb91ae67222232817745b3fcdeb2379e8ec76
intercom-react-native: 4972c5f28ed080e890474dd446c8040c4a9e08e8
MMKV: 7b5df6a8bf785c6705cc490c541b9d8a957c4a64
MMKVCore: 3f40b896e9ab522452df9df3ce983471aa2449ba
RCT-Folly: 59ec0ac1f2f39672a0c6e6cecdd39383b764646f
Expand Down
11 changes: 10 additions & 1 deletion intercom-react-native.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@

require "json"
require File.join(__dir__, "ios", "intercom_ios_sdk")

package = JSON.parse(File.read(File.join(__dir__, "package.json")))
intercom_ios_sdk_version = "19.9.2"
intercom_ios_sdk_sha256 = "6af284508f58de833313b263f3d82eaebc255c4d9a56ad9adf2fc6af4228c0f4"

Pod::Spec.new do |s|
s.name = "intercom-react-native"
Expand All @@ -19,7 +22,13 @@ Pod::Spec.new do |s|

s.pod_target_xcconfig = { "DEFINES_MODULE" => "YES" }

s.dependency "Intercom", '~> 19.9.2'
s.vendored_frameworks = IntercomIosSdk.install_xcframework(
version: intercom_ios_sdk_version,
sha256: intercom_ios_sdk_sha256,
package_root: __dir__
)
Comment thread
niamh-coleman marked this conversation as resolved.
Resolved
s.frameworks = ["Foundation", "UIKit", "Accelerate", "Security", "SystemConfiguration", "MobileCoreServices", "ImageIO", "AudioToolbox", "QuartzCore", "CoreGraphics", "Photos", "WebKit"]
s.libraries = "icucore", "xml2"
Comment thread
niamh-coleman marked this conversation as resolved.
Resolved

is_new_arch_enabled = ENV["RCT_NEW_ARCH_ENABLED"] == "1"
folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
Expand Down
66 changes: 66 additions & 0 deletions ios/intercom_ios_sdk.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
require 'digest'
require 'fileutils'
require 'tmpdir'

# The Intercom pod is no longer published to the CocoaPods trunk; fetch the GitHub release instead.
module IntercomIosSdk
RELEASE_URL = 'https://github.com/intercom/intercom-ios/releases/download/%s/Intercom.xcframework.zip'.freeze

module_function

def install_xcframework(version:, sha256:, package_root:)
relative_path = File.join('intercom-ios-sdk', version, 'Intercom.xcframework')
Comment thread
niamh-coleman marked this conversation as resolved.
Resolved
framework_path = File.join(package_root, relative_path)
unless File.exist?(File.join(framework_path, 'Info.plist'))
extract(cached_zip(version, sha256), File.dirname(framework_path))
remove_other_versions(File.join(package_root, 'intercom-ios-sdk'), version)
end
relative_path
end

def remove_other_versions(sdk_root, version)
(Dir.children(sdk_root) - [version]).each { |entry| FileUtils.rm_rf(File.join(sdk_root, entry)) }
end

def log(message)
defined?(Pod::UI) ? Pod::UI.puts(message) : puts(message)
end

def cache_dir
ENV['INTERCOM_IOS_SDK_CACHE_DIR'] || File.join(Dir.home, 'Library', 'Caches', 'intercom-react-native')
end

def cached_zip(version, sha256)
zip_path = File.join(cache_dir, version, 'Intercom.xcframework.zip')
return zip_path if File.exist?(zip_path) && Digest::SHA256.file(zip_path).hexdigest == sha256

url = format(RELEASE_URL, version)
download_path = "#{zip_path}.download"
FileUtils.mkdir_p(File.dirname(zip_path))
log "[Intercom] Downloading the Intercom iOS SDK #{version} from #{url}"
unless system('curl', '--fail', '--silent', '--show-error', '--location', '--retry', '3', '--output', download_path, url)
FileUtils.rm_f(download_path)
raise "[Intercom] Could not download #{url}. Check your network, or place the zip at #{zip_path} " \
'(set INTERCOM_IOS_SDK_CACHE_DIR to use another cache directory).'
end

actual = Digest::SHA256.file(download_path).hexdigest
unless actual == sha256
FileUtils.rm_f(download_path)
raise "[Intercom] Checksum mismatch for #{url}: expected #{sha256}, got #{actual}."
end
File.rename(download_path, zip_path)
zip_path
end

def extract(zip_path, destination)
FileUtils.mkdir_p(destination)
Dir.mktmpdir('extract-', destination) do |tmp|
raise "[Intercom] Could not unzip #{zip_path}." unless system('unzip', '-q', zip_path, '-d', tmp)

target = File.join(destination, 'Intercom.xcframework')
FileUtils.rm_rf(target)
File.rename(File.join(tmp, 'Intercom.xcframework'), target)
end
end
end
Loading