Skip to Content

iOS

Native iOS SDK that integrates the Zing Fitness experience into your app. The SDK ships as an SPM package via GitHub.

Requirements

RequirementValue / Notes
Minimum iOS version16
Xcode iOS SDK16+
Swift Package Manager5.10

Setup

Add the dependency to your Package.swift:

dependencies: [ .package(url: "https://github.com/Muze-Fitness/zing-coach-sdk-ios.git", from: "2.1.0") ]

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.

Authentication is not part of initialization. Pass credentials later with login(with:) — see Authentication.

InitializationParameters

ParameterTypeDefaultNotes
themeTheme?nilSDK visual configuration
configurationConfigurationConfiguration()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:

PropertyToken typePlatform type
colorsColorTokenUIColor
spacingsSizeTokenCGFloat
cornersRoundingRadiusTokenRadiusAttribute
typographyTypographyTokenTypographyAttributes
assetsAssetTokenUIImage

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

CaseBehaviour
.allCoachesAll coaches are available to every user (default)
.userGenderBasedCoaches are filtered based on the user’s gender

Gender availability

CaseBehaviour
.allAll gender options are available (default)
.binaryOnly 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.

Initializing the SDK

do { let sdk = try await ZingSDK.initialize(with: ZingSDK.InitializationParameters( theme: theme, configuration: ZingSDK.Configuration( coachesAvailability: .allCoaches, genderAvailability: .all, ahBackgroundDeliveryEnabled: false ) )) sdk.criticalErrorHandler = self self.sdk = sdk } catch { print("Initialization failed: \(error)") }

Authentication

Supply credentials when you call login(with:), not during initialization.

For the backend requirements behind these two modes — the JWKS your server must publish for JWT auth, the required claims, and why the API key is the weaker option — see Authorization.

Call login(with:) 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(with:) while already logged in throws .alreadyLoggedIn.

JWT token authentication

Use JWT token authentication when your backend manages user accounts and issues JWT tokens.

Token requirements:

  • Valid JWT format
  • Must contain sub claim with the partner user ID
  • Recommended token lifetime: 15 minutes

Obtain a token from your backend, then pass it to login(with:):

do { let token = try await yourAuthService.getToken() try await sdk.login(with: .jwtToken(token: token)) } catch { // Handle LoginError }

API Key Authentication

Use this mode for testing or when backend integration is not available. Pass a stable partnerUserID so the same user is restored across sessions. If you omit it (nil), the SDK generates a unique identifier.

try await sdk.login(with: .apiKey(key: "your-api-key", partnerUserID: "your-user-id"))

Login

do { try await sdk.login(with: authentication) } catch { // Handle LoginError }

Logout

Call logout() when your user signs out of your app:

do { try await sdk.logout() } catch { // Handle LogoutError (e.g. .notLoggedIn) }

Logout clears the user’s token and removes all locally cached data. You can call login(with:) again to start a new session.

Login state

// Current state let state: ZingSDK.LoginState = sdk.loginState // .loggedOut | .inProgress | .loggedIn(partnerUserID: String) // Reactive updates sdk.loginStatePublisher .sink { state in /* handle state changes */ } .store(in: &cancellables) // Quick checks let isLoggedIn: Bool = sdk.isLoggedIn let isOnboardingCompleted: Bool = sdk.isOnboardingCompleted

Critical errors

Assign a CriticalErrorHandler after initialization to be notified when a restored session can no longer be refreshed — typically HTTP 400 or 401 from the token refresh endpoint. Log the user out of your app and call login(with:) again with fresh credentials.

extension YourErrorHandler: CriticalErrorHandler { func sdkDidFail(with error: Error) { // Session is no longer valid — logout and re-authenticate if let authError = error as? AuthError { switch authError { case .invalidRefreshCredentialsRequest, .invalidCredentialsForRefreshRequest, .refreshRequestedWithoutStoredCredentials, .badToken: break } } } }

Errors

enum LoginError: Error, LocalizedError { /// 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 /// Data synchronisation failed after successful login. case failedToSyncDataAfterLogin } enum LogoutError: Error { /// Logout was attempted when no user is currently logged in. case notLoggedIn } enum AuthError: Error { /// Token refresh was rejected with HTTP 400. case invalidRefreshCredentialsRequest /// Token refresh was rejected with HTTP 401. case invalidCredentialsForRefreshRequest /// The token is invalid: malformed JWT or missing required sub claim. case badToken /// A token refresh was requested but no stored credentials are available. case refreshRequestedWithoutStoredCredentials } protocol CriticalErrorHandler { func sdkDidFail(with error: Error) } extension ZingSDK { enum ProfileUpdateError: Error { /// Profile parameters were set while no user is logged in. case notLoggedIn } enum ScreenPresentationError: Error { /// A screen was requested while no user is logged in. case notLoggedIn } }

User profile

You can supply user profile data to personalise the SDK experience. Log in before setting profile parameters — setProfileParams(_:) throws ProfileUpdateError.notLoggedIn when no user is logged in.

If onboarding is not complete (isOnboardingCompleted == false), the values seed the onboarding flow. After onboarding is complete, they update the existing profile.

ProfileParameters

All fields are optional. Omit any field to leave the existing value unchanged.

FieldTypeNotes
nameString?Display name
genderUserGender?.male, .female, .other, .preferNotToSay
heightDouble?In centimeters (cm). Values outside the valid range are ignored.
weightDouble?In kilograms (kg). Values outside the valid range are ignored.
ageInt?Age in full years
measurementSystemUnit?.metric or .imperial

Setting the profile

let params = ProfileParameters( name: "Username", gender: .female, height: 178, weight: 75, age: 30, measurementSystem: .metric ) do { try sdk.setProfileParams(params) } catch { // Handle ProfileUpdateError (e.g. .notLoggedIn) }

SDK modules

Call sdk.makeScreen(_:) with a ZingSDK.Screen value to build a screen, then present the returned UIViewController. It throws ScreenPresentationError.notLoggedIn when no user is logged in — log the user in with sdk.login(with:) first (see Authentication), then try again.

If the user has not completed onboarding, the SDK shows the onboarding flow before the requested screen.

do { let viewController = try sdk.makeScreen(.program) present(viewController, animated: true) } catch { // error == .notLoggedIn — call sdk.login(with:) before opening a screen }

Available screens

Screen caseDescription
.programMain screen with the personalized workout experience
.assistantChatChat with the AI fitness coach
.profileSettingsProfile and plan settings
.fullScheduleFull workout schedule
.customWorkoutCustom workout builder
.bodyScan(useFrontCamera:)Body scan. Defaults to true (front camera). Pass false to use the rear camera.
.flexibilityTest(useFrontCamera:)Flexibility test. Defaults to true (front camera).
.fitnessTest(useFrontCamera:)Fitness test. Defaults to true (front camera).
Last updated on