iOS Integration
Quick Start
Advanced Features
iOS Integration
Complete guide to integrating Grantiva's iOS SDK into your application for secure device attestation.
Installation
Swift Package Manager (Recommended)
Add Grantiva to your project using Swift Package Manager:
- In Xcode, select File → Add Packages
- Enter the repository URL:
https://github.com/grantiva/ios-sdk
- Select version 1.0.0 or later
- Click Add Package
Setup and Configuration
1. Enable App Attest Capability
In your Xcode project:
- Select your project in the navigator
- Select your app target
- Go to Signing & Capabilities
- Click + Capability
- Add "App Attest"
2. Import and Initialize
import Grantiva
import DeviceCheck
class SecurityManager {
private let grantiva = Grantiva()
init() {
// No configuration needed - bundle ID is inferred automatically
}
}
Basic Implementation
Simple Attestation
func attestDevice() async throws -> AttestationResult {
do {
let result = try await grantiva.validateAttestation()
if result.isValid {
// Store the JWT token for API requests
UserDefaults.standard.set(result.token, forKey: "attestation_token")
print("Device attested successfully")
print("Risk Score: \(result.deviceIntelligence.riskScore)")
print("Jailbreak Detected: \(result.deviceIntelligence.jailbreakDetected)")
return result
} else {
throw AttestationError.validationFailed
}
} catch {
print("Attestation failed: \(error)")
throw error
}
}
Advanced Implementation with Retry
class AttestationManager {
private let grantiva = Grantiva()
private let maxRetries = 3
func attestDeviceWithRetry() async throws -> AttestationResult {
var lastError: Error?
for attempt in 1...maxRetries {
do {
let result = try await grantiva.validateAttestation()
// Cache successful attestation
await cacheAttestation(result)
return result
} catch {
lastError = error
if attempt < maxRetries {
// Exponential backoff
let delay = TimeInterval(attempt * 2)
try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
}
}
}
throw lastError ?? AttestationError.unknownError
}
private func cacheAttestation(_ result: AttestationResult) async {
// Store attestation data
let cache = AttestationCache(
token: result.token,
expiresAt: result.expiresAt,
deviceId: result.deviceIntelligence.deviceId,
riskScore: result.deviceIntelligence.riskScore
)
try? await cache.save()
}
}
Handling Attestation Results
Understanding the Response
struct AttestationResult {
let isValid: Bool
let token: String // JWT for backend authentication
let expiresAt: Date // Token expiration
let deviceIntelligence: DeviceIntelligence
let permissions: [String] // Granted permissions
}
struct DeviceIntelligence {
let deviceId: String
let riskScore: Int // 0-100 (lower is better)
let deviceIntegrity: String // "high", "medium", "low"
let jailbreakDetected: Bool
let attestationCount: Int
let lastAttestationDate: Date?
}
Using the JWT Token
// Add token to API requests
func makeAuthenticatedRequest() async throws {
guard let token = UserDefaults.standard.string(forKey: "attestation_token") else {
throw APIError.notAuthenticated
}
var request = URLRequest(url: apiURL)
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
let (data, response) = try await URLSession.shared.data(for: request)
// Process response...
}
Error Handling
enum AttestationError: LocalizedError {
case deviceNotSupported
case attestationNotAvailable
case networkError
case validationFailed
case tokenExpired
var errorDescription: String? {
switch self {
case .deviceNotSupported:
return "This device doesn't support App Attest"
case .attestationNotAvailable:
return "App Attest is not available in this region"
case .networkError:
return "Network connection error"
case .validationFailed:
return "Device attestation validation failed"
case .tokenExpired:
return "Attestation token has expired"
}
}
}
// Check device support
if !DCAppAttestService.shared.isSupported {
throw AttestationError.deviceNotSupported
}
Best Practices
- Cache attestations: Don't re-attest on every app launch
- Handle failures gracefully: Provide fallback behavior for unsupported devices
- Refresh tokens before expiry: Check expiration and re-attest proactively
- Monitor risk scores: Adjust app behavior based on device risk
- Test on real devices: App Attest doesn't work in simulators
Testing
Important Testing Notes
- App Attest only works on real devices (iOS 14.0+)
- Not available in all regions (check Apple's documentation)
- Requires active internet connection
- TestFlight builds use Apple's test environment