Skip to content

Swift Package Manager integration

Milan Parađina edited this page Oct 22, 2026 · 1 revision

Swift Package Manager Integration

Starting from plugin version 15.1.0, the iOS side of the Mobile Messaging React Native plugin supports Swift Package Manager (SPM) as the dependency manager, in addition to the existing CocoaPods integration.

CocoaPods will still be fully supported until December 2nd, 2026, when CocoaPods Trunk is scheduled to become permanently read-only. If you are not ready to migrate, you can continue using CocoaPods and will receive all plugin updates as before. Until then, no action is required. After December 2nd, the iOS side of the plugin will only be updated with Swift Package Manager.

Notice: SPM support in React Native is experimental. It is provided by the react-native spm command, introduced in React Native 0.87.


Background

The plugin now ships both a Package.swift manifest (for SPM, under ios/Package.swift) and a .podspec (for CocoaPods, infobip-mobile-messaging-react-native-plugin.podspec). Which one is used depends on how your app's iOS project is set up:

Configuration Dependency manager used
ios/Podfile present and pod install executed CocoaPods
npx react-native spm executed Swift Package Manager
  • The react-native spm command reads the Package.swift of every native dependency and injects them, together with the React Native core, into your .xcodeproj.
  • The plugin's Package.swift fetches the Mobile Messaging native SDK from the mobile-messaging-sdk-ios repository.
  • Other native dependencies of your app that don't ship a Package.swift need one generated with npx react-native spm scaffold (see step 4). react-native spm does not generate them automatically.

Prerequisites for SPM

To use SPM you need:

  • React Native 0.87.0 or higher
  • NodeJS 22.13.0 or higher
  • Xcode 16 or higher
  • iOS deployment target 15.1 or higher
  • Swift AppDelegate (Objective-C AppDelegate.m is only supported with the CocoaPods integration)

Migrating from CocoaPods to SPM

1. Update the plugin

In your project's package.json, make sure you're on React Native 0.87.0 and plugin version 15.1.0 or above:

"dependencies": {
    "react-native": "^0.87.0",
    "infobip-mobile-messaging-react-native-plugin": "^15.1.0" // or above
}

Then install:

npm install

2. Disable automatic pod install

react-native run-ios runs pod install automatically whenever a Podfile is present. Disable it in your react-native.config.js:

module.exports = {
  project: {
    ios: { automaticPodsInstallation: false },
  },
};

3. (Optional) Enable WebRTC UI

Just like the CocoaPods integration (where MobileMessaging/WebRTCUI is only added when $WebRTCUIEnabled is set in the Podfile), the SPM integration does not download or link WebRTCUI by default. To enable it, add the following to your app's package.json:

"infobipMobileMessaging": {
  "webRTCUI": true
}

4. Generate missing Package.swift manifests

If any other native dependency of your app doesn't ship a Package.swift, the next step fails with MissingManifestError. Generate the missing manifests from the project root:

npx react-native spm scaffold

The generated files live in node_modules, so npm install removes them. To keep them, persist each one with patch-package and commit the patch:

npx patch-package <dependency-name>

Alternatively, re-run npx react-native spm scaffold after every install.

Generated manifests may need manual fixes. See A generated Package.swift does not work.

5. Remove CocoaPods and add the SPM packages

From the project root, run:

npx react-native spm add --deintegrate

The --deintegrate flag runs pod deintegrate and removes the React Native integration from your Podfile before injecting the Swift packages into your .xcodeproj.

If you get errors from the CocoaPods cache, remove the leftover CocoaPods files and run the command again:

cd ios
bundle exec pod deintegrate
rm -rf Pods YourApp.xcworkspace
cd ..

6. Update the AppDelegate

Open ios/YourApp.xcodeproj in Xcode, go to AppDelegate.swift where MobileMessagingPluginApplicationDelegate is installed, and change the import from MobileMessaging to MobileMessagingReactNative.

The AppDelegate.swift should now look like this:

import UIKit
import React
import MobileMessagingReactNative

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
  func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
  ) -> Bool {
    MobileMessagingPluginApplicationDelegate.install()
    ...
  }
}

If you also use other MobileMessaging APIs in your native code (for example MobileMessaging.messageHandlingDelegate), keep the import MobileMessaging as well.

If you may need to switch back to CocoaPods later, use conditional imports instead (see Switching back from SPM to CocoaPods).

7. Build and run

Run the application directly from Xcode by running it from the YourApp.xcodeproj project, or run:

npx react-native run-ios

Xcode resolves the SPM graph on the first build, fetching MobileMessaging and its dependencies from mobile-messaging-sdk-ios.

Keeping the packages up to date

Re-run the following command after adding, removing or updating native dependencies, or after changing the webRTCUI flag:

npx react-native spm --yes

What changes with SPM

CocoaPods SPM
Dependency resolution pod install npx react-native spm + Xcode resolves packages at build time
Lockfile Podfile.lock Package.resolved
Project to open YourApp.xcworkspace YourApp.xcodeproj
WebRTC UI opt-in $WebRTCUIEnabled = true in the Podfile "infobipMobileMessaging": { "webRTCUI": true } in package.json
AppDelegate import MobileMessaging MobileMessagingReactNative

Notification Service Extension

To integrate the Notification Service Extension for Rich Notifications and better delivery reporting on iOS with Swift Package Manager, follow the guide here.


Staying on CocoaPods

If you are not ready to migrate, keep the existing ios/Podfile in your project and run:

cd ios
bundle exec pod install

No changes to the plugin are needed, as the .podspec remains published and CocoaPods will continue to be used exactly as before.

The plugin will receive updates, including new features and bug fixes with the CocoaPods integration, until the official CocoaPods Trunk becomes permanently read-only. After that period, the iOS part of the plugin will receive regular updates only via Swift Package Manager.

Switching back from SPM to CocoaPods

npx react-native spm deinit
git checkout ios/Podfile # restore the Podfile, since --deintegrate removed the React Native integration from it
cd ios && bundle exec pod install

Then, in AppDelegate.swift, change the import from MobileMessagingReactNative back to MobileMessaging, since the MobileMessagingReactNative module is only available with SPM. Open ios/YourApp.xcworkspace (not the .xcodeproj) again.

To be able to switch between SPM and CocoaPods without editing the AppDelegate, use conditional imports instead, as the Example app does:

import MobileMessaging
#if canImport(MobileMessagingReactNative)
import MobileMessagingReactNative
#endif

...
MobileMessagingPluginApplicationDelegate.install()

Troubleshooting

SPM package resolution fails

Ensure you have a stable internet connection the first time Xcode resolves packages, since it needs to fetch packages from GitHub.

No such module 'MobileMessagingReactNative'

This usually means that the SPM packages were not injected into the project, or the .xcworkspace was opened instead of the .xcodeproj. Run npx react-native spm --yes from the project root and open ios/YourApp.xcodeproj.

Pods are reinstalled when running react-native run-ios

Make sure automaticPodsInstallation: false is set in your react-native.config.js (see step 2).

WebRTC UI is missing after changing the webRTCUI flag

The flag is read when the package manifest is resolved, not at app runtime. After toggling it, re-run npx react-native spm --yes. If Xcode still uses the old resolution, use File > Packages > Reset Package Caches in Xcode.

MissingManifestError after npm install

Package.swift files generated by npx react-native spm scaffold live in node_modules and are removed on reinstall. Re-run npx react-native spm scaffold, or persist the files with patch-package so they survive installs (see step 4).

A generated Package.swift does not work

npx react-native spm scaffold generates each Package.swift from the dependency's .podspec, and doesn't always translate it correctly. Open the generated node_modules/<dependency>/Package.swift and fix it by hand, then persist the fix with npx patch-package <dependency-name>.

  • public headers ("include") directory path for '<Dependency>' is invalid: the target is missing sources and publicHeadersPath. List the dependency's iOS source files and point publicHeadersPath at their folder, for example:

    path: ".",
    sources: [
        "ios/SomeLibrary.h",
        "ios/SomeLibrary.mm",
    ],
    publicHeadersPath: "ios",
    cSettings: [.headerSearchPath("ios"), .headerSearchPath("."), .unsafeFlags(["-include", "react-native-spm-prefix.h"])],
    

    The -include react-native-spm-prefix.h flag needs a react-native-spm-prefix.h file next to the Package.swift. If it's missing, copy it from another scaffolded dependency.

  • The app crashes on launch with attempt to insert nil object from objects[0] in RCTThirdPartyComponentsProvider: a native component class isn't compiled into the app. This happens when the dependency's sources are wrapped in #ifdef RCT_NEW_ARCH_ENABLED and the generated Package.swift doesn't define it. Add the flag to both cSettings and cxxSettings:

    .unsafeFlags(["-DRCT_NEW_ARCH_ENABLED=1"])

Before persisting a hand-edited Package.swift, remove the // AUTO-SCAFFOLDED … comment lines at the top of the file. While that marker is present, npx react-native spm scaffold may regenerate the file and overwrite your fixes. Without it, the file is treated as user-managed and left untouched.

When running npx patch-package, exclude build output so it doesn't end up in the patch:

npx patch-package <dependency-name> --exclude '^package\.json$|^android/build/|^\.swiftpm/'

The Example app contains such patches, including hand-fixed manifests for @react-native-clipboard/clipboard and @react-native-picker/picker.

Clone this wiki locally