Publish Score iOS SDK 0.4.0

This commit is contained in:
bdot
2026-10-06 22:25:11 -04:00
parent 47531c5192
commit 540823aac1
5 changed files with 51 additions and 17 deletions

View File

@@ -8,9 +8,9 @@ identically — see [`conformance/CONTRACT.md`](conformance/CONTRACT.md).
--- ---
## Install — 0.3.0 beta (public, no auth) ## Install — 0.4.0 beta (public, no auth)
Distributed publicly from **[`3HL-SCORE/public-sdks-score`](https://git.3halves-labs.com/3HL-SCORE/public-sdks-score)** (#284) — **no Gitea token / login required**. The iOS XCFramework is a public **release asset** (`mobile-v0.3.0`) on that repo; the Android artifacts live in the org's **Gitea Maven package registry** (anonymous read). Distributed publicly from **[`3HL-SCORE/public-sdks-score`](https://git.3halves-labs.com/3HL-SCORE/public-sdks-score)** (#284) — **no Gitea token / login required**. The iOS XCFramework is a public **release asset** (`mobile-v0.4.0`) on that repo; the Android artifacts live in the org's **Gitea Maven package registry** (anonymous read).
### Android — Gradle (public Maven registry) ### Android — Gradle (public Maven registry)
```kotlin ```kotlin
@@ -18,7 +18,7 @@ repositories {
maven { url = uri("https://git.3halves-labs.com/api/packages/3HL-SCORE/maven") } // public — no credentials maven { url = uri("https://git.3halves-labs.com/api/packages/3HL-SCORE/maven") } // public — no credentials
} }
dependencies { dependencies {
implementation("com.threehalveslabs.scoretracker:shared-android:0.3.0") implementation("com.threehalveslabs.scoretracker:shared-android:0.4.0")
} }
``` ```
@@ -34,17 +34,17 @@ add it by **git URL** — no registry config, no login:
```swift ```swift
// your app's Package.swift // your app's Package.swift
dependencies: [ dependencies: [
.package(url: "https://git.3halves-labs.com/3HL-SCORE/public-sdks-score.git", from: "0.3.0") .package(url: "https://git.3halves-labs.com/3HL-SCORE/public-sdks-score.git", from: "0.4.0")
], ],
// target: .product(name: "ScoreTracker", package: "public-sdks-score") // target: .product(name: "ScoreTracker", package: "public-sdks-score")
``` ```
Or in Xcode: **File → Add Packages…** → paste the URL. The binary `ScoreTrackerKit` Or in Xcode: **File → Add Packages…** → paste the URL. The binary `ScoreTrackerKit`
target pulls the XCFramework from the **public** `mobile-v0.3.0` release asset (SPM target pulls the XCFramework from the **public** `mobile-v0.4.0` release asset (SPM
verifies the checksum) — no `~/.netrc` credentials needed. Verified with an verifies the checksum) — no `~/.netrc` credentials needed. Verified with an
anonymous `swift package resolve`. anonymous `swift package resolve`.
### iOS — direct XCFramework ### iOS — direct XCFramework
Download `ScoreTracker-pod.zip` from the **mobile-v0.3.0** release on Download `ScoreTracker-pod.zip` from the **mobile-v0.4.0** release on
[`public-sdks-score`](https://git.3halves-labs.com/3HL-SCORE/public-sdks-score/releases). [`public-sdks-score`](https://git.3halves-labs.com/3HL-SCORE/public-sdks-score/releases).
It holds `ScoreTrackerKit.xcframework` and the Swift facade sources It holds `ScoreTrackerKit.xcframework` and the Swift facade sources
(`Sources/ScoreTracker/`: `ScoreClient.swift`, `Exports.swift`, `AppLifecycle.swift`). (`Sources/ScoreTracker/`: `ScoreClient.swift`, `Exports.swift`, `AppLifecycle.swift`).
@@ -220,6 +220,27 @@ score.setTokenProvider { credentialsManager.awaitCredentials().accessToken }
score.identify("auth0|abc123") score.identify("auth0|abc123")
``` ```
### Signing in: traits and replay (#488)
`identify` takes the fan's traits, sent on an `identify` event (consent-gated
and PII-scrubbed like any props). Pass `replayAnonymousEvents` to re-send the
events the app already sent while the fan was signed out, now with the fan
attached, so a poll vote cast before sign-in still earns its karma:
```swift
score.identify(userId: "auth0|abc123", traits: ["team": "urc-leinster"], replayAnonymousEvents: true)
```
```kotlin
score.identify("auth0|abc123", traits = mapOf("team" to "urc-leinster"), replayAnonymousEvents = true)
```
Replay is opt-in. It re-sends at most the last 100 anonymously sent events
(`identifyReplayBufferSize` on the factory / `ScoreClient` init), only those
current consent still allows, marked `_backfill: true` with `_originalEventId`.
Events still waiting in the queue aren't replayed: they go out with the fan on
the next flush. The buffer lives in memory only, so it's empty after a cold
start.
How the provider is used: How the provider is used:
- **Before each flush**, and **single-flight**: overlapping flushes share one - **Before each flush**, and **single-flight**: overlapping flushes share one
in-flight call instead of starting several. in-flight call instead of starting several.

View File

@@ -15,8 +15,8 @@ let package = Package(
targets: [ targets: [
.binaryTarget( .binaryTarget(
name: "ScoreTrackerKit", name: "ScoreTrackerKit",
url: "https://git.3halves-labs.com/3HL-SCORE/public-sdks-score/releases/download/mobile-v0.3.0/ScoreTrackerKit.xcframework.zip", url: "https://git.3halves-labs.com/3HL-SCORE/public-sdks-score/releases/download/mobile-v0.4.0/ScoreTrackerKit.xcframework.zip",
checksum: "81edf75ed592ef2b4cf17af8aea406333cebba62b2e5b69c28e386e8bc415a7d" checksum: "7a900bb0b672188f1bd994aa9161817f6318d2746a4c32c4a56ca95a27392ade"
), ),
.target( .target(
name: "ScoreTracker", name: "ScoreTracker",

View File

@@ -9,13 +9,13 @@ Public, auth-free distribution of the **Score mobile tracker** SDKs for iOS and
> This repo hosts the **consumable artifacts** (release assets + package manifests). The SDK source lives in 3 Halves Labs' private platform repo. > This repo hosts the **consumable artifacts** (release assets + package manifests). The SDK source lives in 3 Halves Labs' private platform repo.
## Install — 0.3.0 (no token / login required) ## Install — 0.4.0 (no token / login required)
### iOS — Swift Package Manager ### iOS — Swift Package Manager
Add this repo by git URL. There's no registry to configure and no login. Add this repo by git URL. There's no registry to configure and no login.
```swift ```swift
// Package.swift // Package.swift
dependencies: [ .package(url: "https://git.3halves-labs.com/3HL-SCORE/public-sdks-score.git", from: "0.3.0") ] dependencies: [ .package(url: "https://git.3halves-labs.com/3HL-SCORE/public-sdks-score.git", from: "0.4.0") ]
// target: .product(name: "ScoreTracker", package: "public-sdks-score") // target: .product(name: "ScoreTracker", package: "public-sdks-score")
``` ```
Or in Xcode: **File → Add Packages…** and paste the URL. (The old Swift package Or in Xcode: **File → Add Packages…** and paste the URL. (The old Swift package
@@ -29,7 +29,7 @@ pod 'ScoreTracker',
``` ```
### iOS — direct XCFramework ### iOS — direct XCFramework
Download `ScoreTracker-pod.zip` from the [`mobile-v0.3.0` release](https://git.3halves-labs.com/3HL-SCORE/public-sdks-score/releases). It contains `ScoreTrackerKit.xcframework` plus the Swift facade sources (`Sources/ScoreTracker/`). Drag the framework into Xcode and add the Swift files to your app target. Download `ScoreTracker-pod.zip` from the [`mobile-v0.4.0` release](https://git.3halves-labs.com/3HL-SCORE/public-sdks-score/releases). It contains `ScoreTrackerKit.xcframework` plus the Swift facade sources (`Sources/ScoreTracker/`). Drag the framework into Xcode and add the Swift files to your app target.
### Android — Gradle ### Android — Gradle
```kotlin ```kotlin
@@ -37,7 +37,7 @@ repositories {
maven { url = uri("https://git.3halves-labs.com/api/packages/3HL-SCORE/maven") } // public — no credentials maven { url = uri("https://git.3halves-labs.com/api/packages/3HL-SCORE/maven") } // public — no credentials
} }
dependencies { dependencies {
implementation("com.threehalveslabs.scoretracker:shared-android:0.3.0") implementation("com.threehalveslabs.scoretracker:shared-android:0.4.0")
} }
``` ```

View File

@@ -1,6 +1,6 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = 'ScoreTracker' s.name = 'ScoreTracker'
s.version = '0.3.0' s.version = '0.4.0'
s.summary = 'Score mobile tracker (iOS) — KMP shared core + Swift facade.' s.summary = 'Score mobile tracker (iOS) — KMP shared core + Swift facade.'
s.description = <<-DESC s.description = <<-DESC
Lightweight app analytics / fan-engagement tracker. Emits the same event Lightweight app analytics / fan-engagement tracker. Emits the same event

View File

@@ -30,7 +30,8 @@ public final class ScoreClient {
locale: String? = nil, locale: String? = nil,
initialConsent: ConsentState = ConsentState.companion.NONE, initialConsent: ConsentState = ConsentState.companion.NONE,
pii: PiiOptions? = nil, pii: PiiOptions? = nil,
tokenRefreshTimeoutSeconds: Double = 5 tokenRefreshTimeoutSeconds: Double = 5,
identifyReplayBufferSize: Int = 100
) { ) {
let config = TrackerConfig( let config = TrackerConfig(
publishableKey: publishableKey, publishableKey: publishableKey,
@@ -42,7 +43,8 @@ public final class ScoreClient {
config: config, config: config,
initialConsent: initialConsent, initialConsent: initialConsent,
pii: pii, pii: pii,
tokenRefreshTimeoutMs: Int64(tokenRefreshTimeoutSeconds * 1000) tokenRefreshTimeoutMs: Int64(tokenRefreshTimeoutSeconds * 1000),
identifyReplayBufferSize: Int32(identifyReplayBufferSize)
) )
} }
@@ -82,8 +84,19 @@ public final class ScoreClient {
// MARK: - Identity / Auth0 // MARK: - Identity / Auth0
public func identify(userId: String, token: String? = nil) { /// Bind events to the fan (#488). Sends an `identify` event carrying
tracker.identify(userId: userId, token: token) /// `traits` (consent-gated and PII-scrubbed like any props). With
/// `replayAnonymousEvents`, events already sent before sign-in are re-sent
/// with the fan attached, marked `_backfill`, so a vote cast while signed
/// out earns its karma. Events still queued need no replay: they go out
/// with the user on the next flush.
public func identify(
userId: String,
token: String? = nil,
traits: [String: Any] = [:],
replayAnonymousEvents: Bool = false
) {
tracker.identify(userId: userId, token: token, traits: traits, replayAnonymousEvents: replayAnonymousEvents)
} }
public func setToken(_ token: String?) { tracker.setToken(token: token) } public func setToken(_ token: String?) { tracker.setToken(token: token) }