This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Use the xcodebuildmcp CLI (skill: xcodebuildmcp-cli) instead of raw xcodebuild. This is an Xcode project (.xcodeproj), not a workspace.
# Build iOS SDK
xcodebuildmcp simulator build \
--project-path PACECloudSDK.xcodeproj \
--scheme PACECloudSDK \
--simulator-name "iPhone 16" \
--configuration Development
# Build watchOS SDK
xcodebuildmcp simulator build \
--project-path PACECloudSDK.xcodeproj \
--scheme PACECloudWatchSDK \
--simulator-name "Apple Watch Series 10 (46mm)" \
--configuration Development
# Run all tests
xcodebuildmcp simulator test \
--project-path PACECloudSDK.xcodeproj \
--scheme PACECloudSDKTests \
--simulator-name "iPhone 16" \
--configuration Development
# Run a single test class
xcodebuildmcp simulator test \
--project-path PACECloudSDK.xcodeproj \
--scheme PACECloudSDKTests \
--simulator-name "iPhone 16" \
--configuration Development \
--json '{"extraArgs": ["-only-testing", "PACECloudSDKTests/GeoServiceTests"]}'
# Run a single test method
xcodebuildmcp simulator test \
--project-path PACECloudSDK.xcodeproj \
--scheme PACECloudSDKTests \
--simulator-name "iPhone 16" \
--configuration Development \
--json '{"extraArgs": ["-only-testing", "PACECloudSDKTests/GeoServiceTests/testLocationBasedStations"]}'
# Lint
swiftlint --config .swiftlint.yml
# Run tests via fastlane (CI only)
bundle exec fastlane testPlatform: iOS 15+, watchOS 7+, Swift 5.5+. Build configurations: Development, Sandbox, Production. Tests use Development.
PACECloudSDK is a Connected Fueling SDK organized into three Kits, each a singleton:
- IDKit (
PACECloudSDK/IDKit/) — OpenID Connect authentication via AppAuth. Manages OAuth2 flows, token lifecycle (auto-refresh on 401), session caching, biometry/PIN, and token exchange for service delegation. - AppKit (
PACECloudSDK/AppKit/) — Hosts web apps in WKWebView with a JavaScript-to-native bridge (WKScriptMessageHandler). Handles app discovery, lifecycle, cookie management, and a drawer UI for location-based apps. - POIKit (
PACECloudSDK/POIKit/) — Gas station discovery and GeoJSON data.GeoAPIManagerfetches/caches GeoJSON from CDN (1-hour TTL).GeoJSONStreamParserdoes memory-efficient streaming parse with optional spatial filtering and per-feature autoreleasepool.
API layer (PACECloudSDK/API/) — Code-generated clients (SwagGen from OpenAPI specs) under Generated/ subdirectories. CustomAPIClient adds exponential backoff (max 8 retries), automatic 401 token refresh, and concurrent JSON decoding. Sub-APIs: POI, Pay, Fueling, User, CDN, Communication.
Utils (PACECloudSDK/Utils/) — SDKUserDefaults and SDKKeychain wrappers (direct UserDefaults/Keychain access is forbidden by SwiftLint), Logger with file rotation, environment config from Plist files, and shared extensions.
Entry point: PACECloudSDK.shared.setup(with: Configuration(...)) initializes all kits.
Environment-specific URLs (API gateway, OID endpoints, CDN) are loaded from Utils/Plists/Environment-{development,sandbox,production}.plist.
| Target | Platform | Description |
|---|---|---|
PACECloudSDK |
iOS | Full SDK (source) |
PACECloudSlimSDK |
iOS | Slim variant for App Clips (binary) |
PACECloudWatchSDK |
watchOS | Watch companion apps (binary) |
PACECloudSDKTests |
iOS | Unit tests |
UnitTestDummy |
iOS | Host app for tests |
When adding new source files, add them to both PACECloudSDK and PACECloudWatchSDK targets in project.pbxproj.
Files under API/*/Generated/ directories are auto-generated by SwagGen from OpenAPI specs — never edit them by hand. Directories excluded from linting: API/Generated, API/Pay/Generated, API/POI/Generated, API/Fueling/Generated, API/User/Generated, API/GeoJSON/Generated, API/Communication/Generated. Also excluded: POIKit/Vendor, POIKit/POIKitApi/Model, POIKit/POISearch/Model.
SwiftLint runs as a build tool plugin. Key rules beyond defaults:
force_unwrapping— opt-in, warns on!sorted_imports— imports must be alphabetically sortedswitch_case_on_newline— each case on its own line- Custom:
vertical_whitespace_between_cases— blank line required between switch cases - Custom:
user_defaults_wrapper(error) — must useSDKUserDefaults, notUserDefaults - Custom:
keychain_wrapper(error) — must useSDKKeychain, not direct Keychain APIs - Line length limit: 180 characters
- Generated code directories are excluded from linting
Tests use MockURLProtocol registered via PACECloudSDK.shared.customURLProtocol to intercept network requests. Standard test setUp:
PACECloudSDK.shared.customURLProtocol = MockURLProtocol()
PACECloudSDK.shared.setup(with: .init(
apiKey: "apiKey",
clientId: "unit-test-dummy",
environment: .development,
isRedirectSchemeCheckEnabled: false,
geoAppsScope: "pace-drive-ios-min"
))Mock responses are switched using CommandLineArgument enum values added via addCommandLineArguments(_:) helper on XCTestCase. Test execution order is randomized.
- AppAuth (~> 1.5.0) — OAuth2/OpenID Connect (PACE fork)
- SwiftProtobuf (~> 1.22.0) — Protocol Buffers for vector tile models
- Japx (~> 5.0.0) — JSON:API parsing
Distributed via SPM, CocoaPods, and Carthage.