diff --git a/.github/workflows/build-kits.yml b/.github/workflows/build-kits.yml index e55962c81..990612569 100644 --- a/.github/workflows/build-kits.yml +++ b/.github/workflows/build-kits.yml @@ -37,10 +37,15 @@ jobs: - name: Pod Lib Lint run: | echo "Linting: ${{ matrix.kit.podspec }}" + INCLUDES="mParticle-Apple-SDK-Swift.podspec,mParticle-Apple-SDK-ObjC.podspec,mParticle-Apple-SDK.podspec" + if [ -n "${{ matrix.kit.pod_lint_include_podspecs }}" ]; then + INCLUDES="${INCLUDES},${{ matrix.kit.pod_lint_include_podspecs }}" + fi + INCLUDES="{${INCLUDES}}" for attempt in 1 2 3; do pod lib lint "${{ matrix.kit.podspec }}" \ --allow-warnings \ - --include-podspecs="{mParticle-Apple-SDK-Swift.podspec,mParticle-Apple-SDK-ObjC.podspec,mParticle-Apple-SDK.podspec}" \ + --include-podspecs="$INCLUDES" \ && break [ $attempt -lt 3 ] && echo "Attempt $attempt failed, retrying in 60s..." && sleep 60 || exit 1 done @@ -69,35 +74,57 @@ jobs: - name: Resolve SPM dependencies run: | SCHEME=$(echo '${{ toJson(matrix.kit.schemes) }}' | jq -r '.[0].scheme') - PROJECT="$(ls -d ${{ matrix.kit.local_path }}/*.xcodeproj | head -1)" - - xcodebuild -resolvePackageDependencies -project "$PROJECT" \ - -scheme "$SCHEME" -derivedDataPath DerivedData + LOCAL_PATH="${{ matrix.kit.local_path }}" + if compgen -G "$LOCAL_PATH"/*.xcodeproj > /dev/null; then + PROJECT="$(ls -d "$LOCAL_PATH"/*.xcodeproj | head -1)" + xcodebuild -resolvePackageDependencies -project "$PROJECT" \ + -scheme "$SCHEME" -derivedDataPath DerivedData + else + # Swift package without .xcodeproj (see rokt-sdk-plus-ios). + (cd "$LOCAL_PATH" && xcodebuild -resolvePackageDependencies -scheme "$SCHEME" -derivedDataPath "$OLDPWD/DerivedData") + fi - name: Build kit schemes run: | + LOCAL_PATH="${{ matrix.kit.local_path }}" echo '${{ toJson(matrix.kit.schemes) }}' | jq -c '.[]' | while IFS= read -r ENTRY; do SCHEME=$(echo "$ENTRY" | jq -r '.scheme') DEST=$(echo "$ENTRY" | jq -r '.destination') - if [ -d "${{ matrix.kit.local_path }}/$SCHEME.xcodeproj" ]; then - PROJECT="${{ matrix.kit.local_path }}/$SCHEME.xcodeproj" + if [ -d "$LOCAL_PATH/$SCHEME.xcodeproj" ]; then + PROJECT="$LOCAL_PATH/$SCHEME.xcodeproj" + XB=(xcodebuild build -project "$PROJECT" -scheme "$SCHEME") + elif compgen -G "$LOCAL_PATH"/*.xcodeproj > /dev/null; then + PROJECT="$(ls -d "$LOCAL_PATH"/*.xcodeproj | head -1)" + XB=(xcodebuild build -project "$PROJECT" -scheme "$SCHEME") else - PROJECT="$(ls -d ${{ matrix.kit.local_path }}/*.xcodeproj | head -1)" + XB=(xcodebuild build -scheme "$SCHEME") fi - xcodebuild build -project "$PROJECT" -scheme "$SCHEME" \ - -destination "generic/platform=$DEST" \ - -derivedDataPath DerivedData \ - CODE_SIGN_IDENTITY= CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO - - xcodebuild build -project "$PROJECT" -scheme "$SCHEME" \ - -destination "generic/platform=$DEST Simulator" \ - -derivedDataPath DerivedData \ - CODE_SIGN_IDENTITY= CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO + if compgen -G "$LOCAL_PATH"/*.xcodeproj > /dev/null; then + "${XB[@]}" \ + -destination "generic/platform=$DEST" \ + -derivedDataPath DerivedData \ + CODE_SIGN_IDENTITY= CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO + "${XB[@]}" \ + -destination "generic/platform=$DEST Simulator" \ + -derivedDataPath DerivedData \ + CODE_SIGN_IDENTITY= CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO + else + # Package.swift only (e.g. rokt-sdk-plus-ios): xcodebuild must run from the package root; no -project. + (cd "$LOCAL_PATH" && "${XB[@]}" \ + -destination "generic/platform=$DEST" \ + -derivedDataPath "$OLDPWD/DerivedData" \ + CODE_SIGN_IDENTITY= CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO) + (cd "$LOCAL_PATH" && "${XB[@]}" \ + -destination "generic/platform=$DEST Simulator" \ + -derivedDataPath "$OLDPWD/DerivedData" \ + CODE_SIGN_IDENTITY= CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO) + fi done - name: Build SPM-Swift-Example + if: ${{ matrix.kit.skip_example_builds != true }} run: | PROJECT="$(ls -d ${{ matrix.kit.local_path }}/Example/SPM-Swift-Example/*.xcodeproj | head -1)" xcodebuild build -project "$PROJECT" -scheme SPM-Swift-Example \ @@ -106,6 +133,7 @@ jobs: CODE_SIGN_IDENTITY= CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO - name: Build SPM-Objc-Example + if: ${{ matrix.kit.skip_example_builds != true }} run: | PROJECT="$(ls -d ${{ matrix.kit.local_path }}/Example/SPM-Objc-Example/*.xcodeproj | head -1)" xcodebuild build -project "$PROJECT" -scheme SPM-Objc-Example \ @@ -114,6 +142,7 @@ jobs: CODE_SIGN_IDENTITY= CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO - name: Run SPM tests + if: ${{ matrix.kit.skip_spm_tests != true }} run: | cd ${{ matrix.kit.local_path }} PACKAGE=$(grep -E '^\s*name:' Package.swift | head -1 | sed 's/.*"\([^"]*\)".*/\1/') diff --git a/.github/workflows/release-draft.yml b/.github/workflows/release-draft.yml index 868c73fe5..d86efb373 100644 --- a/.github/workflows/release-draft.yml +++ b/.github/workflows/release-draft.yml @@ -88,6 +88,8 @@ jobs: run: | sed -i '' "s/kMPRoktKitVersion = @\"[^\"]*\"/kMPRoktKitVersion = @\"${VERSION}\"/" \ Kits/rokt/rokt/Sources/mParticle-Rokt/MPKitRokt.m + sed -i '' 's/public static let version = "[^"]*"/public static let version = "'"${VERSION}"'"/' \ + Kits/rokt-sdk-plus/rokt-sdk-plus-ios/Sources/RoktSDKPlus/RoktSDKPlus.swift sed -i '' "s/kLibVersion = @\"[^\"]*\"/kLibVersion = @\"${VERSION}\"/" \ Kits/clevertap/clevertap-7/Sources/mParticle-CleverTap/MPKitCleverTap.m sed -i '' "s/registerPluginName:@\"mParticleKit\" version:@\"[^\"]*\"/registerPluginName:@\"mParticleKit\" version:@\"${VERSION}\"/" \ @@ -157,7 +159,7 @@ jobs: ### Files updated - All podspecs (core, Swift, kits) → \`${VERSION}\` - \`mParticle-Apple-SDK/MPIConstants.m\` - - Kit source version strings (Rokt \`kMPRoktKitVersion\`, CleverTap \`kLibVersion\`, Branch \`registerPluginName\`) + - Kit source version strings (Rokt \`kMPRoktKitVersion\`, RoktSDKPlus \`RoktSDKPlus.version\`, CleverTap \`kLibVersion\`, Branch \`registerPluginName\`) - \`Framework/Info.plist\` - Integration test mappings - Kit \`Package.swift\`: \`let version\` → core SDK SPM pin (major bumps only) diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 0c7f86978..08a3c935a 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -162,8 +162,10 @@ jobs: if [ -d "$LOCAL_PATH/$SCHEME.xcodeproj" ]; then PROJECT="$LOCAL_PATH/$SCHEME.xcodeproj" - else + elif compgen -G "$LOCAL_PATH"/*.xcodeproj > /dev/null; then PROJECT="$(ls -d "$LOCAL_PATH"/*.xcodeproj | head -1)" + else + PROJECT="" fi ARCHIVE_DEVICE="archives/${MODULE}-${DEST}" @@ -184,10 +186,19 @@ jobs: ;; esac - xcodebuild archive -project "$PROJECT" -scheme "$SCHEME" \ - -destination "$PLATFORM_DEVICE" -archivePath "$ARCHIVE_DEVICE" $BUILD_SETTINGS - xcodebuild archive -project "$PROJECT" -scheme "$SCHEME" \ - -destination "$PLATFORM_SIM" -archivePath "$ARCHIVE_SIM" $BUILD_SETTINGS + REPO_ROOT="$PWD" + if [ -n "$PROJECT" ]; then + xcodebuild archive -project "$PROJECT" -scheme "$SCHEME" \ + -destination "$PLATFORM_DEVICE" -archivePath "$ARCHIVE_DEVICE" $BUILD_SETTINGS + xcodebuild archive -project "$PROJECT" -scheme "$SCHEME" \ + -destination "$PLATFORM_SIM" -archivePath "$ARCHIVE_SIM" $BUILD_SETTINGS + else + # Package.swift only: archive from package root; archive paths stay under repo $REPO_ROOT. + (cd "$LOCAL_PATH" && xcodebuild archive -scheme "$SCHEME" \ + -destination "$PLATFORM_DEVICE" -archivePath "$REPO_ROOT/$ARCHIVE_DEVICE" $BUILD_SETTINGS) + (cd "$LOCAL_PATH" && xcodebuild archive -scheme "$SCHEME" \ + -destination "$PLATFORM_SIM" -archivePath "$REPO_ROOT/$ARCHIVE_SIM" $BUILD_SETTINGS) + fi XCFRAMEWORK_ARGS+=" -archive ${ARCHIVE_DEVICE}.xcarchive -framework ${MODULE}.framework" XCFRAMEWORK_ARGS+=" -archive ${ARCHIVE_SIM}.xcarchive -framework ${MODULE}.framework" @@ -210,7 +221,7 @@ jobs: runs-on: macOS-latest needs: [load-matrix, build-kits, release-core-sdk] env: - DEST_ORG: mparticle-integrations + DEST_ORG: ${{ matrix.kit.dest_org || 'mparticle-integrations' }} strategy: fail-fast: false matrix: @@ -242,7 +253,7 @@ jobs: with: client-id: ${{ secrets.SDK_RELEASE_GITHUB_CLIENT_ID }} private-key: ${{ secrets.SDK_RELEASE_GITHUB_APP_PRIVATE_KEY }} - owner: ${{ env.DEST_ORG }} + owner: ${{ matrix.kit.dest_org || 'mparticle-integrations' }} repositories: ${{ matrix.kit.dest_repo }} permission-contents: write @@ -359,21 +370,15 @@ jobs: - name: Wait for core SDK on CocoaPods CDN run: | VERSION=$(head -n 1 VERSION | tr -d '\r\n ') - echo "⏳ Polling CocoaPods for mParticle-Apple-SDK $VERSION..." - MAX_ATTEMPTS=30 - for i in $(seq 1 $MAX_ATTEMPTS); do - FOUND=$(curl -sf "https://trunk.cocoapods.org/api/v1/pods/mParticle-Apple-SDK" \ - | jq -r --arg v "$VERSION" '.versions[] | select(.name == $v) | .name' 2>/dev/null || true) - if [ "$FOUND" = "$VERSION" ]; then - echo "✅ mParticle-Apple-SDK $VERSION confirmed on CocoaPods trunk" - echo "⏳ Waiting 5 minutes for CDN propagation before publishing kits..." - sleep 300 - exit 0 - fi - [ "$i" -eq "$MAX_ATTEMPTS" ] && echo "❌ Timed out after 30 minutes" && exit 1 - echo " Attempt $i/$MAX_ATTEMPTS: Not yet available. Retrying in 60s..." - sleep 60 - done + . Scripts/pod_push.sh + wait_for_pod_version_on_trunk mParticle-Apple-SDK "$VERSION" 300 + + - name: Wait for mParticle-Rokt on CocoaPods CDN (RoktSDKPlus) + if: matrix.kit.name == 'rokt-sdk-plus-ios' + run: | + VERSION=$(head -n 1 VERSION | tr -d '\r\n ') + . Scripts/pod_push.sh + wait_for_pod_version_on_trunk mParticle-Rokt "$VERSION" - name: Publish to CocoaPods if: matrix.kit.podspec != '' diff --git a/CHANGELOG.md b/CHANGELOG.md index 84c20a0bd..b266f8afb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,10 @@ For each release, **Core** (main SDK) changes are listed first, followed by **Ki ### Kits +#### Added + +- **Rokt SDK+ (`RoktSDKPlus`)** — Umbrella Swift package and CocoaPods pod at `Kits/rokt-sdk-plus/rokt-sdk-plus-ios`, versioned with the core SDK and mirrored to [ROKT/rokt-sdk-plus-ios](https://github.com/ROKT/rokt-sdk-plus-ios). + #### Rokt ##### Changed diff --git a/Kits/matrix.json b/Kits/matrix.json index dd132f0b1..b27f63c74 100644 --- a/Kits/matrix.json +++ b/Kits/matrix.json @@ -393,6 +393,24 @@ } ] }, + { + "name": "rokt-sdk-plus-ios", + "local_path": "Kits/rokt-sdk-plus/rokt-sdk-plus-ios", + "podspec": "Kits/rokt-sdk-plus/rokt-sdk-plus-ios/RoktSDKPlus.podspec", + "dest_repo": "rokt-sdk-plus-ios", + "dest_org": "ROKT", + "spm_package_only": true, + "skip_example_builds": true, + "skip_spm_tests": true, + "pod_lint_include_podspecs": "Kits/rokt/rokt/mParticle-Rokt.podspec", + "schemes": [ + { + "scheme": "RoktSDKPlus", + "module": "RoktSDKPlus", + "destination": "iOS" + } + ] + }, { "name": "singular-12", "local_path": "Kits/singular/singular-12", diff --git a/Kits/rokt-sdk-plus/rokt-sdk-plus-ios/LICENSE b/Kits/rokt-sdk-plus/rokt-sdk-plus-ios/LICENSE new file mode 100644 index 000000000..309b476d4 --- /dev/null +++ b/Kits/rokt-sdk-plus/rokt-sdk-plus-ios/LICENSE @@ -0,0 +1,191 @@ +Copyright 2018 mParticle, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/Kits/rokt-sdk-plus/rokt-sdk-plus-ios/Package.swift b/Kits/rokt-sdk-plus/rokt-sdk-plus-ios/Package.swift new file mode 100644 index 000000000..fc0cc589e --- /dev/null +++ b/Kits/rokt-sdk-plus/rokt-sdk-plus-ios/Package.swift @@ -0,0 +1,52 @@ +// swift-tools-version: 5.9 + +import Foundation +import PackageDescription + +let version = "9.1.0" + +let useLocalVersion = ProcessInfo.processInfo.environment["USE_LOCAL_VERSION"] != nil + +let mParticleRoktKitURL = "https://github.com/mparticle-integrations/mp-apple-integration-rokt.git" + +let mParticleRoktDependency: Package.Dependency = { + if useLocalVersion { + return .package(path: "../../rokt/rokt") + } + if version.isEmpty { + return .package(url: mParticleRoktKitURL, branch: "main") + } + return .package(url: mParticleRoktKitURL, .upToNextMajor(from: Version(version)!)) +}() + +let mParticleRoktProduct: Target.Dependency = useLocalVersion + ? .product(name: "mParticle-Rokt", package: "rokt") + : .product(name: "mParticle-Rokt", package: "mp-apple-integration-rokt") + +let package = Package( + name: "RoktSDKPlus", + platforms: [.iOS(.v15)], + products: [ + .library( + name: "RoktSDKPlus", + targets: ["RoktSDKPlus"] + ) + ], + dependencies: [ + mParticleRoktDependency, + .package( + url: "https://github.com/ROKT/rokt-payment-extension-ios.git", + .upToNextMajor(from: "2.0.0") + ) + ], + targets: [ + .target( + name: "RoktSDKPlus", + dependencies: [ + mParticleRoktProduct, + .product(name: "RoktPaymentExtension", package: "rokt-payment-extension-ios") + ], + path: "Sources/RoktSDKPlus" + ) + ] +) diff --git a/Kits/rokt-sdk-plus/rokt-sdk-plus-ios/README.md b/Kits/rokt-sdk-plus/rokt-sdk-plus-ios/README.md new file mode 100644 index 000000000..3920f1af1 --- /dev/null +++ b/Kits/rokt-sdk-plus/rokt-sdk-plus-ios/README.md @@ -0,0 +1,91 @@ +# Rokt SDK+ for iOS (RoktSDKPlus) + +**Rokt SDK+** is an umbrella that pulls the [mParticle Apple SDK](https://github.com/mParticle/mparticle-apple-sdk), the [mParticle Rokt kit](https://github.com/mparticle-integrations/mp-apple-integration-rokt) (**mParticle-Rokt** with `rokt-contracts-apple` 2.x), and [Rokt Payment Extension](https://github.com/ROKT/rokt-payment-extension-ios). + +Swift module and CocoaPods pod name: **`RoktSDKPlus`**. + +This library is developed in the [mParticle Apple SDK monorepo](https://github.com/mParticle/mparticle-apple-sdk) under `Kits/rokt-sdk-plus/rokt-sdk-plus-ios` and mirrored to **[ROKT/rokt-sdk-plus-ios](https://github.com/ROKT/rokt-sdk-plus-ios)** for SwiftPM and CocoaPods releases. Version numbers follow the **mParticle Apple SDK** ecosystem (`VERSION` in the monorepo). + +## Upstream READMEs + +For full installation options, troubleshooting, and advanced topics, use the upstream guides (this repo only summarizes what you need alongside **RoktSDKPlus**): + +- **[mParticle Apple SDK — README](https://github.com/mParticle/mparticle-apple-sdk/blob/main/README.md)** — core SDK via SwiftPM/CocoaPods, kits overview, and initialization. +- **[mParticle Rokt kit — README](https://github.com/mparticle-integrations/mp-apple-integration-rokt/blob/main/README.md)** — kit-specific SwiftPM/CocoaPods lines, Shoppable Ads, and event APIs. + +Official documentation: [mParticle iOS SDK](https://docs.mparticle.com/developers/sdk/ios/), [Rokt + mParticle](https://docs.rokt.com/developers/integration-guides/rokt-ads/customer-data-platforms/mparticle/). + +## Versioning + +**RoktSDKPlus**, **mParticle-Rokt**, and **mParticle-Apple-SDK** share the same semver for each monorepo release. **RoktPaymentExtension** remains on its own 2.x line. + +## Swift Package Manager + +In `Package.swift` or Xcode → _Package Dependencies_: + +```swift +.package( + url: "https://github.com/ROKT/rokt-sdk-plus-ios.git", + .upToNextMajor(from: "9.1.0") +) +``` + +Add the **`RoktSDKPlus`** product to your app target, then import `RoktSDKPlus` and the upstream modules you need (for example `mParticle_Apple_SDK`, `mParticle_Rokt_Swift`, `RoktPaymentExtension`). + +If you were integrating **without** this umbrella, the core SDK would be added from `https://github.com/mParticle/mparticle-apple-sdk` (product **`mParticle-Apple-SDK`**) and the kit from `https://github.com/mparticle-integrations/mp-apple-integration-rokt` (product **`mParticle-Rokt`**), per the [mParticle README](https://github.com/mParticle/mparticle-apple-sdk/blob/main/README.md) and [Rokt kit README](https://github.com/mparticle-integrations/mp-apple-integration-rokt/blob/main/README.md). + +## CocoaPods + +In your `Podfile`: + +```ruby +pod 'RoktSDKPlus', '~> 9.1' +``` + +Match deployment target and Swift version expectations to the upstream podspecs (this podspec uses **iOS 15.6** and **Swift 5.9**). + +## mParticle core (essential) + +The [mParticle Apple SDK](https://github.com/mParticle/mparticle-apple-sdk) is the hub for forwarding data to enabled integrations; kits (such as Rokt) are separate targets your app links against—**RoktSDKPlus** already links the core and Rokt kit for you. + +Initialize the SDK from `application(_:didFinishLaunchingWithOptions:)` (do **not** defer startup with `dispatch_async`). A minimal Swift start: + +```swift +import mParticle_Apple_SDK + +func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? +) -> Bool { + let options = MParticleOptions(key: "<<>>", secret: "<<>>") + MParticle.sharedInstance().start(with: options) + return true +} +``` + +See the full [mParticle Apple SDK README](https://github.com/mParticle/mparticle-apple-sdk/blob/main/README.md) for Objective‑C, tvOS, crash reporter, and the supported kits table. + +## mParticle Rokt kit (essential) + +After install, rebuild and run with mParticle log level **Debug** or higher; you should see `Included kits: { Rokt }` in the Xcode console ([Rokt kit README](https://github.com/mparticle-integrations/mp-apple-integration-rokt/blob/main/README.md)). + +**Placements** (Swift): + +```swift +MParticle.sharedInstance().rokt.selectPlacements( + "checkout", + attributes: ["email": "user@example.com"], + embeddedViews: ["Location1": embeddedView], + config: nil +) { event in + if event is RoktEvent.PlacementReady { + // Placement is ready + } +} +``` + +**Shoppable Ads** use a registered payment extension (for example Stripe) and `selectShoppableAds`; see the [Rokt kit README](https://github.com/mparticle-integrations/mp-apple-integration-rokt/blob/main/README.md) for `registerPaymentExtension` / `selectShoppableAds` snippets and [MIGRATING.md](https://github.com/mparticle-integrations/mp-apple-integration-rokt/blob/main/MIGRATING.md) for event types. + +## License + +See [LICENSE](LICENSE). Upstream SDKs remain under their respective licenses. diff --git a/Kits/rokt-sdk-plus/rokt-sdk-plus-ios/RoktSDKPlus.podspec b/Kits/rokt-sdk-plus/rokt-sdk-plus-ios/RoktSDKPlus.podspec new file mode 100644 index 000000000..5056118e2 --- /dev/null +++ b/Kits/rokt-sdk-plus/rokt-sdk-plus-ios/RoktSDKPlus.podspec @@ -0,0 +1,19 @@ +Pod::Spec.new do |s| + s.name = 'RoktSDKPlus' + s.version = '9.1.0' + s.summary = 'Rokt SDK+ umbrella: mParticle Apple SDK, mParticle–Rokt kit, and Rokt Payment Extension.' + s.description = <<-DESC + Single CocoaPods entry point for the mParticle Apple SDK, the mParticle Rokt integration kit + (mParticle-Rokt with RoktContracts 2.x), and Rokt Payment Extension (Shoppable Ads). + DESC + s.homepage = 'https://github.com/ROKT/rokt-sdk-plus-ios' + s.license = { :type => 'Apache 2.0', :file => 'LICENSE' } + s.author = { 'ROKT' => 'nativeappsdev@rokt.com' } + s.source = { :git => 'https://github.com/ROKT/rokt-sdk-plus-ios.git', :tag => 'v' + s.version.to_s } + s.swift_version = '5.9' + s.ios.deployment_target = '15.6' + s.requires_arc = true + s.source_files = 'Sources/RoktSDKPlus/**/*.swift' + s.dependency 'mParticle-Rokt', s.version.to_s + s.dependency 'RoktPaymentExtension', '~> 2.0' +end diff --git a/Kits/rokt-sdk-plus/rokt-sdk-plus-ios/Sources/RoktSDKPlus/RoktSDKPlus.swift b/Kits/rokt-sdk-plus/rokt-sdk-plus-ios/Sources/RoktSDKPlus/RoktSDKPlus.swift new file mode 100644 index 000000000..fafb64d6c --- /dev/null +++ b/Kits/rokt-sdk-plus/rokt-sdk-plus-ios/Sources/RoktSDKPlus/RoktSDKPlus.swift @@ -0,0 +1,12 @@ +import Foundation + +/// Entry point for **Rokt SDK+** (SwiftPM product and CocoaPods pod: **RoktSDKPlus**). +/// +/// This module links the [mParticle Apple SDK](https://github.com/mParticle/mparticle-apple-sdk), +/// the [mParticle Rokt](https://github.com/mparticle-integrations/mp-apple-integration-rokt) kit, and +/// [Rokt Payment Extension](https://github.com/ROKT/rokt-payment-extension-ios). Import the +/// upstream modules you use alongside `RoktSDKPlus` as needed. +public enum RoktSDKPlus { + /// Umbrella package version for **Rokt SDK+** (module `RoktSDKPlus`), aligned with the mParticle Apple SDK ecosystem. + public static let version = "9.1.0" +} diff --git a/RELEASE.md b/RELEASE.md index fd5868f26..f5b1e6a7e 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -27,10 +27,13 @@ This bumps versions across podspecs, `Package.swift`, constants files, and `CHAN Review and merge the PR. On merge, the **Release – Publish** workflow runs automatically: - Builds xcframeworks for every kit -- Mirrors each kit subtree to its own repo under `mparticle-integrations/` +- Mirrors each kit subtree to its own repo under `mparticle-integrations/` (and **RoktSDKPlus** to `ROKT/rokt-sdk-plus-ios`) - Creates GitHub releases and tags (used by SPM consumers) - Publishes the core SDK and all kit podspecs to CocoaPods trunk +> [!NOTE] +> The release GitHub App must be installed on **ROKT** with access to `rokt-sdk-plus-ios` for that mirror push to succeed (same app credentials as `mparticle-integrations` mirrors). + > [!NOTE] > The Swift SDK podspec (`mParticle-Apple-SDK-Swift`) is not yet published automatically — push it manually before the core SDK if required: > diff --git a/Scripts/pod_push.sh b/Scripts/pod_push.sh index 17d2963a4..297407744 100755 --- a/Scripts/pod_push.sh +++ b/Scripts/pod_push.sh @@ -1,12 +1,48 @@ #!/usr/bin/env bash # # CocoaPods trunk push with retries. Treats trunk "duplicate entry" as success (transient 5xx after publish). +# Poll trunk until a pod version is visible (used before publishing dependent podspecs in CI). # # Source from the repo root: . Scripts/pod_push.sh # On GitHub Actions, run `set +e` before calling—the default run shell uses bash -e; callers rely on # explicit status checks inside this function instead of errexit aborting mid-retry. # +# Poll https://trunk.cocoapods.org until the given version exists for the pod. +# Usage: wait_for_pod_version_on_trunk [post_success_sleep_seconds] +# post_success_sleep_seconds: optional extra sleep after the version is found (e.g. 300 for CDN). +wait_for_pod_version_on_trunk() { + local pod_name="$1" + local version="$2" + local post_success_sleep="${3:-0}" + local max_attempts=30 + local sleep_seconds=60 + local i found + + echo "⏳ Polling CocoaPods trunk for ${pod_name} ${version}..." + for ((i = 1; i <= max_attempts; i++)); do + found=$( + curl -sf "https://trunk.cocoapods.org/api/v1/pods/${pod_name}" | + jq -r --arg v "${version}" '.versions[] | select(.name == $v) | .name' 2>/dev/null || true + ) + if [[ ${found} == "${version}" ]]; then + echo "✅ ${pod_name} ${version} confirmed on CocoaPods trunk" + if [[ ${post_success_sleep} -gt 0 ]]; then + echo "⏳ Waiting ${post_success_sleep}s for CDN propagation..." + sleep "${post_success_sleep}" + fi + return 0 + fi + if [[ ${i} -eq ${max_attempts} ]]; then + echo "❌ Timed out after $((max_attempts * sleep_seconds))s waiting for ${pod_name} ${version} on trunk" + return 1 + fi + echo " Attempt ${i}/${max_attempts}: Not yet available. Retrying in ${sleep_seconds}s..." + sleep "${sleep_seconds}" + done + return 1 +} + trunk_push_with_retries() { local podspec="$1" local attempt status