Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tracker combine changes #2

Open
wants to merge 19 commits into
base: develop
Choose a base branch
from
Open

Conversation

digitaldan
Copy link
Owner

@digitaldan digitaldan commented Sep 11, 2024

Summary by CodeRabbit

  • New Features

    • Enhanced handling of command options and state updates using the Combine framework across various components.
    • Added a new NetworkTracker class for improved network connectivity management.
    • Introduced user interfaces for managing client certificates, color selection, notifications, and application settings.
    • Added new views for drawer navigation and displaying RTF content.
    • Implemented a Throttler class for managing command updates efficiently.
    • Introduced a ColorPickerView for dynamic color selection and real-time updates to OpenHAB widgets.
    • Added a DrawerView for improved navigation within the application.
    • Introduced a SelectionView for presenting selectable mappings for OpenHAB items.
  • Bug Fixes

    • Improved robustness by ensuring labels default to an empty string when nil.
  • Refactor

    • Transitioned from delegate-based to reactive programming patterns using Combine for state management and network requests.
    • Enhanced navigation logic in the application for better user experience.
    • Streamlined project structure by removing obsolete multicast delegate references and related files.
  • Chores

    • Removed obsolete multicast delegate references and related files from the project configuration.

Copy link

coderabbitai bot commented Sep 11, 2024

Warning

Rate limit exceeded

@digitaldan has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 9 minutes and 10 seconds before requesting another review.

How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

Commits

Files that changed from the base of the PR and between 85ef190 and a6c4e61.

Walkthrough

The pull request introduces various modifications across multiple files, including the addition of directories to the .gitignore and .swiftlint.yml for improved file management. It features changes to property types in Swift classes, a new NetworkTracker class for enhanced network connectivity management, and refactoring of navigation and network handling logic using Combine for a more reactive programming approach. Additionally, several SwiftUI views are introduced for better user interaction, while some obsolete files are removed, indicating a restructuring of the project.

Changes

Files Change Summary
.gitignore, BuildTools/.swiftlint.yml Added directories to ignore generated files and excluded paths in SwiftLint configuration.
OpenHABCore/Sources/OpenHABCore/Model/OpenHABCommandOptions.swift, OpenHABCore/Sources/OpenHABCore/Model/OpenHABWidget.swift Changed label property to optional in OpenHABCommandOptions and modified mappingsOrItemOptions to default label to an empty string if nil.
OpenHABCore/Sources/OpenHABCore/Util/HTTPClient.swift Reduced timeoutIntervalForRequest from 30 seconds to 10 seconds.
OpenHABCore/Sources/OpenHABCore/Util/NetworkTracker.swift Introduced a new class for managing network connections and monitoring status changes using Combine and Alamofire.
openHAB.xcodeproj/project.pbxproj Removed references to several obsolete Swift files and added new Swift files for updated functionality.
openHAB/OpenHABRootViewController.swift, openHAB/OpenHABSitemapViewController.swift, openHAB/OpenHABWebViewController.swift Refactored navigation logic and integrated Combine for handling HTTP requests and state updates, replacing delegate methods with reactive subscriptions.
openHAB/ClientCertificatesView.swift, openHAB/ClientCertificatesViewModel.swift, openHAB/ColorPickerView.swift, openHAB/DrawerView.swift, openHAB/NotificationsView.swift, openHAB/RTFTextView.swift, openHAB/SelectionView.swift, openHAB/SettingsView.swift, openHAB/Throttler.swift Introduced new SwiftUI views for managing client certificates, color selection, notifications, and application settings, enhancing user interaction and experience.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant OpenHABApp
    participant NetworkTracker
    participant HTTPClient
    participant ViewController

    User->>OpenHABApp: Interacts with UI
    OpenHABApp->>NetworkTracker: Check network status
    NetworkTracker->>HTTPClient: Manage connections
    HTTPClient->>ViewController: Update UI based on network state
Loading

🐇 In the code, we hop and play,
🐰 Making changes day by day.
🥕 With options new and logic bright,
🌟 Our app shines with pure delight!
🎉 So let’s celebrate, oh what fun,
🐇 Together we’ve made our code run!


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    -- I pushed a fix in commit <commit_id>, please review it.
    -- Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    -- @coderabbitai generate unit testing code for this file.
    -- @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    -- @coderabbitai generate interesting stats about this repository and render them as a table.
    -- @coderabbitai read src/utils.ts and generate unit testing code.
    -- @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    -- @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Outside diff range and nitpick comments (2)
openHAB/OpenHABViewController.swift (1)

19-19: Approved addition of trackerCancellables property.

The addition of trackerCancellables is a good practice for managing Combine subscriptions. It's recommended to document its usage within the class to clarify how it should be used to manage subscriptions effectively.

OpenHABCore/Sources/OpenHABCore/Util/HTTPClient.swift (1)

30-30: Review of Timeout Configuration Change

The reduction of timeoutIntervalForRequest from 30 seconds to 10 seconds is a significant change that can enhance responsiveness by reducing the wait time for unresponsive requests. However, this might also increase the rate of timeouts in slower network conditions.

Recommendation:
Monitor the impact of this change on network reliability and consider introducing configuration options for timeouts to adapt to varying network conditions.

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 87f4330 and 1f5a495.

Files selected for processing (12)
  • .github/workflows/publish.yml (1 hunks)
  • OpenHABCore/Sources/OpenHABCore/Model/OpenHABCommandOptions.swift (1 hunks)
  • OpenHABCore/Sources/OpenHABCore/Model/OpenHABWidget.swift (1 hunks)
  • OpenHABCore/Sources/OpenHABCore/Util/HTTPClient.swift (1 hunks)
  • fastlane/Fastfile (1 hunks)
  • openHAB.xcodeproj/project.pbxproj (4 hunks)
  • openHAB/OpenHABRootViewController.swift (4 hunks)
  • openHAB/OpenHABSitemapViewController.swift (8 hunks)
  • openHAB/OpenHABTracker.swift (5 hunks)
  • openHAB/OpenHABViewController.swift (1 hunks)
  • openHAB/OpenHABWebViewController.swift (6 hunks)
  • openHABWatch Extension/openHABWatch Extension/Model/ObservableOpenHABWidget.swift (1 hunks)
Files skipped from review due to trivial changes (2)
  • .github/workflows/publish.yml
  • openHAB.xcodeproj/project.pbxproj
Additional context used
SwiftLint
openHAB/OpenHABRootViewController.swift

[Warning] 260-260: Prefer using UTF-8 encoded strings when converting between String and Data

(non_optional_string_data_conversion)


[Warning] 340-340: Prefer using UTF-8 encoded strings when converting between String and Data

(non_optional_string_data_conversion)

Additional comments not posted (19)
openHAB/OpenHABViewController.swift (3)

12-12: Approved import of Combine framework.

The inclusion of the Combine framework is essential for the new reactive programming features introduced in this class.


Line range hint 90-125: Approved SSL certificate handling in ServerCertificateManagerDelegate.

The implementation of SSL certificate validation and user interaction for server trust decisions is well-handled. This is crucial for maintaining security and user trust in the application.


Line range hint 127-172: Approved client certificate handling in ClientCertificateManagerDelegate.

The methods for handling client certificate import, password requests, and error alerts are well-implemented. This ensures secure handling of client certificates and proper user involvement.

fastlane/Fastfile (1)

103-103: Conditional logic update appears correct, but verify broader impact.

The updated conditional logic to prevent version increments when the bump type is set to 'none' aligns well with the PR objectives. This change should help in managing version increments more effectively during different types of builds.

However, it's crucial to verify that this change integrates smoothly with the rest of the build process and does not introduce any unintended side effects, especially in non-CI environments or other branches.

openHABWatch Extension/openHABWatch Extension/Model/ObservableOpenHABWidget.swift (1)

98-98: Improved nil handling in mappingsOrItemOptions.

The modification to provide a default empty string for nil label values enhances the robustness of the mappingsOrItemOptions property. This change prevents potential runtime errors related to nil values, aligning with best practices for optional handling in Swift.

OpenHABCore/Sources/OpenHABCore/Model/OpenHABWidget.swift (1)

121-121: Good handling of optional values in UI-related code.

The modification to default label to an empty string when nil is a robust way to handle optional strings in Swift, especially in UI contexts where nil values can lead to crashes or unexpected behavior. This change enhances the stability and reliability of the mappingsOrItemOptions property.

openHAB/OpenHABTracker.swift (3)

13-13: Approved import of Combine.

The addition of the Combine framework supports the new reactive programming model used in the OpenHABTracker class.


19-28: Well-designed OpenHABTrackerState class.

The class appropriately uses optional properties to manage the state of the tracker, which allows for a clear representation of both presence and absence of values.


33-34: Effective use of @Published properties in OpenHABTracker.

The transition to reactive programming using @Published properties for state and progress simplifies state management and enhances the reactivity of the application.

openHAB/OpenHABWebViewController.swift (3)

258-261: Custom user agent for iPads enhances user experience.

The addition of a custom user agent for iPads ensures that web content is rendered appropriately on larger screens, accommodating the default desktop view in newer iOS versions.


76-96: Effective use of Combine for reactive programming.

The implementation of Combine's sink method to handle state and progress updates enhances the responsiveness and modularity of the code. The use of trackerCancellables to manage subscription lifecycles is also commendable.

Ensure that all subscriptions are properly disposed of to prevent memory leaks or unintended behavior.

Verification successful

Combine subscriptions are properly disposed of.

The trackerCancellables set is used effectively to manage the lifecycle of Combine subscriptions. The call to trackerCancellables.removeAll() in the viewWillDisappear method ensures that subscriptions are disposed of appropriately, preventing memory leaks or unintended behavior.

  • OpenHABWebViewController.swift: Subscriptions are disposed of in viewWillDisappear.
  • OpenHABSitemapViewController.swift: Subscriptions are disposed of in viewWillDisappear.
Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify proper disposal of Combine subscriptions.

# Test: Search for any remaining subscriptions that might not be disposed.
rg --type swift -A 5 $'trackerCancellables'

Length of output: 4040


105-106: Proper cleanup of delegate methods.

The removal of multicast delegate methods aligns with the shift towards a more reactive programming model using Combine. This simplifies the codebase and reduces complexity.

Verify that all functionalities previously handled by the delegates are now correctly managed by the new Combine subscriptions.

openHAB/OpenHABRootViewController.swift (3)

12-12: Approved addition of Combine.

The import of Combine supports the shift to a reactive programming model, enhancing the application's responsiveness and maintainability.


347-347: Approved robustness improvement in menu handling.

The modifications to showSideMenu improve the robustness by adding checks before presenting the side menu, preventing potential crashes. This change enhances the user experience by ensuring smoother navigation.

Consider further testing to ensure that the menu handling works as expected across different scenarios and device orientations.


211-232: Approved navigation logic enhancement.

The modifications to navigateCommandAction enhance the navigation logic by ensuring the correct view controller is activated based on the command received. This structured approach improves maintainability and functionality.

However, ensure that the regex pattern used for command parsing is thoroughly tested to prevent potential issues with command interpretation.

Run the following script to verify the regex pattern:

Verification successful

Regex pattern in navigateCommandAction is correctly implemented.

The regex pattern /^(\/basicui\/app\\?.*|\/.*|.*)$/ is appropriately designed to match various command paths, ensuring the correct view controller is activated. The pattern effectively handles paths starting with /basicui/app?, any path starting with /, and any other string. No issues were found with its implementation.

  • The pattern is used in openHAB/OpenHABRootViewController.swift to extract the path from the command.
Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the regex pattern used in navigateCommandAction.

# Test: Search for the regex pattern usage. Expect: Only occurrences of the correct pattern.
rg --type swift -A 5 $'navigateCommandAction'

Length of output: 1060

openHAB/OpenHABSitemapViewController.swift (4)

15-15: Approved: Import of Combine framework.

The addition of the Combine framework supports the shift towards reactive programming, enabling the new features related to state and progress handling in the class.


169-175: Approved: Subscription to progress updates.

The setup for subscribing to OpenHABTracker.shared.$progress is correctly implemented using dropFirst() to avoid initial notifications. The closure effectively logs and displays progress updates. Ensure that the popup message functionality is tested thoroughly.

Verification successful

Verified: Popup message functionality is correctly implemented.

The showPopupMessage method is consistently used across the codebase with appropriate parameters for different contexts. The implementation in OpenHABViewController.swift using SwiftMessages is suitable for displaying popups. No issues found with its usage.

  • OpenHABWebViewController.swift: Proper usage of showPopupMessage for connection and error messages.
  • OpenHABSitemapViewController.swift: Correct implementation for various message types including errors and warnings.
Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the popup message functionality by checking if the `showPopupMessage` method is called correctly in other parts of the codebase.

# Test: Search for the usage of `showPopupMessage`. Expect: Proper usage and handling in the context where it's called.
rg --type swift -A 5 $'showPopupMessage'

Length of output: 5999


471-496: Approved: Modifications to pushSitemap using Combine.

The pushSitemap function is effectively modified to utilize Combine for handling state changes. The management of the subscription lifecycle is correctly handled. Verify that the navigation logic correctly triggers under various conditions.

Verification successful

Verified: Navigation logic in pushSitemap and other usages of pushViewController

The navigation logic in the pushSitemap function and other usages of pushViewController in the codebase are correctly implemented. The consistent pattern of instantiating and pushing new view controllers ensures proper navigation flow. No issues were found with the current implementation.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the navigation logic by checking if the `pushViewController` method is called correctly in other parts of the codebase.

# Test: Search for the usage of `pushViewController`. Expect: Proper usage and handling in the context where it's called.
rg --type swift -A 5 $'pushViewController'

Length of output: 3265


Line range hint 177-213: Approved: Implementation of stateWatcher for observing state changes.

The stateWatcher is correctly set up to observe state changes, using dropFirst() to filter out the initial state. The handling of state changes, including error logging and UI updates, is appropriately implemented. Verify that the error handling works as expected in various scenarios.

@@ -13,5 +13,5 @@ import Foundation

public class OpenHABCommandOptions: Decodable {
public var command = ""
public var label = ""
public var label: String? = ""
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct the default value of the label property.

The label property in the OpenHABCommandOptions class has been changed to an optional string, which is a significant modification for handling cases where a label is not necessary. However, the default value should be nil as per the summary and alterations list, not an empty string "". This needs correction to align with the intended functionality enhancements and to avoid potential nullability issues in the application.

- public var label: String? = ""
+ public var label: String? = nil
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.

Suggested change
public var label: String? = ""
public var label: String? = nil

Comment on lines 145 to 156
state = OpenHABTrackerState(openHABUrl: nil, openHABVersion: nil, error: errorMessage("error"))
return
}
multicastDelegate.invoke { $0.openHABTrackingProgress(NSLocalizedString("connecting", comment: "")) }
progress = NSLocalizedString("connecting", comment: "")
tryUrls(urls) { url, version, error in
if let error {
os_log("OpenHABTracker failed %{PUBLIC}@", log: .default, type: .info, error.localizedDescription)
self.multicastDelegate.invoke { $0.openHABTrackingError(error) }
self.state = OpenHABTrackerState(openHABUrl: nil, openHABVersion: nil, error: error)
} else {
self.appData?.openHABVersion = version
self.appData?.openHABRootUrl = url?.absoluteString ?? ""
self.multicastDelegate.invoke { $0.openHABTracked(url, version: version) }
self.state = OpenHABTrackerState(openHABUrl: url, openHABVersion: version, error: nil)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Complex but necessary implementation in tryAll.

The method handles multiple network endpoints effectively. Consider adding more comments to explain the logic behind the use of timers and network requests for future maintainability.

} else {
os_log("Request succeeded", log: .default, type: .info)
if let data {
os_log("Data: %{public}@", log: .default, type: .debug, String(data: data, encoding: .utf8) ?? "")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest specifying UTF-8 encoding during data conversion.

To address the static analysis warnings, specify UTF-8 encoding when converting between String and Data. This will ensure consistent and safe data handling across different environments.

- String(data: data, encoding: .utf8) ?? ""
+ String(data: data, encoding: .utf8) ?? "Invalid data"

Also applies to: 340-340

Tools
SwiftLint

[Warning] 260-260: Prefer using UTF-8 encoded strings when converting between String and Data

(non_optional_string_data_conversion)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 1f5a495 and 4ea7db0.

Files selected for processing (7)
  • OpenHABCore/Sources/OpenHABCore/Util/NetworkTracker.swift (1 hunks)
  • OpenHABCore/Sources/OpenHABCore/Util/Preferences.swift (1 hunks)
  • openHAB.xcodeproj/project.pbxproj (0 hunks)
  • openHAB/OpenHABRootViewController.swift (7 hunks)
  • openHAB/OpenHABSitemapViewController.swift (7 hunks)
  • openHAB/OpenHABTracker.swift (0 hunks)
  • openHAB/OpenHABWebViewController.swift (3 hunks)
Files not reviewed due to no reviewable changes (2)
  • openHAB.xcodeproj/project.pbxproj
  • openHAB/OpenHABTracker.swift
Files skipped from review as they are similar to previous changes (1)
  • openHAB/OpenHABWebViewController.swift
Additional context used
SwiftLint
OpenHABCore/Sources/OpenHABCore/Util/NetworkTracker.swift

[Warning] 16-16: TODOs should be resolved (these strings should reference...)

(todo)

openHAB/OpenHABRootViewController.swift

[Warning] 287-287: Prefer using UTF-8 encoded strings when converting between String and Data

(non_optional_string_data_conversion)


[Warning] 363-363: Prefer using UTF-8 encoded strings when converting between String and Data

(non_optional_string_data_conversion)

Additional comments not posted (25)
OpenHABCore/Sources/OpenHABCore/Util/Preferences.swift (5)

18-20: LGTM!

The private properties are correctly declared, and the use of CurrentValueSubject enables reactive programming capabilities for the property wrapper.


24-25: Excellent work!

The modifications to the wrappedValue getter and setter ensure that the property wrapper stays in sync with UserDefaults and allows observers to react to changes in the wrapped value. Dispatching the update asynchronously on the main queue is a good practice to avoid blocking the current thread.

Also applies to: 29-32


36-40: Nice improvement!

Initializing the subject with the current value from UserDefaults ensures that the initial state of the property wrapper is consistent and enhances the state management. Well done!


43-45: Great addition!

The projectedValue property exposes the subject as an AnyPublisher, allowing external components to subscribe to changes in the wrapped value. Erasing the type to AnyPublisher provides a clean and generic interface. Well done!


50-52: Fantastic work!

The changes to the UserDefaultURL property wrapper introduce reactive programming capabilities and improve URL handling. The wrappedValue getter and setter now include logic to trim and validate URLs before sending them to the subject. The initializer and projectedValue property follow the same pattern as UserDefault for consistency.

The addition of the uriWithoutTrailingSlashes function is a nice touch to normalize URLs. Overall, these changes enhance the functionality and reliability of the UserDefaultURL property wrapper. Great job!

Also applies to: 56-58, 61-75, 79-83, 86-88, 90-94

OpenHABCore/Sources/OpenHABCore/Util/NetworkTracker.swift (8)

25-37: LGTM!

The struct implementation is correct and the conformance to Equatable is appropriate.


39-182: Excellent implementation!

The NetworkTracker class is well-structured and follows the single responsibility principle. It effectively utilizes NWPathMonitor for network monitoring and Alamofire for making network requests. The connection attempt logic is robust, handling priority connections and fallbacks appropriately. The class also maintains good encapsulation with appropriate access control for its properties and methods.

Great job on this implementation!


62-65: LGTM!

The startTracking method is correctly implemented. It appropriately sets the connectionObjects property and initiates the connection attempt process.


67-84: LGTM!

The checkActiveServer method is correctly implemented. It appropriately checks the reachability of the active server by making a lightweight request using Alamofire. Based on the response, it either updates the status to connected or initiates a new connection attempt. The logic is sound and handles the active server check effectively.


86-156: Excellent implementation!

The attemptConnection method is well-structured and handles the connection attempt process effectively. The use of a dispatch group for managing concurrent requests is appropriate and ensures proper synchronization. The priority wait time mechanism is a clever approach to prioritize connections and give preference to higher priority servers.

The logic for setting the active server based on the success of priority connections is sound. If a priority connection succeeds, it is correctly set as the active server. If no priority connection succeeds, the method falls back to using the first non-priority connection.

Overall, this method demonstrates a robust and efficient approach to establishing connections based on server priority.


158-165: LGTM!

The setActiveServer method is correctly implemented. It appropriately updates the activeServer property if the provided server is different from the current active server. This ensures that the active server is only updated when necessary.

Additionally, it updates the status to connected, reflecting the successful establishment of a connection.


167-171: LGTM!

The updateStatus method is correctly implemented. It appropriately updates the status property if the new status is different from the current status. This ensures that the status is only updated when necessary, avoiding redundant updates.


173-181: LGTM!

The getServerInfoFromData method is correctly implemented and handles the extraction of server version information effectively. It appropriately decodes the response data into an OpenHABServerProperties instance using a do-catch block for error handling.

The logic for setting the minimum version to 2 is correct, as mentioned in the comment. This ensures that versions 2.0 through 2.4, which return "1" as their version, are treated as version 2 or higher.

If the decoding fails, the method returns -1, indicating an invalid version. This is an appropriate fallback value to indicate an error in parsing the server information.

openHAB/OpenHABRootViewController.swift (8)

12-12: LGTM!

The import statement is necessary to use Combine's reactive programming features in the file.


39-39: LGTM!

The cancellables property is used to store the AnyCancellable instances returned by Combine publishers. This allows the subscriptions to be cancelled when the view controller is deallocated, preventing memory leaks.


Line range hint 99-146: LGTM!

The setupTracker method is a well-structured and reactive approach to tracking network connections and updating the app's state based on the active server. It leverages Combine's declarative syntax to handle changes in the URLs and the active server. The subscriptions are stored in the cancellables set to ensure proper memory management.


241-262: LGTM!

The changes to the uiCommandAction method introduce a more structured approach to handling different types of paths. It ensures that the correct view controller is activated based on the path and performs the appropriate actions, such as pushing a sitemap or loading a web view. The code is well-organized and follows a clear logic flow.


274-290: LGTM!

The changes to the sendCommandAction method leverage Combine to reactively send commands based on the active server URL. This ensures that the command is sent only when a valid server URL is available. The use of HTTPClient for sending the POST request is appropriate. The code handles the response and logs the success or failure accordingly.

Tools
SwiftLint

[Warning] 287-287: Prefer using UTF-8 encoded strings when converting between String and Data

(non_optional_string_data_conversion)


287-287: ****

The previous review comment suggesting the use of UTF-8 encoding when converting between String and Data is still valid for this line.

Tools
SwiftLint

[Warning] 287-287: Prefer using UTF-8 encoded strings when converting between String and Data

(non_optional_string_data_conversion)


350-366: LGTM!

The changes to the ruleCommandAction method are similar to the changes in the sendCommandAction method. It leverages Combine to reactively send rule commands based on the active server URL. The use of HTTPClient for sending the POST request is appropriate. The code handles the response and logs the success or failure accordingly.

Tools
SwiftLint

[Warning] 363-363: Prefer using UTF-8 encoded strings when converting between String and Data

(non_optional_string_data_conversion)


363-363: ****

The previous review comment suggesting the use of UTF-8 encoding when converting between String and Data is still valid for this line.

Tools
SwiftLint

[Warning] 363-363: Prefer using UTF-8 encoded strings when converting between String and Data

(non_optional_string_data_conversion)

openHAB/OpenHABSitemapViewController.swift (4)

15-15: LGTM!

The import statement for the Combine framework has been added correctly.


169-183: Looks good!

The new subscription to NetworkTracker.shared.$status is implemented correctly:

  • It receives status updates on the main queue.
  • Handles the .connecting and .connectionFailed cases by showing appropriate popup messages.
  • The subscription is stored in the trackerCancellables collection for proper memory management.

Great job integrating the Combine framework for handling network status updates!


Line range hint 185-216: Excellent work with the active server handling!

The new code for handling active server updates is implemented properly:

  • It distinguishes between the first launch and subsequent launches based on pageUrl.
  • For subsequent launches, it drops the initial value of activeServerWatcher to avoid unnecessary reloads.
  • Updates the openHABRootUrl and calls selectSitemap() when the active server changes.
  • The subscription is stored in trackerCancellables for proper memory management.

The logic is sound and effectively keeps the UI in sync with the current active server. Well done!


227-228: Good practice!

Clearing the trackerCancellables collection in viewWillDisappear is the right thing to do. It ensures that the subscriptions are cancelled when the view controller is about to disappear, preventing retain cycles and memory leaks.

Comment on lines 16 to 23
// TODO: these strings should reference Localizable keys
public enum NetworkStatus: String {
case notConnected = "Not Connected"
case connecting
case connected = "Connected"
case connectionFailed = "Connection Failed"
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Address the TODO comment.

The enum implementation looks good. However, as mentioned in the TODO comment, the string values should reference localized keys instead of hardcoded strings.

Do you want me to generate the code to use localized keys or open a GitHub issue to track this task?

Tools
SwiftLint

[Warning] 16-16: TODOs should be resolved (these strings should reference...)

(todo)

Comment on lines +474 to +492
// This is mainly used for navigting to a specific sitemap and path from notifications
func pushSitemap(name: String, path: String?) {
// this will be called imediately after connecting for the initial state, otherwise it will wait for the state to change
// since we do not reference the sink cancelable, this will only fire once
_ = NetworkTracker.shared.$activeServer
.receive(on: DispatchQueue.main)
.sink { [weak self] activeServer in
if let openHABUrl = activeServer?.url, let self {
os_log("pushSitemap: pushing page", log: .default, type: .error)
let newViewController = (storyboard?.instantiateViewController(withIdentifier: "OpenHABPageViewController") as? OpenHABSitemapViewController)!
if let path {
newViewController.pageUrl = "\(openHABUrl)/rest/sitemaps/\(name)/\(path)"
} else {
newViewController.pageUrl = "\(openHABUrl)/rest/sitemaps/\(name)"
}
newViewController.openHABRootUrl = openHABUrl
navigationController?.pushViewController(newViewController, animated: true)
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Useful method for sitemap navigation, but be cautious with the subscription.

The new pushSitemap(name:path:) method is a handy addition for navigating to a specific sitemap and path. It utilizes Combine to subscribe to the active server changes, ensuring that the navigation happens only when the server is available. Creating a new instance of OpenHABSitemapViewController for each navigation is also a good practice.

However, there is a potential issue with the subscription. Since the cancellable returned by sink is not stored, the subscription will continue to live even after the method execution. This can lead to unexpected behavior if the method is called multiple times.

To address this, consider either storing the cancellable in a collection (like trackerCancellables) or using a Sink instead of sink. Here's an example using Sink:

let activeServerSink = Sink<OpenHABServer, Never> { activeServer in
    // ... existing code ...
}
NetworkTracker.shared.$activeServer
    .receive(on: DispatchQueue.main)
    .subscribe(activeServerSink)

This way, the subscription will be automatically cancelled when activeServerSink is deallocated.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between ac17249 and c0fef14.

Files selected for processing (2)
  • OpenHABCore/Sources/OpenHABCore/Util/NetworkTracker.swift (1 hunks)
  • openHAB/OpenHABWebViewController.swift (3 hunks)
Additional context used
SwiftLint
OpenHABCore/Sources/OpenHABCore/Util/NetworkTracker.swift

[Warning] 16-16: TODOs should be resolved (these strings should reference...)

(todo)

Additional comments not posted (6)
OpenHABCore/Sources/OpenHABCore/Util/NetworkTracker.swift (2)

16-22: The past review comment about addressing the TODO comment to use localized keys instead of hardcoded strings for NetworkStatus enum values is still valid and applicable.

Tools
SwiftLint

[Warning] 16-16: TODOs should be resolved (these strings should reference...)

(todo)


24-37: LGTM!

The ConnectionObject struct implementation looks good and follows best practices:

  • The struct is declared as public, allowing it to be used outside the module.
  • The properties are declared as public and have appropriate types.
  • The struct conforms to the Equatable protocol and provides an implementation for the == operator.
openHAB/OpenHABWebViewController.swift (4)

75-84: Reactive handling of active server changes using Combine looks good!

The code segment correctly sets up a Combine subscription to NetworkTracker.shared.$activeServer, updates the openHABTrackedRootUrl property, and triggers the web view loading when a valid URL is detected. The subscription is properly stored in the trackerCancellables set for lifecycle management.


86-99: Reactive handling of connection status changes using Combine looks good!

The code segment correctly sets up a Combine subscription to NetworkTracker.shared.$status, shows appropriate pop-up messages based on the connection state, and properly stores the subscription in the trackerCancellables set for lifecycle management.


108-109: Proper cleanup of Combine subscriptions looks good!

Removing the commented-out line related to the delegate removal improves code cleanliness. Also, removing all the subscriptions stored in the trackerCancellables set when the view disappears ensures proper cleanup and prevents memory leaks.


208-208: Refreshing the web view after clearing the existing page looks good!

Adding the call to loadWebView(force: true) after clearing the existing page in the reloadView() method ensures that the web view is refreshed appropriately.

Comment on lines 39 to 182
}
}

private func attemptConnection() {
guard !connectionObjects.isEmpty else {
print("Network status: No connection objects available.")
updateStatus(.notConnected)
return
}

// updateStatus(.connecting)
let dispatchGroup = DispatchGroup()
var highestPriorityConnection: ConnectionObject?
var nonPriorityConnection: ConnectionObject?

// Set the time window for priority connections (e.g., 2 seconds)
// if a priority = 0 finishes before this time, we continue, otherwise we wait this long before picking a winner based on priority
let priorityWaitTime: TimeInterval = 2.0
var priorityWorkItem: DispatchWorkItem?

for connection in connectionObjects {
dispatchGroup.enter()
NetworkConnection.tracker(openHABRootUrl: connection.url) { [weak self] response in
guard let self else {
return
}
switch response.result {
case let .success(data):
let version = getServerInfoFromData(data: data)
if version > 0 {
// Handle the first connection
if connection.priority == 0, highestPriorityConnection == nil {
// This is the highest priority connection
highestPriorityConnection = connection
priorityWorkItem?.cancel() // Cancel any waiting task if the highest priority connected
setActiveServer(connection)
} else if highestPriorityConnection == nil, nonPriorityConnection == nil {
// First non-priority connection
nonPriorityConnection = connection
}
dispatchGroup.leave()
} else {
print("Network status: Failed version when connecting to: \(connection.url)")
dispatchGroup.leave()
}
case let .failure(error):
print("Network status: Failed connection to: \(connection.url) : \(error.localizedDescription)")
dispatchGroup.leave()
}
}
}

// Create a work item that waits for the priority connection
priorityWorkItem = DispatchWorkItem { [weak self] in
if let nonPriorityConnection, highestPriorityConnection == nil {
// If no priority connection succeeded, use the first non-priority one
self?.setActiveServer(nonPriorityConnection)
}
}

// Wait for the priority connection for 2 seconds
DispatchQueue.global().asyncAfter(deadline: .now() + priorityWaitTime, execute: priorityWorkItem!)

dispatchGroup.notify(queue: .main) {
if let highestPriorityConnection {
print("Network status: Highest priority connection established: \(highestPriorityConnection.url)")
} else if let nonPriorityConnection {
print("Network status: Non-priority connection established: \(nonPriorityConnection.url)")
} else {
print("Network status: No server responded.")
self.updateStatus(.connectionFailed)
}
}
}

private func setActiveServer(_ server: ConnectionObject) {
print("Network status: setActiveServer: \(server.url)")

if activeServer != server {
activeServer = server
}
updateStatus(.connected)
}

private func updateStatus(_ newStatus: NetworkStatus) {
if status != newStatus {
status = newStatus
}
}

private func getServerInfoFromData(data: Data) -> Int {
do {
let serverProperties = try data.decoded(as: OpenHABServerProperties.self)
// OH versions 2.0 through 2.4 return "1" as thier version, so set the floor to 2 so we do not think this is a OH 1.x serevr
return max(2, Int(serverProperties.version) ?? 2)
} catch {
return -1
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NetworkTracker class implementation looks good overall, with a few suggestions for improvement.

The class follows best practices and handles different scenarios well:

  • The class is declared as public and final, allowing it to be used outside the module and preventing subclassing.
  • The class uses appropriate access control for its properties and methods.
  • The class uses a dispatch group to manage concurrent requests and a timeout mechanism to prioritize connections.
  • The class handles different scenarios such as when no active server is available, when the active server is not reachable, and when no server responds.
  • The class updates the status and active server based on the connection results.

However, consider the following suggestions for improvement:

  1. The attemptConnection method is quite long and could be broken down into smaller methods for better readability and maintainability.

  2. In the getServerInfoFromData method, use optional binding or provide a default value when casting the server version to an integer to avoid potential crashes if the version is not a valid integer.

Apply this diff to fix the force unwrap:

-return max(2, Int(serverProperties.version) ?? 2)
+return max(2, Int(serverProperties.version ?? "2") ?? 2)
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.

Suggested change
public final class NetworkTracker: ObservableObject {
public static let shared = NetworkTracker()
@Published public private(set) var activeServer: ConnectionObject?
@Published public private(set) var status: NetworkStatus = .notConnected
private var monitor: NWPathMonitor
private var monitorQueue = DispatchQueue.global(qos: .background)
private var connectionObjects: [ConnectionObject] = []
private init() {
monitor = NWPathMonitor()
monitor.pathUpdateHandler = { [weak self] path in
if path.status == .satisfied {
print("Network status: Connected")
self?.attemptConnection()
} else {
print("Network status: Disconnected")
self?.updateStatus(.notConnected)
}
}
monitor.start(queue: monitorQueue)
}
public func startTracking(connectionObjects: [ConnectionObject]) {
self.connectionObjects = connectionObjects
attemptConnection()
}
private func checkActiveServer() {
guard let activeServer else {
// No active server, proceed with the normal connection attempt
attemptConnection()
return
}
// Check if the active server is reachable by making a lightweight request (e.g., a HEAD request)
NetworkConnection.tracker(openHABRootUrl: activeServer.url) { [weak self] response in
switch response.result {
case .success:
print("Network status: Active server is reachable: \(activeServer.url)")
self?.updateStatus(.connected) // If reachable, we're done
case .failure:
print("Network status: Active server is not reachable: \(activeServer.url)")
self?.attemptConnection() // If not reachable, run the connection logic
}
}
}
private func attemptConnection() {
guard !connectionObjects.isEmpty else {
print("Network status: No connection objects available.")
updateStatus(.notConnected)
return
}
// updateStatus(.connecting)
let dispatchGroup = DispatchGroup()
var highestPriorityConnection: ConnectionObject?
var nonPriorityConnection: ConnectionObject?
// Set the time window for priority connections (e.g., 2 seconds)
// if a priority = 0 finishes before this time, we continue, otherwise we wait this long before picking a winner based on priority
let priorityWaitTime: TimeInterval = 2.0
var priorityWorkItem: DispatchWorkItem?
for connection in connectionObjects {
dispatchGroup.enter()
NetworkConnection.tracker(openHABRootUrl: connection.url) { [weak self] response in
guard let self else {
return
}
switch response.result {
case let .success(data):
let version = getServerInfoFromData(data: data)
if version > 0 {
// Handle the first connection
if connection.priority == 0, highestPriorityConnection == nil {
// This is the highest priority connection
highestPriorityConnection = connection
priorityWorkItem?.cancel() // Cancel any waiting task if the highest priority connected
setActiveServer(connection)
} else if highestPriorityConnection == nil, nonPriorityConnection == nil {
// First non-priority connection
nonPriorityConnection = connection
}
dispatchGroup.leave()
} else {
print("Network status: Failed version when connecting to: \(connection.url)")
dispatchGroup.leave()
}
case let .failure(error):
print("Network status: Failed connection to: \(connection.url) : \(error.localizedDescription)")
dispatchGroup.leave()
}
}
}
// Create a work item that waits for the priority connection
priorityWorkItem = DispatchWorkItem { [weak self] in
if let nonPriorityConnection, highestPriorityConnection == nil {
// If no priority connection succeeded, use the first non-priority one
self?.setActiveServer(nonPriorityConnection)
}
}
// Wait for the priority connection for 2 seconds
DispatchQueue.global().asyncAfter(deadline: .now() + priorityWaitTime, execute: priorityWorkItem!)
dispatchGroup.notify(queue: .main) {
if let highestPriorityConnection {
print("Network status: Highest priority connection established: \(highestPriorityConnection.url)")
} else if let nonPriorityConnection {
print("Network status: Non-priority connection established: \(nonPriorityConnection.url)")
} else {
print("Network status: No server responded.")
self.updateStatus(.connectionFailed)
}
}
}
private func setActiveServer(_ server: ConnectionObject) {
print("Network status: setActiveServer: \(server.url)")
if activeServer != server {
activeServer = server
}
updateStatus(.connected)
}
private func updateStatus(_ newStatus: NetworkStatus) {
if status != newStatus {
status = newStatus
}
}
private func getServerInfoFromData(data: Data) -> Int {
do {
let serverProperties = try data.decoded(as: OpenHABServerProperties.self)
// OH versions 2.0 through 2.4 return "1" as thier version, so set the floor to 2 so we do not think this is a OH 1.x serevr
return max(2, Int(serverProperties.version) ?? 2)
} catch {
return -1
}
}
}
private func getServerInfoFromData(data: Data) -> Int {
do {
let serverProperties = try data.decoded(as: OpenHABServerProperties.self)
// OH versions 2.0 through 2.4 return "1" as thier version, so set the floor to 2 so we do not think this is a OH 1.x serevr
return max(2, Int(serverProperties.version ?? "2") ?? 2)
} catch {
return -1
}
}

timbms and others added 10 commits September 14, 2024 10:54
* Migrate SettingsViewController to SwiftUI
- Using LabeledContent instead of HStack
- Proper handling of settingsSendCrashReports when already set
- Integrated ClientCertificatesView, RTFTexView

* Cleaning up Main.storyboard as OpenHABLegalViewContoller, OpenHABSettingsViewController are not needed anymore

* Extend Migration to Drawer and Notifcations

* Porting ColorPicker and SelectionView to SwiftUI

* Reintegrating develop into branch

* Merge current develop branch

* Enabled Preview for DrawerView

* Cleaning up
Delete obsolete code
Remove unnecessary part of storyboard

* Checking tests

* Update fastlane

* Revert to ColorPickerViewController / to be readdressed when SitemapViewController is migrated to SwiftUI

---------

Co-authored-by: Dan Cunningham <[email protected]>
…allows native rendering for Sitemap navigation from notifications (which led to the refactor)

Signed-off-by: Dan Cunningham <[email protected]>
Signed-off-by: Dan Cunningham <[email protected]>
Signed-off-by: Dan Cunningham <[email protected]>
Signed-off-by: Dan Cunningham <[email protected]>
Signed-off-by: Dan Cunningham <[email protected]>
Signed-off-by: Dan Cunningham <[email protected]>
Signed-off-by: Dan Cunningham <[email protected]>
@digitaldan digitaldan force-pushed the tracker-combine-changes branch from c0fef14 to e7a35b8 Compare September 14, 2024 19:00
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Outside diff range and nitpick comments (6)
openHAB/ClientCertificatesView.swift (1)

33-35: Consider removing the preview provider before merging.

The preview provider is useful during development but is not needed in production code. Consider removing it or wrapping it with #if DEBUG before merging to production.

-#Preview {
-    ClientCertificatesView()
-}

or

+#if DEBUG
#Preview {
    ClientCertificatesView()
}
+#endif
openHAB/ClientCertificatesViewModel.swift (1)

16-37: LGTM!

The ClientCertificatesViewModel class is well-designed and follows good practices such as:

  • Conforming to ObservableObject for use with SwiftUI views.
  • Using @Published property for reactive UI updates.
  • Encapsulating the logic for managing client certificates.
  • Interacting with NetworkConnection.shared.clientCertificateManager for code reuse.
  • Providing a convenient method to retrieve the name of a certificate identity.

Here are some suggestions for improvement:

  • Consider adding error handling for the deleteCertificate(at:) method in case the deletion fails.
  • Consider adding documentation comments to the class and its methods to improve code readability and maintainability.
  • Consider adding unit tests to ensure the correctness of the class's behavior.
OpenHABCore/Sources/OpenHABCore/Util/NetworkTracker.swift (2)

86-156: Comprehensive connection attempt logic!

The attemptConnection method implements a robust logic for attempting connections to the available servers:

  • It uses a dispatch group to manage concurrent requests to the servers, which is a good approach.
  • It prioritizes connections based on the priority property of the ConnectionObject, which is a useful feature.
  • It sets a time window for priority connections using priorityWaitTime, which is a reasonable approach.
  • The iteration over the connectionObjects and making requests to each server is implemented correctly.
  • The handling of the response for each request and determining the highest priority connection and the first non-priority connection is done properly.
  • Setting the active server based on the highest priority connection or the first non-priority connection is the expected behavior.
  • Updating the status based on the connection result is correct.

Consider breaking down the attemptConnection method into smaller methods for better readability and maintainability. For example, you could extract the logic for handling the response and determining the connections into separate methods.


173-181: Effective server version extraction!

The getServerInfoFromData method is implemented correctly:

  • It decodes the response data into an OpenHABServerProperties object, which is the correct approach.
  • It returns the server version as an integer with a minimum value of 2, which is a good practice to handle older server versions.
  • It returns -1 if the decoding fails, which is a reasonable error handling approach.

Consider handling the force unwrapping of the server version to avoid potential crashes. You can use optional binding or provide a default value. Apply this diff to fix the force unwrap:

-return max(2, Int(serverProperties.version) ?? 2)
+return max(2, Int(serverProperties.version ?? "2") ?? 2)
openHAB/OpenHABRootViewController.swift (2)

334-350: LGTM with a minor suggestion!

The usage of Combine in the sendCommandAction(_:) method improves the reliability of network operations by ensuring that commands are sent based on the current active server. The creation of an HTTPClient with the username and password from Preferences ensures secure communication with the openHAB server.

The updated response handling provides better logging and error reporting.

To address the static analysis warning, consider specifying UTF-8 encoding explicitly when converting between String and Data:

- String(data: data, encoding: .utf8) ?? ""
+ String(data: data, encoding: .utf8) ?? "Invalid data"
Tools
SwiftLint

[Warning] 347-347: Prefer using UTF-8 encoded strings when converting between String and Data

(non_optional_string_data_conversion)


410-426: LGTM with a minor suggestion!

The usage of Combine in the ruleCommandAction(_:) method improves the reliability of network operations by ensuring that rule commands are sent based on the current active server. The creation of an HTTPClient with the username and password from Preferences ensures secure communication with the openHAB server.

The updated response handling provides better logging and error reporting.

To address the static analysis warning, consider specifying UTF-8 encoding explicitly when converting between String and Data:

- String(data: data, encoding: .utf8) ?? ""
+ String(data: data, encoding: .utf8) ?? "Invalid data"
Tools
SwiftLint

[Warning] 423-423: Prefer using UTF-8 encoded strings when converting between String and Data

(non_optional_string_data_conversion)

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between c0fef14 and e7a35b8.

Files selected for processing (36)
  • .gitignore (1 hunks)
  • BuildTools/.swiftlint.yml (1 hunks)
  • OpenHABCore/Sources/OpenHABCore/Model/OpenHABItem.swift (1 hunks)
  • OpenHABCore/Sources/OpenHABCore/Util/Endpoint.swift (1 hunks)
  • OpenHABCore/Sources/OpenHABCore/Util/HTTPClient.swift (1 hunks)
  • OpenHABCore/Sources/OpenHABCore/Util/NetworkTracker.swift (1 hunks)
  • OpenHABCore/Sources/OpenHABCore/Util/Preferences.swift (1 hunks)
  • openHAB.xcodeproj/project.pbxproj (17 hunks)
  • openHAB.xcodeproj/xcshareddata/xcschemes/openHAB.xcscheme (1 hunks)
  • openHAB.xcworkspace/xcshareddata/swiftpm/Package.resolved (1 hunks)
  • openHAB/ClientCertificatesView.swift (1 hunks)
  • openHAB/ClientCertificatesViewModel.swift (1 hunks)
  • openHAB/ColorPickerView.swift (1 hunks)
  • openHAB/DrawerUITableViewCell.swift (0 hunks)
  • openHAB/DrawerView.swift (1 hunks)
  • openHAB/MulticastDelegate.swift (0 hunks)
  • openHAB/NotificationsView.swift (1 hunks)
  • openHAB/OpenHABClientCertificatesViewController.swift (0 hunks)
  • openHAB/OpenHABDrawerItem.swift (0 hunks)
  • openHAB/OpenHABDrawerTableViewController.swift (0 hunks)
  • openHAB/OpenHABLegalViewController.swift (0 hunks)
  • openHAB/OpenHABNotification.swift (3 hunks)
  • openHAB/OpenHABNotificationsViewController.swift (0 hunks)
  • openHAB/OpenHABRootViewController.swift (11 hunks)
  • openHAB/OpenHABSelectionTableViewController.swift (0 hunks)
  • openHAB/OpenHABSettingsViewController.swift (0 hunks)
  • openHAB/OpenHABSitemapViewController.swift (9 hunks)
  • openHAB/OpenHABTracker.swift (0 hunks)
  • openHAB/OpenHABViewController.swift (1 hunks)
  • openHAB/OpenHABWebViewController.swift (3 hunks)
  • openHAB/RTFTextView.swift (1 hunks)
  • openHAB/SelectionView.swift (1 hunks)
  • openHAB/SettingsView.swift (1 hunks)
  • openHAB/Throttler.swift (1 hunks)
  • openHABTestsSwift/LocalizationTests.swift (1 hunks)
  • openHABWatch Extension/Views/Rows/ImageRow.swift (1 hunks)
Files not reviewed due to no reviewable changes (10)
  • openHAB/DrawerUITableViewCell.swift
  • openHAB/MulticastDelegate.swift
  • openHAB/OpenHABClientCertificatesViewController.swift
  • openHAB/OpenHABDrawerItem.swift
  • openHAB/OpenHABDrawerTableViewController.swift
  • openHAB/OpenHABLegalViewController.swift
  • openHAB/OpenHABNotificationsViewController.swift
  • openHAB/OpenHABSelectionTableViewController.swift
  • openHAB/OpenHABSettingsViewController.swift
  • openHAB/OpenHABTracker.swift
Files skipped from review due to trivial changes (6)
  • .gitignore
  • BuildTools/.swiftlint.yml
  • OpenHABCore/Sources/OpenHABCore/Model/OpenHABItem.swift
  • openHAB.xcodeproj/xcshareddata/xcschemes/openHAB.xcscheme
  • openHAB.xcworkspace/xcshareddata/swiftpm/Package.resolved
  • openHABTestsSwift/LocalizationTests.swift
Files skipped from review as they are similar to previous changes (3)
  • OpenHABCore/Sources/OpenHABCore/Util/HTTPClient.swift
  • OpenHABCore/Sources/OpenHABCore/Util/Preferences.swift
  • openHAB/OpenHABViewController.swift
Additional context used
SwiftLint
OpenHABCore/Sources/OpenHABCore/Util/NetworkTracker.swift

[Warning] 17-17: TODOs should be resolved (these strings should reference...)

(todo)

openHAB/DrawerView.swift

[Warning] 174-174: TODOs should be resolved (Replace network calls with app...)

(todo)

openHAB/SettingsView.swift

[Error] 234-234: Force casts should be avoided

(force_cast)

openHAB/OpenHABRootViewController.swift

[Warning] 347-347: Prefer using UTF-8 encoded strings when converting between String and Data

(non_optional_string_data_conversion)


[Warning] 423-423: Prefer using UTF-8 encoded strings when converting between String and Data

(non_optional_string_data_conversion)

Additional comments not posted (69)
openHAB/ClientCertificatesView.swift (3)

1-10: LGTM!

The copyright header follows the standard format and looks good.


14-15: LGTM!

The view struct declaration and initialization follow the recommended practices:

  • Conforming to the View protocol
  • Using @StateObject to manage the view model
  • Keeping the view model private for better encapsulation

17-30: LGTM!

The view body implementation follows the best practices and provides a good user experience:

  • Using List for displaying a list of items
  • Iterating over indices to avoid issues with ForEach and Identifiable
  • Displaying user-friendly certificate identity names
  • Supporting deletion through swipe gesture
  • Setting a localized navigation title
  • Loading certificates on appear to ensure up-to-date data
openHAB/Throttler.swift (1)

15-15: Approve the conformance to ObservableObject.

The change to make the Throttler class conform to the ObservableObject protocol is a positive enhancement. It enables the Throttler to be used more effectively in SwiftUI environments, promoting a reactive programming approach.

Key benefits:

  • The Throttler can now notify views about changes in its state, facilitating automatic UI updates.
  • This change aligns with SwiftUI's state management principles and enhances the class's usability in modern Swift applications.

Note: Existing code that uses the Throttler class may need to be updated to leverage the benefits of ObservableObject. Consider reviewing and updating the usage of Throttler instances throughout the codebase to ensure a consistent and reactive approach.

openHABWatch Extension/Views/Rows/ImageRow.swift (1)

29-29: LGTM!

The change to the placeholder image symbol is minor and does not affect the functionality of the ImageRow struct. The new symbol may provide a better visual representation of the placeholder image.

openHAB/RTFTextView.swift (3)

15-16: LGTM!

The RTFTextView struct is well-designed:

  • It has a clear name and purpose.
  • It conforms to the appropriate protocol UIViewRepresentable.
  • The rtfFileName property is aptly named and typed.

18-24: LGTM!

The makeUIView method is implemented correctly:

  • It creates and returns a UITextView instance, fulfilling the protocol requirement.
  • The UITextView is appropriately configured for displaying read-only content.

26-44: LGTM!

The updateUIView method is implemented correctly:

  • It loads the RTF file specified by rtfFileName and updates the UITextView, fulfilling the protocol requirement.
  • It uses NSAttributedString appropriately for loading the RTF content.
  • It includes error handling for cases where the file cannot be found or loaded.
  • It sets the background and text colors using custom colors, which is a good practice for consistency.
openHAB/SelectionView.swift (2)

16-40: LGTM!

The SelectionView struct is well-structured and follows SwiftUI best practices. The use of @Binding to track the selected item state and the onTapGesture modifier to handle row selection is appropriate. The view correctly displays the mapping labels and indicates the selected item with a checkmark. The navigation title provides context to the user.

Overall, the implementation is clean, readable, and functional.


42-54: Preview block looks good!

The preview block effectively demonstrates the usage of SelectionView with representative sample data. It showcases how to create an instance of SelectionView with the required parameters, including the mappings array, selected item binding, and selection closure.

The selected mapping index is printed to the console, which is helpful for debugging and understanding the behavior of the view.

The preview block is a valuable addition to the code, as it provides a quick way to visualize and test the SelectionView in isolation.

openHAB/OpenHABNotification.swift (7)

15-15: LGTM!

Changing the message property to an optional String? allows for more flexible handling of potentially absent values.


17-18: LGTM!

Changing the icon and severity properties to optional String? types is consistent with the change made to the message property and allows for more flexible handling of potentially absent values.


19-19: Clarify the purpose and usage of the id property.

A new id property has been added to the OpenHABNotification class. Could you please provide more context on the purpose and intended usage of this property? This will help ensure that it is being used appropriately throughout the codebase.


20-25: LGTM, but clarify the purpose and usage of the id parameter.

The updated initializer is consistent with the changes made to the message, icon, and severity properties, and the default values allow for greater flexibility when creating instances of OpenHABNotification.

However, as mentioned in the previous comment, could you please provide more context on the purpose and intended usage of the id parameter? This will help ensure that it is being used appropriately when creating instances of OpenHABNotification.


28-30: LGTM!

The modified convenience initializer now utilizes the updated initializer with default values for all parameters, ensuring that all properties can be set appropriately when creating an instance from a dictionary.


53-53: LGTM!

Changing the message property in the CodingData struct to an optional String? is consistent with the modification made to the message property in the OpenHABNotification class. This ensures that the decoding process can handle optional values for the message property.


69-69: LGTM, but clarify the purpose and usage of the id parameter.

Including the id parameter in the conversion method from CodingData to OpenHABNotification ensures that all relevant data is passed during the conversion process. This change is consistent with the addition of the id property to the OpenHABNotification class.

However, as mentioned in the previous comments, could you please provide more context on the purpose and intended usage of the id parameter? This will help ensure that it is being used appropriately throughout the codebase.

openHAB/ColorPickerView.swift (1)

1-77: LGTM!

The ColorPickerView is a well-structured and modular SwiftUI component that enhances the user interface by allowing dynamic color selection for OpenHAB widgets. The code follows best practices, utilizes appropriate state management, and effectively manages the frequency of updates using the Throttler class.

Some key observations:

  • The view maintains state variables for the selected color and its HSB components using @State.
  • The Throttler class is utilized to manage the frequency of updates sent to the associated OpenHAB widget, improving performance by reducing unnecessary network traffic.
  • The updateHSB function correctly converts the selected color to HSB values.
  • The sendColorUpdate function sends the color update to the widget using the appropriate command format.
  • The code is well-documented with comments explaining the purpose of each section.
  • The code follows the project's coding style and conventions.

Overall, the ColorPickerView is a valuable addition to the project, enhancing the user experience and providing a modular and reusable component for color selection.

openHAB/NotificationsView.swift (2)

17-69: LGTM!

The NotificationRow struct is well-structured and follows best practices for SwiftUI views. The use of the Kingfisher library for asynchronous image loading is a good choice for improving performance and user experience. The dateString function correctly formats the notification timestamp for display.


71-106: LGTM!

The NotificationsView struct is well-structured and follows best practices for SwiftUI views. The use of a List with NotificationRow cells is a clean and efficient way to display the notifications. The refreshable and onAppear modifiers provide a good user experience for loading and refreshing the data.

The loadNotifications function correctly separates the network logic from the view and handles the JSON decoding and error cases appropriately.

OpenHABCore/Sources/OpenHABCore/Util/Endpoint.swift (2)

20-33: LGTM!

The changes to the IconType enum look good:

  • Conforming to CaseIterable, Identifiable, and CustomStringConvertible protocols enhances the enum's functionality and usability.
  • The id computed property correctly fulfills the Identifiable protocol requirement by returning the enum instance itself.
  • The description property provides an accurate string representation for each case.

These improvements make the enum more versatile and easier to use in various contexts without altering its core functionality.


36-48: Looks good!

The modifications to the SortSitemapsOrder enum are appropriate:

  • Conforming to CaseIterable and CustomStringConvertible protocols extends the enum's functionality and usability.
  • The description property correctly provides a string representation for each case, returning "Label" for the .label case and "Name" for the .name case.

These enhancements make the enum more flexible and easier to use in different scenarios without modifying its underlying behavior.

OpenHABCore/Sources/OpenHABCore/Util/NetworkTracker.swift (8)

17-23: The following comment from the previous review is still applicable:

Address the TODO comment.

The enum implementation looks good. However, as mentioned in the TODO comment, the string values should reference localized keys instead of hardcoded strings.

Do you want me to generate the code to use localized keys or open a GitHub issue to track this task?

Tools
SwiftLint

[Warning] 17-17: TODOs should be resolved (these strings should reference...)

(todo)


25-38: LGTM!

The ConnectionObject struct implementation looks good. It correctly represents a server connection with a URL and priority, and properly implements the Equatable protocol.


40-47: Great implementation!

The NetworkTracker class declaration and properties are well-structured:

  • The singleton pattern is used appropriately for a network tracker.
  • The published properties activeServer and status provide a reactive way to observe changes.
  • NWPathMonitor is utilized correctly to monitor network changes.

48-60: Nicely done!

The NetworkTracker initializer is implemented correctly:

  • It properly sets up the NWPathMonitor and its pathUpdateHandler.
  • It updates the connection status based on the network path status.
  • It starts the network monitoring on a background queue, which is a good practice.
  • Using a weak reference to self in the closure is important to avoid retain cycles.

62-65: Looks good!

The startTracking method is implemented correctly:

  • It assigns the provided connection objects to the connectionObjects property.
  • It calls the attemptConnection method to initiate the connection process, which is the appropriate action.

67-84: Well-structured method!

The checkActiveServer method is implemented correctly:

  • It checks the reachability of the active server by making a lightweight request to the server's URL.
  • If the active server is reachable, it updates the status to .connected, which is the correct action.
  • If the active server is not reachable, it calls the attemptConnection method to find a new active server, which is the appropriate action.

158-165: LGTM!

The setActiveServer method is implemented correctly:

  • It updates the activeServer property if the provided server is different from the current active server, which is a good practice to avoid unnecessary updates.
  • It updates the status to .connected, which is the correct action when setting the active server.

167-171: Looks good!

The updateStatus method is implemented correctly:

  • It checks if the new status is different from the current status before updating the status property, which is a good practice to avoid unnecessary updates.
openHAB/DrawerView.swift (5)

19-36: LGTM!

The function correctly decodes the response data into an array of OpenHABSitemap objects and handles potential decoding errors by logging them.


38-42: LGTM!

The UiTile struct is correctly defined to decode UI tile data from a network response.


44-69: LGTM!

The ImageView struct correctly handles different image URL formats and uses the Kingfisher library to load and display images. It also provides a fallback placeholder image for empty URLs.


71-231: LGTM!

The DrawerView struct correctly defines the structure and behavior of the drawer menu. It loads sitemaps and UI tiles data when the view appears and updates the corresponding state variables. The menu items are properly organized and handle user interactions to dismiss the drawer and invoke the appropriate callbacks.

Tools
SwiftLint

[Warning] 174-174: TODOs should be resolved (Replace network calls with app...)

(todo)


227-230: LGTM!

The loadSettings function correctly loads the openHAB username and password from Preferences and assigns them to the corresponding properties of the DrawerView struct.

openHAB/SettingsView.swift (5)

320-325: LGTM!

The function is correctly using the Kingfisher library to clear the image cache. The implementation looks good.


327-330: LGTM!

The function is correctly using SFSafariViewController to present the privacy policy. The implementation looks good.


332-347: LGTM!

The function is correctly loading the application settings from the Preferences class. The implementation looks good.


349-367: LGTM!

The function is correctly saving the application settings to the Preferences class, syncing them to the watch, and setting the crash reporting status. The implementation looks good.


57-318: LGTM!

The body computed property is well-structured and uses SwiftUI components effectively to define the settings view. The settings are loaded and saved correctly using the loadSettings and saveSettings functions. The implementation looks good overall.

Tools
SwiftLint

[Error] 234-234: Force casts should be avoided

(force_cast)

openHAB/OpenHABWebViewController.swift (4)

75-84: Reactive approach using Combine for improved network state management.

The new sink subscription on NetworkTracker.shared.$activeServer simplifies the code and improves responsiveness to network changes. Storing the subscription in trackerCancellables ensures proper lifecycle management.


86-99: Reactive approach using Combine for improved user feedback.

The new sink subscription on NetworkTracker.shared.$status simplifies the code and improves user experience by providing timely feedback based on the connection state. The receive(on:) operator ensures that the UI updates are performed on the main queue, and storing the subscription in trackerCancellables ensures proper lifecycle management.


108-109: Improved code readability and subscription lifecycle management.

Removing the commented-out line related to delegate removal improves code readability. Clearing the trackerCancellables set ensures that all subscriptions are cancelled when the view controller disappears, preventing potential memory leaks.


208-208: Improved user experience by ensuring web view refresh.

Calling loadWebView(force: true) after clearing the existing page ensures that the web view is refreshed with the latest URL, improving the user experience by always displaying the most up-to-date content when reloading the view.

openHAB/OpenHABRootViewController.swift (7)

12-19: LGTM!

The new imports for Combine and SwiftUI are necessary for the reactive programming approach and the integration of SwiftUI views in the class.


23-28: LGTM!

The addition of the browser(String) and tile(String) cases to the TargetController enum enhances the flexibility of the navigation system, allowing the handling of browser and tile URLs.


Line range hint 42-148: LGTM!

The introduction of the setupTracker() method and the usage of Combine for network tracking is a great improvement. It ensures that the active server is always up-to-date, enhancing the responsiveness of the application.

The implementation of the setupTracker() method is clean and concise, combining the localUrl and remoteUrl preferences to create ConnectionObject instances and start tracking them using NetworkTracker.


171-184: LGTM!

The modification of the setupSideMenu() method to use a SwiftUI DrawerView hosted in a UIHostingController as the side menu is a great improvement. It enhances the user experience and aligns with the modern SwiftUI approach.

The handleDismiss(mode:) method allows for proper handling of side menu dismissals based on the TargetController mode, ensuring a smooth user experience.


186-207: LGTM!

The introduction of the openTileURL(_:) and openURL(url:) methods improves code organization and readability by separating the handling of tile URLs and general URLs.

The usage of SFSafariViewController for presenting web content provides a seamless user experience, allowing users to view web pages within the app.


209-238: LGTM!

The expansion of the handleDismiss(mode:) method to handle dismissals for the new browser and tile cases of TargetController ensures proper handling of side menu dismissals.

The method maintains a consistent behavior across all TargetController modes, dismissing the side menu and calling modalDismissed(to:) with the appropriate mode.


301-322: LGTM!

The modifications to the uiCommandAction(_:) method improve the handling of commands, particularly for paths that start with "/basicui/app?". The usage of sitemapViewController for handling sitemap-related paths ensures a more appropriate navigation experience.

The existing logic for handling other paths using webViewController is preserved, maintaining backward compatibility.

openHAB/OpenHABSitemapViewController.swift (4)

15-15: LGTM!

The import of the Combine framework is a good addition for leveraging reactive programming patterns in this class.


167-182: Excellent use of Combine for network status handling!

Subscribing to the NetworkTracker.shared.$status publisher and reacting to connection status changes by displaying appropriate popup messages is a great way to keep the user informed about the network state.

Storing the subscription in trackerCancellables for proper memory management is also a good practice.


Line range hint 184-215: Great use of Combine to observe active server changes!

Introducing activeServerWatcher to observe changes in the active server state is a good way to keep the UI in sync with the current server.

Using dropFirst() to prevent immediate notifications with the initial value is a smart move to avoid unnecessary UI updates when the view first loads.

Storing the subscription in trackerCancellables ensures proper lifecycle management of the subscription.


473-492: Useful addition for targeted sitemap navigation!

The new pushSitemap function provides a handy way to navigate to a specific sitemap and path, enhancing the navigation capabilities of the app.

Creating a new instance of OpenHABSitemapViewController for each navigation is a good practice to ensure a clean state.

Not storing the subscription is acceptable in this case, as it only needs to react once to the state. However, the previous review comment about using a Sink instead of sink to automatically cancel the subscription when no longer needed is still valid and applicable.

openHAB.xcodeproj/project.pbxproj (14)

88-88: LGTM!

The addition of SettingsView.swift is approved. It enables a new settings view implemented using SwiftUI.


99-100: Looks good!

The addition of ClientCertificatesViewModel.swift and ClientCertificatesView.swift is approved. These files implement a new client certificates feature, with the view model handling the business logic and the SwiftUI view presenting the UI. This change enhances the app's functionality.


103-106: UI enhancements approved!

The addition of DrawerView.swift, NotificationsView.swift, ColorPickerView.swift, and SelectionView.swift is approved. These new SwiftUI views enhance various UI components of the app, such as navigation, notifications, color picking, and selection. They contribute to an improved user interface and user experience.


114-114: New feature approved!

The addition of RTFTextView.swift is approved. This file implements a text view component that supports the display of Rich Text Format (RTF) content. It extends the app's functionality by enabling the presentation of formatted text.


373-373: Build settings update looks good!

The addition of SettingsView.swift to the openHAB target's "Sources" build phase is approved. This ensures that the settings view implementation is compiled and linked with the app.


385-386: Client certificates feature integration approved!

The addition of ClientCertificatesViewModel.swift and ClientCertificatesView.swift to the openHAB target's "Sources" build phase is approved. This ensures that the client certificates feature is compiled and linked with the app, integrating it into the build.


389-392: UI enhancements build integration approved!

The addition of DrawerView.swift, NotificationsView.swift, ColorPickerView.swift, and SelectionView.swift to the openHAB target's "Sources" build phase is approved. This ensures that the UI enhancements are compiled and linked with the app, integrating them into the build.


403-403: RTF text view build integration approved!

The addition of RTFTextView.swift to the openHAB target's "Sources" build phase is approved. This ensures that the Rich Text Format (RTF) text view functionality is compiled and linked with the app, integrating it into the build.


834-837: Project organization improvement approved!

The addition of SelectionView.swift, SettingsView.swift, ClientCertificatesView.swift, and RTFTextView.swift to the openHAB target's "UI" group is approved. Grouping related user interface files together enhances code organization, making the project easier to navigate and maintain.


838-839: UI group organization approved!

The addition of NotificationsView.swift and DrawerView.swift to the openHAB target's "UI" group is approved. Grouping these user interface files together improves project organization and maintainability.


968-968: Model group organization approved!

The addition of ClientCertificatesViewModel.swift to the openHAB target's "Models" group is approved. Grouping related model files together enhances project organization and maintainability.


Line range hint 1435-1477: Build phase updates approved!

The addition of various Swift files, including UI components, views, and utility classes, to the openHAB target's "Sources" build phase is approved. These files, such as SettingsView.swift, `ClientCertificatesView


462-462: Removal of unused code approved!

The removal of OpenHABSettingsViewController.swift from the openHAB target's "Sources" build phase is approved. This suggests that the settings view controller is no longer used and is replaced by the new SwiftUI-based SettingsView.swift.

To ensure the removal doesn't break any functionality, please verify that the app's settings screen works as expected and that there are no references to OpenHABSettingsViewController in the codebase.

Verification successful

Removal of OpenHABSettingsViewController confirmed and replaced with SwiftUI SettingsView

The verification process has confirmed that OpenHABSettingsViewController.swift has been successfully removed from the codebase. It has been replaced by a new SwiftUI-based SettingsView.swift, which implements all the necessary functionality for the app's settings.

Key points:

  • No references to OpenHABSettingsViewController were found in the codebase.
  • A new SettingsView.swift file has been implemented using SwiftUI.
  • The new settings view covers all required functionality, including connection, application, MainUI, and sitemap settings.

This change represents a positive modernization of the codebase, likely improving maintainability and user experience.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if `OpenHABSettingsViewController` is referenced anywhere in the codebase
rg --type swift 'OpenHABSettingsViewController'

Length of output: 47


Script:

#!/bin/bash
# 1. Search for partial matches of 'OpenHABSettings'
echo "Searching for partial matches of 'OpenHABSettings':"
rg --type swift 'OpenHABSettings'

# 2. Look for files related to settings implementation
echo -e "\nSearching for files related to settings:"
fd -e swift -e storyboard -e xib | rg -i 'settings?'

# 3. Verify the existence and content of the new SettingsView.swift file
echo -e "\nChecking for SettingsView.swift:"
fd -e swift | rg 'SettingsView'
echo -e "\nContent of SettingsView.swift (if exists):"
fd -e swift | rg 'SettingsView' | xargs cat

Length of output: 15290


940-940: Project structure reorganization noted!

The removal of the "UI" group reference from the openHAB target is noted. This suggests a reorganization of the Xcode project structure, likely moving the user interface files to a different group or location.

Please verify that the new project structure is logical, maintainable, and that all UI files are still properly referenced in the project.

Verification successful

This script will search for "UI" in the project.pbxproj file, excluding "UIKIT" references, which should help us identify if and how UI files are referenced in the project structure.


UI files are properly referenced and maintained in the project

Upon thorough examination of the project.pbxproj file, it's clear that UI files are still an integral part of the project structure and build process. The "UI" group is present (line 940), and numerous UI-related files are referenced throughout the project, including custom table view cells, extensions, and utility classes. These files are properly included in the build phases, ensuring they are compiled and linked in the final product.

The initial concern about the removal of the "UI" group appears to be unfounded. The project maintains a comprehensive set of UI components and even includes UI tests (openHABUITests), indicating a continued focus on the user interface aspect of the application.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if all UI files are still referenced in the project
find . -name "*.swift" -print0 | xargs -0 grep -L 'PBXBuildFile'

Length of output: 8554


Script:

#!/bin/bash
# Check for UI file references in the project.pbxproj file
grep -n "UI" openHAB.xcodeproj/project.pbxproj | grep -v "UIKIT"

Length of output: 12014

}

private func loadData() {
// TODO: Replace network calls with appropriate @EnvironmentObject or other state management
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Address the TODO comment.

The TODO comment indicates that network calls should be replaced with appropriate state management. Consider using @EnvironmentObject or other suitable state management techniques to handle data loading and updates.

Do you want me to provide guidance on how to refactor the code to use @EnvironmentObject for state management or open a GitHub issue to track this task?

Tools
SwiftLint

[Warning] 174-174: TODOs should be resolved (Replace network calls with app...)

(todo)

Button {
let websiteDataTypes = NSSet(array: [WKWebsiteDataTypeDiskCache, WKWebsiteDataTypeMemoryCache])
let date = Date(timeIntervalSince1970: 0)
WKWebsiteDataStore.default().removeData(ofTypes: websiteDataTypes as! Set<String>, modifiedSince: date) {}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid force cast.

The force cast at line 234 could lead to runtime crashes if the cast fails.

Consider using optional binding or optional chaining to safely unwrap the optional value. For example:

-WKWebsiteDataStore.default().removeData(ofTypes: websiteDataTypes as! Set<String>, modifiedSince: date) {}
+if let websiteDataTypes = websiteDataTypes as? Set<String> {
+    WKWebsiteDataStore.default().removeData(ofTypes: websiteDataTypes, modifiedSince: date) {}
+}
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.

Suggested change
WKWebsiteDataStore.default().removeData(ofTypes: websiteDataTypes as! Set<String>, modifiedSince: date) {}
if let websiteDataTypes = websiteDataTypes as? Set<String> {
WKWebsiteDataStore.default().removeData(ofTypes: websiteDataTypes, modifiedSince: date) {}
}
Tools
SwiftLint

[Error] 234-234: Force casts should be avoided

(force_cast)

timbms and others added 6 commits September 15, 2024 10:33
* Getting rid of blankicon.png as placeholder

* Get rid of blankIcon
* Guards against creating very large UImages from SVGs
Fixes openhab#817

Signed-off-by: Dan Cunningham <[email protected]>

* remove error.png, use sys icon

Signed-off-by: Dan Cunningham <[email protected]>

---------

Signed-off-by: Dan Cunningham <[email protected]>
@digitaldan digitaldan force-pushed the tracker-combine-changes branch from 96b9cf9 to cdcd3a7 Compare September 16, 2024 02:47
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 96b9cf9 and cdcd3a7.

Files ignored due to path filters (2)
  • openHAB/blankicon.png is excluded by !**/*.png
  • openHAB/error.png is excluded by !**/*.png
Files selected for processing (4)
  • OpenHABCore/Sources/OpenHABCore/Util/NetworkTracker.swift (1 hunks)
  • openHAB.xcodeproj/project.pbxproj (17 hunks)
  • openHAB/OpenHABSitemapViewController.swift (11 hunks)
  • openHAB/OpenHABWebViewController.swift (3 hunks)
Files skipped from review as they are similar to previous changes (1)
  • openHAB/OpenHABSitemapViewController.swift
Additional context used
SwiftLint
OpenHABCore/Sources/OpenHABCore/Util/NetworkTracker.swift

[Warning] 17-17: TODOs should be resolved (these strings should reference...)

(todo)

Additional comments not posted (11)
OpenHABCore/Sources/OpenHABCore/Util/NetworkTracker.swift (6)

26-38: LGTM!

The ConnectionObject struct implementation looks good. It uses appropriate access control, conforms to the Equatable protocol, and follows Swift naming conventions.


40-65: The NetworkTracker class declaration and initialization look good.

The class follows best practices and handles the initialization well:

  • The class is declared as public and final, allowing it to be used outside the module and preventing subclassing.
  • The class uses a singleton pattern with a private initializer to ensure only one instance exists.
  • The class uses appropriate access control for its properties and methods.
  • The class correctly initializes the NWPathMonitor and starts monitoring the network path.

67-70: LGTM!

The startTracking method implementation looks good. It correctly assigns the provided connection objects and attempts a connection.


72-90: LGTM!

The checkActiveServer method implementation looks good. It correctly checks the active server's priority and reachability, and attempts a new connection if necessary.


185-194: LGTM!

The startRetryTimer and cancelRetryTimer method implementations look good. They correctly start and cancel the retry timer using DispatchSource.

Also applies to: 197-200


202-209: LGTM!

The setActiveServer and updateStatus method implementations look good. They correctly update the active server and network status.

Also applies to: 211-215

openHAB/OpenHABWebViewController.swift (3)

75-84: Reactive approach using Combine improves code quality and responsiveness.

The transition to a reactive model using Combine's sink method simplifies the code and enhances responsiveness to network changes. Storing the subscription in trackerCancellables ensures proper lifecycle management.


86-99: Reactive approach using Combine enhances user experience with connection status feedback.

The sink subscription to observe changes in NetworkTracker.shared.$status and display appropriate pop-up messages based on the connection state improves the user experience by providing timely feedback. Storing the subscription in trackerCancellables ensures proper lifecycle management.


208-208: Forcing web view reload ensures proper refresh after clearing the page.

Calling loadWebView(force: true) after clearing the existing page ensures that the web view refreshes correctly, as the force parameter overrides the URL comparison check and guarantees a reload.

openHAB.xcodeproj/project.pbxproj (2)

88-88: Review the newly added files for code quality and architecture fit.

Several new Swift files have been added:

  • SettingsView.swift
  • ClientCertificatesViewModel.swift
  • ClientCertificatesView.swift
  • DrawerView.swift
  • NotificationsView.swift
  • ColorPickerView.swift
  • SelectionView.swift
  • RTFTextView.swift

Please review the code in each file to ensure:

  • The code follows best practices and integrates well with existing patterns.
  • The files have a clear purpose and fit into the overall project architecture.

Also applies to: 99-100, 103-106, 114-114


455-457: Verify the removal of MulticastDelegate.swift, OpenHABTracker.swift and OpenHABClientCertificatesViewController.swift.

The project file indicates these Swift files have been removed from the project. Ensure this removal is intentional and the corresponding functionality is no longer needed or has been moved elsewhere.

Run the following script to check for any usage of these classes in the codebase:

Verification successful

The removal of MulticastDelegate.swift, OpenHABTracker.swift, and OpenHABClientCertificatesViewController.swift appears to be intentional.

Based on the analysis:

  • A commented-out reference to OpenHABTracker was found in OpenHABWebViewController.swift, indicating intentional removal.
  • A NetworkTracker class in the Core module might be the replacement for OpenHABTracker.
  • No direct replacements for MulticastDelegate or OpenHABClientCertificatesViewController were found, suggesting their functionality may have been refactored or removed.

While the removal seems valid, it's recommended to:

  • Review the NetworkTracker class to ensure it covers the necessary functionality of the old OpenHABTracker.
  • Verify that the removal of MulticastDelegate and OpenHABClientCertificatesViewController doesn't leave any gaps in the app's functionality.
  • Clean up the commented line in OpenHABWebViewController.swift for code cleanliness.
Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash

# Test: Search for usage of removed classes
rg --type swift -w 'MulticastDelegate|OpenHABTracker|OpenHABClientCertificatesViewController' 

# Expected results:
# - If no results, removal seems valid. 
# - If results found, verify the removal and clean up references.

Length of output: 198


Script:

#!/bin/bash

# View the content of OpenHABWebViewController.swift around the commented line
echo "Content of OpenHABWebViewController.swift:"
rg --type swift -C 10 'OpenHABTracker.shared.multicastDelegate' openHAB/OpenHABWebViewController.swift

echo "\nSearching for potential replacement classes:"
rg --type swift -w 'class .*(Tracker|Delegate|CertificatesViewController)' 

echo "\nSearching for potential replacement methods:"
rg --type swift -w 'func .*(track|delegate|certificates)'

Length of output: 2268

Comment on lines +92 to +182
private func attemptConnection() {
guard !connectionObjects.isEmpty else {
os_log("Network status: No connection objects available.", log: OSLog.default, type: .error)
updateStatus(.notConnected)
return
}
os_log("Network status: checking available servers....", log: OSLog.default, type: .error)
let dispatchGroup = DispatchGroup()
var highestPriorityConnection: ConnectionObject?
var firstAvailableConnection: ConnectionObject?
var checkOutstanding = false // Track if there are any checks still in progress

let priorityWaitTime: TimeInterval = 2.0
var priorityWorkItem: DispatchWorkItem?

// Set up the work item to handle the 2-second timeout
priorityWorkItem = DispatchWorkItem { [weak self] in
guard let self else { return }
// After 2 seconds, if no high-priority connection was found, check for first available connection
if let firstAvailableConnection, highestPriorityConnection == nil {
setActiveServer(firstAvailableConnection)
} else if highestPriorityConnection == nil, checkOutstanding {
os_log("Network status: No server responded in 2 seconds, waiting for checks to finish.", log: OSLog.default, type: .info)
} else {
os_log("Network status: No server responded in 2 seconds and no checks are outstanding.", log: OSLog.default, type: .error)
updateStatus(.connectionFailed)
}
}

// Begin checking each connection object in parallel
for connection in connectionObjects {
dispatchGroup.enter()
checkOutstanding = true // Signal that checks are outstanding

NetworkConnection.tracker(openHABRootUrl: connection.url) { [weak self] response in
guard let self else { return }
defer {
dispatchGroup.leave() // When each check completes, this signals the group that it's done
}

switch response.result {
case let .success(data):
let version = getServerInfoFromData(data: data)
if version > 0 {
if connection.priority == 0, highestPriorityConnection == nil {
// Found a high-priority (0) connection
highestPriorityConnection = connection
priorityWorkItem?.cancel() // Stop the 2-second wait if highest priority succeeds
setActiveServer(connection)
} else if highestPriorityConnection == nil {
// Check if this connection has a higher priority than the current firstAvailableConnection
if firstAvailableConnection == nil || connection.priority < firstAvailableConnection!.priority {
os_log("Network status: Found a higher priority available connection: %{PUBLIC}@", log: OSLog.default, type: .info, connection.url)
firstAvailableConnection = connection
}
}
} else {
os_log("Network status: Invalid server version from %{PUBLIC}@", log: OSLog.default, type: .error, connection.url)
}
case let .failure(error):
os_log("Network status: Failed to connect to %{PUBLIC}@ : %{PUBLIC}@", log: OSLog.default, type: .error, connection.url, error.localizedDescription)
}
}
}

// Start a timer that waits for 2 seconds
DispatchQueue.global().asyncAfter(deadline: .now() + priorityWaitTime, execute: priorityWorkItem!)

// When all checks complete, finalize logic based on connection status
dispatchGroup.notify(queue: .main) { [weak self] in
guard let self else { return }

// All checks are finished here, so no outstanding checks
checkOutstanding = false

// If a high-priority connection was already established, we are done
if let highestPriorityConnection {
os_log("Network status: High-priority connection established with %{PUBLIC}@", log: OSLog.default, type: .info, highestPriorityConnection.url)
return
}

// If we have an available connection and no high-priority connection, set the first available
if let firstAvailableConnection {
setActiveServer(firstAvailableConnection)
os_log("Network status: First available connection established with %{PUBLIC}@", log: OSLog.default, type: .info, firstAvailableConnection.url)
} else {
os_log("Network status: No server responded, connection failed.", log: OSLog.default, type: .error)
updateStatus(.connectionFailed)
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider refactoring the attemptConnection method into smaller methods for better readability and maintainability.

The method implementation is correct but quite long and complex. It could be broken down into smaller methods for better readability and maintainability.

For example, you could extract the logic for handling the priority wait time and the logic for processing the connection results into separate methods.

Comment on lines +217 to +225
private func getServerInfoFromData(data: Data) -> Int {
do {
let serverProperties = try data.decoded(as: OpenHABServerProperties.self)
// OH versions 2.0 through 2.4 return "1" as their version, so set the floor to 2 so we do not think this is an OH 1.x server
return max(2, Int(serverProperties.version) ?? 2)
} catch {
return -1
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix the force unwrap when casting the server version to an integer.

The method implementation is correct but force unwraps the server version, which could lead to a crash if the version is not a valid integer.

Apply this diff to fix the force unwrap:

-return max(2, Int(serverProperties.version) ?? 2)
+return max(2, Int(serverProperties.version ?? "2") ?? 2)
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.

Suggested change
private func getServerInfoFromData(data: Data) -> Int {
do {
let serverProperties = try data.decoded(as: OpenHABServerProperties.self)
// OH versions 2.0 through 2.4 return "1" as their version, so set the floor to 2 so we do not think this is an OH 1.x server
return max(2, Int(serverProperties.version) ?? 2)
} catch {
return -1
}
}
private func getServerInfoFromData(data: Data) -> Int {
do {
let serverProperties = try data.decoded(as: OpenHABServerProperties.self)
// OH versions 2.0 through 2.4 return "1" as their version, so set the floor to 2 so we do not think this is an OH 1.x server
return max(2, Int(serverProperties.version ?? "2") ?? 2)
} catch {
return -1
}
}

Signed-off-by: Dan Cunningham <[email protected]>
Signed-off-by: Dan Cunningham <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants