This is the multi-page printable view of this section. Click here to print.
Blog
Version 2.5.5
Version 2.5.5 (build 147) - May 23, 2025
This is a maintenance release. In the latest release, there were several UI updates. This release fixes some of the minor issues discovered after previous release. There is also one refactor of internal code, see comment about Picker and optional values.
- a few days ago I did read a blog about Picker and nil values and how to deal with optional values
- a Picker is like the profile selector and optional values means there may be no selected value (
nil
) - when a new user commence using RsyncUI, there are no profiles but only the default path, the profile picker has no values but
nil
- a profile is a folder and RsyncUI stores data for profile in folder (or directory), the profile name, displayed in profile picker, is name of the folder
- a Picker is like the profile selector and optional values means there may be no selected value (
- if you have ON “Observe mounting of external drives” in settings, RsyncUI will automatically load profile if mounted volume path in destination
- when unmounting the volume, RsyncUI now also automatically load default profile
- the automated selection of profile when mounting a volume is only valid if there is created one or more profiles
- in Tasks, view for adding new tasks, pressing the Enter key does not jump to the next logical field in view
- pressing Enter key automatically jumps to next input field and by end automatically adds new task
- in QuickTasks, RsyncUI flips local and remote when executing a syncremote task, aka pull data from remote to local
- Quicktask is also only valid if there is a remote server involved, for local attached volumes use the macOS Finder
- there is a minor issue selecting a task for adding parameters to rsync within the Rsync parameters view
- the issue occurs if there is selected a task in the main Synchronize view, switch to Rsync parameters view, selecting the task will not enable adding parameters to rsync
- the issue is caused due to the fix for situation where RsyncUI become unresponsive with a bouncing beach ball, see comments for blog about version 2.5.3
- the global replace function, in Tasks view, select the Globus on the toolbar
- left text field part of text to be replaced
- right text field replaced with what
- any part of word may be replaced, as soon as you start type in the replace field the view is dynamically updated
Version 2.5.3
Version 2.5.3 (build 145) - May 14, 2025
A note about the term folder and directory. Quote ChatGPT: “Both “folder” and “directory” refer to the same concept: a container used to organise files on a computer. “Folder” is more commonly used in graphical user interfaces, while “directory” is often used in command-line environments. They are interchangeable in meaning, with the context determining which term is preferred.
As my native language isn’t English, I sometimes struggle with word distinctions. Currently, RsyncUI uses the term “folder,” and the RsyncUI documentation uses both terms “folder” and “directory.” I might change “folder” to “directory” in the RsyncUI, but I’d appreciate native English speakers’ guidance to ensure accuracy and clarity.
There has been a lot of UI-updates and cleanups of the UI within the latest release. There are no changes to the model part or the process part, except for the schedule function which is new.
Recently, I encountered an unusual hang issue with RsyncUI, characterized by a spinning beach ball effect that caused RsyncUI to freeze. Yesterday, I was able to identify the specific cause and location of the problem, although the underlying reason remains unknown.
By simply commenting out a single line of code, I can reproduce the hang. I have attempted to pinpoint the cause using Xcode, but Xcode remains silent and does not generate any errors. While I suspect a potential bug in SwiftUI, the most significant achievement is that I have successfully recreated the hang and identified the precise code fix.
Major updates in the this release are, a detailed changelog on the GitHub release page. And thanks very much to Johnny Sauce for valuable input and feedback.
- a fix for the above mentioned spinning beach ball
- the Verify Remote is now not enabled by default, enable in User settings
- the Verify Remote function is slightly changed as well
- please read about the function in section Verify Remote
- update changes in User settings is now manual update, the automated save settings when changed did not work 100%
- there is a new view for Verify Tasks
- the verify buttons, play icon, in Tasks and Rsync parameters are removed
- the new view is very explicit about dry-run parameter is enabled
- German and Norwegian localization are deleted
- 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
- the Tasks view is cleaned up, new help button for info add and delete the –delete parameter to rsync
- add or delete, by default not added when new tasks
- the Rsync parameters view is cleaned up, new help button for info add and delete the –delete parameter to rsync
- the annoying popup when adding SSH-keys in Tasks and SSH-settings are removed and replaced
- the values added are marked red text until values are compliant with validated input
- example, if you add like
22d
in SSH port number, the text is marked red until the letterd
is removed
- the annoying popup adding your own path for rsync and restore path is also removed and replaced with red text until validated OK
- a new calendar for schedule actions, please read about the scheduler below before commence using it
- to delete a schedule, just select it and press the back space button
The scheduler is implemented by using the Timer library, quote Apple: “A timer that fires after a certain time interval has elapsed, sending a specified message to a target object.” The timer has a strong reference to the run loop on the main thread. That also means that if the application goes to sleep, so does the run loop. And the timer is only active as long as RsyncUI is active.
Its primary function is to automate selected synchronization of tasks as long as RsyncUI is alive. It may prove useful for users who require scheduled synchronization of data during work. RsyncUI may be minimized or not the active window and the timer will still work. But if you leave your Mac and it goes to sleep, the timer will not work.
Number of files
Numbers updated: May 23, 2025, version 2.5.5
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
314 text files.
281 unique files.
70 files ignored.
github.com/AlDanial/cloc v 2.04 T=0.08 s (3376.8 files/s, 555122.6 lines/s)
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
Text 6 12 0 21921
Swift 229 2361 2690 17892
XML 25 0 0 593
C 2 36 72 254
JSON 8 0 0 180
make 1 22 2 59
Markdown 6 33 0 48
YAML 2 0 0 12
Bourne Shell 1 0 1 2
C/C++ Header 1 1 3 0
-------------------------------------------------------------------------------
SUM: 281 2465 2768 40961
-------------------------------------------------------------------------------
Main Repository:
- RsyncUI (https://github.com/rsyncOSX/RsyncUI) - The primary repository for RsyncUI.
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.
- RsyncArguments (https://github.com/rsyncOSX/RsyncArguments) - Generate parameters for
rsync
based on configurations. - sshCreateKey (https://github.com/rsyncOSX/sshCreateKey) - Assist in creating an SSH identity file and key using RsyncUI.
- Generate an RSA-based SSH key for default and user-defined keys, including the SSH port number.
- DecodeEncodeGeneric (https://github.com/rsyncOSX/DecodeEncodeGeneric) - Generic code for decoding and encoding JSON data.
- ParseRsyncOutput (https://github.com/rsyncOSX/ParseRsyncOutput) - Parse and extract numerical values from the output of
rsync
. This data is used to display details and log results for synchronized tasks. - RsyncUIDeepLinks (https://github.com/rsyncOSX/RsyncUIDeepLinks) - parse end return valid URL deeplink for execute tasks direct within RsyncUI
Historic releases
Previous releases
Version and dates only:
- Version 2.4.1 - 2025-03-21
- Version 2.3.9 - 2025-03-09
- Version 2.3.7 - 2025-03-07
- Version 2.3.5 - 2025-02-26
- Version 2.3.4 - 2025-02-12
- Version 2.3.2 - 2025-01-29
- Version 2.3.0 - 2025-01-15
- 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
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() {
Task {
async let readconfigurations = ActorReadSynchronizeConfigurationJSON()
let data = 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
rsyncUIdata.configurations = data
}
}
The below code is unstructured concurrency. The root function readconfigurations()
may be completed before the asynchronous code within the Task {}
.
func readconfigurations() {
Task {
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()
}
}
Access to properties within an actor must be performed asynchronously, that is why the Task { ... }
above is requiered. The Swift runtime makes sure that only one thread a time get access to properties within an actor. The code below is probably not an OK example. The main reason for make this an actor is to execute it on a thread from the CTP for not block the Main Thread.
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).
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
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
