MOBILE-146: SPM support and Example update#199
Conversation
Ship a Package.swift alongside the existing podspec, so the plugin
resolves via SPM (Flutter ≥ 3.29) and via CocoaPods unchanged. Swift
sources move to ios/mindbox_ios/Sources/mindbox_ios/ — the layout
required by SwiftPM — and the podspec source_files now points there
plus the legacy ObjC AppDelegate from Classes/.
Drop the Objective-C plugin bridge (MindboxIosPlugin.{h,m}); the plugin
registers directly through the renamed Swift class MindboxIosPlugin
(the `Swift` prefix was a carry-over from the ObjC bridge era and
stopped describing anything once the bridge was deleted). Flutter's
GeneratedPluginRegistrant.m falls back from `#import` of the missing
ObjC header to `@import mindbox_ios;`, which picks up the Swift class
via the auto-generated mindbox_ios-Swift.h. The corresponding stale
#import in the ObjC AppDelegate goes away too.
ObjC MindboxFlutterAppDelegateObjc stays in Classes/ for existing
CocoaPods consumers, marked deprecated. It is intentionally not exposed
via the SPM target — gating that on Flutter ≥ 3.41 (the documented
minimum for SPM ObjC plugins with FlutterFramework) is not worth
bumping the SPM-minimum for a deprecated path.
Misc cleanups in the moved Swift files: drop the unused
'MindboxNotifications' import in MindboxFlutterAppDelegate.swift, and
simplify writeNativeLog by seeding the local 'level' var from
'Mindbox.logger.logLevel' to inherit the LogLevel type via inference.
That drops the 'import MindboxLogger' from the plugin — which would
otherwise fail in SPM mode since MindboxLogger is a target in
ios-sdk's Package.swift but not exposed as a product.
Verified locally on Flutter 3.44 (Example) and Flutter 3.29
(flutter-app) on iPhone 17 Pro simulator: both SPM and CocoaPods builds
initialize the SDK, return an APNS token and device UUID. No crash, no
duplicate framework load. 'pod lib lint mindbox_ios.podspec' passes
after dropping the local cocoapods-spm gem (its macro_pods hook breaks
unrelated specs).
Drop the artificial Dart 2 compat-mode constraint ('<3.0.0') on all
four packages — mindbox, mindbox_ios, mindbox_android,
mindbox_platform_interface. Lower bound (>=2.12.0) is unchanged, so
this is hygiene, not a consumer-visible breaking change.
Motivated by the SDK ecosystem catching up to Dart 3; clients on modern
Flutter no longer need a 'dependency_overrides' shim to satisfy the
constraint.
Integrate MindboxNotifications into the example's notification extensions
through Swift Package Manager instead of CocoaPods, so the example builds
the SDK entirely via SPM in SPM mode.
- Add the mindbox-cloud/ios-sdk package at the Xcode project level and link
its products to the extension targets:
MindboxNotificationsService -> MindboxNotificationServiceExtension
MindboxNotificationsContent -> MindboxNotificationContentExtension
Mindbox itself is NOT linked to Runner (left as "None"): the main app gets
it transitively via FlutterGeneratedPluginSwiftPackage -> mindbox_ios.
SPM unifies this project-level ios-sdk with the one the plugin pulls into
the same package identity, so the whole app resolves to a single ios-sdk
version (verified: Package.resolved lists ios-sdk once at 2.15.1).
- Use an "up to next major" rule (>= 2.15.1, < 3.0.0) for that package so it
auto-aligns with whatever exact version the plugin pins, surviving 2.x
releases without manual pbxproj edits. A conflicting/too-narrow rule would
fail resolution loudly rather than drift silently.
- Drop the CocoaPods extension blocks (pod 'MindboxNotifications') from the
Podfile; extensions no longer use CocoaPods. The Podfile stays only for the
Runner target, since shared_preference_app_group and permission_handler_apple
have no SPM support yet.
- Drop the duplicate top-level pod 'Mindbox' from Runner (it double-loaded the
Mindbox framework and mismatched versions, crashing inside MBDatabaseRepository
on first launch). Pin platform :ios, '13.0' so CocoaPods stops auto-assigning
15.0. Hard-set flutter.config.enable-swift-package-manager: true so the
example always exercises the SPM path.
.gitignore picks up dev-only artifacts (pubspec_overrides.yaml, ios/.spm.pods/,
**/xcshareddata/swiftpm/). pubspec.lock regenerated from the Dart SDK bound bump.
Verified on Flutter 3.44, iPhone 17 Pro simulator: clean build with no Mindbox
pods, NSE/NCE compile against MindboxNotifications via SPM, app launches and the
SDK initializes.
The native iOS SDK version prompted by the release tooling now also bumps the 'exact:' pin in mindbox_ios/ios/mindbox_ios/Package.swift, alongside the existing podspec bump. Without this hook a release would ship a podspec on the new native SDK but an SPM manifest still pinned to the previous one. Two release entry points exist and both are updated in lockstep: - git-release-branch.sh — local interactive bash script (BSD sed). - .github/workflows/manual-prepare_release_branch.yml — the actual CI-driven release prep (GNU sed on Ubuntu, runs under 'set -euo pipefail'). The substitution is addressed by the 'mindbox-cloud/ios-sdk' URL so unrelated 'exact:' pins (if more SPM dependencies are added later) stay untouched. Verified with both BSD and GNU sed against normal X.Y.Z, X.Y.Z-rc, multi-digit versions, repeated runs (idempotent), and a synthetic Package.swift with a second unrelated 'exact:' pin.
Flutter's SPM docs don't cover the Mindbox-specific steps: wiring the notification extensions to the ios-sdk products in Xcode, and removing the CocoaPods Mindbox pods (Runner's `pod 'Mindbox'` and the extensions' `pod 'MindboxNotifications'`) so the SDK isn't loaded by both pod and SPM — which duplicates the framework and crashes at runtime. SPM_MIGRATION.md documents this (mirroring UISCENE_MIGRATION.md) and is linked from the README; the CocoaPods path is unchanged.
permission_handler 12 and shared_preference_app_group 2 now ship Swift Package Manager support, so every iOS plugin in the example resolves via SPM — CocoaPods is no longer needed. Deintegrated it: removed the Podfile, the Pods xcconfig includes, the Pods workspace reference, the empty Pods group, the now-dead Pods framework/header search paths on the notification extensions, and an orphan Mindbox.framework reference. Runner.xcworkspace stays (Flutter needs it to manage SPM integration; it errors 'Xcode workspace not found' without it) but now references only Runner.xcodeproj. Bump the remaining direct deps to latest (permission_handler ^12, shared_preference_app_group ^2, flutter_lints ^6) and align the extensions' marketing version with the app (1.0.0) to clear the parent/extension version-match warning. Verified pod-free build and run on the simulator: SDK init, permission grant, and in-app all work.
The example's notification-extension targets referenced MindboxNotificationsService/Content only through their Frameworks build phase — the targets' packageProductDependencies arrays were missing (a known Xcode SPM wiring bug; see ios-app SPM-NOTES). Re-adding the ios-sdk package wires each product into both the build phase and the target's packageProductDependencies, so the structure is complete and Xcode manages add/remove correctly. Pin (Up to Next Major 2.15.1), Mindbox=None on Runner, and product→target mapping are unchanged. objectVersion bumped 54→60 to match the toolchain. Verified: pod-free build + in-app on the simulator.
Removed the obsolete ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES (the Swift runtime ships with iOS 13+), enabled String Catalog symbol generation, bumped LastUpgradeCheck. Kept CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER off at the project level — enabling it floods unfixable warnings from Flutter.framework's own quoted-include headers. Verified: clean build + in-app on the simulator.
flutter_lints 6 enables strict_top_level_inference, which flags the example's static ViewModel methods that have no explicit return type — failing `flutter analyze` in CI. Add void return types to the eight methods. No behavior change.
There was a problem hiding this comment.
Pull request overview
This PR adds Swift Package Manager (SPM) support for the Mindbox Flutter SDK’s iOS implementation (while keeping CocoaPods support), updates the example app to exercise the SPM path, and adjusts release automation to bump the iOS SPM pin alongside the podspec.
Changes:
- Added SPM migration documentation and a README section describing SPM setup (including extension wiring).
- Introduced an iOS
Package.swiftformindbox_iosand updated iOS plugin sources to build withoutMindboxLoggerbeing directly imported in SPM mode. - Updated example app iOS project to be “pure SPM” (remove Pods/Podfile), refreshed dependencies, and tightened release scripts/workflow to verify version substitutions.
Reviewed changes
Copilot reviewed 27 out of 31 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
SPM_MIGRATION.md |
New guide for using the plugin with Flutter SPM + extension wiring + pod removal. |
README.md |
Adds a short “iOS Swift Package Manager” section linking to the migration doc. |
mindbox/pubspec.yaml |
Expands Dart SDK upper bound to <4.0.0. |
mindbox_platform_interface/pubspec.yaml |
Expands Dart SDK upper bound to <4.0.0. |
mindbox_ios/pubspec.yaml |
Expands Dart SDK upper bound to <4.0.0. |
mindbox_android/pubspec.yaml |
Expands Dart SDK upper bound to <4.0.0. |
mindbox_ios/ios/mindbox_ios/Sources/mindbox_ios/MindboxIosPlugin.swift |
Renames plugin class, removes MindboxLogger import, and adjusts log-level handling for SPM. |
mindbox_ios/ios/mindbox_ios/Sources/mindbox_ios/MindboxFlutterAppDelegate.swift |
Removes MindboxNotifications import (SPM compatibility). |
mindbox_ios/ios/mindbox_ios/Sources/mindbox_ios/inappCallbacks/Callbacks.swift |
Adds in-app callback delegate glue types used by the plugin. |
mindbox_ios/ios/mindbox_ios/Sources/mindbox_ios/Constants.swift |
Adds a constants container for the plugin channel name. |
mindbox_ios/ios/mindbox_ios/Package.swift |
New SwiftPM manifest pinning ios-sdk for SPM builds. |
mindbox_ios/ios/mindbox_ios.podspec |
Updates source_files to point at SwiftPM-style Sources/ layout and retains pod dependencies. |
mindbox_ios/ios/Classes/MindboxIosPlugin.m |
Removes the Objective-C shim for plugin registration (deleted). |
mindbox_ios/ios/Classes/MindboxIosPlugin.h |
Removes the Objective-C shim header (deleted). |
mindbox_ios/ios/Classes/MindboxFlutterAppDelegate.m |
Removes now-unneeded include of the removed plugin header. |
mindbox_ios/ios/Classes/MindboxFlutterAppDelegate.h |
Deprecates the ObjC AppDelegate base class with a removal notice. |
mindbox_ios/.gitignore |
Ignores SwiftPM build artifacts under the plugin package. |
git-release-branch.sh |
Adds substitution assertions and bumps iOS SPM pin in addition to the podspec. |
.github/workflows/manual-prepare_release_branch.yml |
Adds pin assertions and bumps iOS SPM pin during manual release prep. |
example/flutter_example/pubspec.yaml |
Updates deps and forces SPM via per-project Flutter config. |
example/flutter_example/pubspec.lock |
Updates lockfile (currently includes path-sourced Mindbox packages). |
example/flutter_example/lib/view_model/view_model.dart |
Adds explicit void return types and updates method signatures. |
example/flutter_example/ios/.../Package.resolved (2 files) |
Commits SPM resolution for reproducible native dependency locking. |
example/flutter_example/ios/Runner.xcworkspace/contents.xcworkspacedata |
Removes Pods project reference from the workspace. |
example/flutter_example/ios/Runner.xcodeproj/.../Runner.xcscheme |
Updates scheme version. |
example/flutter_example/ios/Runner.xcodeproj/project.pbxproj |
Removes CocoaPods phases/configs and wires Mindbox extension products via SPM. |
example/flutter_example/ios/Podfile |
Removes CocoaPods integration for the example (deleted). |
example/flutter_example/ios/Flutter/Debug.xcconfig |
Removes Pods xcconfig include. |
example/flutter_example/ios/Flutter/Release.xcconfig |
Removes Pods xcconfig include. |
example/flutter_example/.gitignore |
Ignores SwiftPM workspace scratch while keeping Package.resolved; ignores pubspec_overrides.yaml. |
Files not reviewed (1)
- example/flutter_example/ios/Runner.xcworkspace/contents.xcworkspacedata: Language not supported
Comments suppressed due to low confidence (2)
mindbox_ios/ios/mindbox_ios/Sources/mindbox_ios/MindboxIosPlugin.swift:17
- The Swift plugin is now the only implementation (the ObjC shim files were removed), but there is no public Objective-C entry point/header left for
GeneratedPluginRegistrantin Objective-C Flutter apps to import/call. This is likely to break CocoaPods consumers (and any ObjC-based Runner) because the default registrant expects an ObjC-visibleMindboxIosPluginsymbol.
example/flutter_example/lib/view_model/view_model.dart:62 - Use a typed callback (and fix the
complitiontypo) so callers get proper type checking and IDE support.
static void getDeviceUUID(Function complition) {
Mindbox.instance.getDeviceUUID((value) {
print(value);
complition(value);
});
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Address PR #199 review: replace the untyped 'Function complition' parameter with 'void Function(String) completion' in getSDKVersion/getToken/getDeviceUUID so callers get proper type checking, and fix the typo. flutter analyze clean.
| s.source_files = 'mindbox_ios/Sources/mindbox_ios/**/*.swift', 'Classes/MindboxFlutterAppDelegate.{h,m}' | ||
| s.dependency 'Flutter' | ||
| s.dependency 'Mindbox', '2.15.1' | ||
| s.dependency 'MindboxNotifications', '2.15.1' |
There was a problem hiding this comment.
The plugin is a standard Swift plugin, so no ObjC shim/header is needed:
MindboxIosPlugin is public class MindboxIosPlugin: NSObject, FlutterPlugin → it's Objective-C-visible through the generated mindbox_ios-Swift.h (NSObject subclass + the @objc register(with:) from FlutterPlugin), so ObjC can call +[MindboxIosPlugin registerWithRegistrar:].
The generated GeneratedPluginRegistrant.m already handles the missing ObjC header:
#if __has_include(<mindbox_ios/MindboxIosPlugin.h>)
#import <mindbox_ios/MindboxIosPlugin.h>
#else
@import mindbox_ios; // ← shim removed ⇒ falls back here
#endif
[MindboxIosPlugin registerWithRegistrar:[registry registrarForPlugin:@"MindboxIosPlugin"]];
Verified on an ObjC-based Runner + CocoaPods (our pushok_objc regression app): it builds, registers the plugin, and runs. Removing the ObjC shim is correct.
Issue