Releases: AppGlance/appglance-apple
Releases · AppGlance/appglance-apple
Release list
1.0.2
Fixed
- First launches no longer count as two sessions. The session id used to be minted only at the
first foreground, afterinstallhad already been recorded with no session id, and a
session-id-less event opens its own session server-side, so every first launch produced one
session holdinginstalland another holdingsession.start. The id is now minted at
client startup whenever a fresh session is inevitable (a first launch, or a relaunch after
sessionTimeout), soinstalland every event recorded before the first foreground carry
the same session id thatsession.startthen adopts, and the server folds them into one
session. The pre-minted id is persisted as unadopted: if the process dies before its first
foreground, the next launch reuses it, so the sessioninstallopened still receives its
session.start. Resumed sessions behave exactly as before. - The environment correction now retries. A fresh install's very first ask can find no cached
AppTransaction(and the fetch can fail on an offline first launch), and 1.0.1 asked
exactly once per process, so such installs kept theappstoreguess for the whole run. The
store is re-asked at every flush until it answers, and a flush waits at most three seconds
for it, so a slow or offline first launch cannot delay sending; a late answer corrects the
label for everything still queued and everything that follows. - The heartbeat's timing now survives a relaunch. The last-heartbeat stamp was memory only, so
quitting and relaunching inside the session timeout beat again immediately and slightly
inflated the additive presence and session-length rollups. The stamp is persisted alongside
the last-active stamp and honored at startup, so a relaunch beats only once the heartbeat
interval has genuinely passed. - A test seeded with a label equal to the iOS Simulator host's own environment guess no longer
fails there, and a formatting violation is fixed, so the whole CI matrix is green again.
Added
- Failed sends now back off. After a retryable failure (offline,
429,5xx), automatic
delivery waits exponentially longer between attempts, with jitter, capped at 60 seconds and
reset by any successful send, and a numericRetry-Afteron a429is honored as the floor
for that wait. An explicitAppGlance.flush()still attempts immediately.
Changed
- Publishing a release is now gated on CI: the release workflow runs the full lint, build and
test matrix first and publishes only when everything passes. - The README and the doc comments describe the TestFlight versus App Store split as currently
known to behave: the mapping from the store's signedAppTransactionis in place and
covered by tests, but on-device confirmation from a real TestFlight install is still
pending, and TestFlight installs have been observed reportingappstorein the field.
1.0.1
Fixed
- TestFlight builds are labelled
testflightagain. The legacy receipt heuristic stopped
distinguishing TestFlight from the App Store for apps built with the iOS 18 SDK or later
(both channels get a receipt URL ending in plainreceipt), so TestFlight events landed in
the App Store scope. The environment is now read from the store's signedAppTransaction,
with the receipt heuristic kept as the fallback for builds the store cannot vouch for. The
answer arrives asynchronously; the first flush waits for it and restamps anything queued, so
no event leaves with the wrong label. A store-signed build launched from the developer tools
(AppTransaction.environment == .xcode) now labels itselfdebugrather thanappstore.
1.0.0
First public release.
Added
AppGlance.configure(apiKey:)for the hosted service;AppGlance.configure(_:)with an
AppGlance.Configurationfor full control, including your own Supabase project..trackAppLifecycle()for SwiftUI;AppGlance.setActive(_:)for UIKit;.trackScreen(_:)
andAppGlance.trackScreen(_:)for funnels.- A random install id kept in the Keychain, so delete-and-reinstall counts as one user. Never
the IDFA, never an IP-derived location, never a device fingerprint. - Sessions with a
session_idon every event and a configurablesessionTimeout(default five
minutes, matching how the dashboard splits sessions); a foreground presence heartbeat; the
device's region setting as the country (collectsCountry); and anything youtrack. - Optional user properties:
identify(id:email:name:properties:),setUserProperties(_:),
reset(). Sent asuser.identifyonly when they change; keys and values clamped to the
server's limits (20 keys, 40 / 200 characters). - Environments: every event is tagged
appstore,testflight,simulatorordebug.
enabledEnvironmentsdefaults to[.appStore, .testFlight], so Simulator and Debug builds
stay out of your numbers; TestFlight is detected at runtime on every platform, including macOS. - Debug mode (
debug: true): the current build sends whatever its environment (the tag stays
truthful, so those events appear under All in the dashboard and never in Live) and logs to
the console. Without it, a gated build prints one line explaining why nothing is sent. - Delivery: events are persisted as they are tracked, sent oldest-first in slices of 100 through
one serial sender, and retried after transient failures. Every event carries a client-minted
event_id; both the hosted ingest and the Supabase backend ignore replays, so a batch whose
acknowledgement was lost is stored once. Presence pings, which the server folds into rollups
on arrival, are re-sent only when it provably never saw them - including after a process killed
mid-request, since the on-disk queue never holds an in-flight ping. - Calls apply strictly in call order through one internal queue, timestamps taken at call time.
Calls made beforeconfigure, or before the Keychain is readable after a reboot, are held
(up to 200) and replayed;installis always the first event. - A flush on the way to the background runs under a process assertion. Permanent
4xx
responses drop the slice instead of retrying forever;413halves it. - Supabase mode requires the AppGlance events schema; its unique index over
(app_id, event_id)
is what makes a retried batch idempotent. - iOS 16+, macOS 13+, tvOS 16+, watchOS 9+, visionOS 1+. Swift 5.10. No dependencies. Compiles
cleanly under strict concurrency checking and in Swift 6 language mode.