iOS
Native iOS SDK that integrates the Zing Fitness experience into your app. The SDK ships as an SPM package via GitHub.
Requirements
| Requirement | Value / Notes |
|---|---|
| Minimum iOS version | 16 |
| Xcode iOS SDK | 16+ |
| Swift Package Manager | 5.10 |
Setup
Add the dependency to your Package.swift:
dependencies: [
.package(url: "https://github.com/Muze-Fitness/zing-coach-sdk-ios.git", from: "1.4.2")
]Then add ZingCoachSDK to your target’s dependencies:
.target(
name: "YourTarget",
dependencies: ["ZingCoachSDK"]
)Initialization
The SDK instance owns internal state and resources. Retain it at app-level scope for the application’s lifetime; releasing it deallocates internal state and interrupts background sync.
InitializationParameters
| Parameter | Type | Default | Notes |
|---|---|---|---|
authentication | AuthenticationType | required | |
theme | Theme? | nil | SDK visual configuration |
configuration | Configuration | Configuration() | Coach availability, gender availability, HealthKit background delivery |
Theming
Pass a Theme value to InitializationParameters to override the SDK’s built-in visual defaults.
let theme = Theme(
colors: TokenProvider { token in
switch token {
case .brand(.primary):
UIColor(red: 0x00/255, green: 0x40/255, blue: 0x70/255, alpha: 1)
case .brand(.secondary):
UIColor(red: 0x00/255, green: 0x4F/255, blue: 0x91/255, alpha: 1)
default:
nil // keep SDK default for all other color tokens
}
},
assets: TokenProvider { token in
switch token {
case .planBackground:
UIImage(named: "MyPlanBackground")
default:
nil
}
}
)Theme accepts five optional providers:
| Property | Token type | Platform type |
|---|---|---|
colors | ColorToken | UIColor |
spacings | SizeToken | CGFloat |
cornersRounding | RadiusToken | RadiusAttribute |
typography | TypographyToken | TypographyAttributes |
assets | AssetToken | UIImage |
Returning nil from a resolution closure keeps the SDK’s built-in default for that token. You do not need to handle every case — only override the tokens your brand requires.
Configuration
Coach availability
| Case | Behaviour |
|---|---|
.allCoaches | All coaches are available to every user (default) |
.userGenderBased | Coaches are filtered based on the user’s gender |
Gender availability
| Case | Behaviour |
|---|---|
.all | All gender options are available (default) |
.binary | Only male and female options are presented |
HealthKit background delivery
Set ahBackgroundDeliveryEnabled to true to enable HealthKit background delivery so the SDK can sync Apple Health data while your app is suspended. Your app must include the HealthKit entitlement and request the appropriate read authorizations.
Handling InitializationResult
let result = await ZingSDK.initialize(with: ZingSDK.InitializationParameters(
authentication: .externalToken(provider: self, errorHandler: self),
theme: theme,
configuration: ZingSDK.Configuration(
coachesAvailability: .allCoaches,
genderAvailability: .all,
ahBackgroundDeliveryEnabled: false
)
))
switch result {
case .success(let sdk):
self.sdk = sdk
case .failure(let error):
print("Initialization failed: \(error)")
}Authentication
External token authentication
Use external token authentication when your backend manages user accounts and issues JWT tokens.
Token requirements:
- Valid JWT format
- Must contain
subclaim with the partner user ID - Recommended token lifetime: 15 minutes
Conform to ZingSDK.AuthProvider to supply tokens:
extension YourAuthProvider: ZingSDK.AuthProvider {
func didRequestAuthToken() async -> Result<String, Error> {
let token = try await yourAuthService.getToken()
return .success(token)
}
}Conform to ZingSDK.ErrorHandler to receive authentication errors:
extension YourErrorHandler: ZingSDK.ErrorHandler {
func didReceiveError(_ error: AuthError) {
switch error {
case .partnerIDMismatch:
break
case .badToken:
break
case .externalError(let underlyingError):
break
}
}
}API Key Authentication
Use this mode for testing or when backend integration is not available:
let result = await ZingSDK.initialize(
with: ZingSDK.InitializationParameters(
authentication: .apiKey(key: "your-api-key")
)
)Login
Call login() after initialization to authenticate the user and start a session:
let result = await sdk.login()
switch result {
case .success:
// User logged in successfully
case .failure(let error):
// Handle LoginError
}Call login() only when loginState is .loggedOut — typically only on the first session. On subsequent launches, ZingSDK.initialize(with:) automatically restores the previous session and loginState will already be .loggedIn. Calling login() while already logged in returns .failure(.alreadyLoggedIn).
Logout
Call logout() when your user signs out of your app:
let result = await sdk.logout()
switch result {
case .success:
// User logged out successfully
case .failure(let error):
// Handle LogoutError (e.g. .notLoggedIn)
}Logout clears the user’s token and removes all locally cached data. You can call login() again to start a new session.
Login state
// Current state
let state: ZingSDK.LoginState = sdk.loginState // .loggedOut | .inProgress | .loggedIn(userID: String)
// Reactive updates
sdk.loginStatePublisher
.sink { state in /* handle state changes */ }
.store(in: &cancellables)
// Quick check
let isLoggedIn: Bool = sdk.isLoggedInErrors
enum LoginError: Error {
/// Login was attempted while a user is already logged in.
case alreadyLoggedIn
/// Login was attempted while another login operation is in progress.
case loginAlreadyInProgress
/// Token retrieval failed during the login process.
case failedToGetToken
/// Profile synchronisation failed after successful token retrieval.
case failedToGetProfile
}
enum LogoutError: Error {
/// Logout was attempted when no user is currently logged in.
case notLoggedIn
}
enum AuthError: Error {
/// The partner user ID in the new token does not match the previously authenticated user.
case partnerIDMismatch
/// The token is invalid: malformed JWT or missing required sub claim.
case badToken
/// The external authentication provider returned an error.
case externalError(Error)
}User profile
You can supply user profile data to personalise the SDK experience. Pass profile parameters before or after login.
ProfileParameters
All fields are optional. Omit any field to leave the existing value unchanged.
| Field | Type | Notes |
|---|---|---|
name | String? | Display name |
gender | UserGender? | .male, .female, .other, .preferNotToSay |
height | Double? | In centimeters (cm). Values outside the valid range are ignored. |
weight | Double? | In kilograms (kg). Values outside the valid range are ignored. |
age | Int? | Age in full years |
measurementSystem | Unit? | .metric or .imperial |
Setting the profile
let params = ProfileParameters(
name: "Username",
gender: .female,
height: 178,
weight: 75,
age: 30,
measurementSystem: .metric
)
sdk.setProfileParams(params)SDK modules
Every SDK module returns a UIViewController.
If the user has not completed onboarding, the SDK automatically shows the onboarding flow before the requested module.
Workout program program
Present the user’s personalised workout program:
let programVC = sdk.makeProgramModule()
present(programVC, animated: true)AI assistant chat
Open the AI fitness assistant:
let assistantVC = sdk.makeAssistantChat()
present(assistantVC, animated: true)Profile settings
Allow users to edit their profile and settings:
let settingsVC = sdk.makeProfileSettings()
present(settingsVC, animated: true)Full schedule
Display the user’s full workout schedule:
let scheduleVC = sdk.makeFullSchedule()
present(scheduleVC, animated: true)Workout preview
Display a preview of a specific workout. Obtain a Workout value from CustomWorkoutDelegate.didCreateWorkout(_:):
let previewVC = sdk.makeWorkoutPreview(for: workout)
present(previewVC, animated: true)Custom workout builder
Let users build a custom workout. Provide an optional delegate to receive the created workout:
class WorkoutDelegate: CustomWorkoutDelegate {
func didCreateWorkout(_ workout: Workout) {
// workout.id is a stable string identifier for the created workout
}
}
let delegate = WorkoutDelegate()
let customWorkoutVC = sdk.makeCustomWorkoutModule(delegate)
present(customWorkoutVC, animated: true)