Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 17 additions & 0 deletions Common/FeatureFlags.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ struct FeatureFlagConfiguration: Decodable {
let allowAlgorithmExperiments: Bool
let isInvestigationalDevice: Bool
let devBranchWarningEnabled: Bool
let sportModeEnabled: Bool
let sportModePushEnabled: Bool

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

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

// Needs Sport Mode, and a push key the user sets up on the phone.
#if SPORT_MODE_ENABLED && SPORT_MODE_PUSH_ENABLED
self.sportModePushEnabled = true
#else
self.sportModePushEnabled = false
#endif
}
}

Expand All @@ -260,6 +275,8 @@ extension FeatureFlagConfiguration : CustomDebugStringConvertible {
"* observeHealthKitGlucoseSamplesFromOtherApps: \(observeHealthKitGlucoseSamplesFromOtherApps)",
"* predictedGlucoseChartClampEnabled: \(predictedGlucoseChartClampEnabled)",
"* remoteCommandsEnabled: \(remoteCommandsEnabled)",
"* sportModeEnabled: \(sportModeEnabled)",
"* sportModePushEnabled: \(sportModePushEnabled)",
"* 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
782 changes: 719 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
2 changes: 2 additions & 0 deletions Loop/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>DevelopmentTeam</key>
<string>$(DEVELOPMENT_TEAM)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
Expand Down
Loading