Skip to content
Open
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
14 changes: 12 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,23 @@

Critical Maps iOS

## [4.10.0] - 2026-06-08

### Added

- Highlight groups feature - Participants that are close to one another are highlighted as groups, while riders outside of a group remain visible but less prominent.

### Updated

- Updated dependencies

## [4.9.0] - 2026-01-03

### Updated

- Migrated to `swift-tools-version:6.2`
- Refactored SwiftUI views following best practices for improved performance:
- Performance optimization for RequestTimer and InfoOverlayView
- Refactored SwiftUI views following best practices for improved performance
- Performance optimisation for RequestTimer and InfoOverlayView
- Updated dependencies

### Fixed
Expand Down
10 changes: 10 additions & 0 deletions Config/Debug-Local.xcconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Build configuration for running against the local mock server, which lives in
// its own repository (criticalmaps-mock-server).
//
// Used by the "Debug Local" build configuration / "Critical Maps (Local)" scheme.
// Sets the DEBUG_LOCAL compile flag so the app target injects a ServerConfiguration
// pointing at http://localhost:8080 (see iOS/AppConfiguration.swift).

#include "Shared.xcconfig"

SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) DEBUG_LOCAL
5 changes: 5 additions & 0 deletions Config/Shared.xcconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Shared base settings for app build configurations.
//
// Intentionally minimal today — a place to put settings common to all
// configurations (e.g. an externalized API host) without touching the project
// file. The "Debug Local" configuration includes this via Debug-Local.xcconfig.
117 changes: 117 additions & 0 deletions CriticalMaps.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
5ECACE0EB79FB71190DA0CCC /* AppConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D32ED4D6F091FA6990994EBE /* AppConfiguration.swift */; };
730741DD2F00737D0080985D /* AppIcon-Sun.icon in Resources */ = {isa = PBXBuildFile; fileRef = 730741DC2F00737D0080985D /* AppIcon-Sun.icon */; };
730741DE2F00737D0080985D /* AppIcon.icon in Resources */ = {isa = PBXBuildFile; fileRef = 730741D82F00737D0080985D /* AppIcon.icon */; };
730741DF2F00737D0080985D /* AppIcon-Dark.icon in Resources */ = {isa = PBXBuildFile; fileRef = 730741D92F00737D0080985D /* AppIcon-Dark.icon */; };
Expand All @@ -26,6 +27,7 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
20A6EB76B0234461A4A91977 /* Debug-Local.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Debug-Local.xcconfig"; sourceTree = "<group>"; };
730741D82F00737D0080985D /* AppIcon.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = AppIcon.icon; sourceTree = "<group>"; };
730741D92F00737D0080985D /* AppIcon-Dark.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = "AppIcon-Dark.icon"; sourceTree = "<group>"; };
730741DA2F00737D0080985D /* AppIcon-Neon.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = "AppIcon-Neon.icon"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -57,6 +59,8 @@
73EA92432C59585D008DEEFE /* SocialFeature.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = SocialFeature.xctestplan; sourceTree = "<group>"; };
73EB0310263F23A100941D57 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
73F010D82B94A19700C2B613 /* LisbonPortugal.gpx */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = LisbonPortugal.gpx; sourceTree = "<group>"; };
A2505A49BB8F102AF4496DBD /* Shared.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = "<group>"; };
D32ED4D6F091FA6990994EBE /* AppConfiguration.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AppConfiguration.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -169,6 +173,7 @@
739A58322D4156F100B80B41 /* SettingsFeaturePreview.app */,
739A58332D4156F100B80B41 /* GuideFeaturePreview.app */,
73CA22F82D4158650040C7CF /* Frameworks */,
895C7647794A2614C2AA6100 /* Config */,
);
sourceTree = "<group>";
};
Expand All @@ -186,10 +191,21 @@
73654C6B26C85FCF004BE38B /* Launch Screen.storyboard */,
739C4980276733E20001466A /* Berlin:Germany.gpx */,
73F010D82B94A19700C2B613 /* LisbonPortugal.gpx */,
D32ED4D6F091FA6990994EBE /* AppConfiguration.swift */,
);
path = iOS;
sourceTree = "<group>";
};
895C7647794A2614C2AA6100 /* Config */ = {
isa = PBXGroup;
children = (
20A6EB76B0234461A4A91977 /* Debug-Local.xcconfig */,
A2505A49BB8F102AF4496DBD /* Shared.xcconfig */,
);
name = Config;
path = Config;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -385,6 +401,7 @@
buildActionMask = 2147483647;
files = (
73EB0311263F23A100941D57 /* App.swift in Sources */,
5ECACE0EB79FB71190DA0CCC /* AppConfiguration.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -399,6 +416,71 @@
/* End PBXSourcesBuildPhase section */

/* Begin XCBuildConfiguration section */
1479D1E4E7FFDDA1E9B95334 /* Debug Local */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 6.0;
};
name = "Debug Local";
};
73238EC527728043003DE01F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
Expand Down Expand Up @@ -774,6 +856,39 @@
};
name = Release;
};
F588BCFCE13D3727E800503D /* Debug Local */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 20A6EB76B0234461A4A91977 /* Debug-Local.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "AppIcon-Sun AppIcon-Rainbow AppIcon-Neon AppIcon-Dark";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
CODE_SIGN_IDENTITY = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 250902;
DEVELOPMENT_TEAM = 5YLLXUZBZ2;
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 5YLLXUZBZ2;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = "$(SRCROOT)/iOS/Info.plist";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.navigation";
IPHONEOS_DEPLOYMENT_TARGET = 17;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 4.7.0;
PRODUCT_BUNDLE_IDENTIFIER = de.pokuslabs.criticalmassberlin;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "Critical Maps Prov Profile";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Criticalmaps dist";
RUN_DOCUMENTATION_COMPILER = NO;
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 6.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = "Debug Local";
};
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section */
Expand All @@ -800,6 +915,7 @@
buildConfigurations = (
73CF5FE5263EAF5C001925A3 /* Debug */,
73CF5FE6263EAF5C001925A3 /* Release */,
1479D1E4E7FFDDA1E9B95334 /* Debug Local */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
Expand All @@ -809,6 +925,7 @@
buildConfigurations = (
73CF5FE8263EAF5C001925A3 /* Debug */,
73CF5FE9263EAF5C001925A3 /* Release */,
F588BCFCE13D3727E800503D /* Debug Local */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1620"
version = "2.1">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "73CF5FC1263EAF5B001925A3"
BuildableName = "Critical Maps.app"
BlueprintName = "Critical Maps"
ReferencedContainer = "container:CriticalMaps.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "NO"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "NO">
<TestPlanReference
reference = "container:Testplans/UnitTests.xctestplan">
</TestPlanReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<TestPlans>
<TestPlanReference
reference = "container:Testplans/UnitTests.xctestplan"
default = "YES">
</TestPlanReference>
<TestPlanReference
reference = "container:Testplans/SnapshotTests.xctestplan">
</TestPlanReference>
</TestPlans>
</TestAction>
<LaunchAction
buildConfiguration = "Debug Local"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
enableThreadSanitizer = "YES"
language = "de"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "73CF5FC1263EAF5B001925A3"
BuildableName = "Critical Maps.app"
BlueprintName = "Critical Maps"
ReferencedContainer = "container:CriticalMaps.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Debug Local"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "73CF5FC1263EAF5B001925A3"
BuildableName = "Critical Maps.app"
BlueprintName = "Critical Maps"
ReferencedContainer = "container:CriticalMaps.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug Local">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
14 changes: 11 additions & 3 deletions CriticalMapsKit/Sources/ApiClient/APIService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,23 @@ public struct APIService: Sendable {
extension APIService: DependencyKey {
public static var liveValue: APIService {
@Dependency(\.apiClient) var apiClient
@Dependency(\.serverConfiguration) var serverConfiguration

let locationsEndpoint = Endpoint(
baseUrl: serverConfiguration.locationsHost,
pathComponents: ["locations"],
scheme: serverConfiguration.scheme,
port: serverConfiguration.locationsPort
)

return Self(
getRiders: {
let request: Request = .get(.locations)
let request: Request = .get(locationsEndpoint)
let (data, _) = try await apiClient.send(request)
return try data.decoded()
},
},
postRiderLocation: { body in
let request: Request = .put(.locations, body: try? body.encoded())
let request: Request = .put(locationsEndpoint, body: try? body.encoded())
let (data, _) = try await apiClient.send(request)
return try data.decoded()
},
Expand Down
11 changes: 10 additions & 1 deletion CriticalMapsKit/Sources/ApiClient/Endpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@ import Foundation

/// A structure to define an endpoint on the Critical Maps API
public struct Endpoint: Sendable {
public let scheme: String
public let baseUrl: String
public let port: Int?
public let pathComponents: [String]

public init(baseUrl: String, pathComponents: [String] = []) {
public init(
baseUrl: String,
pathComponents: [String] = [],
scheme: String = "https",
port: Int? = nil
) {
self.baseUrl = baseUrl
self.pathComponents = pathComponents
self.scheme = scheme
self.port = port
}

var url: String {
Expand Down
Loading