diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..e1a12a2 --- /dev/null +++ b/Package.swift @@ -0,0 +1,73 @@ +// swift-tools-version: 6.0 +// Experimental: for React Native's Swift Package Manager mode (React Native 0.87 or later), which +// React Native itself marks as not for production. CocoaPods apps do not use this file. +// +// The ReactNative and React-GeneratedCode packages are the local packages React Native's autolinker +// generates for each app, at these paths relative to this package, the same layout other React +// Native libraries use. Revisit them when React Native publishes a remote Swift package. + +import PackageDescription + +let package = Package( + name: "ReactNativeMparticle", + // React Native's autolinker requires iOS 15; the mParticle SDK declares the same. + platforms: [.iOS(.v15)], + products: [ + .library(name: "ReactNativeMparticle", targets: ["ReactNativeMparticle"]), + ], + dependencies: [ + .package(name: "ReactNative", path: "../../../../xcframeworks"), + .package(name: "React-GeneratedCode", path: "../../../ios"), + // Same URLs as the mParticle kits use, so SwiftPM unifies them into one package identity. + .package(url: "https://github.com/mParticle/mparticle-apple-sdk", "9.2.2"..<"10.0.0"), + .package(url: "https://github.com/ROKT/rokt-contracts-apple.git", "2.0.0"..<"3.0.0"), + ], + targets: [ + // A separate target: SwiftPM cannot mix Swift and Objective-C++ in one target. + .target( + name: "RNMParticleSwift", + dependencies: [.product(name: "RoktContracts", package: "rokt-contracts-apple")], + path: "ios/RNMParticle/Swift" + ), + .target( + name: "ReactNativeMparticle", + dependencies: [ + "RNMParticleSwift", + .product(name: "ReactHeaders", package: "ReactNative"), + .product(name: "ReactNativeHeaders", package: "ReactNative"), + .product(name: "ReactNativeDependenciesHeaders", package: "ReactNative"), + .product(name: "ReactAppHeaders", package: "React-GeneratedCode"), + .product(name: "mParticle-Apple-SDK", package: "mparticle-apple-sdk"), + .product(name: "RoktContracts", package: "rokt-contracts-apple"), + ], + path: ".", + sources: [ + "ios/RNMParticle/RNMPRokt.h", + "ios/RNMParticle/RNMPRokt.mm", + "ios/RNMParticle/RNMPRoktSwift.h", + "ios/RNMParticle/RNMPSDKImports.h", + "ios/RNMParticle/RNMParticle.h", + "ios/RNMParticle/RNMParticle.mm", + "ios/RNMParticle/RoktEventManager.h", + "ios/RNMParticle/RoktEventManager.mm", + "ios/RNMParticle/RoktLayoutManager.m", + "ios/RNMParticle/RoktNativeLayoutComponentView.h", + "ios/RNMParticle/RoktNativeLayoutComponentView.mm", + "ios/RNMParticle/RoktPlaceholderRegistry.h", + "ios/RNMParticle/RoktPlaceholderRegistry.m", + ], + publicHeadersPath: "ios", + // Without RCT_NEW_ARCH_ENABLED the TurboModule and Fabric code compiles out, and the app + // crashes looking up the RoktNativeLayout component. + cSettings: [.define("RCT_NEW_ARCH_ENABLED", to: "1"), .headerSearchPath("ios/RNMParticle")], + cxxSettings: [ + .define("RCT_NEW_ARCH_ENABLED", to: "1"), + .headerSearchPath("ios/RNMParticle"), + .define("DEBUG", .when(configuration: .debug)), + .define("NDEBUG", .when(configuration: .release)), + ], + linkerSettings: [.linkedFramework("UIKit"), .linkedFramework("Foundation"), .linkedFramework("CoreGraphics")] + ), + ], + cxxLanguageStandard: .cxx20 +) diff --git a/README.md b/README.md index a71d923..aec8be5 100644 --- a/README.md +++ b/README.md @@ -419,6 +419,17 @@ Every mParticle and Rokt SDK must then come from Swift Package Manager, and none - The build fails with `[mParticle] $RNMParticleUseSPM is set but the mParticle-Apple-SDK Swift package is not linked into the app target`: `mparticle_spm_post_install` did not run, or the package was removed from the app target afterwards. Call it in `post_install` and run `pod install` again. - A Debug build shows the red box `[mParticle] The mParticle SDK is loaded more than once`: the SDK comes from both CocoaPods and Swift Package Manager. Remove the mParticle and Rokt pods and Swift packages you added by hand, then set up one mode as described above. +### Experimental: React Native Swift Package Manager mode + +> **Not for production.** React Native's own Swift Package Manager mode (React Native 0.87 or later) is experimental, and so is this package's support for it. Use CocoaPods, with or without the opt-in mode above, for apps you ship. + +This package ships a `Package.swift`, so `npx react-native spm add` links it without a scaffolded manifest. The app target must also link the mParticle core SDK and each kit as Swift packages, for example in Xcode (File › Add Package Dependencies): + +- `https://github.com/mParticle/mparticle-apple-sdk`, product `mParticle-Apple-SDK` +- `https://github.com/mparticle-integrations/mp-apple-integration-rokt`, product `mParticle-Rokt` + +Use exactly these URLs, with no `.git` suffix on the first, so Swift Package Manager treats them as the same packages the kits depend on. Then start mParticle in your Swift `AppDelegate` with `import mParticle_Apple_SDK`, as in the CocoaPods setup. + ## Android (Manual Setup) 1. Copy your mParticle key and secret from [your workspace's dashboard](https://app.mparticle.com/setup/inputs/apps) and construct an `MParticleOptions` object. diff --git a/js/__tests__/package-swift-sources.test.ts b/js/__tests__/package-swift-sources.test.ts new file mode 100644 index 0000000..657a6b8 --- /dev/null +++ b/js/__tests__/package-swift-sources.test.ts @@ -0,0 +1,24 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +/** + * The podspec compiles every file under ios/ by glob, but Package.swift lists the Objective-C++ + * target's sources by hand, and CI does not build that manifest. Keep the two in step. + */ +const root = path.join(__dirname, '..', '..'); + +it('lists every ios/RNMParticle source in Package.swift', () => { + const manifest = fs.readFileSync(path.join(root, 'Package.swift'), 'utf-8'); + const listed = new Set( + [...manifest.matchAll(/"(ios\/RNMParticle\/[^"]+\.(?:h|m|mm))"/g)].map( + match => match[1] + ) + ); + const onDisk = fs + .readdirSync(path.join(root, 'ios', 'RNMParticle'), { recursive: true }) + .map(String) + .filter(file => /\.(h|m|mm)$/.test(file)) + .map(file => `ios/RNMParticle/${file}`); + + expect([...listed].sort()).toEqual(onDisk.sort()); +}); diff --git a/package.json b/package.json index 4256faa..f9b6fb3 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "lib", "react-native-mparticle.podspec", "app.plugin.js", - "plugin" + "plugin", + "Package.swift" ], "publishConfig": { "access": "public",