Repository navigation
Releases: muhiro12/MHPlatform
Release list
1.20.0
Changes
- Restore retained update evidence automatically before the first operation
check, including App Intents and other headless entry points (#17). - Add read-only decision evaluations with policy/publication freshness,
evaluation reasons, and restoration/refresh diagnostics (#18). - Coordinate evidence reads and revision-checked writes across controllers and
processes. Older responses and failed delayed publication saves cannot roll
back newer accepted policy. Empty publication results retain their ordering. - Share overlapping refresh requests while preserving independent caller
cancellation. Cancelling the last waiter invalidates late responses. - Unify public schedule JSON encoding and decoding, separate private cache
representation, and validate update feature format versions independently. - Add a publisher-only configuration validator that uses the runtime decoder
and immutable-revision rules, without adding app products or dependencies.
Compatibility
Standard controller construction and operation checks keep their existing API.
Existing shared configuration documents and retained snapshots remain readable;
restoration never renews their observation timestamps.
Direct MHRemoteUpdateSchedule Codable now uses the documented shared Config
envelope and ISO 8601 dates, matching encoded() and decode(_:), rather than
the previous flat cache shape. Use MHRemoteUpdateSnapshot for retained evidence.
features.remoteUpdate.schemaVersion describes the update feature format;
the current value is 1, and omission still means 1. It is independent of the
shared envelope version and the policy revision. Unsupported feature formats
fail only that selected section. Previously distributed readers cannot learn
this check retroactively; incompatible future formats need a reader migration
and a separate endpoint while older readers remain supported.
Validation
- 485 package tests / 600 executions passed, with no failures or skipped tests.
- Five consumer fixtures passed 25 tests; publisher tool passed 7 behavior tests.
- Example iOS app build passed; native build diagnostics reported no warnings
or errors for the package and example app. - Three trials of 32 independent concurrent processes retained the highest
policy revision in every trial.
Full changelog: 1.19.0...1.20.0
1.19.0
Remote update plans without weakening active requirements
- Keep the current
requirementwhile announcing additional timedscheduledRequirements. The strictest active minimum applies, including when plans overlap; ending or removing a plan leaves the active baseline in force. - Retain and reevaluate plans across restarts with the existing immutable revisions and bounded policy/publication evidence. A future unpublished version does not invalidate sufficient publication evidence for the current minimum.
- Add
MHRemoteConfigSection<Value>so missing or invalid feature payloads remain observable without discarding valid siblings from one shared response. Broken JSON, envelope/identity failures, and cancellation still reject the shared request. Failed update sections cannot renew retained update evidence.
Compatibility and adoption
- Standard endpoint/Store-ID initialization, lifecycle preparation, and operation checks are unchanged. Existing schedule/feature initializers, 1.18.0 documents, and retained snapshots remain supported.
- Deploy this reader before relying on
scheduledRequirements: older readers ignore the new field and enforce only the baseline. Keep that baseline when announcing plans. Clear both the baseline and all plans, with a higher revision, for full cancellation. - No announcement feature or additional app-side plan-selection/recovery machinery is introduced.
See the remote update adoption guide for the wire format, failure boundaries, and rollout order.
1.18.0
Changes
- Add the standalone, Foundation-only
MHRemoteConfigproduct for shared HTTPS fetching, app identity validation, and typed configuration decoding. It is also available throughMHPlatformCoreandMHPlatform. - Support static update policies without periodic publisher renewal. A validated network fetch renews bounded retained evidence; disk reads, failures, and reused responses do not.
- Keep planned activation, immutable revisions, postponement, explicit cancellation, and App Store publication evidence separate. Unknown evidence remains distinguishable from a confirmed lack of an update requirement.
- Omit the storefront country by default, with an optional explicit
storefrontoverride for publication lookup.
Migration from 1.17.0
This release deliberately replaces the scheduled-update contract before operational adoption. It is an explicit minor-release exception to normal source and wire compatibility.
- Rename the standard initializer's
policyURL:argument toconfigurationURL:. - Use the common configuration envelope with
features.remoteUpdate. Remove publisher-authoredissuedAtandvalidUntil; each feature owns its policy and revisions. - Advanced providers now return
MHRemoteConfigResponse<MHRemoteUpdateSchedule>with the original network observation. UsepolicyLifetime:in place ofmaximumValidity:. - Existing 1.17.0 snapshots are not migrated or reinterpreted.
prepare()attempts a fresh fetch after an incompatible cache; without valid evidence, protected operations remain unavailable. - Set
storefront: "jp"explicitly to retain 1.17.0's reference store. Omission uses Apple's service default, which is not a global catalog.
See the migration and adoption guide. Existing legacy comparison and evaluator primitives remain available. This package release does not migrate consuming apps or deploy their configuration.
Full Changelog: 1.17.0...1.18.0
1.17.0
What's New
- Adds
MHRemoteUpdate, a Foundation-only module available through
MHPlatformCore,MHPlatform, or its standalone product. - Provides version comparison, identity-validated App Store lookup, and explicit
notRequired,updateRequired, andunavailabledecisions. - Retains scheduled update requirements across launches and re-evaluates
activation, expiry, installed version, and publication evidence when checking
an operation. - Adds standard App Store integration with a policy URL and Store ID,
package-managed persistence, preparation, and operation checks.
Adoption
import MHPlatformCore
let updates = try MHRemoteUpdateController(
appStoreID: appStoreID,
policyURL: policyURL
)
let preparation = try await updates.prepare()Call prepare() at startup and foreground entry points. Place
checkOperationAllowed() before protected operations; it requires confirmed
permission and refreshes once when evidence is unavailable. App-designated
low-impact operations can explicitly use .allowWhenUnavailable. A confirmed
update requirement blocks both rules.
Standard integration uses the jp storefront, a maximum policy-document
lifetime of 30 days, and publication evidence valid for 24 hours. Apps supply
release policy, classify operations, and connect lifecycle hooks, UI, and
logging. Adoption requires app changes; existing binaries do not acquire
enforcement automatically.
Publish operational policy independently of app source releases. See the
remote update adoption guide
for the JSON contract, deployment approach, legacy migration, and advanced
overrides.
Reliability
- Preserves immutable policy identity through JSON round trips by normalizing
dates to whole seconds and rejecting collapsed intervals. - Retries failed initial cache reads on a later preparation call until evidence
initializes. - Bypasses local HTTP cache when observing App Store publication, preventing
stale metadata from renewing publication evidence.
Compatibility and Verification
This is an additive minor release from 1.16.0; existing public APIs remain
available. Verification includes 114 relevant iPhone Simulator tests, five
consumer fixtures, SwiftLint, the Example app build, and eight version-selection
tests. App-specific adoption and live Store or policy deployment remain separate
verification steps.
Full Changelog: 1.16.0...1.17.0
1.16.0
1.15.0
1.14.0
Native ads: migration required
MHPlatform 1.14.0 adopts GoogleMobileAdsWrapper 2.0.0 and replaces the old native-ad size API with layout choices. This is an explicitly documented breaking transition within a minor release. There are no compatibility aliases.
| Previous API | Replacement |
|---|---|
MHNativeAdSize.small |
MHNativeAdLayout.compact |
MHNativeAdSize.medium |
MHNativeAdLayout.media |
runtime.nativeAdView(size:) |
runtime.nativeAdView(layout:) |
MHRuntimeNativeAdViewFactory.makeView(size:) |
MHRuntimeNativeAdViewFactory.makeView(layout:) |
// Before
runtime.nativeAdView(size: .small)
runtime.nativeAdView(size: .medium)
// After
runtime.nativeAdView(layout: .compact)
runtime.nativeAdView(layout: .media)Custom native-ad factory closures now receive MHNativeAdLayout and are @MainActor. Call makeView(layout:) on the main actor as well, matching SwiftUI view creation. Update stored selection properties, picker tags, and switches accordingly. Consumers do not need to import Google SDK types.
If an app persists the old enum raw values, migrate "small" to "compact" and "medium" to "media" before decoding MHNativeAdLayout. MHPlatform does not automatically rewrite app-owned preferences.
The adapter waits for shared SDK initialization before requesting ads. The code-built views use the available width and natural height, with compact text and media-forward layouts. Apps retain ownership of backgrounds, padding, placement, and optional frame constraints. Recheck any fixed heights chosen for the previous Small or Medium layouts.
Consent orchestration and Privacy Options integration are not enabled automatically by this update. Complete applicable consent and audience configuration before starting the runtime or displaying ads. Shared orchestration remains tracked in #14; per-app integration remains separate work.
Preferences and startup
This release also includes the preference improvements accumulated since 1.13:
- Add
MHPreferenceRegistryand synchronous startup migration, with async-only step rejection before migration or cleanup begins. - Add explicit defaults-selection initializers for preference and deep-link stores.
- Include domain cleanup allowlists in diagnostics.
- Document descriptor-first migration, external raw-key preservation, startup ordering, and enum-backed
AppStorageshorthand.
Read the preference migration guide before enabling migration or whole-domain cleanup. Existing destination values remain protected; callers own the complete storage allowlist and serialization of migration runs.
Dependencies
| Dependency | Resolved version |
|---|---|
| GoogleMobileAdsWrapper | 2.0.0 (updated) |
| Google Mobile Ads SDK | 13.10.0 (updated) |
| SwiftLintPlugins | 0.65.1 (updated) |
| StoreKitWrapper | 1.4.0 (already current) |
| LicenseList | 2.5.0 (minimum raised to refresh older consumer resolutions) |
| Google User Messaging Platform | 3.1.0 (already current) |
Versioning
Releases now use MAJOR.MINOR.PATCH. Automatic releases increment the minor number and reset patch to zero; the next automatic release after this one is 1.15.0. Manual workflow dispatch supports explicit major or patch releases. Legacy two-component tags are normalized for version selection, and rerunning an already tagged commit does not publish a second release.
Following the 1.14.0 transition exception above, incompatible public API changes require a major release.
Verification
- iOS package tests: 342 passed, zero failures; one non-iOS-only test is not applicable to the iOS run.
- Package build including test targets, macOS umbrella build, and watchOS Simulator umbrella build passed.
- SwiftLint, model-directory rules, and all five consumer fixture checks passed.
- Eight isolated Git-based release-version tests passed.
- Example Simulator smoke: actual Google test ad displayed through Compact → Media → Compact with natural List sizing; final Example build passed with current dependencies and no actor-isolation warning.
The final iOS package test used official xcodebuild after the Xcode integration timed out and disconnected. Real-device, purchase/restore, production consent, and full ad-policy validation are outside this release's targeted verification. An adjacent unavailable-subscription row appeared blank in the Example Simulator; this was recorded separately from the successful ad layout check.
1.13
Changes
- Adopt StoreKitWrapper 1.4 and derive premium status from verified purchased product identifiers independently of product metadata. A failed catalog request no longer makes a verified subscription appear inactive.
- Adopt GoogleMobileAdsWrapper 1.4 and Google Mobile Ads SDK 13.9.0, including native ad layout and loading lifecycle fixes.
- Use the wrapper's typed ad-size API inside MHAppRuntimeAds while preserving the public MHNativeAdSize cases.
- Add a local StoreKit subscription catalog and complete the official test-ad configuration in the example app. Remove empty ad spacing when ads are disabled.
- Expand regression coverage for purchase updates, premium ad suppression, and native ad adapter configuration.
Integration notes
- MHAppRuntime.StartStore and its purchased-product-identifier callback are MainActor-isolated. Custom startup bridges must preserve this isolation. Invoke a stored bridge from the main actor or await it from another actor.
- The minimum deployment targets remain iOS 18, macOS 15, and watchOS 11. The package requires a Swift 6.2-capable toolchain.
Full Changelog: 1.12...1.13