Swift package for reading data from the Glooko diabetes data platform. Retrieves insulin pump data (Omnipod 5), CGM readings, and statistics via Glooko's internal web API.
Important: Glooko data is not real-time. The Omnipod 5 app syncs data to Glooko periodically — typically once every few hours, and only when the phone has connectivity. This means bolus entries, IOB values, and pump mode data can lag behind actual pump activity by 1-4 hours. Do not rely on this library for time-critical insulin dosing decisions. It is best suited for retrospective analysis, trend monitoring, and enriching recommendation systems with historical pump context.
Note: Glooko does not provide a public API. This library authenticates by replicating the browser login flow (CSRF token extraction, cookie-based sessions). It may break if Glooko changes their web application.
See also: glooko-reader (Python version)
Add to your Package.swift:
dependencies: [
.package(url: "https://github.com/spamsch/glooko-reader-swift.git", from: "0.1.0"),
],
targets: [
.target(
name: "YourTarget",
dependencies: [
.product(name: "GlookoReader", package: "glooko-reader-swift"),
]
),
]import GlookoReader
let client = GlookoClient(email: "you@example.com", password: "your_password")
try await client.authenticate()
// Fetch insulin delivery data
let data = try await client.getGraphData(dateRange: .lastDays(1))
// Parse into structured objects
if let data {
let boluses = parseBolusEntries(from: data)
for b in boluses {
print("\(b.timestamp) — \(b.units)u (IOB: \(b.insulinOnBoard ?? 0), carbs: \(b.carbsInput ?? 0)g)")
}
}
// Pump mode and statistics
let stats = try await client.getStatistics(dateRange: .lastDays(1))
if let stats, let pump = parsePumpMode(from: stats) {
print("Pump: \(pump.mode ?? .auto) mode, \(pump.totalInsulinPerDay ?? 0) units/day")
}
// Connected devices
let deviceData = try await client.getDeviceSettings()
if let deviceData {
let devices = parseDevices(from: deviceData)
for d in devices {
print("\(d.name) — last sync: \(d.lastSync?.description ?? "unknown")")
}
}let client = GlookoClient(email: String, password: String, sessionTimeoutMinutes: Int = 55)All API methods are async throws.
| Method | Returns | Description |
|---|---|---|
authenticate() |
Void |
Perform browser-based login flow |
ensureAuthenticated() |
Void |
Re-authenticate if session expired |
getGraphData(dateRange:) |
[String: Any]? |
CGM readings + delivered bolus insulin |
getStatistics(dateRange:) |
[String: Any]? |
Overall stats, pump modes, insulin totals |
getDailyData(dateRange:) |
[String: Any]? |
Daily CGM averages and percentile bands |
getDeviceSettings() |
[String: Any]? |
Connected devices and configuration |
testConnection() |
Bool |
Verify authentication works |
DateRange.today // Today
DateRange.lastDays(7) // Last 7 days
DateRange(from: startDate, to: endDate) // Custom range| Function | Returns | Description |
|---|---|---|
parseBolusEntries(from:) |
[BolusEntry] |
Insulin bolus deliveries |
parseIOBFromBolus(from:maxAgeMinutes:) |
Double? |
IOB from most recent bolus |
parsePumpMode(from:) |
PumpStatistics? |
Pump mode and insulin stats |
parseDevices(from:) |
[DeviceInfo] |
Connected device info |
parseTimestamp(_:) |
Date? |
Parse Glooko timestamp formats |
BolusEntry — A single insulin bolus delivery
| Property | Type | Description |
|---|---|---|
timestamp |
Date |
When the bolus was delivered |
units |
Double |
Insulin units delivered |
carbsInput |
Double? |
Carbs entered at bolus time |
insulinOnBoard |
Double? |
IOB reported by pump |
bloodGlucoseInput |
Int? |
BG reading at bolus time |
bloodGlucoseSource |
String? |
Source ("CGM", etc.) |
correctionUnits |
Double? |
Insulin for correction |
carbUnits |
Double? |
Insulin for carbs |
isManual |
Bool |
Whether manually entered |
deviceName |
String? |
Delivering device |
PumpStatistics — Pump mode and insulin statistics
| Property | Type | Description |
|---|---|---|
mode |
PumpMode? |
.auto, .manual, or .limited |
autoPercentage |
Double |
Time in auto mode (%) |
manualPercentage |
Double |
Time in manual mode (%) |
totalInsulinPerDay |
Double? |
Average daily insulin |
inRangePercentage |
Double? |
Time in range (%) |
carbsPerDay |
Double? |
Average daily carbs (g) |
DeviceInfo — Connected device
| Property | Type | Description |
|---|---|---|
name |
String |
Display name |
brand |
String? |
Manufacturer |
model |
String? |
Model name |
deviceType |
String? |
"pump", "cgm", etc. |
lastSync |
Date? |
Last data sync |
Glooko uses a multi-step browser login flow:
GET https://my.glooko.com/users/sign_in— follows redirect to regional server- Extracts CSRF token from HTML
POSTlogin form with credentials and CSRF token- Extracts
patient_idandapiUrlfrom dashboard JavaScript - Subsequent API calls use cookie-based authentication with CORS headers
- Date format: Full ISO datetime required (
2026-03-29T00:00:00.000Z), notYYYY-MM-DD - CORS headers: Graph/statistics endpoints require
OriginandRefererheaders - URL encoding:
series[]must use literal brackets, not percent-encoded%5B%5D - Regional routing: Users are routed to regional servers (e.g.,
de-fr.api.glooko.com)
A mock client is included:
let mock = MockGlookoClient(scenario: .normal)
// Also: .highBasal, .manualMode, .podChange, .noData
let data = mock.getGraphData(dateRange: .today)
let boluses = parseBolusEntries(from: data!)- Insulet Omnipod 5 System
- Dexcom G7 (via Omnipod 5 integration)
- Glooko EU region (
de-fr.api.glooko.com)
- Swift 5.9+
- macOS 13+ / iOS 16+
- No external dependencies
MIT