Skip to content

feat!: Support SPM for kiosk_mode - #905

Merged
alboiuvlad29 merged 1 commit into
mainfrom
noticket-kiosk-mode-spm
Jul 28, 2026
Merged

feat!: Support SPM for kiosk_mode#905
alboiuvlad29 merged 1 commit into
mainfrom
noticket-kiosk-mode-spm

Conversation

@alboiuvlad29

@alboiuvlad29 alboiuvlad29 commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Closes #799

Summary

kiosk_mode was the last plugin in some consumer apps still forcing CocoaPods — Flutter only drops CocoaPods from an app when every plugin ships a Package.swift, so one holdout keeps pod install in the build. This adds Swift Package Manager support following the official plugin-author migration guide.

The migration is additive — the podspec stays, so CocoaPods-only apps keep working.

Plugin (kiosk_mode/ios/)

  • Added kiosk_mode/Package.swift (iOS 13, one target, explicit FlutterFramework dependency).
  • Dropped the legacy ObjC shim (Classes/KioskModePlugin.h/.m). SPM has no bridging-header dance, so the Swift class is now the registered plugin directly: SwiftKioskModePluginKioskModePlugin, annotated @objc(KioskModePlugin) so lookup by ObjC name at registration still resolves. This matches the existing pluginClass: KioskModePlugin in pubspec.yaml, so no Dart-side or consumer-facing API change.
  • Moved sources to ios/kiosk_mode/Sources/kiosk_mode/ (via git mv, history preserved) and repointed source_files in the podspec.
  • Bumped the podspec's platform from 8.0 to 13.0 (stale — Flutter already enforces iOS 13+) and removed the dead EXCLUDED_ARCHS => i386 workaround.
  • Fixed podspec metadata that was still flutter create boilerplate (homepage was http://example.com, author was "Your Company").
  • Deleted ios/Assets/, which held only a .gitkeep and was never referenced by the podspec.

Minimum Flutter version: >=3.38.0>=3.44.0

The explicit FlutterFramework package dependency in Package.swift landed in Flutter 3.44.0. Without it, 3.44+ emits Plugin kiosk_mode has a Package.swift for ios but is missing a dependency on FlutterFramework. Declaring it is the forward-looking choice, but it does raise the floor, so CI (.github/actions/setup + publish) moves off 3.38.5 to 3.44.8 — otherwise flutter pub get on this package would fail in CI. Other packages in the monorepo are untouched: they have no native code and are unaffected. The Dart SDK constraint is deliberately left at >=3.10.1 — nothing here needs newer Dart, and raising it would only narrow compatibility for no gain.

Example app

Mostly Flutter tool auto-migrations triggered by building on 3.44 (Podfile, AppDelegate.swift UIScene lifecycle, Info.plist, Runner.xcscheme, Podfile.lock, deployment target 9.0 → 13.0). These re-apply on any build, so they're committed rather than fought.

Note the example is intentionally left CocoaPods-integrated. Committing the SPM integration that the tool bakes into project.pbxproj makes the example hard-fail with the package at '.../FlutterFramework' cannot be accessed for anyone who has SPM disabled. With it left out, the integration is added automatically on build for anyone who has the flag on. Added Flutter/ephemeral/ to the example's .gitignore so the generated packages stay untracked.

Testing steps

Both integration paths were verified locally on Flutter 3.44.8 — note that CI does not build iOS at all (check.yml is ubuntu-only, dart format + flutter analyze), so this cannot be confirmed by the pipeline.

  1. SPM pathflutter config --enable-swift-package-manager, then from kiosk_mode/example: flutter clean && flutter build ios --no-codesign. ✅ Build succeeds, log reports All plugins found for ios are Swift Packages, and the FlutterFramework advisory warning is gone.
  2. CocoaPods path (regression risk)flutter config --no-enable-swift-package-manager, flutter clean, flutter build ios --no-codesign against a pristine project.pbxproj. ✅ Build succeeds with the relocated sources.
  3. pod lib lint kiosk_mode.podspec from kiosk_mode/ios. ✅ Passes (catches a bad source_files glob).
  4. flutter analyze . ✅ no issues; dart format ✅ 0 files changed; flutter test ✅ 4/4.
  5. Example app runs on the iOS simulator.

Follow-up issues

None.

Check during review

  • Diff is ~115 additions, well under the 300 threshold; roughly half of it is Flutter tool auto-migration of the example app rather than hand-written code.
  • No test coverage added: this is a packaging change with no behavioural delta, and the existing 4 unit tests still pass.

@alboiuvlad29
alboiuvlad29 requested review from a team as code owners July 28, 2026 14:55
@moxly

moxly commented Jul 28, 2026

Copy link
Copy Markdown

Review posted. View review · run

@moxly moxly left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Risk Assessment

Score: 4/10 — medium

This is a packaging change with no Dart-side behavioural delta, and the additive approach (podspec retained alongside Package.swift) keeps the CocoaPods path alive. The risk that keeps this above a 2: plugin registration now depends on the @objc(KioskModePlugin) name resolving through the generated registrant's @import kiosk_mode fallback instead of the deleted ObjC shim, and no CI job builds iOS — so the MethodChannel/EventChannel round-trip after the rename rests on local simulator builds, with on-device verification still outstanding by your own note.

Review Summary

Verdict: COMMENT

1 warning, 3 nits.

The migration itself checks out against the repo:

  • pluginClass: KioskModePlugin in kiosk_mode/pubspec.yaml:26 is unchanged, and @objc(KioskModePlugin) keeps the ObjC-visible name stable, so the generated registrant's @import kiosk_mode fallback resolves the class now that the header is gone. register(with:) still maps to registerWithRegistrar: via the @objc FlutterPlugin conformance.
  • No stale references anywhere in the repo — grepping SwiftKioskModePlugin, Classes/, and kiosk_mode-Swift returns nothing outside the deleted files.
  • s.source_files = 'kiosk_mode/Sources/kiosk_mode/**/*.swift' resolves correctly relative to the podspec's directory, and matches the relocated source without picking up Package.swift.
  • s.license = { :type => 'BSD-3-Clause' } matches the actual kiosk_mode/LICENSE (BSD 3-Clause, Mews).
  • Package.swift follows the plugin-author guide's shape: package name matching the plugin, hyphenated product name, explicit FlutterFramework product dependency.
  • The CI Flutter bump is safe constraint-wise: no package in the monorepo declares a Flutter upper bound, and nothing depends on kiosk_mode except its own example.
  • The new .vscode/launch.json entry points at a real kiosk_mode/example/lib/main.dart, and incidentally drops a stray trailing comma.

PR template: all four sections are present and filled, with no leftover placeholders. Every removal/replacement claimed in the description is reflected in the diff (ObjC shim, ios/Assets/, EXCLUDED_ARCHS, podspec boilerplate metadata, Flutter/ephemeral/ gitignore entry).

Leaving the example CocoaPods-integrated is a reasonable call given the hard-fail it avoids for users with SPM disabled — the trade-off is that nothing in the repo exercises the SPM path automatically, which lines up with the CI gap you already logged as a follow-up.

Fix All — prompt for AI agent

Fix the following issues in this PR:

  1. Release semantics (kiosk_mode/pubspec.yaml line 8): the Flutter floor raise >=3.38.0>=3.44.0 plus the podspec s.platform bump to iOS 13 are consumer-facing breaking changes, but melos maps a plain feat: on a pre-1.0 package to a patch bump (0.8.1). Mark the change breaking — retitle the PR/squash commit to feat!: Support SPM for kiosk_mode or add a BREAKING CHANGE: footer — so melos cuts 0.9.0 and the generated changelog carries the breaking-change note, matching how the Flutter 3.38 migration (#864) was released.
  2. In kiosk_mode/ios/.gitignore: add Swift Package Manager artifacts so they can't be committed accidentally — .build/, .swiftpm/, and Package.resolved.
  3. In kiosk_mode/example/ios/Flutter/AppFrameworkInfo.plist (line 23): confirm the MinimumOSVersion key removal actually came from the Flutter tool. The known deployment-target migrator rewrites the value rather than deleting the key, and optimus_widgetbook/ios/Flutter/AppFrameworkInfo.plist still has it. If it was a hand edit, restore the key with 13.0.
  4. Before merging, run melos exec --scope="optimus_widgetbook" -- "flutter build web" on Flutter 3.44.8. The shared .github/actions/setup bump changes the toolchain for the widgetbook deploy workflows, which only run on push to main, so a regression there would not surface on this PR.

After all changes, run the project's linter/formatter.

Comment thread kiosk_mode/pubspec.yaml
Comment thread kiosk_mode/ios/kiosk_mode/Package.swift
Comment thread kiosk_mode/example/ios/Flutter/AppFrameworkInfo.plist
Comment thread .github/actions/setup/action.yml
Add a Package.swift so the plugin can be consumed via Swift Package
Manager, letting consumer apps drop CocoaPods entirely. The podspec is
kept, so CocoaPods-only apps keep working.

Drop the legacy ObjC shim and register the Swift class directly, move
the sources to ios/kiosk_mode/Sources/kiosk_mode/, and repoint the
podspec at the new location.

Closes #799

BREAKING CHANGE: The minimum Flutter version is raised from 3.38.0 to
3.44.0, and the podspec's iOS platform floor from 8.0 to 13.0. The
explicit FlutterFramework package dependency required by Package.swift
was only introduced in Flutter 3.44.0.
@alboiuvlad29
alboiuvlad29 force-pushed the noticket-kiosk-mode-spm branch from 5b68fd7 to 49dbe9b Compare July 28, 2026 15:12
@alboiuvlad29 alboiuvlad29 changed the title feat: Support SPM for kiosk_mode feat!: Support SPM for kiosk_mode Jul 28, 2026
@alboiuvlad29
alboiuvlad29 requested a review from moxly July 28, 2026 15:12
@moxly

moxly commented Jul 28, 2026

Copy link
Copy Markdown

Review posted. View review · run

@moxly moxly left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Risk Assessment

Score: 3/10 — low

The one consumer-facing concern from the last round — the release being cut as a patch despite a Flutter-floor and iOS-deployment-target raise — is resolved now that the change is marked feat!, so melos will cut 0.9.0 with a breaking-change note. What keeps this off a 1-2 is unchanged: no CI job builds iOS, so the @objc(KioskModePlugin) registration rename and both integration paths rest entirely on the local verification in the description. The remaining two carry-forwards are nit-level and don't affect the plugin's runtime behaviour.

Review Summary

Verdict: APPROVE

Re-review of commit 49dbe9b. The incremental diff could not be computed (the previously-reviewed commit 5b68fd7 is no longer reachable — the branch was force-pushed), so I re-analysed the full diff and reconciled it against the previous review rather than scoping to new lines only.

0 new findings. 2 resolved — 2 fixed, 0 dismissed; 2 carried forward (both nits).

Resolved since the last review

  • feat! marker (was a warning, kiosk_mode/pubspec.yaml:8). The PR title and head commit are now feat!: Support SPM for kiosk_mode. With version: 0.8.0+1 in kiosk_mode/pubspec.yaml:3, melos will map the breaking marker to a minor bump (0.9.0) and emit the breaking-change note in the changelog — matching how the Flutter 3.38 migration shipped (kiosk_mode/CHANGELOG.md:10, 0.8.0). Both tightening constraints (flutter: >=3.44.0, podspec s.platform iOS 13) are now covered by that marker.
  • SPM artifact hygiene (was a nit). kiosk_mode/ios/.gitignore:39-42 now carries .build/, .swiftpm/, and Package.resolved. The patterns have no leading slash, so they match inside kiosk_mode/ios/kiosk_mode/ where Package.swift lives — correct placement.

Unresolved from previous review (2 nits — neither blocks)

  1. kiosk_mode/example/ios/Flutter/AppFrameworkInfo.plistMinimumOSVersion is still deleted rather than rewritten. I re-read the current file: the key is absent, and the sibling optimus_widgetbook/ios/Flutter/AppFrameworkInfo.plist still carries it. Every other deployment-target change in this PR bumps its value (Podfile and three IPHONEOS_DEPLOYMENT_TARGET entries, 9.013.0), which is why the deletion still looks unlike the tool's migrator. Example-app only, so impact is low.
  2. .github/actions/setup/action.yml:9 — the shared composite action is still bumped 3.38.5 → 3.44.8, and the widgetbook web-build workflows (deploy-widgetbook.yml, deploy-widgetbook-azure.yml) remain push: main / workflow_dispatch only, so a regression there would surface only after merge.

Re-verified as still clean

  • No stale references to the removed ObjC shim: SwiftKioskModePlugin returns 0 matches repo-wide, and nothing under kiosk_mode/ references Classes/ or the old iOS 8.0/9.0 floors (the only 3.38 hit is the historical changelog entry).
  • @objc(KioskModePlugin) on kiosk_mode/ios/kiosk_mode/Sources/kiosk_mode/KioskModePlugin.swift:4-5 keeps the ObjC-visible name aligned with the unchanged pluginClass: KioskModePlugin in kiosk_mode/pubspec.yaml:26, and the method/event channel names are byte-identical to before the rename.
  • .vscode/launch.json parses as valid JSON with the new kiosk_mode_example_app entry pointing at a real kiosk_mode/example/lib/main.dart.
  • The SPM layout is consistent: target kiosk_mode resolves to Sources/kiosk_mode/ by SPM's default convention, and the podspec's source_files glob targets the same relocated directory without picking up Package.swift.
Fix All — prompt for AI agent

Fix the following issues in this PR:

  1. In kiosk_mode/example/ios/Flutter/AppFrameworkInfo.plist: confirm the MinimumOSVersion key deletion actually came from the Flutter 3.44 tool migration. If it was a hand edit, restore the key with <string>13.0</string>AppFrameworkInfo.plist becomes App.framework's Info.plist, and an embedded framework missing MinimumOSVersion can trip App Store validation.
  2. Before merging, run melos exec --scope="optimus_widgetbook" -- "flutter build web" on Flutter 3.44.8. The .github/actions/setup bump changes the toolchain for the widgetbook deploy workflows, which only run on push to main, so a regression there would not surface on this PR.

After all changes, run the project's linter/formatter.

@JulioSarmientoMews JulioSarmientoMews left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE — thorough, well-documented packaging change. Verified in an isolated checkout of 49dbe9b.

What I independently confirmed

  • Package.swift matches the official plugin-author template exactly: layout ios/kiosk_mode/Package.swift, .iOS("13.0"), hyphenated kiosk-mode product over underscored kiosk_mode target, and .package(name: "FlutterFramework", path: "../FlutterFramework").
  • The shim removal is safe. @objc(KioskModePlugin) preserves the ObjC name that pluginClass: KioskModePlugin (kiosk_mode/pubspec.yaml:26) resolves, DEFINES_MODULE => YES is retained so the registrant's @import kiosk_mode fallback works, and the method/event channel names are byte-identical across Swift, Kotlin and Dart. Zero stale references to SwiftKioskModePlugin or Classes/ remain.
  • source_files = 'kiosk_mode/Sources/kiosk_mode/**/*.swift' resolves relative to the podspec and matches the relocated sources without picking up Package.swift. The pod lib lint pass is the most load-bearing item in your testing list.
  • The toolchain bump can't break the other packages: none has a Flutter upper bound, none has native code, and melos bs bootstraps all of them inside the green check job.
  • feat! is right — melos will cut 0.9.0 and carry the breaking note, covering both the Flutter floor and the podspec iOS floor.

Resolving a nit from the earlier review round

The MinimumOSVersion deletion in kiosk_mode/example/ios/Flutter/AppFrameworkInfo.plist is genuine Flutter tool behaviour, not a hand edit — see flutter/flutter#185039, "MinimumOSVersion in ios/Flutter/AppFrameworkInfo.plist is auto removed when running on iOS". The tool strips the key so it can inject the value from IPHONEOS_DEPLOYMENT_TARGET at framework-copy time. optimus_widgetbook still has it only because it hasn't been built on 3.44. Nothing to restore here. Heads-up for downstream though: real apps moving to 3.44 may see the same removal and hit the App Store upload issues in that thread.

Validation gap worth naming

Beyond "CI doesn't build iOS": CI runs no automated tests at all. The 12 checks are check (format + analyze), dcm, PR size, CodeQL and six Wiz scanners — there is no flutter test in .github/. So flutter test ✅ 4/4 is local-only, same bucket as both iOS builds. Pre-existing, not introduced here, but it means the registration rename rests entirely on your local runs. Since Guided Access can't be exercised on the simulator, a physical-device smoke test of watchKioskMode() is the one thing I'd want before publishing — it's what actually proves @objc(KioskModePlugin) resolves at registration.

Cross-repo: nothing in this monorepo consumes kiosk_mode except its own example. The real consumer is the Kiosk app in tech-mo; worth confirming it's on Flutter ≥3.44 before 0.9.0 ships. Low risk, since dropping CocoaPods requires 3.44+ SPM anyway.

Two more nits, neither touching lines this PR changed so noted here instead of inline:

  • optimus/pubspec.yaml:8 and optimus_icons/pubspec.yaml:8 still declare flutter: ">=3.38.0". Before this PR the CI pin (3.38.5) matched that floor exactly; now CI only ever exercises 3.44.8, so the declared floor of both flagship published packages is tested nowhere. Not this PR's job to fix, but worth raising the floors in a follow-up or accepting the drift consciously.
  • kiosk_mode/ios/kiosk_mode/Sources/kiosk_mode/KioskModePlugin.swift — pre-existing, unrelated to this diff (outside the patch hunk, so I can't anchor it inline): onCancel nils the sink but never calls removeObserver, so each listen → cancel → listen cycle registers another NotificationCenter observer and handleNotification fires N times per status change afterward. kiosk_mode.dart doesn't .distinct() the stream, so the duplicates do reach consumers. One-line fix (NotificationCenter.default.removeObserver(self) in onCancel), happy for it to be a separate issue.

Two non-blocking notes inline below. Nothing here needs to change before merge — the only ask is dispatching the widgetbook web build on this branch first.

Comment thread .github/actions/setup/action.yml
Comment thread .github/actions/publish/action.yml
@alboiuvlad29
alboiuvlad29 merged commit 67ca17c into main Jul 28, 2026
15 checks passed
@alboiuvlad29
alboiuvlad29 deleted the noticket-kiosk-mode-spm branch July 28, 2026 15:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[iOS] Swift Package Manager: SPM-Support

3 participants