Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Common/FeatureFlags.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ struct FeatureFlagConfiguration: Decodable {
let allowAlgorithmExperiments: Bool
let isInvestigationalDevice: Bool
let devBranchWarningEnabled: Bool
let sportModeEnabled: Bool

fileprivate init() {
// Swift compiler config is inverse, since the default state is enabled.
Expand Down Expand Up @@ -239,6 +240,12 @@ struct FeatureFlagConfiguration: Decodable {
#else
self.devBranchWarningEnabled = true
#endif

#if SPORT_MODE_ENABLED
self.sportModeEnabled = true
#else
self.sportModeEnabled = false
#endif
}
}

Expand All @@ -260,6 +267,7 @@ extension FeatureFlagConfiguration : CustomDebugStringConvertible {
"* observeHealthKitGlucoseSamplesFromOtherApps: \(observeHealthKitGlucoseSamplesFromOtherApps)",
"* predictedGlucoseChartClampEnabled: \(predictedGlucoseChartClampEnabled)",
"* remoteCommandsEnabled: \(remoteCommandsEnabled)",
"* sportModeEnabled: \(sportModeEnabled)",
"* scenariosEnabled: \(scenariosEnabled)",
"* showEventualBloodGlucoseOnWatchEnabled: \(showEventualBloodGlucoseOnWatchEnabled)",
"* simulatedCoreDataEnabled: \(simulatedCoreDataEnabled)",
Expand Down
26 changes: 26 additions & 0 deletions Common/Models/BuildDetails+SportMode.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// BuildDetails+SportMode.swift
// Loop
//
// Copyright © 2026 LoopKit Authors. All rights reserved.
//
// Sport Mode's build identity, shown on both devices and sent with a loan request.
//

import Foundation

extension BuildDetails {
/// The workspace commit plus build date and time: the same on phone and watch when built
/// together. CFBundleVersion is pinned, so it cannot tell builds apart.
var codeIdentity: String {
let sha = (dict["com-loopkit-Loop-commit-sha"] as? String)
?? (dict["com-loopkit-LoopWorkspace-git-revision"] as? String)
?? gitRevision
guard let sha else { return "sha?" }
// "Wed Aug 19 15:59:38 EDT 2026" -> "Aug19 15:59".
let parts = (dict["com-loopkit-Loop-build-date"] as? String)?
.split(separator: " ").map(String.init) ?? []
let stamp = parts.count >= 4 ? " \(parts[1])\(parts[2]) \(parts[3].prefix(5))" : ""
return sha + stamp
}
}
615 changes: 615 additions & 0 deletions Documentation/SportMode/Control.md

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Loop.xcconfig
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ LOOP_LOCAL_CACHE_DURATION_DAYS = 90

// Entitlements [DEFAULT]
LOOP_ENTITLEMENTS = Loop/Loop.entitlements
// The watch signs with none, as stock. Sport Mode builds set it to
// "WatchApp Extension/SportMode.entitlements" (HealthKit for the workout session,
// time-sensitive notifications for wrist alarms), next to SPORT_MODE_ENABLED.
LOOP_WATCH_ENTITLEMENTS =

// Code signing and provisioning [DEFAULT]
LOOP_DEVELOPMENT_TEAM =
Expand Down
817 changes: 754 additions & 63 deletions Loop.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

64 changes: 64 additions & 0 deletions Loop.xcodeproj/xcshareddata/xcschemes/WatchAppTests.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "43A943711B926B7B0051FA24"
BuildableName = "WatchApp.app"
BlueprintName = "WatchApp"
ReferencedContainer = "container:Loop.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "NO"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9A7CE001302D82A800A03F53"
BuildableName = "WatchAppTests.xctest"
BlueprintName = "WatchAppTests"
ReferencedContainer = "container:Loop.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9A7CE001302D82A800A03F53"
BuildableName = "WatchAppTests.xctest"
BlueprintName = "WatchAppTests"
ReferencedContainer = "container:Loop.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
3 changes: 3 additions & 0 deletions Loop/Extensions/DeviceDataManager+DeviceStatus.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ extension DeviceDataManager {
return DeviceDataManager.resumeOnboardingStatusHighlight
} else if pumpManager == nil {
return DeviceDataManager.addPumpStatusHighlight
} else if let podLoanStatusHighlight {
return podLoanStatusHighlight
} else {
return (pumpManager as? PumpManagerUI)?.pumpStatusHighlight
}
Expand All @@ -54,6 +56,7 @@ extension DeviceDataManager {
}

var pumpLifecycleProgress: DeviceLifecycleProgress? {
if let podLoanProgress = podLoanLifecycleProgress { return podLoanProgress }
return (pumpManager as? PumpManagerUI)?.pumpLifecycleProgress
}

Expand Down
Loading