Synchronizing files using file provider extensions
At a glance
| Item | Summary |
|---|---|
| Purpose | Make remote files available in macOS and iOS, and synchronize their states by using file provider extensions. |
| App architecture | A C/Objective-C header, Swift sample bundle with entry-bearing project variants FruitBasket-iOS, FruitBasket, each leading to FileProvider APIs. |
| Main patterns | View-controller organization, Protocol-oriented abstraction, Delegate or data-source callbacks, Service object, Central store, Actor isolation |
| Project style | 62 scanned source file(s) across C/Objective-C header, Swift, organized around ranked entry, type, and file boundaries. |
| Execution model | Source-visible boundaries: Task, await suspension point, MainActor.run, Sendable or @Sendable, DispatchQueue.main; none alone proves a background thread. |
| State/event model | Source-visible mechanisms: ObservableObject, @Published, NotificationCenter. |
| Key frameworks/packages | Common, FileProvider, os, Foundation, SwiftUI; these are source dependencies, not architecture labels. |
Project structure
Source bundle/
├── FruitBasket-iOS/
│ ├── FruitBasketApp.swift
│ ├── SettingsView.swift
│ ├── AddDomain.swift
│ └── ContentView.swift
├── FruitBasket/
│ └── AppDelegate.swift
├── Common/
│ ├── AccountService.swift
│ ├── DomainService.swift
│ ├── EnumerationView.swift
│ └── ContentBasedChunkStore.swift
├── Provider/
│ └── main.swift
├── Action/
│ └── ConflictViewController.swift
└── Server/
└── ItemDatabase.swift
Structure observations
- Architecturally prominent files are ranked from entry points and role-named declarations; resource-only paths are omitted.
- Primary languages: C/Objective-C header, Swift.
- The verified tree contains 20 project/configuration file(s) and 182 source declaration(s).
Overall architecture
flowchart LR
Bundle["Sample bundle"]
V1["FruitBasket-iOS"]
V2["FruitBasket"]
Boundary["FileProvider APIs"]
Bundle --> V1
V1 --> Boundary
Bundle --> V2
V2 --> Boundary
Reference code
FruitBasket-iOS/FruitBasketApp.swift:10 — architecture anchor
@main
struct FruitBasketApp: App {
var body: some Scene {
WindowGroup {
ContentView().navigationTitle("Domains")
}
}
}Interpretation
The branches represent separate entry-bearing project variants in the downloaded bundle, not runtime calls between those variants. Each branch is intentionally collapsed at the documented framework boundary; the detailed target-local flow remains in the cited files. Ownership is claimed only where the next section cites a stored property or assignment.
Ownership and state
classDiagram
Account *-- String : identifier
Account *-- String : secret
Account *-- String : displayName
Account o-- Entry : rootItem
Ownership evidence
Common/AccountService.swift:15 — stored dependency or nearest verified ownership anchor
public struct Account: Codable {
public let identifier: String
// ...
}| Owner | Object or state | Relationship | Mutation authority |
|---|---|---|---|
Account |
String (identifier) |
owns value state | Initialized by the owner; the binding is immutable |
Account |
String (secret) |
owns value state | Initialized by the owner; the binding is immutable |
Account |
String (displayName) |
owns value state | Initialized by the owner; the binding is immutable |
Account |
Entry (rootItem) |
stores or receives | Initialized by the owner; the binding is immutable |
Composition arrows indicate a source-visible construction expression or locally owned value state; aggregation means the owner stores or receives a dependency without proving exclusive lifetime ownership.
Concurrency, scheduling, and thread safety
Evidence limit: actor isolation, async/await, or Task creation does not by itself prove background-thread execution; Sendable conformance alone does not prove thread-safe mutation.
| Concern | Source mechanism | Verified placement or handoff | Evidence |
|---|---|---|---|
| Task creation | Task |
The source creates an unstructured task; surrounding context determines inherited actor isolation. | Action/AuthenticationViewController.swift:42 |
| Suspension boundary | await suspension point |
The source declares or crosses an asynchronous boundary; it does not by itself establish background execution. | Action/AuthenticationViewController.swift:51 |
| Main isolation | MainActor.run |
The cited operation explicitly enters a main-actor-isolated region. | Action/ConflictViewController.swift:93 |
| Transfer contract | Sendable or @Sendable |
The source declares a sendability boundary; this alone does not synchronize mutable state. | Common/DigitFormatter.swift:10 |
| Queue scheduling | DispatchQueue.main |
The source addresses the main dispatch queue. | Common/EnumerationView.swift:126 |
@MainActor/MainActor.run, DispatchQueue.main, and RunLoop.main are reported as distinct isolation, queue, and event-loop mechanisms. A plain Task is kept separate from Task.detached; neither is labeled as a background thread.
Reference code
Action/AuthenticationViewController.swift:42 — representative execution boundary
Task {
// ...
let conn = DomainConnection(domainIdentifier: self.actionViewController.domain.identifier.rawValue, secret: "",
hostname: UserDefaults.sharedContainerDefaults.hostname, port: self.port)
// ...
}State propagation, frameworks, and dependencies
Evidence limit: an import proves a source-level compilation dependency at the cited line; it does not prove runtime use, architectural adoption, or whether a Swift package is a direct application dependency.
| Category | Mechanism or module | Verified role | Evidence |
|---|---|---|---|
| State propagation | ObservableObject |
ObservableObject supplies an observation contract. | Common/EnumerationView.swift:20 |
| State propagation | @Published |
A published property can emit owner-controlled changes. | Common/EnumerationView.swift:23 |
| State propagation | NotificationCenter |
NotificationCenter distributes named process-local events. | Common/EnumerationView.swift:58 |
| Source import | Common |
The cited file imports this module; runtime use and architectural role are not inferred. | Action/ActionViewController.swift:10 |
| Source import | FileProvider |
The cited file imports this module; runtime use and architectural role are not inferred. | Common/AccountService.swift:10 |
| Source import | os |
The cited file imports this module; runtime use and architectural role are not inferred. | Action/ActionViewController.swift:12 |
| Source import | Foundation |
The cited file imports this module; runtime use and architectural role are not inferred. | Common/AccountService.swift:8 |
| Source import | SwiftUI |
The cited file imports this module; runtime use and architectural role are not inferred. | Common/EnumerationView.swift:8 |
receive(on:) describes downstream delivery scheduling, while subscribe(on:) describes upstream subscription/request/cancel scheduling. An import Combine alone establishes neither behavior nor a Store, reducer, Redux, or other application architecture.
Class and protocol design
Common/ContentBasedChunkStore.swift:11 — representative type boundary
public protocol ContentBasedChunkStore {
func chunkExists(sha256OfChunk: Data) -> Bool
func createChunk(sha256OfChunk: Data, chunkData: Data) throws
func retrieveChunk(sha256OfChunk: Data) throws -> Data?
func close()
}| Type | Responsibility | Depends on or conforms to |
|---|---|---|
FruitBasketApp |
Application entry and top-level composition | App |
AppDelegate |
Receives callback-driven events | NSObject, NSApplicationDelegate, NSXPCListenerDelegate |
ContentBasedChunkStore |
Defines a capability or collaboration contract | Concrete collaborators/imported frameworks |
ConcreteActionViewController |
Defines a capability or collaboration contract | NSViewController |
MillisecondTransformer |
Transforms feature data or representations | ValueTransformer |
PercentageTransformer |
Transforms feature data or representations | ValueTransformer |
AccountService |
Framework-facing operations | Concrete collaborators/imported frameworks |
DomainService |
Framework-facing operations | Concrete collaborators/imported frameworks |
EnumerationView |
User-interface presentation and input forwarding | View |
SettingsView |
User-interface presentation and input forwarding | View |
The source explicitly defines local protocol relationships: AuthenticationViewController → ConcreteActionViewController, ConflictViewController → ConcreteActionViewController, InfoParameter → JSONParameter, ListAccountParameter → JSONParameter, CreateAccountParameter → JSONParameter, RemoveAccountParameter → JSONParameter.
Access control
| Symbol | Access | Verified effect | Likely rationale |
|---|---|---|---|
logger (Action/ActionViewController.swift:19) |
private |
Use is restricted to the lexical declaration and same-file extensions allowed by Swift. | Inference: keep state mutation or dependency lifetime inside the owning implementation. |
prepare (Action/ActionViewController.swift:42) |
public |
The symbol is visible to importing modules. | Inference: make the declaration available across a module or target boundary. |
prepare (Action/ActionViewController.swift:50) |
public |
The symbol is visible to importing modules. | Inference: make the declaration available across a module or target boundary. |
logger (Action/AuthenticationViewController.swift:15) |
private |
Use is restricted to the lexical declaration and same-file extensions allowed by Swift. | Inference: keep state mutation or dependency lifetime inside the owning implementation. |
Reference code
Action/ActionViewController.swift:19 — representative boundary
public class ActionViewController: FPUIActionExtensionViewController {
private let logger = Logger(subsystem: "com.example.apple-samplecode.FruitBasket", category: "action")
// ...
}Swift declarations without a modifier are internal; explicit private, fileprivate, private(set), public, or open entries above are interpreted by language semantics. Objective-C/C samples instead rely on header and implementation boundaries, which are not equivalent to Swift lexical privacy.
Logic ownership and placement
| Logic | Owning type or file | Placement rationale |
|---|---|---|
| Application entry and top-level composition | FruitBasketApp |
The source’s App suffix makes this role explicit. |
| View lifecycle, callbacks, and feature coordination | ActionViewController, AuthenticationViewController, ConcreteActionViewController, ConflictViewController |
The source’s Controller suffix makes this role explicit. |
| Receives callback-driven events | AppDelegate |
The source’s Delegate suffix makes this role explicit. |
| Observes and relays feature changes | DomainObserver |
The source’s Observer suffix makes this role explicit. |
| Framework-facing operations | AccountService, DomainService |
The source’s Service suffix makes this role explicit. |
| Centralized state or persistence access | ContentBasedChunkStore, LocalContentBasedChunkStore |
The source’s Store suffix makes this role explicit. |
| Transforms feature data or representations | EditDateValueTransformer, MillisecondTransformer, PercentageTransformer |
The source’s Transformer suffix makes this role explicit. |
Design patterns
| Pattern | Source evidence | Purpose or tradeoff |
|---|---|---|
| View-controller organization | Action/ActionViewController.swift:18 |
A controller is the verified coordination boundary; this is MVC-style only where a separate model is present. |
| Protocol-oriented abstraction | Action/AuthenticationViewController.swift:14 |
A local protocol and concrete conformance create an explicit capability boundary. |
| Delegate or data-source callbacks | Extension/Extension+Servicing.swift:51 |
Callback protocols invert event delivery back into the sample’s owner. |
| Service object | Common/AccountService.swift:13 |
A role-named service contains framework-facing operations. |
| Central store | Common/ContentBasedChunkStore.swift:11 |
A store-named type centralizes feature state or persistence. |
| Actor isolation | FruitBasket/AppDelegate.swift:23 |
A declared actor creates an explicit isolation boundary; its executor is not described as a background thread. |
Main application flow
sequenceDiagram
participant ConflictViewController
participant MainActor
participant FileManager
participant service
ConflictViewController->>ConflictViewController: getUserVisibleURL()
ConflictViewController->>MainActor: Run UI-isolated work
ConflictViewController->>FileManager: FileManager()
ConflictViewController->>service: fileProviderConnection()
Reference code
Action/ConflictViewController.swift:97 — withServiceConnection()
nonisolated
internal func withServiceConnection() async throws -> FruitBasketServiceV1 {
let url = try await self.manager.getUserVisibleURL(for: self.itemIdentifier)
await MainActor.run {
self.itemURL = url
}
let services = try await FileManager().fileProviderServicesForItem(at: url)
guard let service = services[fruitBasketServiceName] else {
self.logger.error("couldn't get service, fruitBasketServiceName not present")
throw NSFileProviderError(.providerNotFound)
}
let connection: NSXPCConnection
connection = try await service.fileProviderConnection()
connection.remoteObjectInterface = fruitBasketServiceInterface
connection.interruptionHandler = {
self.logger.error("service connection interrupted")
}
connection.resume()
guard let proxy = connection.remoteObjectProxy as? FruitBasketServiceV1 else {
throw NSFileProviderError(.serverUnreachable)
}
return proxy
}Naming conventions
- Types: App: FruitBasketApp; Controller: ActionViewController, AuthenticationViewController, ConcreteActionViewController, ConflictViewController, EditDomainWindowController; Delegate: AppDelegate; Observer: DomainObserver; Service: AccountService, DomainService; Store: ContentBasedChunkStore, LocalContentBasedChunkStore; Transformer: EditDateValueTransformer, MillisecondTransformer, PercentageTransformer; View: BlockedProcessesEditorView, ContentView, DomainDetailView, EnumerationView, RemoteAccountView.
- Protocols:
ContentBasedChunkStore,ConcreteActionViewController,JSONParameter,FruitBasketServiceV1,XAttrGettable,DispatchBackend,BackendCallRegistration,XAttrSettable. - Methods:
notify,serverChangeReceived,signalItems,setupDomainPipe,setupDomainPipeAsync,handleDomainPipeError,itemsChanged,updateItemCount. - Files:
FruitBasket-iOS/FruitBasketApp.swift,FruitBasket/AppDelegate.swift,Common/AccountService.swift,Common/DomainService.swift,Common/EnumerationView.swift,FruitBasket-iOS/SettingsView.swift.
Architecture takeaways
FruitBasketAppis the main source-visible entry or composition anchor for this sample.- Framework work reaches Common, FileProvider, SwiftUI, SQLite through a deliberately small high-level chain; the detailed API graph remains inside the cited implementation files.
- Stored-property evidence identifies lifecycle collaboration; it does not by itself prove exclusive object ownership.
- Access-control conclusions separate verified language visibility from the likely design rationale.
- Local protocol relationships provide an explicit substitution boundary.
Source map
| Source file | Relevant symbols |
|---|---|
FruitBasket-iOS/FruitBasketApp.swift |
Cited implementation, FruitBasketApp |
Common/AccountService.swift |
Cited implementation, AccountService, FileProvider, Foundation, Account, InfoParameter, InfoReturn, ListAccountParameter, ListAccountReturn, CreateAccountParameter, CreateAccountReturn, RemoveAccountParameter, RemoveAccountReturn, ResetSyncAnchorParameter, ResetSyncAnchorReturn |
Common/ContentBasedChunkStore.swift |
ContentBasedChunkStore, LocalContentBasedChunkStore, ChunkFileLocator, HexEncodingOptions |
Action/ActionViewController.swift |
Cited implementation, ActionViewController, Common, os, ConcreteActionViewController |
Action/AuthenticationViewController.swift |
Cited implementation, Task, await suspension point, AuthenticationViewController |
Extension/Extension+Servicing.swift |
Cited implementation, FruitBasketServiceV1, FruitBasketServiceSource |
FruitBasket/AppDelegate.swift |
Notifier, AppDelegate, MillisecondTransformer, PercentageTransformer |
Action/ConflictViewController.swift |
MainActor.run, ConflictViewController, ConflictDescriptor, EditDateValueTransformer |
Common/DigitFormatter.swift |
Sendable or @Sendable, DigitFormatter |
Common/EnumerationView.swift |
DispatchQueue.main, ObservableObject, @Published, NotificationCenter, SwiftUI, EnumerationView, EnumerationType, EnumeratorObservableObject, State, Entry, ItemType, ItemRow |
Common/DomainService.swift |
JSONMethod, JSONParameter, DomainService, EntryType, Version, ConflictVersion, RankToken, ItemIdentifier, CodingKeys, EntryMetadata, ValidEntries, ExtendedAttributes, Entry, UserInfo, ContentStorageType, ContentSha256S, ContentStorageTypeParameter, ListFolderParameter, ListFolderReturn, ListChangesParameter, ListChangesReturn, LatestRankParameter, LatestRankReturn, CreateParameter, ConflictStrategy, CreateReturn, ModifyContentsParameter, ModifyContentsReturn, CreateDataChunkParameter, CreateDataChunkReturn, GetDataChunkParameter, GetDataChunkReturn, CheckChunkExistsParameter, CheckChunkExistsReturn, ModifyMetadataParameter, ModifyMetadataReturn, FetchItemParameter, FetchItemReturn, DownloadItemParameter, DownloadItemReturn, FetchItemContentStorageTypeParameter, FetchItemContentStorageTypeReturn, DeleteItemParameter, DeleteItemReturn, TrashItemParameter, TrashItemReturn, UpdateThumbnailParameter, UpdateThumbnailReturn, FetchThumbnailParameter, FetchThumbnailReturn, ConflictVersionsParameter, ConflictVersionsReturn, ResolveConflictVersionsParameter, ResolveConflictVersionsReturn, CreateConflictParameter, CreateConflictReturn, MarkParameter, MarkReturn, PingLockParameter, PingLockReturn, RemoveLockParameter, RemoveLockReturn, ForceLockParameter, ForceLockReturn, PushRegistrationParameter, PushRegistrationReturn, PushDevice, SimulatedError, AccessType, SimulateErrorParameter, SimulateErrorReturn, SimulateErrorListParameter, SimulateErrorListReturn, ListLocksParameter, ListLocksReturn, Lock |
FruitBasket-iOS/SettingsView.swift |
SettingsView, SubSettings, ServiceBrowser, ServiceEntry, State, HostListEntry, HostList |
Provider/main.swift |
Feature implementation |
FruitBasket-iOS/AddDomain.swift |
AccountFetcher, AddDomain, RemoteAccountView, NavigationPane, ViewState, AccountList, Fetching |
FruitBasket-iOS/ContentView.swift |
ContentView, NavigationPane, ActiveSheet |
Server/ItemDatabase.swift |
Synchronized, Lock, ItemDatabase, ConflictResolutionStrategy, SignalStrategy, ListenerWrapper, ChangeType |