1 Commits
0.1.0 ... 0.2.0

Author SHA1 Message Date
bdot
411afa70a2 Publish Score iOS SDK 0.2.0 2026-10-02 14:28:42 -04:00
5 changed files with 111 additions and 57 deletions

View File

@@ -8,57 +8,45 @@ identically — see [`conformance/CONTRACT.md`](conformance/CONTRACT.md).
--- ---
## Install — 0.1.0 beta (hosted on Gitea) ## Install — 0.2.0 beta (public, no auth)
Everything is hosted on the org's Gitea — no external registry. The Android 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.2.0`) on that repo; the Android artifacts live in the org's **Gitea Maven package registry** (anonymous read).
artifacts live in **Gitea's Maven package registry**; the iOS XCFramework is a
**Gitea release asset** (`mobile-v0.1.0`).
### Android — Gradle (Gitea Maven registry) ### Android — Gradle (public Maven registry)
```kotlin ```kotlin
repositories { repositories {
maven { maven { url = uri("https://git.3halves-labs.com/api/packages/3HL-SCORE/maven") } // public — no credentials
url = uri("https://git.3halves-labs.com/api/packages/3HL-SCORE/maven")
// If the package registry isn't public, add a Gitea token:
// credentials(HttpHeaderCredentials::class) { name = "Authorization"; value = "token ${'$'}giteaToken" }
// authentication { create<HttpHeaderAuthentication>("header") }
}
} }
dependencies { dependencies {
implementation("com.threehalveslabs.scoretracker:shared-android:0.1.0") implementation("com.threehalveslabs.scoretracker:shared-android:0.2.0")
} }
``` ```
### iOS — CocoaPods (release asset) ### iOS — CocoaPods (public release asset)
```ruby ```ruby
pod 'ScoreTracker', pod 'ScoreTracker',
:podspec => 'https://git.3halves-labs.com/3HL-SCORE/score-anything-loyalty/raw/branch/main/mobile/ios/ScoreTracker/ScoreTracker.podspec' :podspec => 'https://git.3halves-labs.com/3HL-SCORE/public-sdks-score/raw/branch/main/ScoreTracker.podspec'
``` ```
### iOS — Swift Package Manager (Gitea Swift registry) ### iOS — Swift Package Manager (public git URL)
Published to Gitea's **Swift package registry** as `threehalveslabs.ScoreTracker`. The SPM package lives at the **root** of the public `public-sdks-score` repo, so
Configure the registry once (per machine / CI), then add it as a normal add it by **git URL** — no registry config, no login:
dependency by **id** — no git URL needed:
```bash
swift package-registry set https://git.3halves-labs.com/api/packages/3HL-SCORE/swift
# private registry → log in with a Gitea token:
swift package-registry login https://git.3halves-labs.com/api/packages/3HL-SCORE/swift --token <gitea-token>
```
```swift ```swift
// your app's Package.swift // your app's Package.swift
dependencies: [ .package(id: "threehalveslabs.ScoreTracker", from: "0.1.0") ] dependencies: [
// then: .product(name: "ScoreTracker", package: "ScoreTracker") .package(url: "https://git.3halves-labs.com/3HL-SCORE/public-sdks-score.git", from: "0.2.0")
],
// target: .product(name: "ScoreTracker", package: "public-sdks-score")
``` ```
The package's binary `ScoreTrackerKit` target pulls the XCFramework from the Or in Xcode: **File → Add Packages…** → paste the URL. The binary `ScoreTrackerKit`
`mobile-v0.1.0` release asset (SPM verifies the checksum). For a private release target pulls the XCFramework from the **public** `mobile-v0.2.0` release asset (SPM
asset, SPM reads credentials from `~/.netrc`. verifies the checksum) — no `~/.netrc` credentials needed. Verified with an
anonymous `swift package resolve`.
> Note: SPM-by-git-URL isn't available (the package lives in a monorepo subdir,
> which SPM can't resolve) — the Swift registry above is the supported path.
### iOS — direct XCFramework ### iOS — direct XCFramework
Download `ScoreTrackerKit.xcframework.zip` from the **mobile-v0.1.0** release and Download `ScoreTrackerKit.xcframework.zip` from the **mobile-v0.2.0** release on
drag the `.xcframework` into your Xcode project; add `ScoreClient.swift`. [`public-sdks-score`](https://git.3halves-labs.com/3HL-SCORE/public-sdks-score/releases)
and drag the `.xcframework` into your Xcode project; add `ScoreClient.swift`.
--- ---
@@ -71,7 +59,7 @@ let score = ScoreClient(
publishableKey: "pk_live_…", publishableKey: "pk_live_…",
siteId: "urc-ios", siteId: "urc-ios",
endpoint: "https://<middleware-host>", endpoint: "https://<middleware-host>",
sdkVersion: "0.1.0", sdkVersion: "0.2.0",
appVersion: Bundle.main.shortVersion, appVersion: Bundle.main.shortVersion,
os: "iOS \(UIDevice.current.systemVersion)", os: "iOS \(UIDevice.current.systemVersion)",
device: UIDevice.current.model, device: UIDevice.current.model,
@@ -106,7 +94,7 @@ val score = newScoreTracker(
publishableKey = "pk_live_…", publishableKey = "pk_live_…",
siteId = "urc-android", siteId = "urc-android",
endpoint = "https://<middleware-host>", endpoint = "https://<middleware-host>",
sdk = SdkInfo("@3halves-labs/score-tracker-android", "0.1.0"), sdk = SdkInfo("@3halves-labs/score-tracker-android", "0.2.0"),
device = DeviceDescriptor(os = "Android ${Build.VERSION.RELEASE}", device = Build.MODEL, device = DeviceDescriptor(os = "Android ${Build.VERSION.RELEASE}", device = Build.MODEL,
appVersion = BuildConfig.VERSION_NAME, locale = Locale.getDefault().toLanguageTag()), appVersion = BuildConfig.VERSION_NAME, locale = Locale.getDefault().toLanguageTag()),
), ),
@@ -152,6 +140,53 @@ Events declaring a category (e.g. `trackAdvertising(...)` on iOS, or
`requires = listOf(ConsentCategory.ADVERTISING)` on Android) are dropped until `requires = listOf(ConsentCategory.ADVERTISING)` on Android) are dropped until
that category is granted. that category is granted.
## PII scrubbing
Same options as the web tracker's `pii` (#482). Scrubbing runs **when you call
`track()`, before the event is queued**, so a raw value never reaches the
on-device queue (NSUserDefaults / SharedPreferences) or the network.
- **Denylist:** remove matching keys.
- **Classify:** a per-key action. The first rule for a key wins.
- `DROP`: remove the key.
- `REDACT`: replace the value with `"<redacted>"`.
- `TOKENIZE`: replace it with `t:1`, `t:2`, …, stable for the tracker's lifetime.
- `HASH`: `h:` plus FNV-1a 32-bit hex. It's identical to the web tracker, so
the same value hashes the same on both platforms. **Not** cryptographic.
Keys match exactly (case-sensitive) at any depth, including inside lists.
```kotlin
// Android
val score = newScoreTracker(
context, config,
pii = PiiOptions.Classify(listOf(
PiiRule("email", PiiAction.HASH), // joinable across web + app
PiiRule("phone", PiiAction.REDACT),
PiiRule("memberNumber", PiiAction.TOKENIZE),
PiiRule("dateOfBirth", PiiAction.DROP),
)),
)
score.track("profile_updated", mapOf("email" to "fan@example.com", "favouriteTeam" to "home-club"))
// queued and sent as {"email":"h:141e28bb","favouriteTeam":"home-club"}
```
```swift
// iOS
let score = ScoreClient(
publishableKey: "pk_…", siteId: "app.example.com-1a2b3c",
endpoint: "https://api.example.com", sdkVersion: "0.2.0",
pii: PiiOptions.Classify(rules: [
PiiRule(field: "email", action: .hash),
PiiRule(field: "phone", action: .redact),
PiiRule(field: "memberNumber", action: .tokenize),
PiiRule(field: "dateOfBirth", action: .drop),
])
)
```
To remove keys without per-field actions, use
`PiiOptions.Denylist(listOf("email", "phone"))`.
## Auth0 (JWT) ## Auth0 (JWT)
Mirrors the web tracker (#109): supply a token provider — it's consulted before Mirrors the web tracker (#109): supply a token provider — it's consulted before

View File

@@ -1,10 +1,11 @@
// swift-tools-version:5.9 // swift-tools-version:5.9
import PackageDescription import PackageDescription
// Public SPM package for the Score iOS SDK. `ScoreTracker` (Swift facade, // SPM package for the iOS SDK (#178/#188). The `ScoreTracker` Swift facade
// ScoreClient) wraps the binary `ScoreTrackerKit` XCFramework, hosted as a // (ScoreClient) wraps the binary `ScoreTrackerKit` XCFramework, hosted as a
// public release asset on this repo. Consume via git URL: // **public** Gitea release asset on `3HL-SCORE/public-sdks-score` (#284).
// .package(url: "https://git.3halves-labs.com/3HL-SCORE/public-sdks-score.git", from: "0.1.0") // Build/refresh via `scripts/release.sh` (rebuild the XCFramework, re-zip,
// recompute the checksum, re-upload to the public release).
let package = Package( let package = Package(
name: "ScoreTracker", name: "ScoreTracker",
platforms: [.iOS(.v14)], platforms: [.iOS(.v14)],
@@ -14,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.1.0/ScoreTrackerKit.xcframework.zip", url: "https://git.3halves-labs.com/3HL-SCORE/public-sdks-score/releases/download/mobile-v0.2.0/ScoreTrackerKit.xcframework.zip",
checksum: "ae4b4c9b2a7e386d58c1c1144d15c3b4e5f457099ac2677e7d22894a7e4ab3e5" checksum: "328f8e36fc0c71d5b82bd4cc79d3d12b4520dea2db2358c73922348d83144b47"
), ),
.target( .target(
name: "ScoreTracker", name: "ScoreTracker",

View File

@@ -1,14 +1,24 @@
<!--
Seed README for the PUBLIC distribution repo `3HL-SCORE/public-sdks-score` (#284).
Copy this to that repo's root as README.md when publishing. Source of truth for
the SDK lives in the private score-anything-loyalty repo under `mobile/`.
-->
# Score mobile SDKs (public distribution) # Score mobile SDKs (public distribution)
Public, auth-free distribution of the **Score mobile tracker** SDKs for iOS and Android — the mobile counterpart of the web `@3halves-labs/score-tracker`. Public, auth-free distribution of the **Score mobile tracker** SDKs for iOS and Android — the mobile counterpart of the web `@3halves-labs/score-tracker`. Lightweight app analytics / fan-engagement tracking with a durable offline queue, consent gating, and Auth0 JWT support.
## Install — 0.1.0 (no token / login required) > This repo hosts the **consumable artifacts** (release assets + package manifests). The SDK source lives in 3 Halves Labs' private platform repo.
### iOS — Swift Package Manager (git URL) ## Install — 0.2.0 (no token / login required)
### iOS — Swift Package Manager
```bash
swift package-registry set https://git.3halves-labs.com/api/packages/3HL-SCORE/swift
```
```swift ```swift
// Package.swift // Package.swift
dependencies: [ .package(url: "https://git.3halves-labs.com/3HL-SCORE/public-sdks-score.git", from: "0.1.0") ] dependencies: [ .package(id: "threehalveslabs.ScoreTracker", from: "0.2.0") ]
// target: .product(name: "ScoreTracker", package: "public-sdks-score") // target: .product(name: "ScoreTracker", package: "ScoreTracker")
``` ```
### iOS — CocoaPods ### iOS — CocoaPods
@@ -18,16 +28,22 @@ pod 'ScoreTracker',
``` ```
### iOS — direct XCFramework ### iOS — direct XCFramework
Download `ScoreTrackerKit.xcframework.zip` from the [`mobile-v0.1.0` release](https://git.3halves-labs.com/3HL-SCORE/public-sdks-score/releases) and drag it into Xcode. Download `ScoreTrackerKit.xcframework.zip` from the [`mobile-v0.2.0` release](https://git.3halves-labs.com/3HL-SCORE/public-sdks-score/releases) and drag it into Xcode.
### Android — Gradle ### Android — Gradle
```kotlin ```kotlin
repositories { maven { url = uri("https://git.3halves-labs.com/api/packages/3HL-SCORE/maven") } } // public repositories {
dependencies { implementation("com.threehalveslabs.scoretracker:shared-android:0.1.0") } maven { url = uri("https://git.3halves-labs.com/api/packages/3HL-SCORE/maven") } // public — no credentials
}
dependencies {
implementation("com.threehalveslabs.scoretracker:shared-android:0.2.0")
}
``` ```
## Docs ## Quickstart & API
Full integration guide (init, consent, Auth0, offline/flush, event catalogue): **[`INTEGRATION.md`](INTEGRATION.md)**. See the full integration guide (init, consent, Auth0, offline/flush, event catalogue): **`INTEGRATION.md`** in this repo (published alongside each release).
SDK source lives in 3 Halves Labs' platform repo; this repo hosts the consumable artifacts. Versions tagged `0.1.0` (SPM) / `mobile-v0.1.0` (release asset). ## Support
Issues / integration questions: contact 3 Halves Labs. Versions are tagged `mobile-v<version>`.

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.1.0' s.version = '0.2.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
@@ -13,8 +13,8 @@ Pod::Spec.new do |s|
s.platform = :ios, '14.0' s.platform = :ios, '14.0'
s.swift_version = '5.9' s.swift_version = '5.9'
# Release attaches the zipped XCFramework; point :http at that asset. # Release attaches the zipped XCFramework as a PUBLIC asset on public-sdks-score (#284).
s.source = { :http => 'https://git.3halves-labs.com/3HL-SCORE/public-sdks-score/releases/download/mobile-v0.1.0/ScoreTrackerKit.xcframework.zip' } s.source = { :http => 'https://git.3halves-labs.com/3HL-SCORE/public-sdks-score/releases/download/mobile-v0.2.0/ScoreTrackerKit.xcframework.zip' }
s.vendored_frameworks = 'ScoreTrackerKit.xcframework' s.vendored_frameworks = 'ScoreTrackerKit.xcframework'
s.source_files = 'Sources/ScoreTracker/**/*.swift' s.source_files = 'Sources/ScoreTracker/**/*.swift'

View File

@@ -13,6 +13,7 @@ public final class ScoreClient {
/// - Parameters: /// - Parameters:
/// - endpoint: middleware base URL; events POST to `<endpoint>/api/tracking/website`. /// - endpoint: middleware base URL; events POST to `<endpoint>/api/tracking/website`.
/// - initialConsent: defaults to "none" — nothing fires until `setConsent(...)`. /// - initialConsent: defaults to "none" — nothing fires until `setConsent(...)`.
/// - pii: field-level PII handling (#482), scrubbed before events are queued on the device.
public init( public init(
publishableKey: String, publishableKey: String,
siteId: String, siteId: String,
@@ -22,7 +23,8 @@ public final class ScoreClient {
os: String? = nil, os: String? = nil,
device: String? = nil, device: String? = nil,
locale: String? = nil, locale: String? = nil,
initialConsent: ConsentState = ConsentState.companion.NONE initialConsent: ConsentState = ConsentState.companion.NONE,
pii: PiiOptions? = nil
) { ) {
let config = TrackerConfig( let config = TrackerConfig(
publishableKey: publishableKey, publishableKey: publishableKey,
@@ -31,7 +33,7 @@ public final class ScoreClient {
sdk: SdkInfo(name: "@3halves-labs/score-tracker-ios", version: sdkVersion), sdk: SdkInfo(name: "@3halves-labs/score-tracker-ios", version: sdkVersion),
device: DeviceDescriptor(os: os, device: device, appVersion: appVersion, locale: locale) device: DeviceDescriptor(os: os, device: device, appVersion: appVersion, locale: locale)
) )
tracker = IosPlatformKt.doNewScoreTracker(config: config, initialConsent: initialConsent) tracker = IosPlatformKt.doNewScoreTracker(config: config, initialConsent: initialConsent, pii: pii)
} }
// MARK: - Consent (wire to your CMP / OneTrust) // MARK: - Consent (wire to your CMP / OneTrust)