Version 2.8.2

Version 2.8.2 (build 173) - not yet released

Why are these blogs about RsyncUI? I am writing these blogs primarily to document my work, but I believe it is important to be transparent about how I develop RsyncUI. I am not sure who reads these blogs, but I know that the changelogs are frequently read when there are new releases. Additionally, I am learning every day and consider myself an average competent Swift developer. I commenced use Visual Studio Code and MCP because I “stumbled” upon an YouTube video about them. In essence, my primary method of staying informed about Swift development involves reading blogs and watching videos created by other developers.

I have commenced a comprehensive code formatting cleanup utilizing SwiftLint, SwiftFormat, and periphery. Despite using these tools for several years, some cleanup was still required.

All of the aforementioned tools are widely recognized and utilized by numerous developers.

The periphery application is employed to eliminate unused code. SwiftFormat is utilized for code formatting and SwiftLint code quality. All instructions for SwiftFormat in code are removed, and both tools now have a single configuration file each containing instructions. They work very well together.

As a result, numerous code modifications have been made, primarily due to the formatting adjustments implemented using the aforementioned tools.

Visual Studio Code (VSC) and GitHub MCP services

Model Context Protocol (MCP) is an open standard, introduced in November 2024 by Anthropic. Quote Google “The Model Context Protocol (MCP) is an open standard designed to solve this. Introduced by Anthropic in November 2024, MCP provides a secure and standardized “language” for LLMs to communicate with external data, applications, and services. It acts as a bridge, allowing AI to move beyond static knowledge and become a dynamic agent that can retrieve current information and take action, making it more accurate, useful, and automated.”

I have recently begun utilizing Visual Studio Code (VSC) and its integration with GitHub MCP services. I initiated a request to VSC, utilizing the MCP services, to analyze the codebase for RsyncUI and identify issues in naming conventions. I am highly impressed by the capabilities of AI in supporting developers. The aforementioned request resulted in several code updates, and two reports have been generated as a result. These reports are authored by AI and are accessible from the root directory of the RsyncUI repository.

Although Xcode remains my primary development tool, Visual Studio Code (VSC) is also officially supported by swift.org. Utilizing Xcode ensures that the latest Swift toolchain is always up-to-date. VSC can also utilize the Xcode-installed toolchain, and it offers a wide range of extensions.

Version 2.8.1

Version 2.8.1 (build 172) - Dec 5, 2025

An AI-generated changelog is available at the bottom of this page.

This maintenance release addresses a few issues.

All annoying messages regarding missing files are resolved.

Empty stats file

Please see the note about no stats in the changelog (blog) for version 2.8.0.

If logging fails due to the aforementioned reason, this error will be generated, if enabled in version 2.8.2.

Release Notes: v2.8.0 β†’ v2.8.1

Overview

This release cycle includes 25 commits across 32 files, focusing on stability improvements, architectural refactoring, and enhanced error handling. The release emphasizes code maintainability and robustness.

Stats: 526 insertions(+), 386 deletions(-)


πŸ—οΈ Architectural Improvements

Execution Logic Refactoring

  • Split EstimateExecute.swift into separate Estimate.swift and Execute.swift files
  • Cleaned up initialization logic and removed convenience initializers
  • Improved separation of concerns between estimation and execution phases
  • Refactored EstimateExecute initialization logic

Error Handling & Robustness

  • Enhanced error handling in RemoteDataNumbers initialization
  • Added fallback mechanisms for missing stats with default values
  • Improved error catching in log record handling
  • Added conditional error handling when appending default stats to logs
  • Refactored error handling and logging method names

Code Cleanup

  • Removed network monitoring feature and related code
  • Removed sleeptime setting and configuration (after initial addition, then reverted)
  • Removed WidgetVerify extension and related files
  • Removed verify URL support and related code
  • Cleaned up unused code in estimation initialization

πŸ“Š Logging & Monitoring Enhancements

Advanced Logging System

  • Refactored log record handling with improved error catching
  • Added conditional logging for summary log records
  • Added debug logging for getstats() success and failure tracking
  • Added commented-out function for permanent log storage (future feature)
  • Improved rsync output parsing to use getstats() with comprehensive error handling
  • Updated Logging.swift with enhanced functionality

Log Settings

  • Updated ObservableLogSettings.swift with refined configurations
  • Enhanced log file handling in storage actors
  • Improved ActorReadLogRecordsJSON.swift error handling

🎨 UI & Progress Tracking

Progress View Improvements

  • Extracted SynchronizeProgressView to separate file for better modularity
  • Added RestoreProgressView for improved progress display during restore operations
  • Set max value on dry run in restore and quicktask operations
  • Refactored progress view with max value support
  • Improved UI progress indicators and logging

View Enhancements

  • Increased minHeight for sheet views in TasksView
  • Refactored URL estimate button logic in AddTaskView
  • Updated query item handling in SidebarTasksView
  • Updated estimation in progress view

πŸ”§ Process & Configuration Management

Path & Catalog Handling

  • Fixed config path concatenation in Homepath
  • Refactored catalog and volume models and services
  • Updated catalog path logic and progress handling
  • Added sharedPathForRestore to Params with formatted restore arguments
  • Refactored path concatenation and alert handling

Process Management

  • Refactored RsyncProcess termination configuration
  • Improved TCPconnections handling
  • Enhanced execution flow in estimation and task views
  • Updated Execute.swift with improved process handling

πŸ“¦ Dependencies & Project Structure

  • Updated Package.resolved and RsyncUIDeepLinks package revision
  • Updated Makefile configuration
  • Updated project.pbxproj with structural changes
  • Added version 2.8.1 JSON configuration files
  • Updated README.md and Sonoma JSON to reference v2.8.0

🧹 Code Quality Improvements

  • Consistent code formatting improvements across multiple files
  • Enhanced ReadAllTasks utility with better error management
  • Improved alert handling patterns in views
  • Refactored ActorReadSynchronizeConfigurationJSON for better readability
  • Updated EditValueErrorScheme with refined error handling

Key Files Modified

  • EstimateExecute (split into Estimate.swift and Execute.swift)
  • Logging.swift
  • RemoteDataNumbers.swift
  • SynchronizeProgressView.swift (extracted)
  • RestoreTableView.swift
  • Multiple view files in Views

Testing Focus

  • Verify estimation and execution separation works correctly
  • Test error handling for missing stats and log records
  • Validate progress tracking in restore operations
  • Confirm all removed features (network monitoring, verify widgets) don’t cause issues

Release Target: v2.8.1rc1
Branch: version-2.8.1
Previous Release: v2.8.0

Version 2.8.0

Version 2.8.0 (build 171) - Dec 2, 2025

An AI-generated changelog is available at the bottom of this page.

The following summarizes changes in this release:

  • The major update within this release is that all seven Swift Packages (SPM) are updated to version 2.0.
    • All SPM are validated by their own test, using the new Swift Testing framework.
  • Several cleanups and refactorings have been made in the code.
    • There are approximately 17,000 lines of Swift code, and some code has not been reviewed for some time.
      • It has been about five years since I commenced development of RsyncUI, based on code end experience from my previous project.
      • I have learned a lot during these years, and now most of the code has been reviewed at least once since the start of development.
      • Almost every time I review old code, there are some refactorings, simplifications, and cleaning of the code.
  • A bug in the restore data functionality has been resolved.
  • Only one Widget is now available, Estimate and Execute.
  • All synchronization, such as quicktask and restore, now includes a progress bar if there has been an estimate ahead.
  • Real-time capture of rsync includes capturing to a file. Users can view either the RsyncUI logfile or the rsync capture to file in the view logfile.
  • A gesture has been added to indicate when buttons are pressed.

No stats

There is more info about the upcoming maintenance release, version 2.8.1, about this issue in the blog about version 2.8.1

Swift Packages

All SPM packages include their own testing mechanisms, and all tests have been successfully passed. SPM packages are generally small and focused on a specific purpose, which simplifies testing for edge cases and typical usage scenarios.

Main Repository

Swift Packages used by RsyncUI

All SPM packages are refactored, updated, and checked into the main branch. RsyncUI is a depended on all packages, but the last one is not mandatory. SSH keys can be generated via command line.

Development Summary: v2.7.8 β†’ v2.8.0

Overview

This issue summarizes the major development work completed since the release of version 2.7.8, including architectural improvements, UI enhancements, and new features leading toward version 2.8.0.

πŸ“Š Development Statistics

pie title "Commits by Category (70 total commits)"
    "Refactoring" : 25
    "UI/UX Improvements" : 18
    "Feature Additions" : 15
    "Package Updates" : 12

πŸ—οΈ Major Feature Areas

mindmap
  root((v2.7.8 β†’ v2.8.0))
    Progress Tracking
      RestoreProgressView
      SynchronizeProgressView
      Max value support
      Real-time updates
    Logging System
      Dedicated log window
      Actor-based concurrency
      File output option
      Delete optimization
    Architecture
      Params struct
      SSHParams refactor
      Actor concurrency
      Path handling
    UI Refinements
      Glass button style
      Dynamic text colors
      Toolbar layouts
      Progress indicators
    Widget System
      Remove WidgetVerify
      URL handling updates
      DeepLinks improvements

πŸ”„ Development Flow

gitGraph
    commit id: "v2.7.8 Release"
    branch version-2.7.9
    checkout version-2.7.9
    commit id: "Add log window"
    commit id: "Actor concurrency"
    commit id: "File output option"
    commit id: "Glass button style"
    commit tag: "v2.7.9rc1"
    commit id: "Process termination"
    commit id: "Widget refinements"
    commit id: "Restore progress"
    commit tag: "v2.7.9rc2"
    checkout main
    merge version-2.7.9
    branch version-2.8.0
    checkout version-2.8.0
    commit id: "Catalog refactor"
    commit id: "Progress improvements"
    commit id: "Path concatenation fix"
    commit id: "EstimateExecute refactor"
    commit id: "Current HEAD"

πŸ“¦ Major Refactoring Initiatives

1. Architecture & Code Organization

graph TD
    A[Monolithic Code] --> B[Structured Architecture]
    B --> C[Params Struct]
    B --> D[SSHParams Struct]
    B --> E[Actor-based Logging]
    B --> F[Service Layer]
    
    C --> C1[Centralized Parameters]
    D --> D1[SSH Handling]
    E --> E1[DeleteLogrecords Actor]
    E --> E2[ActorReadLogRecordsJSON]
    F --> F1[AttachedVolumesService]
    F --> F2[HomeCatalogsService]
    
    style B fill:#90EE90
    style C fill:#87CEEB
    style D fill:#87CEEB
    style E fill:#87CEEB
    style F fill:#87CEEB

2. Progress Tracking System Enhancement

sequenceDiagram
    participant User
    participant UI
    participant Process
    participant Progress
    
    User->>UI: Initiate Task
    UI->>Process: Start rsync
    Process->>Progress: Report progress
    Progress->>UI: Update RestoreProgressView
    Progress->>UI: Update SynchronizeProgressView
    UI->>User: Real-time feedback
    Process->>Progress: Set max value
    Progress->>UI: Display completion %
    UI->>User: Task complete

3. Logging System Modernization

graph LR
    A[Old Logging] --> B[New Logging System]
    
    B --> C[Dedicated Window]
    B --> D[Actor Concurrency]
    B --> E[File Output]
    B --> F[Optimized Deletion]
    
    C --> C1[Separate Log View]
    D --> D1[Thread-safe Operations]
    E --> E1[Persistent Logs]
    F --> F1[Batch Processing]
    
    style A fill:#FFB6C6
    style B fill:#90EE90
    style C fill:#87CEEB
    style D fill:#87CEEB
    style E fill:#87CEEB
    style F fill:#87CEEB

🎨 UI/UX Improvements

graph TD
    A[UI Enhancements] --> B[Visual Polish]
    A --> C[Component Updates]
    A --> D[Layout Improvements]
    
    B --> B1[Glass Button Style]
    B --> B2[Sustained Pressure Animation]
    B --> B3[Dynamic Text Colors]
    
    C --> C1[ConditionalGlassButton]
    C --> C2[RestoreProgressView]
    C --> C3[SynchronizeProgressView]
    
    D --> D1[Toolbar Layouts]
    D --> D2[Sheet View Heights]
    D --> D3[Progress Indicators]
    
    style A fill:#FFD700
    style B fill:#87CEEB
    style C fill:#87CEEB
    style D fill:#87CEEB

πŸ”§ Technical Improvements

Parameter Handling Refactor

Before:

  • Parameters scattered across multiple classes
  • Inconsistent SSH parameter creation
  • Duplicated argument construction logic

After:

  • Centralized Params struct
  • Dedicated SSHParams struct
  • Consistent parameter construction
  • Better code reusability

Concurrency Model

Key Changes:

  • Introduction of Actor-based concurrency for log management
  • DeleteLogrecords actor for safe log deletion
  • ActorReadLogRecordsJSON optimization
  • Thread-safe state management

Path Handling

Improvements:

  • URL extension for home directory access
  • Fixed config path concatenation
  • Better catalog path logic
  • Unified path handling approach

πŸ—‘οΈ Cleanup & Removal

graph LR
    A[Removed Components] --> B[WidgetVerifyExtension]
    A --> C[Verify URL Support]
    A --> D[AllOutputView]
    A --> E[Unused Environment Variables]
    A --> F[Convenience Initializers]
    
    B --> B1[Simplified widget system]
    C --> C1[Streamlined URL handling]
    D --> D1[Replaced with RsyncRealtimeView]
    E --> E1[Cleaner codebase]
    F --> F1[Explicit initialization]
    
    style A fill:#FFB6C6

πŸ“ˆ Commit Activity Timeline

gantt
    title Development Timeline (v2.7.8 β†’ v2.8.0)
    dateFormat  YYYY-MM-DD
    section Logging System
    Actor concurrency & log management    :done, 2024-10-01, 2024-10-15
    Dedicated log window                  :done, 2024-10-15, 2024-10-25
    File output & optimization            :done, 2024-10-25, 2024-11-05
    
    section Architecture
    Params & SSHParams refactor           :done, 2024-10-20, 2024-11-10
    Service layer improvements            :done, 2024-11-15, 2024-11-25
    
    section UI/UX
    Glass button & animations             :done, 2024-10-28, 2024-11-08
    Progress tracking enhancements        :done, 2024-11-10, 2024-11-25
    
    section Progress System
    Restore progress view                 :done, 2024-11-12, 2024-11-20
    Synchronize progress extraction       :done, 2024-11-22, 2024-11-28
    Max value support                     :done, 2024-11-28, 2024-12-02
    
    section Cleanup
    Widget removal & refinements          :done, 2024-11-05, 2024-11-15
    Code cleanup & optimization           :done, 2024-11-20, 2024-12-02

🎯 Key Achievements

  1. βœ… Enhanced Progress Tracking

    • New RestoreProgressView for better restore operation visibility
    • Extracted SynchronizeProgressView to separate file
    • Added max value support for accurate progress percentages
    • Improved UI progress indicators across all views
  2. βœ… Modern Logging System

    • Dedicated log window for better log management
    • Actor-based concurrency for thread-safe operations
    • File output option for persistent logging
    • Optimized log deletion with batch processing
  3. βœ… Architectural Improvements

    • Introduced Params struct for centralized parameter management
    • Created SSHParams struct for SSH handling
    • Refactored catalog and volume models with service layer
    • Improved error handling and method naming consistency
  4. βœ… UI Polish

    • Glass button style with sustained pressure animations
    • Dynamic text colors for better visual feedback
    • Refined toolbar layouts across multiple views
    • Increased sheet view heights for better content display
  5. βœ… Code Quality

    • Removed deprecated widget components
    • Fixed path concatenation issues
    • Eliminated unused code and environment variables
    • Improved code organization and modularity

πŸ” Code Quality Metrics

graph LR
    A[Code Improvements] --> B[+2500 lines added]
    A --> C[-1800 lines removed]
    A --> D[Net: +700 lines]
    
    B --> B1[New features]
    B --> B2[Better structure]
    
    C --> C1[Removed redundancy]
    C --> C2[Cleaned deprecated code]
    
    D --> D1[More functionality]
    D --> D2[Better maintainability]
    
    style A fill:#90EE90
    style B fill:#87CEEB
    style C fill:#FFB6C6
    style D fill:#FFD700

πŸš€ Next Steps for v2.8.0

  • Final testing of all refactored components
  • Performance benchmarking of actor-based logging
  • User acceptance testing of new progress views
  • Documentation updates
  • Release notes preparation

πŸ“ Notes

  • Two release candidates (v2.7.9rc1 and v2.7.9rc2) were created during development
  • The current branch version-2.8.0 is actively being developed
  • All changes maintain backward compatibility with existing configurations
  • Swift package dependencies have been updated to their latest stable versions

Total Commits: 70
Files Changed: ~150+
Primary Focus Areas: Architecture, UI/UX, Logging, Progress Tracking
Development Period: Post v2.7.8 release β†’ Present

Verify remote

Overview

This post describes how I use the Verify remote function.

The Remote Setup

I back up my bird photography to multiple locations:

  • a Raspberry Pi 5 server configured with two WD Red SA500 2.5" SSD 1TB drives set up as a mirrored ZFS pool
  • a remote cloud service (JottaCloud)
  • two 1TB NVMe external SSD drives attached to my computers

The first two locations are updated whenever data changes. JottaCloud updates are managed by a service running on my Macs. Updates to the Raspberry Pi 5 server are performed using RsyncUI.

Currently, my bird photography consists of 140GB and 8,000 files, including RAW and sidecar files. During travel, I use my MacBook Pro for photo editing, while at home, I use my Mac Mini M4. I always keep track of which Mac has been updated and stores the most recent files.

For this post, I am using my MacBook Pro as the local device, while the remote server contains more recently updated data.

Requirements for Using Verify Remote

The Procedure

Select the Pictures profile that stores my task to synchronize my local picture raw catalog with my remote server. After selecting the task, open the Verify remote function.

During the initial run, the Adjust output toggle is disabled. When enabled, the function eliminates the last 16 lines of output from both pull and push operations, as well as all other lines that are identical in both. To initiate the evaluation, select the upward arrow.

The remote server has less data than my local Mac because I deleted some photos locally that were previously synced to the remote. The output from rsync is tagged with information that is evaluated by looking at the first characters of each line. The push data in the left table indicates that my local Mac contains more data, which makes sense because the remote needs to be updated to reflect the deleted photos.

The Adjust output toggle is enabled, and a new evaluation begins.

The remote table on the right indicates that there are updates to sidecars and local data that need to be deleted.

I perform a pull of data from the remote to update my local Mac.

Following the pull, Verify remote displays that my local Mac and remote device are in sync.

Number of files

Numbers updated: December 2, 2025, version 2.8.0

There is a very nice and excellent tool, 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/Sources ParseRsyncOutput/Sources RsyncArguments/Sources RsyncUI/RsyncUI RsyncUIDeepLinks/Sources
 SSHCreateKey/Sources RsyncProcess/Sources ProcessCommand/Sources
     203 text files.
     203 unique files.                                          
      16 files ignored.

github.com/AlDanial/cloc v 2.06  T=0.05 s (4239.5 files/s, 475137.0 lines/s)
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
Swift                          197           2518           2750          17058
C                                2             36             72            254
XML                              2              0              0             53
JSON                             1              0              0              6
C/C++ Header                     1              1              3              0
-------------------------------------------------------------------------------
SUM:                           203           2555           2825          17371
-------------------------------------------------------------------------------

Main Repository

Swift Packages used by RsyncUI

All SPM packages are refactored, updated, and checked into the main branch. RsyncUI is a depended on all packages, but the last one is not mandatory. SSH keys can be generated via command line.

Swift concurrency

First, I must acknowledge that my understanding of Swift concurrency is limited. While I have a basic grasp of the subject, if you are reading this and seeking more detailed information, I strongly recommend searching for and reading articles from other sources that provide a more comprehensive explanation of 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 by the cooperative thread pool (CTP), excluding and not blocking the main thread. How the executors and CTP work and interact are details I don’t know aboutβ€”they are managed by the Swift runtime. There are three kinds of executors:

  • the main executor manages jobs on the main thread
  • the global concurrent executor and the serial executor, both execute jobs on threads from the CTP

Most work in RsyncUI is executed on the main thread. By default, SwiftUI ensures all UI updates are performed on the main thread. Below are some tasks within RsyncUI that are executed on the main thread:

  • preparing and executing rsync synchronization tasks (preparing involves computing the correct arguments for rsync)
  • monitoring progress and termination of the actual rsync tasks
    • monitoring progress is an asynchronous sequence running on the main thread, created by an asynchronous sequence of two specific notifications generated by the notification center
  • some write and read operations

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 from 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.

Swift Concurrency and Asynchronous Execution

Concurrency and asynchronous execution are important parts of Swift. The latest version of Swift simplifies writing asynchronous code using the async and await keywords, as well as the actor protocol. While RsyncUI doesn’t inherently require concurrency, it is automatically introduced through the use of actors, async, and await keywords. The objective is to execute most work synchronously on the main thread, provided it doesn’t block the GUI.

Asynchronous execution can be performed on both the main thread and background threads from the CTP. When executing asynchronous operations on the main thread, it is crucial to use Swift’s structured concurrency, specifically the async/await keywords. The await keyword serves as a suspension point, allowing other and more critical tasks to access the main thread.

Cooperative Thread Pool (CTP)

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

  • read synchronization tasks from file
    • JSON data decoding: asynchronous decoding that inherits the thread from the actor reading data
    • JSON data encoding: synchronous encoding on the main thread
  • read and sort log records
  • delete log records
  • preparing output from rsync for display
  • preparing data from the log file 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 only five actors in RsyncUI, but there are more asynchronous functions, some of which run on the main thread as well.

Structured Concurrency

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

Structured concurrency might also dictate the order of execution. The keyword await is a suspension point where execution waits until the asynchronous function is completed before continuing. If there are several await statements in sequence, the next one will execute when the current asynchronous task is completed.

Example of structured concurrency

Approximately 800 log records, comprising the total of 1500, are selected for deletion. Two operations are concurrently executed on a background thread: the actual deletion of log records and the updating of non-deleted records for display.

The deletion of logs commences on the main thread. The user selects the logs to be deleted, and the delete button either initiates the deletion process or aborts it. The deletelogs() function, an asynchronous function, is initiated on the main thread.

Button("Delete", role: .destructive) {
      	Task {
              await deletelogs(selectedloguuids)
            }
  }

The deletelogs starts on the main thread and jumps off to an background thread inside the Task {}.

func deletelogs(_ uuids: Set<UUID>) async {
        Task {
        
            print("(1) start async let updatedRecords deletelogs")
            
            async let updatedRecords: [LogRecords]? = ActorReadLogRecordsJSON().deletelogs(
                uuids,
                logrecords: logrecords,
                profile: rsyncUIdata.profile,
                validhiddenIDs: validhiddenIDs
            )
            let records = await updatedRecords
            
            print("(2) awaited updatedRecords deletelogs from (1))")
            print("(3) start async let updatedRecords updatelogsbyhiddenID)")
            
            async let updatedLogs: [Log]? = ActorReadLogRecordsJSON().updatelogsbyhiddenID(records, hiddenID)
            logrecords = records
            logs = await (updatedLogs ?? [])
            
            print("(4) awaited updatedLogs from (3)")

            WriteLogRecordsJSON(rsyncUIdata.profile, records)
            selectedloguuids.removeAll()
        }
    }

The debug windows in Xcode display the following:

The actors also print whether they execute on the main thread. The first async let statement initiates execution, and the subsequent await statement for the result above (2) suspends the function’s execution until the asynchronous result is computed. The await statement is crucial for suspending the execution of the function until the asynchronous result is available. And then the next (3) and (4).

(1) start async let updatedRecords deletelogs
ActorReadLogRecordsJSON: deletelogs() NOT on main thread, currently on <NSThread: 0xa49e3c200>{number = 18}
ActorReadLogRecordsJSON: DEINIT
(2) awaited updatedRecords deletelogs from (1))
(3) start async let updatedRecords updatelogsbyhiddenID)
ActorReadLogRecordsJSON: updatelogsbyhiddenID() NOT on main thread, currently on <NSThread: 0xa49e3c280>{number = 17}
ActorReadLogRecordsJSON: DEINIT
(4) awaited updatedLogs from (3)
WriteLogRecordsJSON: writeJSONToPersistentStore file:///Users/thomas/.rsyncosx/VPxxxxxxxx/WDBackup/logrecords.json
WriteLogRecordsJSON DEINIT
ActorReadLogRecordsJSON: updatelogsbyhiddenID() NOT on main thread, currently on <NSThread: 0xa4bb72800>{number = 19}
ActorReadLogRecordsJSON: DEINIT

Tagging of data

Overview

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

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

Example:

  • Input string: Number of created files: 7,191 (reg: 6,846, dir: 345)
  • Converted to: [7191, 6846, 345] (the thousands separator is also removed from the string before parsing)

The function below extracts only numbers 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 rsync output is not particularly complex, though it differs somewhat between the latest version of rsync and the default versions.

Version 3.4.x

The trailing output from the latest version of rsync (version 3.4.1) looks 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)

Openrsync

The trailing output from the default version of rsync looks 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 the Tagging Works

The output from rsync is parsed and numbers are extracted. After parsing, these numbers determine whether data should be tagged for synchronization.

Latest Version of rsync

Three numbers determine whether data needs to be synchronized: the number of updates (regular files transferred), new files, and deleted files. Each can be either 0 or a positive number, and all three must be verified.

Default Versions

Only one number determines whether data needs to be synchronized: the number of updates (files transferred).