Productivity and DevEx improvements#4
Conversation
WalkthroughThis update introduces live code injection (hot-reloading) to the SwiftUI app by integrating the Inject package, updating project configuration, and modifying multiple views to support runtime code updates. It also refines cloud functions configuration and emulator support, simplifies app settings management, and updates documentation to reflect these development enhancements. Changes
Sequence Diagram(s)sequenceDiagram
participant Dev as Developer
participant InjectionIII as InjectionIII App
participant App as SwiftUI App
participant Inject as Inject Package
Dev->>InjectionIII: Edit SwiftUI code and save
InjectionIII->>App: Trigger code injection event
App->>Inject: Observe injection via @ObserveInjection
Inject->>App: Notifies affected views to redraw
App->>Dev: Updated UI reflects code changes instantly
Poem
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
README.md (1)
61-65: Improve documentation clarity for hot reloading setup.The hot reloading documentation is helpful but could be refined:
Consider this improved wording to address the static analysis hint:
- - With both Inject and InjectionIII running, you can make changes to your SwiftUI views and see updates in the simulator without rebuilding the entire app. + - With both Inject and InjectionIII running, you can modify SwiftUI views and see live updates in the simulator without rebuilding the entire app.Also consider adding a note about when to use hot reloading (e.g., "Use this during active development for faster iteration cycles").
When/Views/Main.swift (2)
2-2: Remove@_exportedunless re-exporting is intentionally needed.The
@_exportedattribute re-exports the Inject module to any code that imports this file. Unless you specifically need other modules to access Inject through this import, use a regular import instead.-@_exported import Inject +import Inject
2-2: Consider wrapping injection code in DEBUG conditionals.While the injection setup is correctly implemented, consider wrapping the injection-related code in
#if DEBUGconditionals to ensure it doesn't ship in production builds. This is a common practice for development-only features.+#if DEBUG @_exported import Inject +#endif+#if DEBUG @ObserveInjection var redraw +#endif} +#if DEBUG .enableInjection() +#endifAlso applies to: 7-7, 99-99
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
README.md(1 hunks)When.xcodeproj/project.pbxproj(12 hunks)When.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved(2 hunks)When.xcodeproj/xcshareddata/xcschemes/When App.xcscheme(1 hunks)When/Components/DepartureBoard.swift(2 hunks)When/Views/Main.swift(2 hunks)
🧰 Additional context used
🪛 LanguageTool
README.md
[style] ~64-~64: Consider shortening or rephrasing this to strengthen your wording.
Context: ...nject and InjectionIII running, you can make changes to your SwiftUI views and see updates in t...
(MAKE_CHANGES)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Cursor BugBot
🔇 Additional comments (7)
When.xcodeproj/project.pbxproj (2)
20-20: Inject package integration looks well-configured.The Inject Swift package has been properly added with appropriate build settings:
- Debug-only linker flags for interposition
- Correct frontend command line emission settings
- Main app target only (widget excluded appropriately)
Also applies to: 146-146, 263-263
580-584: Verify linker flags are debug-only.The
-Xlinker -interposableflags are correctly added only to debug builds. This is important for performance as these flags can impact release build optimization.Also applies to: 667-670
When.xcodeproj/xcshareddata/xcschemes/When App.xcscheme (1)
54-57: Clarify the relationship between Firebase debug flag and Inject integration.The addition of
-FIRDebugDisabledseems unrelated to the main objective of adding hot reloading with Inject. Could you explain:
- Why this Firebase change is included in this PR?
- Does this flag interact with the injection framework in any way?
Consider separating unrelated changes into different PRs for better traceability.
When/Components/DepartureBoard.swift (2)
5-5: Inject integration is correctly implemented.The
@ObserveInjectionproperty wrapper and.enableInjection()modifier are properly added according to the Inject package documentation.Also applies to: 22-22
5-5: Remove obsolete suggestion: MainView already supports injection
The review’s premise—that onlyDepartureBoardhas injection support—is outdated.
When/Views/Main.swiftalready declares@ObserveInjection var redraw.- No other
struct …: Viewdefinitions without@ObserveInjectionwere found.Likely an incorrect or invalid review comment.
When.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved (1)
85-93: Package resolution correctly reflects Inject dependency.The Package.resolved file properly includes the Inject package at version 1.5.2 with the correct revision hash. This auto-generated file accurately reflects the dependency changes.
When/Views/Main.swift (1)
7-7: LGTM! Proper injection setup implementation.The
@ObserveInjectionproperty wrapper and.enableInjection()view modifier are correctly implemented following the Inject package's standard usage pattern. This will enable hot reloading functionality during development.Also applies to: 99-99
| 1395660E2E19CA0000329958 /* XCRemoteSwiftPackageReference "Inject" */ = { | ||
| isa = XCRemoteSwiftPackageReference; | ||
| repositoryURL = "https://github.com/krzysztofzablocki/Inject.git"; | ||
| requirement = { | ||
| kind = upToNextMajorVersion; | ||
| minimumVersion = 1.5.2; | ||
| }; | ||
| }; |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Verify Inject package version and security considerations.
The minimum version 1.5.2 appears reasonable, but please ensure:
- This version doesn't have known security vulnerabilities
- The package is only included in debug builds in production
Also applies to: 793-797
🏁 Script executed:
#!/bin/bash
# Check if Inject package is properly excluded from release builds
echo "Checking if Inject is properly configured for debug-only usage..."
# Search for any conditional compilation or debug-only usage of Inject
rg -A 3 -B 3 "import Inject|@ObserveInjection|enableInjection" --type swift
# Check build configurations to ensure Inject isn't in release
rg -A 5 -B 5 "Release.*Inject" When.xcodeproj/project.pbxprojLength of output: 1835
Enforce Debug-Only Injection & Audit Inject v1.5.2
Please scope all Inject hooks to debug builds and perform a security review of v1.5.2:
• Wrap every @ObserveInjection and .enableInjection() behind #if DEBUG in:
– When/Views/Main.swift
– When/Components/DepartureBoard.swift
• Restrict the Inject package dependency to your debug scheme or target configuration in Xcode so it isn’t bundled in production releases.
• Verify Inject v1.5.2 against known security advisories (e.g. GitHub Security Advisories, Snyk) to ensure no CVEs apply.
Example change in your Swift views:
- @_exported import Inject
- @ObserveInjection var redraw
- …
- .enableInjection()
+ #if DEBUG
+ @_exported import Inject
+ @ObserveInjection var redraw
+ …
+ .enableInjection()
+ #endifCommittable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In When.xcodeproj/project.pbxproj around lines 750 to 757, the Inject package is
currently included without restricting it to debug builds. To fix this, modify
the Xcode project settings to include the Inject package only in the debug
scheme or target configuration, ensuring it is not bundled in production
releases. Additionally, in When/Views/Main.swift and
When/Components/DepartureBoard.swift, wrap all uses of @ObserveInjection and
.enableInjection() inside #if DEBUG conditional compilation blocks to enforce
debug-only injection. Finally, perform a security review of Inject v1.5.2
against known advisories to confirm no vulnerabilities affect your project.
…emulation; update README
…r hot reloading support
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
When/Components/DepartureBoard.swift(3 hunks)When/Components/MainHeader.swift(2 hunks)When/Components/NoStation.swift(2 hunks)When/Components/StationSearchField.swift(2 hunks)When/Components/StationSearchResults.swift(2 hunks)When/Components/StationTab.swift(2 hunks)When/Views/StationSettings.swift(4 hunks)
✅ Files skipped from review due to trivial changes (2)
- When/Components/StationSearchField.swift
- When/Components/MainHeader.swift
🚧 Files skipped from review as they are similar to previous changes (1)
- When/Components/DepartureBoard.swift
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Cursor BugBot
🔇 Additional comments (5)
When/Components/NoStation.swift (1)
50-50: Injection implementation looks correct.The
.enableInjection()modifier is properly placed on the root ScrollView, following the standard pattern for the Inject package.When/Components/StationSearchResults.swift (1)
5-5: Injection implementation is consistent and correct.The injection pattern is properly implemented with
@ObserveInjectionproperty and.enableInjection()modifier on the root view. This follows the same correct pattern as other components in the codebase.Also applies to: 34-34
When/Components/StationTab.swift (1)
6-6: Injection implementation is consistent and well-placed.The injection setup follows the established pattern correctly, with the property at the top of the struct and the modifier on the root ScrollView.
Also applies to: 79-79
When/Views/StationSettings.swift (2)
7-7: Injection correctly implemented for main view.The injection setup is properly implemented on the main
StationSettingsViewwith the standard pattern.Also applies to: 33-33
119-119: Injection correctly implemented for content view.The injection setup is properly implemented on the private
StationSettingsContentViewas well. Having injection on both the parent and child views makes sense since they are separate SwiftUI views, and most of the UI logic resides in the content view.Also applies to: 223-223
| import SwiftUI | ||
|
|
||
| struct NoStation: View { | ||
| @ObserveInjection var redraw |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Verify Inject package import is available.
The @ObserveInjection property wrapper is used but I don't see a corresponding import for the Inject package. Please ensure the necessary import is present.
🏁 Script executed:
#!/bin/bash
# Check if there's a missing import for the Inject package
rg -A 5 -B 5 "import.*Inject"
# Also check if injection symbols are available globally
ast-grep --pattern 'import $_' --lang swift | head -20Length of output: 1518
Add missing Inject import in NoStation.swift
The @ObserveInjection wrapper comes from the Inject package but isn’t imported in When/Components/NoStation.swift. Please add:
--- a/When/Components/NoStation.swift
+++ b/When/Components/NoStation.swift
@@ 1,3 +1,4 @@
+ import Inject
import SwiftUI
import SwiftData
…This ensures @ObserveInjection is recognized.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @ObserveInjection var redraw | |
| import Inject | |
| import SwiftUI | |
| import SwiftData | |
| … |
🤖 Prompt for AI Agents
In When/Components/NoStation.swift at line 5, the @ObserveInjection property
wrapper is used but the Inject package is not imported. Add the import statement
for Inject at the top of the file to ensure @ObserveInjection is recognized and
the code compiles correctly.
There was a problem hiding this comment.
Bug: AppStorage Initialization Issue
The AppSettings struct is no longer instantiated, preventing its init() method from running. This leaves userDeviceId uninitialized (empty) for new installations. Furthermore, directly modifying an @AppStorage property within a struct's init() is not supported by Swift.
When/Models/AppSettings.swift#L3-L13
When-App-iOS/When/Models/AppSettings.swift
Lines 3 to 13 in 155bbdb
BugBot free trial expires on July 22, 2025
You have used $0.00 of your $50.00 spend limit so far. Manage your spend limit in the Cursor dashboard.
Was this report helpful? Give feedback by reacting with 👍 or 👎
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (1)
When/Views/NoStation.swift (1)
5-5: Verify that the Inject package is properly imported.Same issue as in MainHeader.swift - the
@ObserveInjectionproperty wrapper requires importing the Inject package.
🧹 Nitpick comments (2)
When/Config/AppConfig.swift (2)
3-11: Good struct design, but consider improving initializer flexibility.The struct design is clean and follows Swift conventions. However, the initializer only allows customization of the
enabledparameter whilehostandportuse hardcoded defaults.Consider adding an overloaded initializer for more flexibility:
struct CloudFunctionsEmulatorConfig { var enabled: Bool var host: String = "localhost" var port: Int = 5001 init(enabled: Bool) { self.enabled = enabled } + + init(enabled: Bool, host: String, port: Int) { + self.enabled = enabled + self.host = host + self.port = port + } }This would allow customization of host and port when needed while maintaining the convenience of the simple initializer.
22-29: Robust emulator configuration with minor improvement suggestion.The computed property implementation is well-structured with proper conditional compilation and environment variable checking. The documentation comment is helpful.
Consider making the environment variable check case-insensitive for better developer experience:
static var cloudFunctionsEmulator: CloudFunctionsEmulatorConfig { #if DEBUG - if ProcessInfo.processInfo.environment["CLOUD_FUNCTIONS_EMULATOR"] == "YES" { + if ProcessInfo.processInfo.environment["CLOUD_FUNCTIONS_EMULATOR"]?.uppercased() == "YES" { return CloudFunctionsEmulatorConfig(enabled: true) } #endif return CloudFunctionsEmulatorConfig(enabled: false) }This would accept variations like "yes", "Yes", or "YES" in the environment variable.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
When/Config/AppConfig.swift(1 hunks)When/Models/AppSettings.swift(1 hunks)When/Services/TransportService.swift(1 hunks)When/Views/DepartureBoard.swift(3 hunks)When/Views/MainHeader.swift(2 hunks)When/Views/NoStation.swift(2 hunks)When/Views/StationSearchField.swift(2 hunks)When/Views/StationSearchResults.swift(2 hunks)When/Views/StationTab.swift(2 hunks)
✅ Files skipped from review due to trivial changes (4)
- When/Views/StationSearchResults.swift
- When/Views/StationSearchField.swift
- When/Views/StationTab.swift
- When/Views/DepartureBoard.swift
🚧 Files skipped from review as they are similar to previous changes (2)
- When/Models/AppSettings.swift
- When/Services/TransportService.swift
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Cursor BugBot
🔇 Additional comments (5)
When/Config/AppConfig.swift (1)
19-19: LGTM! Clean configuration constant.The static constant is well-named and follows Swift naming conventions. The region value
"europe-west1"appears to be a valid Firebase Functions region.When/Views/MainHeader.swift (2)
54-54: LGTM! Hot-reloading implementation is correctly positioned.The
.enableInjection()modifier is properly placed at the end of the view modifier chain, which is the correct approach for the Inject package.
5-5: LGTM: Consistent implementation of live code injection.The implementation follows the standard pattern for the Inject package with proper property wrapper usage and view modifier chaining. The changes are minimal and focused on enabling hot-reloading functionality.
Also applies to: 54-54
When/Views/NoStation.swift (2)
50-50: LGTM! Consistent hot-reloading implementation.The implementation matches the pattern used in MainHeader.swift and is correctly positioned in the view modifier chain.
5-5: LGTM: Proper implementation of live code injection.The implementation correctly uses the
@ObserveInjectionproperty wrapper and.enableInjection()modifier to enable hot-reloading functionality. The changes are minimal and non-intrusive to the existing view logic.Also applies to: 50-50
| import SwiftUI | ||
|
|
||
| struct MainHeader: View { | ||
| @ObserveInjection var redraw |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Verify that the Inject package is properly imported.
The @ObserveInjection property wrapper requires importing the Inject package, but no import statement is visible in this file.
🏁 Script executed:
#!/bin/bash
# Check if Inject package is imported globally or in a framework
rg -A 5 -B 5 "import.*Inject" Length of output: 325
🏁 Script executed:
#!/bin/bash
# Locate and display the contents of MainHeader.swift
file_path=$(fd -e swift MainHeader.swift)
echo "File found at: $file_path"
echo "---- File Contents ----"
sed -n '1,200p' "$file_path"Length of output: 5865
Add missing import Inject to MainHeader.swift
Swift import directives are file-local, so even though you have a re-exported import in Main.swift, any file using @ObserveInjection must explicitly import the Inject package.
Please update When/Views/MainHeader.swift:
import PhosphorSwift
import SwiftUI
+import Inject
struct MainHeader: View {
@ObserveInjection var redraw
…📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @ObserveInjection var redraw | |
| import PhosphorSwift | |
| import SwiftUI | |
| import Inject | |
| struct MainHeader: View { | |
| @ObserveInjection var redraw | |
| … |
🤖 Prompt for AI Agents
In When/Views/MainHeader.swift at line 5, the file uses the @ObserveInjection
property wrapper but is missing the required import statement for the Inject
package. Add the line 'import Inject' at the top of the file to explicitly
import the Inject module, ensuring the @ObserveInjection attribute is recognized
and the code compiles correctly.
🛠️ Refactor suggestion
Consider conditional compilation for debug builds only.
The live code injection functionality should ideally be limited to debug builds to avoid shipping development tools to production. Consider wrapping the injection-related code in conditional compilation directives.
Apply this pattern to limit injection to debug builds:
+#if DEBUG
@ObserveInjection var redraw
+#endifAnd for the modifier:
.onDisappear {
liveIconTimer?.invalidate()
liveIconTimer = nil
}
+#if DEBUG
.enableInjection()
+#endifAlso applies to: 54-54
🤖 Prompt for AI Agents
In When/Views/MainHeader.swift at lines 5 and 54, the @ObserveInjection property
and related injection code should be wrapped in conditional compilation
directives to restrict them to debug builds only. Use #if DEBUG and #endif
around these lines to ensure the live code injection functionality is excluded
from production builds, preventing development tools from shipping in release
versions.
💡 Verification agent
🧩 Analysis chain
Verify import statement for Inject package.
The @ObserveInjection property wrapper requires the Inject package to be imported. Ensure the import statement is present or that the package is configured for automatic importing.
Let me check if the import statement is needed:
🏁 Script executed:
#!/bin/bash
# Description: Check if Inject package import is needed by searching for existing imports
# Expected: Should find import statements for Inject package in Swift files
# Search for Inject import statements in the codebase
rg -n "import.*Inject" --type swift
# Search for other files that use @ObserveInjection to see if they have imports
rg -n "@ObserveInjection" --type swift -A 2 -B 2Length of output: 3038
Include explicit import Inject in MainHeader.swift
The @ObserveInjection property wrapper is defined in the Inject package, and Swift imports are file-scoped. Although When/Views/Main.swift uses
@_exported import Injectthat only re-exports the module for external clients—it doesn’t automatically import Inject into other files. You must add:
import Injectto the top of any file that references @ObserveInjection (including When/Views/MainHeader.swift).
Additionally, to ensure hot-reload code isn’t shipped in production, wrap both the property wrapper and the view modifier in debug-only conditionals:
// In When/Views/MainHeader.swift
import SwiftUI
import Inject
struct MainHeader: View {
#if DEBUG
@ObserveInjection var redraw
#endif
// …
}
// In When/Views/Main.swift
var body: some Scene {
WindowGroup {
ContentView()
#if DEBUG
.enableInjection()
#endif
}
}• When/Views/MainHeader.swift: add import Inject above other imports and guard @ObserveInjection with #if DEBUG.
• When/Views/Main.swift: wrap .enableInjection() in #if DEBUG.
🤖 Prompt for AI Agents
In When/Views/MainHeader.swift at line 5, add an explicit `import Inject`
statement at the top of the file to ensure the `@ObserveInjection` property
wrapper is recognized. Also, wrap the `@ObserveInjection var redraw` declaration
inside `#if DEBUG` and `#endif` to prevent hot-reload code from being included
in production builds. Additionally, in When/Views/Main.swift, wrap the
`.enableInjection()` call inside `#if DEBUG` and `#endif` to restrict it to
debug builds only.
| import SwiftUI | ||
|
|
||
| struct NoStation: View { | ||
| @ObserveInjection var redraw |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Consistent implementation with same considerations as MainHeader.swift.
The live code injection implementation is consistent with other files in the PR. The same considerations apply:
- Verify that the Inject package import statement is present or automatically imported
- Consider conditional compilation for debug builds only to prevent shipping development tools to production
Apply conditional compilation pattern:
+#if DEBUG
@ObserveInjection var redraw
+#endifAnd:
.onScrollGeometryChange(for: CGFloat.self) { geometry in
return geometry.contentOffset.y + geometry.contentInsets.top
} action: { _, new in
offset.wrappedValue = new
}
+#if DEBUG
.enableInjection()
+#endifAlso applies to: 50-50
To verify whether the Inject import is present (and consistent with MainHeader.swift), let’s inspect the top of both files:
🏁 Script executed:
#!/bin/bash
# Locate and print the first 20 lines of NoStation.swift
file=$(fd NoStation.swift)
echo "=== $file ==="
sed -n '1,20p' "$file"
# Locate and print the first 20 lines of MainHeader.swift
file2=$(fd MainHeader.swift)
echo "=== $file2 ==="
sed -n '1,20p' "$file2"Length of output: 1451
To confirm whether the @ObserveInjection and .enableInjection() helpers are defined locally (and thus don’t require an external import) or come from a package you need to import, let’s locate any Injection-related files and your SPM manifest:
🏁 Script executed:
#!/bin/bash
# 1. Show your SPM manifest if present
pkg_file=$(fd Package.swift | head -n1)
if [[ -n "$pkg_file" ]]; then
echo "=== $pkg_file ==="
sed -n '1,200p' "$pkg_file"
else
echo "No Package.swift found"
fi
# 2. Find any local Injection*.swift definitions
echo "=== Injection*.swift files ==="
fd -t f -i "Injection*.swift"
# 3. Search for ObserveInjection / enableInjection definitions
echo "=== ObserveInjection definitions ==="
rg -n "struct ObserveInjection" || echo "— no local property-wrapper found —"
echo "=== enableInjection definitions ==="
rg -n "func enableInjection" || echo "— no local view modifier found —"Length of output: 600
Add missing Inject import and guard live‐code injection to DEBUG builds
The @ObserveInjection property wrapper and .enableInjection() modifier aren’t defined locally, so you need to import the module that provides them (for example, import Inject). Also, wrap both the property wrapper and the view modifier in #if DEBUG blocks so they’re excluded from production builds.
Locations to update (both in When/Views/NoStation.swift and When/Views/MainHeader.swift):
-
At the top, add the injection import:
import PhosphorSwift import SwiftUI import Inject // ← add this (or the correct module name)
-
Around the property wrapper:
struct NoStation: View { - @ObserveInjection var redraw + #if DEBUG
-
@ObserveInjection var redraw - #endif
- Around the view modifier:
```diff
ScrollView {
// …
}
+ #if DEBUG
.enableInjection()
+ #endif
🤖 Prompt for AI Agents
In When/Views/NoStation.swift at line 5, the @ObserveInjection property wrapper
is used without importing its defining module. Add the appropriate import
statement (e.g., import Inject) at the top of the file along with existing
imports. Then, wrap the @ObserveInjection property declaration and any usage of
the .enableInjection() view modifier in #if DEBUG ... #endif blocks to ensure
they are only included in debug builds and excluded from production.
Summary by CodeRabbit
New Features
Improvements
Refactor