Version 2.5.1

Version 2.5.1 (build 143) -release candidate

This will most likely stay as a release candidate for some weeks. I need some feedback on the Calendar part before making it a release.

Major updates in this rc are:

  • a new calendar for schedule actions, please read about the scheduler below before commence using it
    • to remove a schedule, just select it and press the back space button
  • German and Norwegian localization are removed
  • the Verify a remote function is slightly changed

The scheduler is an easy to use and simple timer based trigger for synchronize data. The synchronize of data is triggered by the same method, URL-action, as for executing a synchronize data either by Widget or by external URL.

The Norwegian and German localization are also been removed from this version. Regrettably, due to my limited proficiency in German, I am unable to provide a comprehensive translation in German. From this version, RsyncUI speaks English only. 

Version 2.4.1

Version 2.4.1 (build 141) - 21 March 2025

It is imperative that RsyncUI tags tasks with data to be synchronized correctly. If the tagging fails, there may be local data that is not synchronized. RsyncUI supports the latest version of rsync and the older default version of rsync included in macOS 14 and macOS 15. There is a new function for parsing output from rsync in version 2.4.1.

This is most likely the last release before summer 2025. If bugs are reported, they will be fixed.

GUI updates

The sole internal refactoring and update in this release is parsing the output of rsync for numerical values, tagging data for synchronization, and providing a notification in the event of an issue with tagging. The majority of the work in this release consists of GUI updates. The primary challenge is to minimize the number of views while ensuring that all possible settings for RsyncUI are accessible. Additionally, efforts are being made to reduce the complexity of busy views to a minimum.

Parameters to rsync

The Parameters view for rsync is consolidated into one view which includes, pr task, user added parameters for rsync, ssh parameters, backup switch and remove parameters. The --delete parameter is from this version not a default parameter for new tasks. But, the --delete parameter may be added within the Parameters for rsync view.

Please read the Important section about the delete parameter.

Add and update tasks

And there is some cleanup within the Add and update tasks view as well.

Verify a remote

Reduced the info to what is required only.

Future plans for RsyncUI

What is the future for the development of RsyncUI? Following the release of version 2.4.x, the next version is likely to be released after the summer 2025. Of course, any bugs reported will be fixed. The spring and summer are quite busy as a bird photographer. After the release of version 2.4.x, I will focus on my other major hobby, bird photography. You can find a link to some of my photos on my GitHub site.

However, I have some plans for the next major version of RsyncUI. By URL or Deep Links, RsyncUI will support loading any profile, estimating and executing synchronization of data. The next major version of RsyncUI will most likely include a calendar and scheduling of tasks.

Version 2.3.9

Version 2.3.9 (build 139) - 9 March 2025

The issue with tagging data for synchronize is fixed. There is also a new setting, by default on. It is imperative that RsyncUI tags tasks with data to be synchronized correctly. If the tagging fails, there may be local data that is not synchronized. RsyncUI supports the latest version of rsync and the older default version of rsync included in macOS 14 and macOS 15.

However, there is always the possibility to force a synchronization without an initial estimation. If a synchronization is forced without a prior estimation, there will be no progress bar.

RsyncUI supports the latest version of rsync and the older default version of rsync included in macOS 14 and macOS 15.

New setting

The new setting, “Always present the summarized estimate view”, on will always present the summarized estimate view, even if there are no data to synchronize. This setting is overridden when executing tasks by Deep Links (URL links).

Version 2.3.7

Version 2.3.7 (build 137) - 7 March 2025

I sincerely apologize for the two consecutive updates on the same day. And issue is still not fixed in release. But the issue is fixed in code. There will be released a new version early next week, where localization issues are fixed and QA of bugfix is properly completed.

Version 2.3.6

Version 2.3.6 (build 136) - 7 March 2025

Please note that there are currently missing localizations in German and Norwegian. The next version will include updated localizations. Due to the urgency of the bug, it was prioritized to release a quick update rather than address the localization issues.

A bug exists in the discover function when data synchronization is required. This bug may prevent RsyncUI from marking a repository for updates, even when there are new and modified files to be synchronized. The release also includes a few UI enhancements and internal code refactoring.

Another pleasant aspect of constructing a new release using Makefile and command-line tools is the simplicity of the build, notarization, and signing process, which can be completed in a matter of minutes.  Creating a new release is now done in 3-4 minutes by Makefile and command-line tools.

The three only manual updates are:

  • updating the new release on GitHub releases
    • uploading the new dmg file, compute the SHA256 checksum for the dmg file, add the GitHub Full Changlog
  • updating the GitHub file for notify of a new version
    • RsyncUI pulls a version file, a JSON file, from GitHub at startup and decide if there is an update
  • creating a new pull request for Homebrew about new version

How to verify from main view

To verify the availability of data for synchronization, inspect the output generated by rsync. This can be obtained from either the summarized estimated view or the main view, and by selecting the corresponding task.

Alternatively, you can initiate a synchronize operation without prior estimation by clicking the play icon on the toolbar. This action will either synchronize all tasks or the selected tasks, respectively, performing a comprehensive synchronization of the data.

After selecting the task, locate the magnifyingglass icon on the toolbar. This icon will display the output generated by the rsync command. The output from rsync indicates that there are no modified files. 

How to verify from summarized estimated view

Or from the summarized estimated view, blue numbers indicates there are updates:

After selecting a task, the output from rsync shows there are data to be synchronized.

Version 2.3.5

Version 2.3.5 (build 135) - 26 February 2025

The release candidate, 23 February 2025, is unchanged and is the next release.

The following are changed:

  • the estimate process is updated
  • the Verify function, for remote destinations only, is changed
  • parts of the URL-functions or deep links are refactored (internal)
  • some other minor internal refactor as well
The Estimate Process

The internals of the estimation process remains unchanged. However, if there are no data to synchronize, the estimation process automatically returns to the main Synchronize view.

The estimate indicates that there are data to be synchronized for one of the tasks. Blue numbers denote data to be synchronized.

The task is not synchronized; it is only returned by selecting the Navigation arrow on the toolbar. Within the main Synchronize view, the blue checkmark indicates a task with data to be synchronized. The red checkmarks indicate a task that is estimated but has no data to synchronize.

For my Pictures profile, all data is synchronized. After the estimation process, the view automatically returns to the main Synchronize view. All checkmarks are red. Red checkmarks indicate that a task is estimated but no data to synchronize.

The Verify

Below are changes in the Verify function. Important, as an example I have used a git repository. As documented in the user documentation, if remote is a git repository, git manage to update and synchronize local repository. The pictures below are just as an example to present the changes.

The Verify is now triggered from the Synchronize view, by the toolbar. And if you don´t have any remote destinations, the Verify functions are hidden.

After running the Verify function, which executes both a pull and push task, the result is presented.

Version 2.3.4

Version 2.3.4 (build 134) - 12 February 2025

Version 2.3.3 and 2.3.4 are merged into one release, version 2.3.4.

The User documentation has undergone a slight restructure. The primary objective of this restructure is to prioritize the presentation of essential information. Subsequently, new users who are familiar with RsyncUI will be able to explore more advanced features.

The following are enhancements in version 2.3.4:

  • context-sensitive main Sidebar menu
  • automatic selection of a profile when RsyncUI detects a new mount of a local attached disc
    • this feature must be enabled in Settings
    • the automatic selection of a profile requires that there are no tasks running or being estimated

Context sensitive sidebar Meny

The primary Sidebar is context-sensitive. The rationale behind a context-sensitive Sidebar menu is to conceal menu options that may be distracting to the user. If you exclusively utilize RsyncUI for synchronizing tasks to local attached disks, you will not require access to any of the three Sidebar menu options listed below.

There are three Sidebar menu options that are contingent upon the properties of a task:

  • Snapshot: This option is exclusively available for snapshot tasks.
  • Restore: This option is only available for synchronize- and snapshot tasks where the destination is located on a remote server.
  • Verify remote: This option is only available for synchronize tasks where the destination is located on a remote server.

Automatic selection of profile

The second enhancement in version 2.3.4 is the automatic selection of a profile when RsyncUI detects a new mount of a local attached disc. This feature must be enabled in the settings. The feature is only functional if there are additional profiles created beyond the default profile. Additionally, it is only applicable to tasks with a destination on a local attached disc. RsyncUI monitors when a disc is mounted and scans all profiles and tasks, selecting the profile where the destination matches the mounted disc.

Technically, it is feasible to automate synchronization when a new disc is mounted. However, it is crucial that the user has complete control over when a synchronization task is executed. Therefore, automatic synchronization will not be enabled at this time.

Version 2.3.2

Version 2.3.2 (build 132) - 29 January 2025

Updates within this release:

  • halting tasks
  • update widgets
  • hide the sidebar
  • notifications, refactor of the observation part

And there has been some minor internal updates as well.

Halting tasks

By right-clicking on a task, on column Synchronize ID or Task, it can be halted or released from halted status. A halted task will be marked and not available for estimate or execute. When releasing a task from halted status, it remember which kind of task it was before halted.

Update widgets

This update includes a minor enhancement to widgets and the implementation of a URL validation mechanism prior to saving. To update widgets, edit and delete current widgets. Start version 2.3.2 of RsyncUI, edit widgets and add updated. After enable and save URLs, in Tasks View URLs, the widgets look like below.

Hide The Sidebar

The sidebar may be concealed. When concealed, the view is simplified. When concealed, profiles are displayed on the right side. To select another profile, click on the profile names in the table. The sidebar can be hidden on/off by menu or by shortcut, as shown in the View menu bar. Alternatively, it can be hidden by clicking on the toolbar icon. All functions for estimating, synchronizing, and taking action by URLs are functioning as expected when the Sidebar is hidden.

Within the Settings, it is possible to configure “Hide the Sidebar on startup.” If this option is enabled when URL actions are activated, clicking on a link or widget will display a cluttered view when the URL is in action. After testing, I have decided to disable this option in startup. However, if you frequently use RsyncUI, keeping it running will provide a cleaner view. You have to decide yourself.

Notifications

Key feature of RsyncUI is observation for two notifications:

  • NSNotification.Name.NSFileHandleDataAvailable
  • Process.didTerminateNotification

Without observation and required actions when observed, RsyncUI becomes useless. Both observations are linked to the external task executing the actual rsync task. For more info, see blog about “Swift concurrency”.

Version 2.3.0

Version 2.3.0 (build 130) - 15 January 2025

This version is released somewhat sooner than anticipated. Rsync version 3.4.1, protocol version 32, has recently been released and a minor bug, causing a fallback to the default version in macOS when the new version of rsync is enabled, has been fixed.

Version 2.2.5 introduced the primary new function: deep links. Deep links enable direct access to application functions. In RsyncUI, two main deep link functions are now available:

  • estimate and synchronize, all tasks within selected profile
  • verify a remote file, selected profile and task

MacOS supports widgets, which provide a set of features, such as opening URLs. For RsyncUI, these features can be initiated by simply clicking on a widget. Widgets for Estimate and Verify are introduced in this version. By clicking on the upper right corner of the screen, the date and time, the edit widgets are enabled. There are two widgets for RsyncUI. It is recommended to select the middle size for the widget.

Please note that widgets are currently in beta. Before using widgets, in Tasks View URLs, click on the task and Save.

Save URLs

As an example I have saved two URLs for action by widgets. First is my Default profile, by click on the widget Estimate , estimate and synchronize all tasks in default profile.

The second is my Pictures profile, task with Synchronize ID “Pictures backup”. By click on the widget Verify, verify task against remote destination.

URLs Notepad

A few samples of URL´s I am executing from Notepad. URL´s must start with rsyncuiapp://, if not RsyncUI will not recognize the command. My URL´s saved in Notepad for easy access and execution of my most used tasks. The Notepad page might also be saved as a PDF document which includes til URL links.

Sample URLs which I am using

URLs for estimate and execute.

Remote server - raspberrypi with zfs filesystem

  • rsyncuiapp://loadprofileandestimate?profile=Pictures
  • rsyncuiapp://loadprofileandestimate?profile=default

NVME SSD disks

The mount point, such as /Volumes/WDBackup, and the profile name are set to the same value solely for the purpose of simplifying the identification of the mounted disk and the corresponding profile to use in RsyncUI. As documented here, I am performing backups on several SSD disks and also to a remote server. The rationale behind having multiple backups is that it is straightforward to update all backups on a regular basis using RsyncUI. Additionally, if one disk or the server fails, I always have an updated backup to restore from.

Mounted as WDBackup

  • rsyncuiapp://loadprofileandestimate?profile=WDBackup

Mounted as Samsung

  • rsyncuiapp://loadprofileandestimate?profile=Samsung

Mounted as LaCie

  • rsyncuiapp://loadprofileandestimate?profile=LaCie

URL for verify a remote

Verify remote is only for remote destinations. Remote server - raspberrypi with zfs filesystem. Profile is Pictures and Synchronize ID = Pictures backup, the space is replaced by a _ in URL for the id tag in URL, which is the search for the wanted Synchronize ID.

  • rsyncuiapp://loadprofileandverify?profile=Pictures&id=Pictures_backup

Clean log snapshots

When utilizing Snapshots, within the Snapshots View, the current snapshots on storage and log records are synchronized. On every log record for a snapshot synchronization task, the snapshot number is stored. The deletion of snapshots is a destructive action that involves removing old snapshots from storage. Furthermore, when a snapshot is deleted, the corresponding log record is no longer necessary. 

Two snapshotss for delete

Two snaphots are selected for delete.

Within the Log listings View, the two corresponding log records.

After delete of snapshots

The Trash icon on the toolbar indicates the presence of two log records for deletion. The Trash icon is automatically displayed.

Selecting the Trash can, confirm delete of the two not needed log records.

Historic releases

Previous releases

Dates and version only

  • Version 2.2.5 - 2025-01-08
  • Version 2.2.3 - 2024-12-20
  • Version 2.2.1 - 2024-11-30
  • Version 2.2.0 - 2024-11-29
  • Version 2.1.8 - 2024-11-14
  • Version 2.1.6 - 2024-10-29
  • Version 2.1.5 - 2024-10-13
  • Version 2.1.4 - 2024-09-27
  • Version 2.1.3 - 2024-09-16
  • Version 2.1.1 - 2024-07-30
  • Version 1.9.2 (build 100) - 11 June 2024
  • Version 1.9.1 (build 99) - 27 May 2024
  • Version 1.9.0 (build 98) - 12 April 2024
  • Version 1.8.9 (build 97) - 26 March 2024
  • Version 1.8.8 (build 96) - 14 March 2024
  • Version 1.8.7 (build 95) - 20 February 2024
  • Version 1.8.6 (build 94) - 30 January 2024
  • Version 1.8.2 (build 92) - 8 January 2024
  • Version 1.8.1 (build 91) - 29 December 2023
  • Version 1.8.0 (build 90) - 18 December 2023
  • Version 1.7.9 (build 89) - 7 December 2023
  • Version 1.7.8 build (88) and 1.7.5 build (88) - 23 November 2023
  • Version 1.7.3 build (86) - 19 October 2023
  • Version 1.7.5 build (84) - 28 September 2023
  • Version 1.7.2 build (85) - 23 September 2023
  • Version 1.7.1 build(83) - 1 September 2023
  • Version 1.7.0 build(82) - 16 August 2023
  • Version 1.6.6 build(81) - 1 August 2023
  • Version 1.6.5 build(80) - 16 July 2023
  • Version 1.6.3 build(79) - 29 June 2023
  • Version 1.6.1 build(77) - 20 June 2023
  • Version 1.6.0 build(76) - 16 June 2023
  • Version 1.5.0 build(73) - 4 May 2023
  • Version 1.4.8 build(70) - 24 March 2023
  • Version 1.4.7 build(69) - 14 March 2023
  • Version 1.4.5 build(67) - 7 March 2023
  • Version 1.4.3 build (65) - 8 February 2023
  • Version 1.4.2 build (64) - 6 January 2023
  • Version 1.4.0 build (62) - 6 December 2022
  • Version 1.3.9 build (57) - 18 November 2022
  • Version 1.3.8 build (56) release candidate - 10 November 2022
  • Version 1.3.7 build (56) - 5 November 2022
  • Version 1.3.6 build (55) - 31 October 2022
  • Version 1.3.0 build (53) - 30 September 2022
  • Version 1.2.9 build (52) - 8 September 2022
  • Version 1.2.8 build (51) - 20 March 2022
  • Version 1.2.7 build (50) - 17 March 2022
  • Version 1.2.6 build (48) - 31 December 2021
  • Version 1.2.5 build (48) - 6 December 2021
  • Version 1.2.3 build (46) - 11 November 2021
  • Version 1.2.2 build (45) - 28 October 2021
  • Version 1.2.1 build (42) - 21 October 2021
  • Version 1.2.0 build (41) - 28 September 2021
  • Version 1.1.2 build (40) - 9 September 2021
  • Version 1.1.2 build (39) - 1 September 2021
  • Version 1.1.2 build (38) - 28 August 2021
  • Version 1.1.2 build (37) - 21 August 2021
  • Version 1.1.2 build (36) - 16 August 2021
  • Version 1.1.2 build (35) - 11 August 2021
  • Version 1.1.2 build (34) - 30 July 2021
  • Version 1.1.2 build (28)
  • Version 1.1.1 build (27)
  • Version 1.1.0 build (26)
  • Version 1.0.1 build (24)
  • Version 1.0.0 build (23)
  • Prerelease version 0.99 build (22)
  • Prerelease v0.60 build (20) - breaking changes
  • Prerelease v0.55 build (19) - 14 April 2021
  • Prerelease v0.49 build (18)
  • Prerelease v0.48 build (17)
  • Prerelease v0.47 build (16)
  • Prerelease v0.45 build (15)
  • Prerelease v0.42 build (14)
  • Prerelease v0.41 build (12)
  • Prerelease v0.39 build (11)
  • Prerelease v0.36 build (10)
  • Prerelease v0.36 build (8)
  • Prerelease v0.35 build (7)
  • Prerelease v0.3 build (6) - 12 March 2021

Technical details

Observers

A key feature of RsyncUI is observation for two notifications:

  • NSNotification.Name.NSFileHandleDataAvailable
  • Process.didTerminateNotification

Without observation and required actions when observed, RsyncUI becomes useless. Both observations are linked to the external task executing the actual rsync task.

The first observation monitors when the external task generates output. To display the progress of a synchronization task, RsyncUI relies on monitoring the output from rsync. Therefore, the —verbose parameter to rsync is crucial. This parameter instructs rsync to output information during execution.

The second observation monitors when the task is completed, e.g. terminated. Typically, a termination indicates task completion. However, it may also be an abort action from the user, which then sends an interrupt signal to the external task. If RsyncUI fails to detect this signal, RsyncUI will not comprehend when a synchronization task is completed.

In RsyncUI, two methods for enabling observations have been introduced in the version 2.3.2. The preferred method is to utilize the declarative library Combine, developed by Apple. However, the future of Combine is somewhat uncertain. I consulted a developer working with Apple and with a deep understanding of Swift Concurrency, who informed me that Combine is not deprecated but may be in the future. 

The second method involves utilizing a central Notification center. Observers for the two mentioned notifications are added to the Notification center, and the appropriate action is triggered when a signal is observed.

In forthcoming versions of RsyncUI, both methods will be employed. However, if Combine is deprecated in the future, it is straightforward to replace it. In version 2.1.6, a significant refactoring of code utilizing Combine was implemented. 

ChatGPT

ChatGPT about what is recommended of NotificationCenter.default.publisher and NotificationCenter.default.addObserver. In Swift, using NotificationCenter.default.publisher(for:) with the Combine framework is generally preferred for observing notifications, as it offers a more modern, type-safe, and declarative approach compared to the traditional addObserver method. The Combine-based method allows for better memory management and cleaner code, reducing the risk of retain cycles and the need for manual unsubscription. For more details, refer to Apple’s documentation on NotificationCenter publishers.

Until I gain further insights into Apple’s future plans for Combine, NotificationCenter.default.publisher(for:) remains the preferred solution in RsyncUI.

Combine, Publisher and Asynchronous Execution

The Combine framework is exclusively utilized within the Process object, which is responsible for initiating external tasks, such as the rsync synchronize task. Combine is employed to monitor two specific notifications.

  • NSNotification.Name.NSFileHandleDataAvailable
  • Process.didTerminateNotification

and act when they are observed. The rsync synchronize task is completed when the last notification is observed. By using Combine, a publisher is added to the Notification center. Every time the Notification center discover one of the notifications, it publish a message.

// Combine, subscribe to NSNotification.Name.NSFileHandleDataAvailable
NotificationCenter.default.publisher(
  for: NSNotification.Name.NSFileHandleDataAvailable)
  .sink { [self] _ in
  ....
  }.store(in: &subscriptons)
// Combine, subscribe to Process.didTerminateNotification
NotificationCenter.default.publisher(
    for: Process.didTerminateNotification)
        .debounce(for: .milliseconds(500), scheduler: DispatchQueue.main)
        .sink { [self] _ in
        ....
        subscriptons.removeAll()
    }.store(in: &subscriptons)

Observers and Asynchronous Execution

As previously mentioned, the second method for observing notifications involves adding Observers to the Notification center. Upon the discovery of a notification, the completion handler is executed. The Process object is annotated to execute on the main thread. It appears that the addObserver closure is marked as Sendable, indicating that mutating properties within the closure must be asynchronous. This is due to the Swift 6 language mode and strict concurrency checking.

// Observers
var notificationsfilehandle: NSObjectProtocol?
var notificationstermination: NSObjectProtocol?
....
notificationsfilehandle =
    NotificationCenter.default.addObserver(forName: NSNotification.Name.NSFileHandleDataAvailable,
                                                   object: nil, queue: nil)
        { _ in
            Task {
                await self.datahandle(pipe)
            }
        }

notificationstermination =
    NotificationCenter.default.addObserver(forName: Process.didTerminateNotification,
                                                   object: task, queue: nil)
        { _ in
                Task {
                    // Debounce termination for 500 ms
                    try await Task.sleep(seconds: 0.5)
                    await self.termination()
                }
        }

Swift concurrency

RsyncUI is a graphical user interface (GUI) application; the majority of its operations are executed on the main thread. However, some resource-intensive tasks are performed on other threads managed the cooperative thread pool (CTP), excluding and not blocking the main thread. How to the executors and CTP works and interacts is details I dont know about, and it is managed by the Swift runtime. There are three kinds of executors:

  • the Main Executor manage jobs on the Main Thread
  • the global concurrent executor and the serial executor, both executes jobs on threads from the CTP

The most important work are executed on the Main Thread. By default, SwiftUI makes sure all UI-updates are performed on the Main Thread. Below are other tasks on the Main Thread:

  • preparing of and execution of rsync synchronize tasks, preparing is computing the correct arguments for rsync
  • monitoring progress and termination of the real rsync tasks
  • write operations of logdata of synchronize tasks to storage

All read and write operations, transmission of the synchronized data is outside control of RsyncUI and taken care of by rsync itself.

Swift concurrency and asynchronous execution

Concurrency and asynchronous execution are important parts in Swift. The latest version of Swift simplifies the writing of asynchronous code using Swift async and await keywords, as well as the actor protocol. RsyncUI does not require concurrency, but concurrency is automatically introduced by using actors , async and await keywords. It is an objective to execute most work synchronous on the main thread as long as it does not block the GUI.

Swift version 6 and the new concurrency model

Swift version 6 introduced strict concurrency checking. By enabling Swift 6 language mode and strict concurrency checking, Xcode assists in identifying and resolving possible data races at compile time.

Quote swift.org: “More formally, a data race occurs when one thread accesses memory while the same memory is being modified by another thread. The Swift 6 language mode eliminates these issues by preventing data races at compile time.”

RsyncUI adheres to the new concurrency model of Swift 6.

Cooperative thread pool (CTP)

The following tasks are executed asynchronous on threads from the CTP, adhering to the actor protocol:

  • reading operations
  • data decoding and encoding
  • sorting log records
  • preparing output from rsync for display
  • preparing data from the logfile, not logrecords, for display
  • checking for updates to RsyncUI

Adhering to the actor protocol, all access to properties within an actor must be performed asynchronously. There are three types of executors, which manages jobs and put jobs on threads for execution.

The above mentioned tasks are executed on threads from the CTP, and not on the@MainActor. The runtime environment handles scheduling and execution, guaranteeing that all functions within an actor are nonisolated func, which to my understanding, guarantees asynchronous execution on threads from the CTP.

Structured concurrency

Some concurrent functions within RsyncUI are structured by using async let. You may have several async let, and they will all be executed in parallel or concurrent. When all async let tasks are completed, the root task or parent task, will continue execution.

func readconfigurations() async {
        ....
      	async let readconfigurations = ActorReadSynchronizeConfigurationJSON()
     	rsyncUIdata.configurations = await readconfigurations
         	.readjsonfilesynchronizeconfigurations(selectedprofile,
                                                   SharedReference.shared.monitornetworkconnection,
                                                   SharedReference.shared.sshport)
    
    	// after the await is completed, the root task will continue
    	// the structured concurrency is actually not needed here, only one async let
		....
	}
}

The below code is unstructured concurrency, the behaviour is as far as I understand equal for both.

func readconfigurations() async {
        ....
     	rsyncUIdata.configurations = await ActorReadSynchronizeConfigurationJSON()
         	.readjsonfilesynchronizeconfigurations(selectedprofile,
                                                   SharedReference.shared.monitornetworkconnection,
                                                   SharedReference.shared.sshport)
		....
	}
}
Unstructured concurrency

The code snippet below presents an unstructured concurrency. The code within the Task { ... } may be completed after the execution of the calling function, the parent, is completed. Upon the function’s return, the UI is notified on the main thread if there is a new version available.

@MainActor
func somefunction() {
    ....
    Task {
      newversion.notifynewversion = await GetversionofRsyncUI().getversionsofrsyncui()
	}
    ....
}
actor GetversionofRsyncUI {
    nonisolated func getversionsofrsyncui() async -> Bool {
        do {
            let versions = await DecodeGeneric()
            if let versionsofrsyncui =
                try await versions.decodearraydata(VersionsofRsyncUI.self,
                                                   fromwhere: Resources().getResource(resource: .urlJSON))
            {
                let runningversion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
                let check = versionsofrsyncui.filter { runningversion.isEmpty ? true : $0.version == runningversion }
                if check.count > 0 {
                    return true
                } else {
                    return false
                }
            }
        } catch {
            return false
        }
        return false
    }
}

Tagging of data

Tagging of data to be synchronized

It is imperative that RsyncUI tags tasks with data to be synchronized correctly. If the tagging fails, there may be local data that is not synchronized. RsyncUI supports the latest version of rsync and the older default version of rsync included in macOS 14 and macOS 15.

The tagging of data to be synchronized is computed within the package ParseRsyncOutput, a local Swift Package for RsyncUI.

From version 2.4.0, there is a verification of the tagging, if the output from rsync is greater than 20 lines and tagging for data to synchronize is not set, an alert is thrown. Normally, if there are no data to synchronize output from rsync is about 20 lines. Extract numbers from a string containing letters and digits is from version 2.4.0 of RsyncUI is now a one line code.

Example:

  • the string Number of created files: 7,191 (reg: 6,846, dir: 345) as input
  • is converted to [7191,6846,345], the thousand mark is also removed from string ahead parsing

The function below extract numbers only from the input.

 public func returnIntNumber( _ input: String) -> [Int] {
        var numbers: [Int] = []
        let str = input.replacingOccurrences(of: ",", with: "")
        let stringArray = str.components(separatedBy: CharacterSet.decimalDigits.inverted).compactMap { $0.isEmpty == true ? nil : $0 }
        for item in stringArray where item.isEmpty == false {
            if let number = Int(item) {
                numbers.append(number)
            }
        }
        if numbers.count == 0 {
            return [0]
        } else {
            return numbers
        }
    }

The parsing of the output of rsync is not particularly complex, and it is somewhat different for the latest version of rsync compared to the default versions of rsync.

Latest version of rsync

The trail of output from latest version of rsync, version 3.4.1, is like:

....
Number of files: 7,192 (reg: 6,846, dir: 346)
Number of created files: 7,191 (reg: 6,846, dir: 345)
Number of deleted files: 0
Number of regular files transferred: 6,846
Total file size: 24,788,299 bytes
Total transferred file size: 24,788,299 bytes
Literal data: 0 bytes
Matched data: 0 bytes
File list size: 0
File list generation time: 0.003 seconds
File list transfer time: 0.000 seconds
Total bytes sent: 394,304
Total bytes received: 22,226
sent 394,304 bytes  received 22,226 bytes  833,060.00 bytes/sec
total size is 24,788,299  speedup is 59.51 (DRY RUN)

Default version of rsync

The trail of output from default version of rsync is like:

....
Number of files: 7192
Number of files transferred: 6846
Total file size: 24788299 bytes
Total transferred file size: 24788299 bytes
Literal data: 0 bytes
Matched data: 0 bytes
File list size: 336861
File list generation time: 0.052 seconds
File list transfer time: 0.000 seconds
Total bytes sent: 380178
Total bytes received: 43172
sent 380178 bytes  received 43172 bytes  169340.00 bytes/sec
total size is 24788299  speedup is 58.55

How does the tagging work

The output from rsync is parsed and numbers are extracted. After parsing of output, the numbers decide if there is tagging of data to be synchronized.

Latest version of rsync

There are three numbers which decide data to synchronize or not: number of updates (regular files transferred), new files or deleted files. And they all may be 0 or a number, all three must be verified.

Default versions

There is only one number which decide data to synchronize or not: number of updates (files transferred).

Console and OSLog

Included in Swift 5 is a unified logging feature called OSLog. This feature provides several methods for logging and investigating the application’s activities. By utilizing OSLog, print statements are no longer necessary to follow the execution of code. All logging is performed through OSLog, which is displayed as part of Xcode. OSLog is integrated into all objects that perform work, making it straightforward to identify which commands RsyncUI is executing.

OSLog information in Xcode. The logging displays commands and arguments as shown below. This feature facilitates the verification that RsyncUI is executing the correct command.

And the OSLogs might be read by using the Console app. Be sure to set:

  • the Action in Console app menu to Include Info Messages
  • enter no.blogspot.RsyncUI as subsystem within the Search field

Number of files

Numbers updated: 3 April 2025, version 2.4.1

There is a very nice and excellent tool, cloc (https://github.com/AlDanial/cloc), for counting of files and lines of code. Below are the numbers for Swift files which are part of the repository for compiling RsyncUI. RsyncUI does not rely on external libraries; it is constructed using default Swift libraries and Swift/SwiftUI code exclusively.

cloc DecodeEncodeGeneric ParseRsyncOutput RsyncArguments RsyncUI RsyncUIDeepLinks SSHCreateKey
     310 text files.
     277 unique files.                                          
      59 files ignored.

github.com/AlDanial/cloc v 2.04  T=0.13 s (2103.2 files/s, 345765.0 lines/s)
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
Text                             6             12              0          21921
Swift                          226           2249           2658          17378
XML                             24              0              0            582
C                                2             36             72            254
JSON                             8              0              0            196
make                             1             22              2             59
Markdown                         6             32              0             47
YAML                             2              0              0             12
Bourne Shell                     1              0              1              2
C/C++ Header                     1              1              3              0
-------------------------------------------------------------------------------
SUM:                           277           2352           2736          40451
-------------------------------------------------------------------------------

Main Repository:

Local RsyncUI packages:

SPM, Swift Package Manager, makes it easy to create local packages. And each package containes their own tests by Swift Testing, the new framwork for creating tests. All packages are created by me.