← Hive AxylCONTENT HISTORY

Update to Hive Axyl

Snapshot Oct 24, 2026 · 13:42 UTC · version 2.1.0

Collection source: downloaded plugin package.

WHAT CHANGED

First saved snapshot

No earlier snapshot is available to establish a change.

Save Hive Axyl and check future changes in Following.

Up to 10 plugins free · no card required.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "description": "Implements Hive Axyl APNs push in Unity on iOS/macOS: preparation, native forwarding and actual environment, language/consent changes, and app-policy-driven account binding. Use for Apple push integration; does not send campaigns.",
  "included_files": [],
  "name": "axyl-push-apns",
  "skill_md_contents": "---\nname: axyl-push-apns\ndescription: \"Implements Hive Axyl APNs push in Unity on iOS/macOS: preparation, native forwarding and actual environment, language/consent changes, and app-policy-driven account binding. Use for Apple push integration; does not send campaigns.\"\nlicense: Apache-2.0\nmetadata:\n  author: Com2uS Platform Corporation\n  sdk-version: 1.0.0\n  engine: Unity 6000.0+\n---\n\n# APNs permission through Axyl registration\n\nRegisters an iOS or macOS device's APNs token with Axyl, and changes an existing\nregistration's language, consent or player binding. The Installed SDK, SDK results,\nPush registration, Push settings and account binding and APNs sections below apply\nto every step.\n\n**Inputs:** the player's session, the Apple push entitlement, country, IANA time\nzone, language and the three consents; for a change, the registered device token.\n**App owns:** the delegate forwarder, the permission owner, lifecycle, consent\nstorage and the account-binding policy.\n\nFlow: forward the native callbacks → validate the inputs → permission → entitlement\nenvironment → token → `UpsertTokenAsync` with the matching provider type. A settings\nor account change on a known registration skips all of that. Reception and cold\nstart apply only when requested.\n\n## Verify\n\n- [ ] Device-token bytes reach `NotifyDidRegisterForRemoteNotificationsAsync` before\n  `GetTokenAsync`; missing forwarding is diagnosed, not bypassed.\n- [ ] One permission owner; after every Unity Mobile Notifications ask, `Install`\n  re-runs in a `finally` path.\n- [ ] Denial stops before the token, and an unknown environment never becomes\n  production.\n- [ ] A refreshed token and the current consent reach `UpsertTokenAsync` without\n  recursive acquisition.\n- [ ] Delegate wiring, entitlements and delivery are checked on an Apple build,\n  separately from test doubles.\n\n<!-- axyl-shared: core/installed-sdk.md -->\n\n## Installed SDK\n\n- Read `Packages/manifest.json`, the lock file and each resolved `package.json`.\n  All `com.com2usplatform.hiveaxyl.*` packages must be the same release (UPM does\n  not enforce this); `com.hive.axyl.*` packages are a release before 1.0.0-rc.9.\n- The installed SDK wins for APIs, types and contracts. If its release differs\n  from this skill's `sdk-version`, apply a step only after its fields, result\n  variants, platform support and failure behaviour match; otherwise stop at that\n  step and report what is missing. Never change the installed version.\n- The app owns UI, storage, the device key and orchestration. Sample and recipe\n  classes are not SDK APIs.\n\n<!-- /axyl-shared -->\n\n<!-- axyl-shared: core/results.md -->\n\n## SDK results\n\nMatch each call's concrete result and read `Success.Data` only after checking the\nfields the next step needs. The markers live in `Hive.Axyl.Contracts.Result`.\n\n| Result | Handling |\n|---|---|\n| `IUserCanceledOutcome` | The user dismissed OS or provider UI: stop this attempt quietly |\n| `IUnknownOutcome` | Keep `Code` and `RawJson`; never treat it as a known refusal or a success |\n| `IUntypedProblem` | Technical failure in `Problem` (`HiveError`): network, timeout or caller `Cancelled` |\n| Other typed variant | Only that call's documented meaning |\n\nPass `new ApiCallContext { Token = ct }` and check cancellation before each\nfollow-up call; a request cancelled in flight may still have taken effect. Never\nbranch on HTTP status, exception text or raw bodies, and never log tokens or\ncredentials.\n\n<!-- /axyl-shared -->\n\n<!-- axyl-shared: core/push-preparation.md -->\n\n## Push registration\n\nResolve `Hive.Axyl.Push.IPushService`, registered once with `AddPush()`, which\nalready binds `https://app-api.hiveaxyl.com` (`AddPush(baseUrl)` is only for an\nexplicitly provisioned host; Push has no sandbox or zone), and the selected APNs or\nFCM addon. Registration needs the player's authenticated session, from which the\nserver takes the player ID. The registration belongs to the device token, so logout\ndoes not remove it. Never log device tokens or notification payloads.\n\n**Registration inputs:** ISO country, an IANA time zone ID, a defined `LanguageCode`\n(never `Unspecified`, never parsed from a number), the optional server ID and app\nversion, and three explicit consents: information, advertising and night\nadvertising, where `Night = true` requires `Advertise = true`. OS permission is not\nconsent.\n\n1. Validate the inputs before asking for permission.\n2. Check or request permission with the platform procedure. Denial is an expected\n   state: stop before the token and the registration.\n3. Get a nonempty token and its `UpsertTokenRequestProviderType`: `Apns` or\n   `ApnsSandbox` from the actual APNs entitlement environment, `Fcm` for FCM. An\n   unknown environment or an empty token stops here.\n4. `push.UpsertTokenAsync(new UpsertTokenRequest { Token = deviceToken,\n   ProviderType = providerType, Language = language, Country = country,\n   TimezoneId = timezoneId, Agreement = new Agreement { Info = infoConsent,\n   Advertise = advertiseConsent, Night = nightConsent }, ServerId = serverId,\n   AppVersion = appVersion }, new ApiCallContext { Token = ct })`.\n5. `PushUpsertTokenResult.Success` means the registry accepted the update\n   asynchronously; keep the token and provider type used. It does not prove delivery.\n\nCheck cancellation after the permission, token and registration steps; a request\nthat may have reached the server stays uncertain rather than undone. A settings\nchange on a known registration uses the settings patches below, never a new\npermission prompt or token.\n\n### Token renewal\n\nSubscribe to the plugin's `TokenRefreshed` for the app-owned lifetime and\nunsubscribe on teardown. Re-register the latest token with the current account,\nenvironment, language and full consent. Serialize as the recipe does: while a\nregistration runs, keep only the latest event token, and after `Success` register\nagain only if it differs. Stop on failure or cancellation; a bounded retry is the\napp's choice and must keep the latest desired state. A repeated token, including one\nthe token lookup itself emits, must not loop permission or token calls.\nRegistration has no revision field, so client ordering does not prove the server's\nwrite order after a timeout.\n\n<!-- /axyl-shared -->\n\n<!-- axyl-shared: core/push-management.md -->\n\n## Push settings and account binding\n\nUse `Hive.Axyl.Push.IPushService` with the account's authenticated session and the\ndevice token actually registered; a login access token is not a device token. The\nrecipes do not compose these calls: the coordination below is the app's design.\n\n| Change | Call with `new ApiCallContext { Token = ct }` |\n|---|---|\n| Language | `PatchTokenLanguageAsync(new PatchTokenLanguageRequest { Token = deviceToken, Language = language }, ctx)` |\n| Consent | `PatchTokenAgreementAsync(new PatchTokenAgreementRequest { Token = deviceToken, Agreement = new Agreement { Info = info, Advertise = advertise, Night = night } }, ctx)` |\n| Player binding | `DetachTokenIdentifierAsync(new DetachTokenIdentifierRequest { Token = deviceToken }, ctx)` |\n\nEach result has `Success`, `ResourceNotInScope`, `InvalidSubject`, unknown and\n`Failure` variants. `Success` means the server accepted the change asynchronously,\nnot that it was applied or that delivery changed. Keep the latest desired state\nafter a failure; a typed refusal is no reason to resend it unchanged.\n\n- `Language` is a defined `LanguageCode`, never `Unspecified`.\n- The consent patch replaces the whole `Agreement`; `Night = true` requires\n  `Advertise = true`. Consent comes from app policy, never from OS permission.\n- A new registration or token uses the full `UpsertTokenAsync`; a patch changes one\n  setting of a known registration. Run patches and upserts through one app-owned\n  update flow that carries account, token and the latest full state, so an old\n  result never marks a newer token synchronized. Client ordering does not prove\n  server apply order after a timeout.\n\n### Logout and account switch\n\nWhether logout detaches the player binding, and whether a detach failure blocks\nlogout, is app policy: reuse it or ask the app developer, never add it silently.\n\n| Policy | Boundary |\n|---|---|\n| Detach on logout | Capture the player and device token, pause queued upserts, and dispatch the detach while that account is still authenticated, before Auth logout; then apply the app's failure policy and the logout result |\n| Keep the binding | The token keeps targeting that player; login alone does not rebind it, and an app rebind upserts with the new account and its consent |\n\nDetach removes only the player ID from the registration: it does not delete the\ntoken, revoke OS permission, reset consent or recall queued notifications. Never\nsend one account's cleanup as another, or retry it with an invalidated token. If the\ndetach succeeds but Auth logout is refused or cancelled, report that split state;\nnever rebind automatically or call it a rollback.\n\n<!-- /axyl-shared -->\n\n<!-- axyl-shared: push/apns.md -->\n\n## APNs\n\nRegister `AddAPNS()` and resolve `Hive.Axyl.Push.Addon.APNS.IAPNSPlugin` on iOS or\nmacOS. The push entitlement and native delegate forwarding belong to the app; the\naddon does not install the app delegate.\n\n### Forwarding\n\nBefore `GetTokenAsync`, forward the native callbacks to the port:\n`didRegisterForRemoteNotifications` passes the **device-token bytes** to\n`NotifyDidRegisterForRemoteNotificationsAsync(byte[], ct)`, and a failure passes its\ndescription to `NotifyDidFailToRegisterAsync(errorDescription, ct)`. Decode a hex\nstring from a native bridge into bytes; never send its UTF-8 bytes. Build the\nforwarder from the installed SDK's template; the sample's\n`ApnsRemoteNotificationForwarder` is an app example, not an SDK type.\n\nUnity Mobile Notifications re-points the notification-center delegate without\nchaining whenever it asks for permission, for push or local notifications, including\nan implicit ask while scheduling. After every such ask, re-run the forwarder's\n`Install` in a `finally` path (success, failure and cancellation); the template\nre-chains only when replaced, so extra calls are no-ops.\n\n### Permission, environment and token\n\n1. Permission is OS-wide and shared with local notifications, so the app picks one\n   owner. SDK route: `RequestAuthorizationAsync(options, ct)` with app-chosen\n   `UNAuthorizationOption` values; on `ApnsServiceRequestAuthorizationResult.Success`,\n   `Data.Granted == false` stops. The recipe's iOS route uses Unity Mobile\n   Notifications instead: read `AuthorizationStatus` (`Authorized`, `Provisional`\n   and `Ephemeral` are granted, `Denied` is denied) and ask only when undetermined,\n   with an `AuthorizationRequest` for Alert/Badge/Sound and\n   `registerForRemoteNotifications: false`, then apply the reinstall rule. On macOS\n   the recipe uses the addon.\n2. `GetProviderEnvironmentAsync(ct)` must succeed with a usable environment:\n   `ProviderEnvironment.ApnsSandbox` maps to `UpsertTokenRequestProviderType.ApnsSandbox`\n   and `ProviderEnvironment.Apns` to `UpsertTokenRequestProviderType.Apns`. Never\n   infer production from null, an unknown value, the Editor, debug mode or a build name.\n3. `GetTokenAsync(ct)` must return `ApnsServiceGetTokenResult.Success` with a\n   nonempty `Data.Token`. Missing forwarding is diagnosed, never replaced by a\n   synthesized token.\n\n### Reception and cold start\n\nOnly when requested: forward `willPresent` and the notification response through\n`NotifyWillPresentNotificationAsync` and `NotifyDidReceiveResponseAsync`, subscribe\nto `NotificationPresented` and `NotificationOpened`, keep the app's delegate chain\nand tell remote from local notifications. The forwarder template (since\n1.0.0-rc.8) relays a remote `willPresent` and answers the OS itself with\n`kAxylApnsForegroundOptions`, so the app's or Unity's `willPresent` is not called\nfor remote pushes; local notifications and taps still chain. Replace older template\ncopies, and change remote foreground presentation by editing that constant.\n\nFor cold start, capture the launch payload before startup, forward its raw JSON with\n`NotifyColdStartNotificationAsync` and await that before the single-use\n`GetColdStartNotificationAsync`. On macOS the capture must be compiled into the\nlaunch app.\n\n<!-- /axyl-shared -->\n"
}

SHA-256 of public snapshot: 6a2586b49e4daa23c29f4b6543730e20ac16725b6fe1787d6cadefee04b609e0