iOS SDK
Add subscriptions to an iOS app in ~5 minutes. Metera verifies every App Store purchase server-side and tracks who has access to what.
Requirements: iOS 15+ / macOS 12+, Swift 5.9+, StoreKit 2.
Base URL
Point the SDK at https://api.meterahq.com. That is the API; meterahq.com is the dashboard and does not serve these endpoints.
| API (SDK, REST) | https://api.meterahq.com |
| Dashboard (keys, plans, offerings) | https://meterahq.com |
Do not guess this host, and do not copy it from an older integration. Your app key is sent as Authorization: Bearer app_… on every request, so a wrong serverURL does not merely fail — it hands a live credential to whoever owns that domain. Metera is not metera.cn, metera.xyz, metera.io or metera.com; all four are third parties. If a purchase-verification call ever returns 401, check the host before you check the key.
1. Add the package
Swift Package Manager — in your Package.swift:
.package(url: "https://github.com/pytronhq/metera-ios-sdk.git", from: "0.1.0")…and add MeteraSDK to your target. In Xcode: File ▸ Add Package Dependencies.
2. Configure at launch
import MeteraSDK
MeteraClient.shared.configure(
apiKey: "app_xxx", // the app's API key, from the dashboard
appUserId: nil, // nil → anonymous user
serverURL: "https://api.meterahq.com" // Metera's API host — see below
)Omit appUserId to start anonymous — the SDK generates and persists an ID, and you can promote it later with logIn. serverURL must be https://api.meterahq.com; the apiKey is the app key from the dashboard's API-keys screen and is scoped to one app.
3. Show a paywall
let offerings = try await MeteraClient.shared.getOfferings()
guard let offering = offerings.current else { return }
for package in offering.packages {
// Render package.displayName, look up price via StoreKit if you want it.
}Offerings are configured in the dashboard — you change the paywall without shipping an app update.
4. Purchase
do {
let info = try await MeteraClient.shared.purchase(package)
if info.isActive("pro") { /* unlock */ }
} catch MeteraError.purchaseCancelled {
// user backed out — no-op
}purchase runs the StoreKit 2 flow, sends the transaction to Metera for authoritative server-side verification, and returns fresh CustomerInfo.
5. Gate features
let info = try await MeteraClient.shared.getCustomerInfo()
if info.isActive("pro") {
// pro features
} else {
// show upgrade prompt
}getCustomerInfo() fetches from the backend; cachedCustomerInfo() returns the last value with no network call — good for a launch-time gate.
isActive("pro") wraps GET /v1/users/{appUserId}/entitlements, whose entitlements map contains only currently-active entitlements — an expired or never-granted one is an absent key, not isActive: false. If you read the REST payload directly, gate on presence (see the REST API). The string "pro" is the entitlement identifier you created in the dashboard — it must match exactly.
6. Restore purchases
Wire a "Restore Purchases" button to try await MeteraClient.shared.restorePurchases(). It re-syncs every transaction Apple still considers valid.
7. Identity
// On login — merges the anonymous user into the real account:
try await MeteraClient.shared.logIn("user@example.com")
// On logout — starts a fresh anonymous user:
try await MeteraClient.shared.logOut()8. React to renewals & refunds
The SDK observes StoreKit Transaction.updates, so renewals and refunds that happen while the app runs sync automatically:
Task {
for await info in await MeteraClient.shared.customerInfoUpdates() {
updateUI(proActive: info.isActive("pro"))
}
}Notes
- The Metera app must have App Store Server API credentials configured in the dashboard, and its numeric Apple App ID set, for verification to work.
appAccountTokenis attached to every purchase automatically, so Apple server notifications can be attributed to the user.- Prefer the raw HTTP contract? See the REST API.