Apple Fitness workout fixer + Strava uploader

Move Strava credentials to gitignored xcconfig file

Read client ID and secret from Info.plist at runtime, populated via
Secrets.xcconfig build settings. Template file included for reference.
Also adds .gitignore for secrets, xcuserdata, and .claude/.

+18 -2
+3
.gitignore
··· 1 + Secrets.xcconfig 2 + .claude/ 3 + xcuserdata/
+5
Secrets.xcconfig.template
··· 1 + // Copy this file to Secrets.xcconfig and fill in your Strava API credentials 2 + // Register at https://www.strava.com/settings/api 3 + // Set Authorization Callback Domain to: localhost 4 + STRAVA_CLIENT_ID = YOUR_CLIENT_ID 5 + STRAVA_CLIENT_SECRET = YOUR_CLIENT_SECRET
+4
WorkoutEditor.xcodeproj/project.pbxproj
··· 34 34 AA000001AAAA000100000002 /* ActivityGraphView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityGraphView.swift; sourceTree = "<group>"; }; 35 35 AA000002AAAA000200000002 /* RangeSliderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RangeSliderView.swift; sourceTree = "<group>"; }; 36 36 AA000003AAAA000300000002 /* StravaManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StravaManager.swift; sourceTree = "<group>"; }; 37 + AA000004AAAA000400000001 /* Secrets.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Secrets.xcconfig; sourceTree = "<group>"; }; 37 38 /* End PBXFileReference section */ 38 39 39 40 /* Begin PBXFrameworksBuildPhase section */ ··· 62 63 06E7DFA92B3608DD0025260F /* Products */, 63 64 063E686A2B45161A0048778C /* Frameworks */, 64 65 224660462F4F47C600A98FB5 /* Overrun.icon */, 66 + AA000004AAAA000400000001 /* Secrets.xcconfig */, 65 67 ); 66 68 sourceTree = "<group>"; 67 69 }; ··· 185 187 /* Begin XCBuildConfiguration section */ 186 188 06E7DFB42B3608E00025260F /* Debug */ = { 187 189 isa = XCBuildConfiguration; 190 + baseConfigurationReference = AA000004AAAA000400000001 /* Secrets.xcconfig */; 188 191 buildSettings = { 189 192 ALWAYS_SEARCH_USER_PATHS = NO; 190 193 CLANG_ANALYZER_NONNULL = YES; ··· 245 248 }; 246 249 06E7DFB52B3608E00025260F /* Release */ = { 247 250 isa = XCBuildConfiguration; 251 + baseConfigurationReference = AA000004AAAA000400000001 /* Secrets.xcconfig */; 248 252 buildSettings = { 249 253 ALWAYS_SEARCH_USER_PATHS = NO; 250 254 CLANG_ANALYZER_NONNULL = YES;
+4
WorkoutEditor/Info.plist
··· 17 17 </array> 18 18 </dict> 19 19 </array> 20 + <key>StravaClientID</key> 21 + <string>$(STRAVA_CLIENT_ID)</string> 22 + <key>StravaClientSecret</key> 23 + <string>$(STRAVA_CLIENT_SECRET)</string> 20 24 <key>NSHealthShareUsageDescription</key> 21 25 <string>Overrun needs to read your workouts so you can view and edit them.</string> 22 26 <key>NSHealthUpdateUsageDescription</key>
+2 -2
WorkoutEditor/StravaManager.swift
··· 12 12 // MARK: - Configuration 13 13 14 14 enum StravaConfig { 15 - static let clientID = "YOUR_CLIENT_ID" 16 - static let clientSecret = "YOUR_CLIENT_SECRET" 15 + static let clientID = Bundle.main.object(forInfoDictionaryKey: "StravaClientID") as? String ?? "" 16 + static let clientSecret = Bundle.main.object(forInfoDictionaryKey: "StravaClientSecret") as? String ?? "" 17 17 static let redirectURI = "overrun://strava/callback" 18 18 static let authorizeURL = "https://www.strava.com/oauth/mobile/authorize" 19 19 static let tokenURL = "https://www.strava.com/oauth/token"