├── .gitignore ├── Examples ├── XPC Daemon │ ├── Calculator App │ │ ├── CalculatorApp.entitlements │ │ ├── CalculatorApp.swift │ │ ├── ContentView.swift │ │ └── SMAppService.Status+Description.swift │ ├── Calculator │ │ └── Calculator.swift │ ├── CalculatorDaemon │ │ ├── Assets.xcassets │ │ │ ├── AccentColor.colorset │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── CalculatorDaemon.entitlements │ │ └── main.swift │ ├── Config.xcconfig.template │ ├── Shared.swift │ ├── XPCDaemonExample.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ └── com.yourcompany.XPCDistributedActorExample.CalculatorDaemon.plist └── XPC Service │ ├── Calculator App │ ├── CalculatorApp.entitlements │ ├── CalculatorApp.swift │ └── ContentView.swift │ ├── Calculator │ └── Calculator.swift │ ├── CalculatorXPCService │ ├── CalculatorXPCService.entitlements │ ├── Info.plist │ └── main.swift │ ├── Config.xcconfig.template │ └── XPCServiceExample.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ └── contents.xcworkspacedata ├── LICENSE.txt ├── Package.swift ├── README.md └── Sources └── XPCDistributedActorSystem ├── Bundle+FirstXPCService.swift ├── CodeSigningRequirement.swift ├── GenericInvocationDecoder.swift ├── GenericInvocationEncoder.swift ├── InvocationRequest.swift ├── InvocationResponse.swift ├── InvocationResultHandler.swift ├── LiveActorStorage.swift ├── SigningInformationExtractor.swift ├── XPCActor.swift ├── XPCConnection.swift ├── XPCDaemonListener.swift ├── XPCDistributedActorSystem.swift ├── XPCError.swift ├── XPCMessageWithObject.swift └── XPCServiceListener.swift /.gitignore: -------------------------------------------------------------------------------- 1 | *.ipa 2 | *.dSYM.zip 3 | *.dSYM 4 | Packages/ 5 | Package.pins 6 | Package.resolved 7 | .build/ 8 | .swiftpm 9 | .DS_Store 10 | xcshareddata/ 11 | xcuserdata/ 12 | *.xcconfig 13 | -------------------------------------------------------------------------------- /Examples/XPC Daemon/Calculator App/CalculatorApp.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | com.apple.security.temporary-exception.mach-lookup.global-name 10 | 11 | com.yourcompany.XPCDistributedActorExample.CalculatorDaemon.XPC 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Examples/XPC Daemon/Calculator App/CalculatorApp.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | @main 4 | struct CalculatorApp: App { 5 | var body: some Scene { 6 | WindowGroup { 7 | ContentView() 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Examples/XPC Daemon/Calculator App/ContentView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import XPCDistributedActorSystem 3 | import Calculator 4 | import ServiceManagement 5 | 6 | struct ContentView: View 7 | { 8 | let service = SMAppService.daemon(plistName: "com.yourcompany.XPCDistributedActorExample.CalculatorDaemon.plist") 9 | 10 | @State private var xpc: XPCDistributedActorSystem? 11 | @State private var calculator: Calculator? // remote actor in the daemon 12 | @State private var output: String = "No calculation done" 13 | @State private var daemonStatus = "Unknown" 14 | 15 | var body: some View { 16 | VStack { 17 | VStack(alignment: .leading, spacing: 10) { 18 | Label("Important", systemImage: "exclamationmark.triangle") 19 | .bold() 20 | .font(.title) 21 | Text("If you click \"Install Launch Daemon\", a LaunchDaemon process with root privileges will be installed and started. This process will be kept running and automatically restarted if you don't uninstall it by clicking \"Uninstall Launch Daemon\".") 22 | Text("Make sure to uninstall it when you're done testing.") 23 | } 24 | .padding() 25 | .frame(maxWidth: .infinity) 26 | .foregroundStyle(.white) 27 | .background(.orange) 28 | Form { 29 | Section("Service Management (Deamon)") { 30 | LabeledContent("Service name") { 31 | Text("\(service)") 32 | } 33 | LabeledContent("Status") { 34 | HStack { 35 | Text(daemonStatus) 36 | if service.status == .requiresApproval { 37 | Button("Open System Settings") { 38 | SMAppService.openSystemSettingsLoginItems() 39 | } 40 | } 41 | } 42 | } 43 | LabeledContent("Actions") { 44 | HStack { 45 | Button("Install Launch Daemon") { 46 | do { 47 | try service.register() 48 | } catch { 49 | print("Unable to register \(error)") 50 | } 51 | updateDaemonStatus() 52 | } 53 | .disabled(service.status != .notRegistered && service.status != .notFound) 54 | Button("Uninstall Launch Daemon") { 55 | do { 56 | try service.unregister() 57 | } catch { 58 | print("Unable to register \(error)") 59 | } 60 | updateDaemonStatus() 61 | } 62 | .disabled(service.status != .enabled && service.status != .requiresApproval) 63 | } 64 | } 65 | } 66 | Section("XPC") { 67 | LabeledContent("Service identifier") { 68 | Text("\(daemonXPCServiceIdentifier)") 69 | } 70 | LabeledContent("Actions") { 71 | HStack { 72 | Button("Calculate") { 73 | guard let calculator else { 74 | output = "Distributed actor not set up" 75 | return 76 | } 77 | output = "Calculating..." 78 | Task { 79 | do { 80 | let number1 = Int.random(in: 0..<1000) 81 | let number2 = Int.random(in: 0..<1000) 82 | let result = try await calculator.add(number1, number2) 83 | output = "\(number1) + \(number2) = \(result)" 84 | } catch { 85 | output = "Failed to calculate: \(error.localizedDescription)" 86 | } 87 | } 88 | } 89 | Button("Call a remote function (no output)") { 90 | guard let calculator else { 91 | output = "Distributed actor not set up" 92 | return 93 | } 94 | Task { 95 | do { 96 | try await calculator.justARemoteFunction() 97 | output = "" 98 | } catch { 99 | output = "Failed to call remote function: \(error.localizedDescription)" 100 | } 101 | } 102 | } 103 | } 104 | } 105 | LabeledContent("Response") { 106 | Text(output) 107 | } 108 | } 109 | } 110 | .formStyle(.grouped) 111 | .padding() 112 | } 113 | .onAppear(perform: updateDaemonStatus) 114 | .task { 115 | while true { 116 | // TODO: Find a better way to check the service status than this loop 117 | try? await Task.sleep(for: .seconds(1), tolerance: .seconds(1)) 118 | let newDaemonStatus = service.status.description 119 | if newDaemonStatus != daemonStatus { 120 | updateDaemonStatus() 121 | } 122 | } 123 | } 124 | } 125 | 126 | func updateDaemonStatus() 127 | { 128 | self.daemonStatus = service.status.description 129 | 130 | if service.status == .enabled { 131 | configureXPCService() 132 | } else { 133 | self.xpc = nil 134 | self.calculator = nil 135 | } 136 | } 137 | 138 | func configureXPCService() 139 | { 140 | let codeSigningRequirement: CodeSigningRequirement 141 | 142 | do { 143 | codeSigningRequirement = try CodeSigningRequirement.sameTeam 144 | } catch { 145 | print("Failed to set up code signing requirement:", error.localizedDescription) 146 | return 147 | } 148 | 149 | let xpc = XPCDistributedActorSystem(mode: .connectingToDaemon(serviceName: daemonXPCServiceIdentifier), codeSigningRequirement: codeSigningRequirement) 150 | self.xpc = xpc 151 | 152 | do { 153 | self.calculator = try Calculator.resolve(id: .init(1), using: xpc) 154 | } catch { 155 | print("Failed to find remote actor:", error.localizedDescription) 156 | } 157 | } 158 | } 159 | 160 | #Preview { 161 | ContentView() 162 | } 163 | -------------------------------------------------------------------------------- /Examples/XPC Daemon/Calculator App/SMAppService.Status+Description.swift: -------------------------------------------------------------------------------- 1 | import ServiceManagement 2 | 3 | extension SMAppService.Status 4 | { 5 | var description:String { 6 | switch self { 7 | case .notRegistered: 8 | "not registered" 9 | case .enabled: 10 | "enabled" 11 | case .requiresApproval: 12 | "requires approval" 13 | case .notFound: 14 | "not found" 15 | default: 16 | "unknown" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Examples/XPC Daemon/Calculator/Calculator.swift: -------------------------------------------------------------------------------- 1 | import Distributed 2 | import XPCDistributedActorSystem 3 | 4 | distributed public actor Calculator 5 | { 6 | public typealias ActorSystem = XPCDistributedActorSystem 7 | 8 | public init(actorSystem: ActorSystem) 9 | { 10 | self.actorSystem = actorSystem 11 | } 12 | 13 | distributed public func add(_ value1: Int, _ value2: Int) -> Int 14 | { 15 | value1 + value2 16 | } 17 | 18 | distributed public func justARemoteFunction() 19 | { 20 | print("`justARemoteFunction` was called") 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Examples/XPC Daemon/CalculatorDaemon/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Examples/XPC Daemon/CalculatorDaemon/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "scale" : "1x", 6 | "size" : "16x16" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "scale" : "2x", 11 | "size" : "16x16" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "scale" : "1x", 16 | "size" : "32x32" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "scale" : "2x", 21 | "size" : "32x32" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "scale" : "1x", 26 | "size" : "128x128" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "scale" : "2x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "scale" : "1x", 36 | "size" : "256x256" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "scale" : "2x", 41 | "size" : "256x256" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "scale" : "1x", 46 | "size" : "512x512" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "scale" : "2x", 51 | "size" : "512x512" 52 | } 53 | ], 54 | "info" : { 55 | "author" : "xcode", 56 | "version" : 1 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Examples/XPC Daemon/CalculatorDaemon/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/XPC Daemon/CalculatorDaemon/CalculatorDaemon.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Examples/XPC Daemon/CalculatorDaemon/main.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Calculator 3 | import XPCDistributedActorSystem 4 | 5 | let system = XPCDistributedActorSystem(mode: .receivingConnections, codeSigningRequirement: try .sameTeam) 6 | let calculator = Calculator(actorSystem: system) 7 | let listener = try XPCDaemonListener(daemonServiceName: daemonXPCServiceIdentifier, actorSystem: system) 8 | 9 | dispatchMain() 10 | -------------------------------------------------------------------------------- /Examples/XPC Daemon/Config.xcconfig.template: -------------------------------------------------------------------------------- 1 | DEVELOPMENT_TEAM = XXXXXXXXXX 2 | -------------------------------------------------------------------------------- /Examples/XPC Daemon/Shared.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | let daemonXPCServiceIdentifier = "com.yourcompany.XPCDistributedActorExample.CalculatorDaemon.XPC" 4 | -------------------------------------------------------------------------------- /Examples/XPC Daemon/XPCDaemonExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 70; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | AD189A8A2D9C31AD008B987F /* libCalculator.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = AD189A842D9C317D008B987F /* libCalculator.dylib */; }; 11 | AD189A8B2D9C31AD008B987F /* libCalculator.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = AD189A842D9C317D008B987F /* libCalculator.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 12 | AD189A8E2D9C31B5008B987F /* libCalculator.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = AD189A842D9C317D008B987F /* libCalculator.dylib */; }; 13 | AD189AA22D9C31F1008B987F /* XPCDistributedActorSystem in Frameworks */ = {isa = PBXBuildFile; productRef = AD189AA12D9C31F1008B987F /* XPCDistributedActorSystem */; }; 14 | ADBD377A2D02484300A14C26 /* Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADBD37782D02484100A14C26 /* Shared.swift */; }; 15 | ADBD377B2D02484800A14C26 /* Shared.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADBD37782D02484100A14C26 /* Shared.swift */; }; 16 | ADBD378D2D03BC7700A14C26 /* com.yourcompany.XPCDistributedActorExample.CalculatorDaemon.plist in Resources */ = {isa = PBXBuildFile; fileRef = ADBD378C2D03BC7700A14C26 /* com.yourcompany.XPCDistributedActorExample.CalculatorDaemon.plist */; }; 17 | ADBD37912D03BD0000A14C26 /* com.yourcompany.XPCDistributedActorExample.CalculatorDaemon.plist in Copy LaunchDaemon .plist */ = {isa = PBXBuildFile; fileRef = ADBD378C2D03BC7700A14C26 /* com.yourcompany.XPCDistributedActorExample.CalculatorDaemon.plist */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 18 | ADBD37952D03BD3700A14C26 /* CalculatorDaemon.app in Copy Daemon */ = {isa = PBXBuildFile; fileRef = ADBD37402D02471400A14C26 /* CalculatorDaemon.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXContainerItemProxy section */ 22 | AD189A8C2D9C31AD008B987F /* PBXContainerItemProxy */ = { 23 | isa = PBXContainerItemProxy; 24 | containerPortal = AD8C4B5D2CBE9EEB0093E531 /* Project object */; 25 | proxyType = 1; 26 | remoteGlobalIDString = AD189A832D9C317D008B987F; 27 | remoteInfo = Calculator; 28 | }; 29 | AD189A902D9C31B5008B987F /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = AD8C4B5D2CBE9EEB0093E531 /* Project object */; 32 | proxyType = 1; 33 | remoteGlobalIDString = AD189A832D9C317D008B987F; 34 | remoteInfo = Calculator; 35 | }; 36 | ADBD37922D03BD2400A14C26 /* PBXContainerItemProxy */ = { 37 | isa = PBXContainerItemProxy; 38 | containerPortal = AD8C4B5D2CBE9EEB0093E531 /* Project object */; 39 | proxyType = 1; 40 | remoteGlobalIDString = ADBD373F2D02471400A14C26; 41 | remoteInfo = CalculatorDaemon; 42 | }; 43 | /* End PBXContainerItemProxy section */ 44 | 45 | /* Begin PBXCopyFilesBuildPhase section */ 46 | ADBB9AA12CD3EC0700BEACA6 /* Embed Frameworks */ = { 47 | isa = PBXCopyFilesBuildPhase; 48 | buildActionMask = 2147483647; 49 | dstPath = ""; 50 | dstSubfolderSpec = 10; 51 | files = ( 52 | AD189A8B2D9C31AD008B987F /* libCalculator.dylib in Embed Frameworks */, 53 | ); 54 | name = "Embed Frameworks"; 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | ADBD37902D03BCF800A14C26 /* Copy LaunchDaemon .plist */ = { 58 | isa = PBXCopyFilesBuildPhase; 59 | buildActionMask = 2147483647; 60 | dstPath = Contents/Library/LaunchDaemons; 61 | dstSubfolderSpec = 1; 62 | files = ( 63 | ADBD37912D03BD0000A14C26 /* com.yourcompany.XPCDistributedActorExample.CalculatorDaemon.plist in Copy LaunchDaemon .plist */, 64 | ); 65 | name = "Copy LaunchDaemon .plist"; 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | ADBD37942D03BD2800A14C26 /* Copy Daemon */ = { 69 | isa = PBXCopyFilesBuildPhase; 70 | buildActionMask = 2147483647; 71 | dstPath = ""; 72 | dstSubfolderSpec = 6; 73 | files = ( 74 | ADBD37952D03BD3700A14C26 /* CalculatorDaemon.app in Copy Daemon */, 75 | ); 76 | name = "Copy Daemon"; 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | /* End PBXCopyFilesBuildPhase section */ 80 | 81 | /* Begin PBXFileReference section */ 82 | AD189A842D9C317D008B987F /* libCalculator.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libCalculator.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; 83 | ADBB9A7D2CD3EB5000BEACA6 /* Calculator App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Calculator App.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 84 | ADBD36C12CF647A100A14C26 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; 85 | ADBD37402D02471400A14C26 /* CalculatorDaemon.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CalculatorDaemon.app; sourceTree = BUILT_PRODUCTS_DIR; }; 86 | ADBD37782D02484100A14C26 /* Shared.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shared.swift; sourceTree = ""; }; 87 | ADBD378C2D03BC7700A14C26 /* com.yourcompany.XPCDistributedActorExample.CalculatorDaemon.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = com.yourcompany.XPCDistributedActorExample.CalculatorDaemon.plist; sourceTree = ""; }; 88 | /* End PBXFileReference section */ 89 | 90 | /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ 91 | AD189A892D9C31A6008B987F /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { 92 | isa = PBXFileSystemSynchronizedBuildFileExceptionSet; 93 | membershipExceptions = ( 94 | Calculator.swift, 95 | ); 96 | target = AD189A832D9C317D008B987F /* Calculator */; 97 | }; 98 | /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ 99 | 100 | /* Begin PBXFileSystemSynchronizedRootGroup section */ 101 | ADBB9A7E2CD3EB5000BEACA6 /* Calculator App */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = "Calculator App"; sourceTree = ""; }; 102 | ADBB9ABB2CD3ECD100BEACA6 /* Calculator */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (AD189A892D9C31A6008B987F /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Calculator; sourceTree = ""; }; 103 | ADBD37412D02471400A14C26 /* CalculatorDaemon */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = CalculatorDaemon; sourceTree = ""; }; 104 | /* End PBXFileSystemSynchronizedRootGroup section */ 105 | 106 | /* Begin PBXFrameworksBuildPhase section */ 107 | AD189A822D9C317D008B987F /* Frameworks */ = { 108 | isa = PBXFrameworksBuildPhase; 109 | buildActionMask = 2147483647; 110 | files = ( 111 | AD189AA22D9C31F1008B987F /* XPCDistributedActorSystem in Frameworks */, 112 | ); 113 | runOnlyForDeploymentPostprocessing = 0; 114 | }; 115 | ADBB9A7A2CD3EB5000BEACA6 /* Frameworks */ = { 116 | isa = PBXFrameworksBuildPhase; 117 | buildActionMask = 2147483647; 118 | files = ( 119 | AD189A8A2D9C31AD008B987F /* libCalculator.dylib in Frameworks */, 120 | ); 121 | runOnlyForDeploymentPostprocessing = 0; 122 | }; 123 | ADBD373D2D02471400A14C26 /* Frameworks */ = { 124 | isa = PBXFrameworksBuildPhase; 125 | buildActionMask = 2147483647; 126 | files = ( 127 | AD189A8E2D9C31B5008B987F /* libCalculator.dylib in Frameworks */, 128 | ); 129 | runOnlyForDeploymentPostprocessing = 0; 130 | }; 131 | /* End PBXFrameworksBuildPhase section */ 132 | 133 | /* Begin PBXGroup section */ 134 | AD8C4B5C2CBE9EEB0093E531 = { 135 | isa = PBXGroup; 136 | children = ( 137 | ADBD378C2D03BC7700A14C26 /* com.yourcompany.XPCDistributedActorExample.CalculatorDaemon.plist */, 138 | ADBB9A7E2CD3EB5000BEACA6 /* Calculator App */, 139 | ADBB9ABB2CD3ECD100BEACA6 /* Calculator */, 140 | ADBD37412D02471400A14C26 /* CalculatorDaemon */, 141 | ADBD37782D02484100A14C26 /* Shared.swift */, 142 | AD8C4B662CBE9EEB0093E531 /* Products */, 143 | AD8C4C172CC98A780093E531 /* Frameworks */, 144 | ADBD36C12CF647A100A14C26 /* Config.xcconfig */, 145 | ); 146 | sourceTree = ""; 147 | }; 148 | AD8C4B662CBE9EEB0093E531 /* Products */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | ADBB9A7D2CD3EB5000BEACA6 /* Calculator App.app */, 152 | ADBD37402D02471400A14C26 /* CalculatorDaemon.app */, 153 | AD189A842D9C317D008B987F /* libCalculator.dylib */, 154 | ); 155 | name = Products; 156 | sourceTree = ""; 157 | }; 158 | AD8C4C172CC98A780093E531 /* Frameworks */ = { 159 | isa = PBXGroup; 160 | children = ( 161 | ); 162 | name = Frameworks; 163 | sourceTree = ""; 164 | }; 165 | /* End PBXGroup section */ 166 | 167 | /* Begin PBXHeadersBuildPhase section */ 168 | AD189A802D9C317D008B987F /* Headers */ = { 169 | isa = PBXHeadersBuildPhase; 170 | buildActionMask = 2147483647; 171 | files = ( 172 | ); 173 | runOnlyForDeploymentPostprocessing = 0; 174 | }; 175 | /* End PBXHeadersBuildPhase section */ 176 | 177 | /* Begin PBXNativeTarget section */ 178 | AD189A832D9C317D008B987F /* Calculator */ = { 179 | isa = PBXNativeTarget; 180 | buildConfigurationList = AD189A852D9C317D008B987F /* Build configuration list for PBXNativeTarget "Calculator" */; 181 | buildPhases = ( 182 | AD189A802D9C317D008B987F /* Headers */, 183 | AD189A812D9C317D008B987F /* Sources */, 184 | AD189A822D9C317D008B987F /* Frameworks */, 185 | ); 186 | buildRules = ( 187 | ); 188 | dependencies = ( 189 | ); 190 | name = Calculator; 191 | packageProductDependencies = ( 192 | AD189AA12D9C31F1008B987F /* XPCDistributedActorSystem */, 193 | ); 194 | productName = Calculator; 195 | productReference = AD189A842D9C317D008B987F /* libCalculator.dylib */; 196 | productType = "com.apple.product-type.library.dynamic"; 197 | }; 198 | ADBB9A7C2CD3EB5000BEACA6 /* Calculator App */ = { 199 | isa = PBXNativeTarget; 200 | buildConfigurationList = ADBB9A892CD3EB5100BEACA6 /* Build configuration list for PBXNativeTarget "Calculator App" */; 201 | buildPhases = ( 202 | ADBB9A792CD3EB5000BEACA6 /* Sources */, 203 | ADBB9A7A2CD3EB5000BEACA6 /* Frameworks */, 204 | ADBB9A7B2CD3EB5000BEACA6 /* Resources */, 205 | ADBB9AA12CD3EC0700BEACA6 /* Embed Frameworks */, 206 | ADBD37902D03BCF800A14C26 /* Copy LaunchDaemon .plist */, 207 | ADBD37942D03BD2800A14C26 /* Copy Daemon */, 208 | ); 209 | buildRules = ( 210 | ); 211 | dependencies = ( 212 | ADBD37932D03BD2400A14C26 /* PBXTargetDependency */, 213 | AD189A8D2D9C31AD008B987F /* PBXTargetDependency */, 214 | ); 215 | fileSystemSynchronizedGroups = ( 216 | ADBB9A7E2CD3EB5000BEACA6 /* Calculator App */, 217 | ); 218 | name = "Calculator App"; 219 | packageProductDependencies = ( 220 | ); 221 | productName = CalculatorUI; 222 | productReference = ADBB9A7D2CD3EB5000BEACA6 /* Calculator App.app */; 223 | productType = "com.apple.product-type.application"; 224 | }; 225 | ADBD373F2D02471400A14C26 /* CalculatorDaemon */ = { 226 | isa = PBXNativeTarget; 227 | buildConfigurationList = ADBD374C2D02471500A14C26 /* Build configuration list for PBXNativeTarget "CalculatorDaemon" */; 228 | buildPhases = ( 229 | ADBD373C2D02471400A14C26 /* Sources */, 230 | ADBD373D2D02471400A14C26 /* Frameworks */, 231 | ADBD373E2D02471400A14C26 /* Resources */, 232 | ); 233 | buildRules = ( 234 | ); 235 | dependencies = ( 236 | AD189A912D9C31B5008B987F /* PBXTargetDependency */, 237 | ); 238 | fileSystemSynchronizedGroups = ( 239 | ADBD37412D02471400A14C26 /* CalculatorDaemon */, 240 | ); 241 | name = CalculatorDaemon; 242 | packageProductDependencies = ( 243 | ); 244 | productName = CalculatorDaemon; 245 | productReference = ADBD37402D02471400A14C26 /* CalculatorDaemon.app */; 246 | productType = "com.apple.product-type.application"; 247 | }; 248 | /* End PBXNativeTarget section */ 249 | 250 | /* Begin PBXProject section */ 251 | AD8C4B5D2CBE9EEB0093E531 /* Project object */ = { 252 | isa = PBXProject; 253 | attributes = { 254 | BuildIndependentTargetsInParallel = 1; 255 | LastSwiftUpdateCheck = 1610; 256 | LastUpgradeCheck = 1600; 257 | TargetAttributes = { 258 | AD189A832D9C317D008B987F = { 259 | CreatedOnToolsVersion = 16.3; 260 | }; 261 | ADBB9A7C2CD3EB5000BEACA6 = { 262 | CreatedOnToolsVersion = 16.1; 263 | }; 264 | ADBD373F2D02471400A14C26 = { 265 | CreatedOnToolsVersion = 16.1; 266 | }; 267 | }; 268 | }; 269 | buildConfigurationList = AD8C4B602CBE9EEB0093E531 /* Build configuration list for PBXProject "XPCDaemonExample" */; 270 | compatibilityVersion = "Xcode 14.0"; 271 | developmentRegion = en; 272 | hasScannedForEncodings = 0; 273 | knownRegions = ( 274 | en, 275 | Base, 276 | ); 277 | mainGroup = AD8C4B5C2CBE9EEB0093E531; 278 | packageReferences = ( 279 | ADBD36FA2CF648D600A14C26 /* XCLocalSwiftPackageReference "../../../XPCDistributedActorSystem" */, 280 | ); 281 | productRefGroup = AD8C4B662CBE9EEB0093E531 /* Products */; 282 | projectDirPath = ""; 283 | projectRoot = ""; 284 | targets = ( 285 | ADBB9A7C2CD3EB5000BEACA6 /* Calculator App */, 286 | ADBD373F2D02471400A14C26 /* CalculatorDaemon */, 287 | AD189A832D9C317D008B987F /* Calculator */, 288 | ); 289 | }; 290 | /* End PBXProject section */ 291 | 292 | /* Begin PBXResourcesBuildPhase section */ 293 | ADBB9A7B2CD3EB5000BEACA6 /* Resources */ = { 294 | isa = PBXResourcesBuildPhase; 295 | buildActionMask = 2147483647; 296 | files = ( 297 | ADBD378D2D03BC7700A14C26 /* com.yourcompany.XPCDistributedActorExample.CalculatorDaemon.plist in Resources */, 298 | ); 299 | runOnlyForDeploymentPostprocessing = 0; 300 | }; 301 | ADBD373E2D02471400A14C26 /* Resources */ = { 302 | isa = PBXResourcesBuildPhase; 303 | buildActionMask = 2147483647; 304 | files = ( 305 | ); 306 | runOnlyForDeploymentPostprocessing = 0; 307 | }; 308 | /* End PBXResourcesBuildPhase section */ 309 | 310 | /* Begin PBXSourcesBuildPhase section */ 311 | AD189A812D9C317D008B987F /* Sources */ = { 312 | isa = PBXSourcesBuildPhase; 313 | buildActionMask = 2147483647; 314 | files = ( 315 | ); 316 | runOnlyForDeploymentPostprocessing = 0; 317 | }; 318 | ADBB9A792CD3EB5000BEACA6 /* Sources */ = { 319 | isa = PBXSourcesBuildPhase; 320 | buildActionMask = 2147483647; 321 | files = ( 322 | ADBD377B2D02484800A14C26 /* Shared.swift in Sources */, 323 | ); 324 | runOnlyForDeploymentPostprocessing = 0; 325 | }; 326 | ADBD373C2D02471400A14C26 /* Sources */ = { 327 | isa = PBXSourcesBuildPhase; 328 | buildActionMask = 2147483647; 329 | files = ( 330 | ADBD377A2D02484300A14C26 /* Shared.swift in Sources */, 331 | ); 332 | runOnlyForDeploymentPostprocessing = 0; 333 | }; 334 | /* End PBXSourcesBuildPhase section */ 335 | 336 | /* Begin PBXTargetDependency section */ 337 | AD189A8D2D9C31AD008B987F /* PBXTargetDependency */ = { 338 | isa = PBXTargetDependency; 339 | target = AD189A832D9C317D008B987F /* Calculator */; 340 | targetProxy = AD189A8C2D9C31AD008B987F /* PBXContainerItemProxy */; 341 | }; 342 | AD189A912D9C31B5008B987F /* PBXTargetDependency */ = { 343 | isa = PBXTargetDependency; 344 | target = AD189A832D9C317D008B987F /* Calculator */; 345 | targetProxy = AD189A902D9C31B5008B987F /* PBXContainerItemProxy */; 346 | }; 347 | ADBD37932D03BD2400A14C26 /* PBXTargetDependency */ = { 348 | isa = PBXTargetDependency; 349 | target = ADBD373F2D02471400A14C26 /* CalculatorDaemon */; 350 | targetProxy = ADBD37922D03BD2400A14C26 /* PBXContainerItemProxy */; 351 | }; 352 | /* End PBXTargetDependency section */ 353 | 354 | /* Begin XCBuildConfiguration section */ 355 | AD189A862D9C317D008B987F /* Debug */ = { 356 | isa = XCBuildConfiguration; 357 | buildSettings = { 358 | CODE_SIGN_STYLE = Automatic; 359 | DYLIB_COMPATIBILITY_VERSION = 1; 360 | DYLIB_CURRENT_VERSION = 1; 361 | EXECUTABLE_PREFIX = lib; 362 | LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)"; 363 | MACOSX_DEPLOYMENT_TARGET = 15.0; 364 | PRODUCT_NAME = "$(TARGET_NAME)"; 365 | SKIP_INSTALL = YES; 366 | SWIFT_VERSION = 5.0; 367 | }; 368 | name = Debug; 369 | }; 370 | AD189A872D9C317D008B987F /* Release */ = { 371 | isa = XCBuildConfiguration; 372 | buildSettings = { 373 | CODE_SIGN_STYLE = Automatic; 374 | DYLIB_COMPATIBILITY_VERSION = 1; 375 | DYLIB_CURRENT_VERSION = 1; 376 | EXECUTABLE_PREFIX = lib; 377 | LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)"; 378 | MACOSX_DEPLOYMENT_TARGET = 15.0; 379 | PRODUCT_NAME = "$(TARGET_NAME)"; 380 | SKIP_INSTALL = YES; 381 | SWIFT_VERSION = 5.0; 382 | }; 383 | name = Release; 384 | }; 385 | AD8C4B6E2CBE9EEB0093E531 /* Debug */ = { 386 | isa = XCBuildConfiguration; 387 | baseConfigurationReference = ADBD36C12CF647A100A14C26 /* Config.xcconfig */; 388 | buildSettings = { 389 | ALWAYS_SEARCH_USER_PATHS = NO; 390 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 391 | CLANG_ANALYZER_NONNULL = YES; 392 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 393 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 394 | CLANG_ENABLE_MODULES = YES; 395 | CLANG_ENABLE_OBJC_ARC = YES; 396 | CLANG_ENABLE_OBJC_WEAK = YES; 397 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 398 | CLANG_WARN_BOOL_CONVERSION = YES; 399 | CLANG_WARN_COMMA = YES; 400 | CLANG_WARN_CONSTANT_CONVERSION = YES; 401 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 402 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 403 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 404 | CLANG_WARN_EMPTY_BODY = YES; 405 | CLANG_WARN_ENUM_CONVERSION = YES; 406 | CLANG_WARN_INFINITE_RECURSION = YES; 407 | CLANG_WARN_INT_CONVERSION = YES; 408 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 409 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 410 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 411 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 412 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 413 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 414 | CLANG_WARN_STRICT_PROTOTYPES = YES; 415 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 416 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 417 | CLANG_WARN_UNREACHABLE_CODE = YES; 418 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 419 | COPY_PHASE_STRIP = NO; 420 | DEAD_CODE_STRIPPING = YES; 421 | DEBUG_INFORMATION_FORMAT = dwarf; 422 | ENABLE_STRICT_OBJC_MSGSEND = YES; 423 | ENABLE_TESTABILITY = YES; 424 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 425 | GCC_C_LANGUAGE_STANDARD = gnu17; 426 | GCC_DYNAMIC_NO_PIC = NO; 427 | GCC_NO_COMMON_BLOCKS = YES; 428 | GCC_OPTIMIZATION_LEVEL = 0; 429 | GCC_PREPROCESSOR_DEFINITIONS = ( 430 | "DEBUG=1", 431 | "$(inherited)", 432 | ); 433 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 434 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 435 | GCC_WARN_UNDECLARED_SELECTOR = YES; 436 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 437 | GCC_WARN_UNUSED_FUNCTION = YES; 438 | GCC_WARN_UNUSED_VARIABLE = YES; 439 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 440 | MACOSX_DEPLOYMENT_TARGET = 14.5; 441 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 442 | MTL_FAST_MATH = YES; 443 | ONLY_ACTIVE_ARCH = YES; 444 | SDKROOT = macosx; 445 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; 446 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 447 | }; 448 | name = Debug; 449 | }; 450 | AD8C4B6F2CBE9EEB0093E531 /* Release */ = { 451 | isa = XCBuildConfiguration; 452 | baseConfigurationReference = ADBD36C12CF647A100A14C26 /* Config.xcconfig */; 453 | buildSettings = { 454 | ALWAYS_SEARCH_USER_PATHS = NO; 455 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 456 | CLANG_ANALYZER_NONNULL = YES; 457 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 458 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 459 | CLANG_ENABLE_MODULES = YES; 460 | CLANG_ENABLE_OBJC_ARC = YES; 461 | CLANG_ENABLE_OBJC_WEAK = YES; 462 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 463 | CLANG_WARN_BOOL_CONVERSION = YES; 464 | CLANG_WARN_COMMA = YES; 465 | CLANG_WARN_CONSTANT_CONVERSION = YES; 466 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 467 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 468 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 469 | CLANG_WARN_EMPTY_BODY = YES; 470 | CLANG_WARN_ENUM_CONVERSION = YES; 471 | CLANG_WARN_INFINITE_RECURSION = YES; 472 | CLANG_WARN_INT_CONVERSION = YES; 473 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 474 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 475 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 476 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 477 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 478 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 479 | CLANG_WARN_STRICT_PROTOTYPES = YES; 480 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 481 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 482 | CLANG_WARN_UNREACHABLE_CODE = YES; 483 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 484 | COPY_PHASE_STRIP = NO; 485 | DEAD_CODE_STRIPPING = YES; 486 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 487 | ENABLE_NS_ASSERTIONS = NO; 488 | ENABLE_STRICT_OBJC_MSGSEND = YES; 489 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 490 | GCC_C_LANGUAGE_STANDARD = gnu17; 491 | GCC_NO_COMMON_BLOCKS = YES; 492 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 493 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 494 | GCC_WARN_UNDECLARED_SELECTOR = YES; 495 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 496 | GCC_WARN_UNUSED_FUNCTION = YES; 497 | GCC_WARN_UNUSED_VARIABLE = YES; 498 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 499 | MACOSX_DEPLOYMENT_TARGET = 14.5; 500 | MTL_ENABLE_DEBUG_INFO = NO; 501 | MTL_FAST_MATH = YES; 502 | SDKROOT = macosx; 503 | SWIFT_COMPILATION_MODE = wholemodule; 504 | }; 505 | name = Release; 506 | }; 507 | ADBB9A8A2CD3EB5100BEACA6 /* Debug */ = { 508 | isa = XCBuildConfiguration; 509 | buildSettings = { 510 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 511 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 512 | CODE_SIGN_ENTITLEMENTS = "Calculator App/CalculatorApp.entitlements"; 513 | CODE_SIGN_STYLE = Automatic; 514 | COMBINE_HIDPI_IMAGES = YES; 515 | CURRENT_PROJECT_VERSION = 1; 516 | ENABLE_HARDENED_RUNTIME = YES; 517 | ENABLE_PREVIEWS = YES; 518 | GENERATE_INFOPLIST_FILE = YES; 519 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 520 | LD_RUNPATH_SEARCH_PATHS = ( 521 | "$(inherited)", 522 | "@executable_path/../Frameworks", 523 | ); 524 | MACOSX_DEPLOYMENT_TARGET = 15.1; 525 | MARKETING_VERSION = 1.0; 526 | PRODUCT_BUNDLE_IDENTIFIER = com.yourcompany.XPCDistributedActorExample.CalculatorUI; 527 | PRODUCT_NAME = "$(TARGET_NAME)"; 528 | SWIFT_EMIT_LOC_STRINGS = YES; 529 | SWIFT_VERSION = 5.0; 530 | }; 531 | name = Debug; 532 | }; 533 | ADBB9A8B2CD3EB5100BEACA6 /* Release */ = { 534 | isa = XCBuildConfiguration; 535 | buildSettings = { 536 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 537 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 538 | CODE_SIGN_ENTITLEMENTS = "Calculator App/CalculatorApp.entitlements"; 539 | CODE_SIGN_STYLE = Automatic; 540 | COMBINE_HIDPI_IMAGES = YES; 541 | CURRENT_PROJECT_VERSION = 1; 542 | ENABLE_HARDENED_RUNTIME = YES; 543 | ENABLE_PREVIEWS = YES; 544 | GENERATE_INFOPLIST_FILE = YES; 545 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 546 | LD_RUNPATH_SEARCH_PATHS = ( 547 | "$(inherited)", 548 | "@executable_path/../Frameworks", 549 | ); 550 | MACOSX_DEPLOYMENT_TARGET = 15.1; 551 | MARKETING_VERSION = 1.0; 552 | PRODUCT_BUNDLE_IDENTIFIER = com.yourcompany.XPCDistributedActorExample.CalculatorUI; 553 | PRODUCT_NAME = "$(TARGET_NAME)"; 554 | SWIFT_EMIT_LOC_STRINGS = YES; 555 | SWIFT_VERSION = 5.0; 556 | }; 557 | name = Release; 558 | }; 559 | ADBD374D2D02471500A14C26 /* Debug */ = { 560 | isa = XCBuildConfiguration; 561 | buildSettings = { 562 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 563 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 564 | CODE_SIGN_ENTITLEMENTS = CalculatorDaemon/CalculatorDaemon.entitlements; 565 | CODE_SIGN_STYLE = Automatic; 566 | COMBINE_HIDPI_IMAGES = YES; 567 | CURRENT_PROJECT_VERSION = 1; 568 | DEVELOPMENT_ASSET_PATHS = ""; 569 | ENABLE_HARDENED_RUNTIME = YES; 570 | ENABLE_PREVIEWS = YES; 571 | GENERATE_INFOPLIST_FILE = YES; 572 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 573 | LD_RUNPATH_SEARCH_PATHS = "@executable_path/../../../../Frameworks"; 574 | MACOSX_DEPLOYMENT_TARGET = 15.1; 575 | MARKETING_VERSION = 1.0; 576 | PRODUCT_BUNDLE_IDENTIFIER = com.yourcompany.XPCDistributedActorExample.CalculatorDaemon; 577 | PRODUCT_NAME = "$(TARGET_NAME)"; 578 | SKIP_INSTALL = YES; 579 | SWIFT_EMIT_LOC_STRINGS = YES; 580 | SWIFT_VERSION = 5.0; 581 | }; 582 | name = Debug; 583 | }; 584 | ADBD374E2D02471500A14C26 /* Release */ = { 585 | isa = XCBuildConfiguration; 586 | buildSettings = { 587 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 588 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 589 | CODE_SIGN_ENTITLEMENTS = CalculatorDaemon/CalculatorDaemon.entitlements; 590 | CODE_SIGN_STYLE = Automatic; 591 | COMBINE_HIDPI_IMAGES = YES; 592 | CURRENT_PROJECT_VERSION = 1; 593 | DEVELOPMENT_ASSET_PATHS = ""; 594 | ENABLE_HARDENED_RUNTIME = YES; 595 | ENABLE_PREVIEWS = YES; 596 | GENERATE_INFOPLIST_FILE = YES; 597 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 598 | LD_RUNPATH_SEARCH_PATHS = "@executable_path/../../../../Frameworks"; 599 | MACOSX_DEPLOYMENT_TARGET = 15.1; 600 | MARKETING_VERSION = 1.0; 601 | PRODUCT_BUNDLE_IDENTIFIER = com.yourcompany.XPCDistributedActorExample.CalculatorDaemon; 602 | PRODUCT_NAME = "$(TARGET_NAME)"; 603 | SKIP_INSTALL = YES; 604 | SWIFT_EMIT_LOC_STRINGS = YES; 605 | SWIFT_VERSION = 5.0; 606 | }; 607 | name = Release; 608 | }; 609 | /* End XCBuildConfiguration section */ 610 | 611 | /* Begin XCConfigurationList section */ 612 | AD189A852D9C317D008B987F /* Build configuration list for PBXNativeTarget "Calculator" */ = { 613 | isa = XCConfigurationList; 614 | buildConfigurations = ( 615 | AD189A862D9C317D008B987F /* Debug */, 616 | AD189A872D9C317D008B987F /* Release */, 617 | ); 618 | defaultConfigurationIsVisible = 0; 619 | defaultConfigurationName = Release; 620 | }; 621 | AD8C4B602CBE9EEB0093E531 /* Build configuration list for PBXProject "XPCDaemonExample" */ = { 622 | isa = XCConfigurationList; 623 | buildConfigurations = ( 624 | AD8C4B6E2CBE9EEB0093E531 /* Debug */, 625 | AD8C4B6F2CBE9EEB0093E531 /* Release */, 626 | ); 627 | defaultConfigurationIsVisible = 0; 628 | defaultConfigurationName = Release; 629 | }; 630 | ADBB9A892CD3EB5100BEACA6 /* Build configuration list for PBXNativeTarget "Calculator App" */ = { 631 | isa = XCConfigurationList; 632 | buildConfigurations = ( 633 | ADBB9A8A2CD3EB5100BEACA6 /* Debug */, 634 | ADBB9A8B2CD3EB5100BEACA6 /* Release */, 635 | ); 636 | defaultConfigurationIsVisible = 0; 637 | defaultConfigurationName = Release; 638 | }; 639 | ADBD374C2D02471500A14C26 /* Build configuration list for PBXNativeTarget "CalculatorDaemon" */ = { 640 | isa = XCConfigurationList; 641 | buildConfigurations = ( 642 | ADBD374D2D02471500A14C26 /* Debug */, 643 | ADBD374E2D02471500A14C26 /* Release */, 644 | ); 645 | defaultConfigurationIsVisible = 0; 646 | defaultConfigurationName = Release; 647 | }; 648 | /* End XCConfigurationList section */ 649 | 650 | /* Begin XCLocalSwiftPackageReference section */ 651 | ADBD36FA2CF648D600A14C26 /* XCLocalSwiftPackageReference "../../../XPCDistributedActorSystem" */ = { 652 | isa = XCLocalSwiftPackageReference; 653 | relativePath = ../../../XPCDistributedActorSystem; 654 | }; 655 | /* End XCLocalSwiftPackageReference section */ 656 | 657 | /* Begin XCSwiftPackageProductDependency section */ 658 | AD189AA12D9C31F1008B987F /* XPCDistributedActorSystem */ = { 659 | isa = XCSwiftPackageProductDependency; 660 | package = ADBD36FA2CF648D600A14C26 /* XCLocalSwiftPackageReference "../../../XPCDistributedActorSystem" */; 661 | productName = XPCDistributedActorSystem; 662 | }; 663 | /* End XCSwiftPackageProductDependency section */ 664 | }; 665 | rootObject = AD8C4B5D2CBE9EEB0093E531 /* Project object */; 666 | } 667 | -------------------------------------------------------------------------------- /Examples/XPC Daemon/XPCDaemonExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Examples/XPC Daemon/com.yourcompany.XPCDistributedActorExample.CalculatorDaemon.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | KeepAlive 6 | 7 | Label 8 | com.yourcompany.XPCDistributedActorExample.CalculatorDaemon 9 | BundleProgram 10 | Contents/MacOS/CalculatorDaemon.app/Contents/MacOS/CalculatorDaemon 11 | MachServices 12 | 13 | com.yourcompany.XPCDistributedActorExample.CalculatorDaemon.XPC 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Examples/XPC Service/Calculator App/CalculatorApp.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Examples/XPC Service/Calculator App/CalculatorApp.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | @main 4 | struct CalculatorApp: App { 5 | var body: some Scene { 6 | WindowGroup { 7 | ContentView() 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Examples/XPC Service/Calculator App/ContentView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import XPCDistributedActorSystem 3 | import Calculator 4 | 5 | struct ContentView: View 6 | { 7 | @State private var xpc: XPCDistributedActorSystem? 8 | @State private var calculator: Calculator? // remote actor in the XPC service 9 | @State private var output: String = "No calculation done" 10 | 11 | var body: some View { 12 | VStack { 13 | Button("Calculate") { 14 | guard let calculator else { return } 15 | output = "Starting XPC service and calculating..." 16 | Task { 17 | do { 18 | let number1 = Int.random(in: 0..<1000) 19 | let number2 = Int.random(in: 0..<1000) 20 | let result = try await calculator.add(number1, number2) 21 | output = "\(number1) + \(number2) = \(result)" 22 | } catch { 23 | output = "Failed to calculate" 24 | print("Failed to execute XPC request:", error.localizedDescription) 25 | } 26 | } 27 | } 28 | Button("Call a remote function (no output)") { 29 | guard let calculator else { 30 | output = "Distributed actor not set up" 31 | return 32 | } 33 | Task { 34 | do { 35 | try await calculator.justARemoteFunction() 36 | output = "" 37 | } catch { 38 | output = "Failed to call remote function: \(error.localizedDescription)" 39 | } 40 | } 41 | } 42 | Text(output) 43 | } 44 | .disabled(self.calculator == nil) 45 | .padding() 46 | .onAppear(perform: configureXPCService) 47 | } 48 | 49 | func configureXPCService() 50 | { 51 | guard let serviceIdentifier = Bundle.main.firstXPCServiceIdentifier() else { 52 | print("Failed to find a valid XPC service in the app's bundle.") 53 | return 54 | } 55 | 56 | print("Found XPC service in bundle:", serviceIdentifier) 57 | 58 | let codeSigningRequirement: CodeSigningRequirement 59 | 60 | do { 61 | codeSigningRequirement = try CodeSigningRequirement.sameTeam 62 | } catch { 63 | print("Failed to set up code signing requirement:", error.localizedDescription) 64 | return 65 | } 66 | 67 | let xpc = XPCDistributedActorSystem(mode: .connectingToXPCService(serviceName: serviceIdentifier), codeSigningRequirement: codeSigningRequirement) 68 | self.xpc = xpc 69 | 70 | do { 71 | self.calculator = try Calculator.resolve(id: .init(1), using: xpc) 72 | } catch { 73 | print("Failed to find remote actor:", error.localizedDescription) 74 | } 75 | 76 | // The XPC service process won't be launched until the first call to the remote actor 77 | } 78 | } 79 | 80 | #Preview { 81 | ContentView() 82 | } 83 | -------------------------------------------------------------------------------- /Examples/XPC Service/Calculator/Calculator.swift: -------------------------------------------------------------------------------- 1 | import Distributed 2 | import XPCDistributedActorSystem 3 | 4 | distributed public actor Calculator 5 | { 6 | public typealias ActorSystem = XPCDistributedActorSystem 7 | 8 | public init(actorSystem: ActorSystem) 9 | { 10 | self.actorSystem = actorSystem 11 | } 12 | 13 | distributed public func add(_ value1: Int, _ value2: Int) -> Int 14 | { 15 | value1 + value2 16 | } 17 | 18 | distributed public func justARemoteFunction() 19 | { 20 | print("`justARemoteFunction` was called") 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Examples/XPC Service/CalculatorXPCService/CalculatorXPCService.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Examples/XPC Service/CalculatorXPCService/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | XPCService 6 | 7 | ServiceType 8 | Application 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Examples/XPC Service/CalculatorXPCService/main.swift: -------------------------------------------------------------------------------- 1 | import XPCDistributedActorSystem 2 | import Calculator 3 | 4 | let system = XPCDistributedActorSystem(mode: .receivingConnections, codeSigningRequirement: try .sameTeam) 5 | let calculator = Calculator(actorSystem: system) 6 | let listener = try XPCServiceListener(actorSystem: system) 7 | listener.run() 8 | -------------------------------------------------------------------------------- /Examples/XPC Service/Config.xcconfig.template: -------------------------------------------------------------------------------- 1 | DEVELOPMENT_TEAM = XXXXXXXXXX 2 | -------------------------------------------------------------------------------- /Examples/XPC Service/XPCServiceExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 70; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | AD189A5C2D9C2E97008B987F /* libCalculator.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = AD189A562D9C2E80008B987F /* libCalculator.dylib */; }; 11 | AD189A5D2D9C2E97008B987F /* libCalculator.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = AD189A562D9C2E80008B987F /* libCalculator.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 12 | AD189A602D9C2EA0008B987F /* libCalculator.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = AD189A562D9C2E80008B987F /* libCalculator.dylib */; }; 13 | AD189A652D9C2EA7008B987F /* XPCDistributedActorSystem in Frameworks */ = {isa = PBXBuildFile; productRef = AD189A642D9C2EA7008B987F /* XPCDistributedActorSystem */; }; 14 | ADBB9A8C2CD3EB5700BEACA6 /* CalculatorXPCService.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = ADBB9A662CD3EA5D00BEACA6 /* CalculatorXPCService.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 15 | /* End PBXBuildFile section */ 16 | 17 | /* Begin PBXContainerItemProxy section */ 18 | AD189A5E2D9C2E97008B987F /* PBXContainerItemProxy */ = { 19 | isa = PBXContainerItemProxy; 20 | containerPortal = AD8C4B5D2CBE9EEB0093E531 /* Project object */; 21 | proxyType = 1; 22 | remoteGlobalIDString = AD189A552D9C2E80008B987F; 23 | remoteInfo = Calculator2; 24 | }; 25 | AD189A622D9C2EA0008B987F /* PBXContainerItemProxy */ = { 26 | isa = PBXContainerItemProxy; 27 | containerPortal = AD8C4B5D2CBE9EEB0093E531 /* Project object */; 28 | proxyType = 1; 29 | remoteGlobalIDString = AD189A552D9C2E80008B987F; 30 | remoteInfo = Calculator2; 31 | }; 32 | ADBB9A8D2CD3EB5700BEACA6 /* PBXContainerItemProxy */ = { 33 | isa = PBXContainerItemProxy; 34 | containerPortal = AD8C4B5D2CBE9EEB0093E531 /* Project object */; 35 | proxyType = 1; 36 | remoteGlobalIDString = ADBB9A652CD3EA5D00BEACA6; 37 | remoteInfo = CalculatorXPCService; 38 | }; 39 | /* End PBXContainerItemProxy section */ 40 | 41 | /* Begin PBXCopyFilesBuildPhase section */ 42 | ADBB9A8F2CD3EB5700BEACA6 /* Embed XPC Services */ = { 43 | isa = PBXCopyFilesBuildPhase; 44 | buildActionMask = 2147483647; 45 | dstPath = "$(CONTENTS_FOLDER_PATH)/XPCServices"; 46 | dstSubfolderSpec = 16; 47 | files = ( 48 | ADBB9A8C2CD3EB5700BEACA6 /* CalculatorXPCService.xpc in Embed XPC Services */, 49 | ); 50 | name = "Embed XPC Services"; 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | ADBB9AA12CD3EC0700BEACA6 /* Embed Frameworks */ = { 54 | isa = PBXCopyFilesBuildPhase; 55 | buildActionMask = 2147483647; 56 | dstPath = ""; 57 | dstSubfolderSpec = 10; 58 | files = ( 59 | AD189A5D2D9C2E97008B987F /* libCalculator.dylib in Embed Frameworks */, 60 | ); 61 | name = "Embed Frameworks"; 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | /* End PBXCopyFilesBuildPhase section */ 65 | 66 | /* Begin PBXFileReference section */ 67 | AD189A562D9C2E80008B987F /* libCalculator.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libCalculator.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; 68 | ADBB9A662CD3EA5D00BEACA6 /* CalculatorXPCService.xpc */ = {isa = PBXFileReference; explicitFileType = "wrapper.xpc-service"; includeInIndex = 0; path = CalculatorXPCService.xpc; sourceTree = BUILT_PRODUCTS_DIR; }; 69 | ADBB9A7D2CD3EB5000BEACA6 /* Calculator App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Calculator App.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 70 | ADBD36C12CF647A100A14C26 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; 71 | /* End PBXFileReference section */ 72 | 73 | /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ 74 | AD189A5B2D9C2E8B008B987F /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { 75 | isa = PBXFileSystemSynchronizedBuildFileExceptionSet; 76 | membershipExceptions = ( 77 | Calculator.swift, 78 | ); 79 | target = AD189A552D9C2E80008B987F /* Calculator */; 80 | }; 81 | ADBB9A712CD3EA5D00BEACA6 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { 82 | isa = PBXFileSystemSynchronizedBuildFileExceptionSet; 83 | membershipExceptions = ( 84 | Info.plist, 85 | ); 86 | target = ADBB9A652CD3EA5D00BEACA6 /* CalculatorXPCService */; 87 | }; 88 | /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ 89 | 90 | /* Begin PBXFileSystemSynchronizedRootGroup section */ 91 | ADBB9A672CD3EA5D00BEACA6 /* CalculatorXPCService */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (ADBB9A712CD3EA5D00BEACA6 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = CalculatorXPCService; sourceTree = ""; }; 92 | ADBB9A7E2CD3EB5000BEACA6 /* Calculator App */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = "Calculator App"; sourceTree = ""; }; 93 | ADBB9ABB2CD3ECD100BEACA6 /* Calculator */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (AD189A5B2D9C2E8B008B987F /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Calculator; sourceTree = ""; }; 94 | /* End PBXFileSystemSynchronizedRootGroup section */ 95 | 96 | /* Begin PBXFrameworksBuildPhase section */ 97 | AD189A542D9C2E80008B987F /* Frameworks */ = { 98 | isa = PBXFrameworksBuildPhase; 99 | buildActionMask = 2147483647; 100 | files = ( 101 | AD189A652D9C2EA7008B987F /* XPCDistributedActorSystem in Frameworks */, 102 | ); 103 | runOnlyForDeploymentPostprocessing = 0; 104 | }; 105 | ADBB9A632CD3EA5D00BEACA6 /* Frameworks */ = { 106 | isa = PBXFrameworksBuildPhase; 107 | buildActionMask = 2147483647; 108 | files = ( 109 | AD189A602D9C2EA0008B987F /* libCalculator.dylib in Frameworks */, 110 | ); 111 | runOnlyForDeploymentPostprocessing = 0; 112 | }; 113 | ADBB9A7A2CD3EB5000BEACA6 /* Frameworks */ = { 114 | isa = PBXFrameworksBuildPhase; 115 | buildActionMask = 2147483647; 116 | files = ( 117 | AD189A5C2D9C2E97008B987F /* libCalculator.dylib in Frameworks */, 118 | ); 119 | runOnlyForDeploymentPostprocessing = 0; 120 | }; 121 | /* End PBXFrameworksBuildPhase section */ 122 | 123 | /* Begin PBXGroup section */ 124 | AD8C4B5C2CBE9EEB0093E531 = { 125 | isa = PBXGroup; 126 | children = ( 127 | ADBB9A7E2CD3EB5000BEACA6 /* Calculator App */, 128 | ADBB9A672CD3EA5D00BEACA6 /* CalculatorXPCService */, 129 | ADBB9ABB2CD3ECD100BEACA6 /* Calculator */, 130 | AD8C4B662CBE9EEB0093E531 /* Products */, 131 | AD8C4C172CC98A780093E531 /* Frameworks */, 132 | ADBD36C12CF647A100A14C26 /* Config.xcconfig */, 133 | ); 134 | sourceTree = ""; 135 | }; 136 | AD8C4B662CBE9EEB0093E531 /* Products */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | ADBB9A662CD3EA5D00BEACA6 /* CalculatorXPCService.xpc */, 140 | ADBB9A7D2CD3EB5000BEACA6 /* Calculator App.app */, 141 | AD189A562D9C2E80008B987F /* libCalculator.dylib */, 142 | ); 143 | name = Products; 144 | sourceTree = ""; 145 | }; 146 | AD8C4C172CC98A780093E531 /* Frameworks */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | ); 150 | name = Frameworks; 151 | sourceTree = ""; 152 | }; 153 | /* End PBXGroup section */ 154 | 155 | /* Begin PBXHeadersBuildPhase section */ 156 | AD189A522D9C2E80008B987F /* Headers */ = { 157 | isa = PBXHeadersBuildPhase; 158 | buildActionMask = 2147483647; 159 | files = ( 160 | ); 161 | runOnlyForDeploymentPostprocessing = 0; 162 | }; 163 | /* End PBXHeadersBuildPhase section */ 164 | 165 | /* Begin PBXNativeTarget section */ 166 | AD189A552D9C2E80008B987F /* Calculator */ = { 167 | isa = PBXNativeTarget; 168 | buildConfigurationList = AD189A572D9C2E80008B987F /* Build configuration list for PBXNativeTarget "Calculator" */; 169 | buildPhases = ( 170 | AD189A522D9C2E80008B987F /* Headers */, 171 | AD189A532D9C2E80008B987F /* Sources */, 172 | AD189A542D9C2E80008B987F /* Frameworks */, 173 | ); 174 | buildRules = ( 175 | ); 176 | dependencies = ( 177 | ); 178 | name = Calculator; 179 | packageProductDependencies = ( 180 | AD189A642D9C2EA7008B987F /* XPCDistributedActorSystem */, 181 | ); 182 | productName = Calculator2; 183 | productReference = AD189A562D9C2E80008B987F /* libCalculator.dylib */; 184 | productType = "com.apple.product-type.library.dynamic"; 185 | }; 186 | ADBB9A652CD3EA5D00BEACA6 /* CalculatorXPCService */ = { 187 | isa = PBXNativeTarget; 188 | buildConfigurationList = ADBB9A722CD3EA5D00BEACA6 /* Build configuration list for PBXNativeTarget "CalculatorXPCService" */; 189 | buildPhases = ( 190 | ADBB9A622CD3EA5D00BEACA6 /* Sources */, 191 | ADBB9A632CD3EA5D00BEACA6 /* Frameworks */, 192 | ADBB9A642CD3EA5D00BEACA6 /* Resources */, 193 | ); 194 | buildRules = ( 195 | ); 196 | dependencies = ( 197 | AD189A632D9C2EA0008B987F /* PBXTargetDependency */, 198 | ); 199 | fileSystemSynchronizedGroups = ( 200 | ADBB9A672CD3EA5D00BEACA6 /* CalculatorXPCService */, 201 | ); 202 | name = CalculatorXPCService; 203 | packageProductDependencies = ( 204 | ); 205 | productName = CalculatorXPCService; 206 | productReference = ADBB9A662CD3EA5D00BEACA6 /* CalculatorXPCService.xpc */; 207 | productType = "com.apple.product-type.xpc-service"; 208 | }; 209 | ADBB9A7C2CD3EB5000BEACA6 /* Calculator App */ = { 210 | isa = PBXNativeTarget; 211 | buildConfigurationList = ADBB9A892CD3EB5100BEACA6 /* Build configuration list for PBXNativeTarget "Calculator App" */; 212 | buildPhases = ( 213 | ADBB9A792CD3EB5000BEACA6 /* Sources */, 214 | ADBB9A7A2CD3EB5000BEACA6 /* Frameworks */, 215 | ADBB9A7B2CD3EB5000BEACA6 /* Resources */, 216 | ADBB9A8F2CD3EB5700BEACA6 /* Embed XPC Services */, 217 | ADBB9AA12CD3EC0700BEACA6 /* Embed Frameworks */, 218 | ); 219 | buildRules = ( 220 | ); 221 | dependencies = ( 222 | ADBB9A8E2CD3EB5700BEACA6 /* PBXTargetDependency */, 223 | AD189A5F2D9C2E97008B987F /* PBXTargetDependency */, 224 | ); 225 | fileSystemSynchronizedGroups = ( 226 | ADBB9A7E2CD3EB5000BEACA6 /* Calculator App */, 227 | ); 228 | name = "Calculator App"; 229 | packageProductDependencies = ( 230 | ); 231 | productName = CalculatorUI; 232 | productReference = ADBB9A7D2CD3EB5000BEACA6 /* Calculator App.app */; 233 | productType = "com.apple.product-type.application"; 234 | }; 235 | /* End PBXNativeTarget section */ 236 | 237 | /* Begin PBXProject section */ 238 | AD8C4B5D2CBE9EEB0093E531 /* Project object */ = { 239 | isa = PBXProject; 240 | attributes = { 241 | BuildIndependentTargetsInParallel = 1; 242 | LastSwiftUpdateCheck = 1610; 243 | LastUpgradeCheck = 1600; 244 | TargetAttributes = { 245 | AD189A552D9C2E80008B987F = { 246 | CreatedOnToolsVersion = 16.3; 247 | }; 248 | ADBB9A652CD3EA5D00BEACA6 = { 249 | CreatedOnToolsVersion = 16.1; 250 | }; 251 | ADBB9A7C2CD3EB5000BEACA6 = { 252 | CreatedOnToolsVersion = 16.1; 253 | }; 254 | }; 255 | }; 256 | buildConfigurationList = AD8C4B602CBE9EEB0093E531 /* Build configuration list for PBXProject "XPCServiceExample" */; 257 | compatibilityVersion = "Xcode 14.0"; 258 | developmentRegion = en; 259 | hasScannedForEncodings = 0; 260 | knownRegions = ( 261 | en, 262 | Base, 263 | ); 264 | mainGroup = AD8C4B5C2CBE9EEB0093E531; 265 | packageReferences = ( 266 | ADBD36FA2CF648D600A14C26 /* XCLocalSwiftPackageReference "../../../XPCDistributedActorSystem" */, 267 | ); 268 | productRefGroup = AD8C4B662CBE9EEB0093E531 /* Products */; 269 | projectDirPath = ""; 270 | projectRoot = ""; 271 | targets = ( 272 | ADBB9A7C2CD3EB5000BEACA6 /* Calculator App */, 273 | ADBB9A652CD3EA5D00BEACA6 /* CalculatorXPCService */, 274 | AD189A552D9C2E80008B987F /* Calculator */, 275 | ); 276 | }; 277 | /* End PBXProject section */ 278 | 279 | /* Begin PBXResourcesBuildPhase section */ 280 | ADBB9A642CD3EA5D00BEACA6 /* Resources */ = { 281 | isa = PBXResourcesBuildPhase; 282 | buildActionMask = 2147483647; 283 | files = ( 284 | ); 285 | runOnlyForDeploymentPostprocessing = 0; 286 | }; 287 | ADBB9A7B2CD3EB5000BEACA6 /* Resources */ = { 288 | isa = PBXResourcesBuildPhase; 289 | buildActionMask = 2147483647; 290 | files = ( 291 | ); 292 | runOnlyForDeploymentPostprocessing = 0; 293 | }; 294 | /* End PBXResourcesBuildPhase section */ 295 | 296 | /* Begin PBXSourcesBuildPhase section */ 297 | AD189A532D9C2E80008B987F /* Sources */ = { 298 | isa = PBXSourcesBuildPhase; 299 | buildActionMask = 2147483647; 300 | files = ( 301 | ); 302 | runOnlyForDeploymentPostprocessing = 0; 303 | }; 304 | ADBB9A622CD3EA5D00BEACA6 /* Sources */ = { 305 | isa = PBXSourcesBuildPhase; 306 | buildActionMask = 2147483647; 307 | files = ( 308 | ); 309 | runOnlyForDeploymentPostprocessing = 0; 310 | }; 311 | ADBB9A792CD3EB5000BEACA6 /* Sources */ = { 312 | isa = PBXSourcesBuildPhase; 313 | buildActionMask = 2147483647; 314 | files = ( 315 | ); 316 | runOnlyForDeploymentPostprocessing = 0; 317 | }; 318 | /* End PBXSourcesBuildPhase section */ 319 | 320 | /* Begin PBXTargetDependency section */ 321 | AD189A5F2D9C2E97008B987F /* PBXTargetDependency */ = { 322 | isa = PBXTargetDependency; 323 | target = AD189A552D9C2E80008B987F /* Calculator */; 324 | targetProxy = AD189A5E2D9C2E97008B987F /* PBXContainerItemProxy */; 325 | }; 326 | AD189A632D9C2EA0008B987F /* PBXTargetDependency */ = { 327 | isa = PBXTargetDependency; 328 | target = AD189A552D9C2E80008B987F /* Calculator */; 329 | targetProxy = AD189A622D9C2EA0008B987F /* PBXContainerItemProxy */; 330 | }; 331 | ADBB9A8E2CD3EB5700BEACA6 /* PBXTargetDependency */ = { 332 | isa = PBXTargetDependency; 333 | target = ADBB9A652CD3EA5D00BEACA6 /* CalculatorXPCService */; 334 | targetProxy = ADBB9A8D2CD3EB5700BEACA6 /* PBXContainerItemProxy */; 335 | }; 336 | /* End PBXTargetDependency section */ 337 | 338 | /* Begin XCBuildConfiguration section */ 339 | AD189A582D9C2E80008B987F /* Debug */ = { 340 | isa = XCBuildConfiguration; 341 | buildSettings = { 342 | CODE_SIGN_STYLE = Automatic; 343 | DYLIB_COMPATIBILITY_VERSION = 1; 344 | DYLIB_CURRENT_VERSION = 1; 345 | EXECUTABLE_PREFIX = lib; 346 | MACOSX_DEPLOYMENT_TARGET = 15.0; 347 | PRODUCT_NAME = "$(TARGET_NAME)"; 348 | SKIP_INSTALL = YES; 349 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 350 | SWIFT_VERSION = 6.0; 351 | }; 352 | name = Debug; 353 | }; 354 | AD189A592D9C2E80008B987F /* Release */ = { 355 | isa = XCBuildConfiguration; 356 | buildSettings = { 357 | CODE_SIGN_STYLE = Automatic; 358 | DYLIB_COMPATIBILITY_VERSION = 1; 359 | DYLIB_CURRENT_VERSION = 1; 360 | EXECUTABLE_PREFIX = lib; 361 | MACOSX_DEPLOYMENT_TARGET = 15.0; 362 | PRODUCT_NAME = "$(TARGET_NAME)"; 363 | SKIP_INSTALL = YES; 364 | SWIFT_VERSION = 6.0; 365 | }; 366 | name = Release; 367 | }; 368 | AD8C4B6E2CBE9EEB0093E531 /* Debug */ = { 369 | isa = XCBuildConfiguration; 370 | baseConfigurationReference = ADBD36C12CF647A100A14C26 /* Config.xcconfig */; 371 | buildSettings = { 372 | ALWAYS_SEARCH_USER_PATHS = NO; 373 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 374 | CLANG_ANALYZER_NONNULL = YES; 375 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 376 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 377 | CLANG_ENABLE_MODULES = YES; 378 | CLANG_ENABLE_OBJC_ARC = YES; 379 | CLANG_ENABLE_OBJC_WEAK = YES; 380 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 381 | CLANG_WARN_BOOL_CONVERSION = YES; 382 | CLANG_WARN_COMMA = YES; 383 | CLANG_WARN_CONSTANT_CONVERSION = YES; 384 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 385 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 386 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 387 | CLANG_WARN_EMPTY_BODY = YES; 388 | CLANG_WARN_ENUM_CONVERSION = YES; 389 | CLANG_WARN_INFINITE_RECURSION = YES; 390 | CLANG_WARN_INT_CONVERSION = YES; 391 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 392 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 393 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 394 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 395 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 396 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 397 | CLANG_WARN_STRICT_PROTOTYPES = YES; 398 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 399 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 400 | CLANG_WARN_UNREACHABLE_CODE = YES; 401 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 402 | COPY_PHASE_STRIP = NO; 403 | DEAD_CODE_STRIPPING = YES; 404 | DEBUG_INFORMATION_FORMAT = dwarf; 405 | ENABLE_STRICT_OBJC_MSGSEND = YES; 406 | ENABLE_TESTABILITY = YES; 407 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 408 | GCC_C_LANGUAGE_STANDARD = gnu17; 409 | GCC_DYNAMIC_NO_PIC = NO; 410 | GCC_NO_COMMON_BLOCKS = YES; 411 | GCC_OPTIMIZATION_LEVEL = 0; 412 | GCC_PREPROCESSOR_DEFINITIONS = ( 413 | "DEBUG=1", 414 | "$(inherited)", 415 | ); 416 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 417 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 418 | GCC_WARN_UNDECLARED_SELECTOR = YES; 419 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 420 | GCC_WARN_UNUSED_FUNCTION = YES; 421 | GCC_WARN_UNUSED_VARIABLE = YES; 422 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 423 | MACOSX_DEPLOYMENT_TARGET = 14.5; 424 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 425 | MTL_FAST_MATH = YES; 426 | ONLY_ACTIVE_ARCH = YES; 427 | SDKROOT = macosx; 428 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; 429 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 430 | }; 431 | name = Debug; 432 | }; 433 | AD8C4B6F2CBE9EEB0093E531 /* Release */ = { 434 | isa = XCBuildConfiguration; 435 | baseConfigurationReference = ADBD36C12CF647A100A14C26 /* Config.xcconfig */; 436 | buildSettings = { 437 | ALWAYS_SEARCH_USER_PATHS = NO; 438 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 439 | CLANG_ANALYZER_NONNULL = YES; 440 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 441 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 442 | CLANG_ENABLE_MODULES = YES; 443 | CLANG_ENABLE_OBJC_ARC = YES; 444 | CLANG_ENABLE_OBJC_WEAK = YES; 445 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 446 | CLANG_WARN_BOOL_CONVERSION = YES; 447 | CLANG_WARN_COMMA = YES; 448 | CLANG_WARN_CONSTANT_CONVERSION = YES; 449 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 450 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 451 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 452 | CLANG_WARN_EMPTY_BODY = YES; 453 | CLANG_WARN_ENUM_CONVERSION = YES; 454 | CLANG_WARN_INFINITE_RECURSION = YES; 455 | CLANG_WARN_INT_CONVERSION = YES; 456 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 457 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 458 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 459 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 460 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 461 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 462 | CLANG_WARN_STRICT_PROTOTYPES = YES; 463 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 464 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 465 | CLANG_WARN_UNREACHABLE_CODE = YES; 466 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 467 | COPY_PHASE_STRIP = NO; 468 | DEAD_CODE_STRIPPING = YES; 469 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 470 | ENABLE_NS_ASSERTIONS = NO; 471 | ENABLE_STRICT_OBJC_MSGSEND = YES; 472 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 473 | GCC_C_LANGUAGE_STANDARD = gnu17; 474 | GCC_NO_COMMON_BLOCKS = YES; 475 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 476 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 477 | GCC_WARN_UNDECLARED_SELECTOR = YES; 478 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 479 | GCC_WARN_UNUSED_FUNCTION = YES; 480 | GCC_WARN_UNUSED_VARIABLE = YES; 481 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 482 | MACOSX_DEPLOYMENT_TARGET = 14.5; 483 | MTL_ENABLE_DEBUG_INFO = NO; 484 | MTL_FAST_MATH = YES; 485 | SDKROOT = macosx; 486 | SWIFT_COMPILATION_MODE = wholemodule; 487 | }; 488 | name = Release; 489 | }; 490 | ADBB9A732CD3EA5D00BEACA6 /* Debug */ = { 491 | isa = XCBuildConfiguration; 492 | buildSettings = { 493 | CODE_SIGN_ENTITLEMENTS = CalculatorXPCService/CalculatorXPCService.entitlements; 494 | CODE_SIGN_STYLE = Automatic; 495 | COMBINE_HIDPI_IMAGES = YES; 496 | CURRENT_PROJECT_VERSION = 1; 497 | ENABLE_HARDENED_RUNTIME = YES; 498 | GENERATE_INFOPLIST_FILE = YES; 499 | INFOPLIST_FILE = CalculatorXPCService/Info.plist; 500 | INFOPLIST_KEY_CFBundleDisplayName = CalculatorXPCService; 501 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 502 | MACOSX_DEPLOYMENT_TARGET = 15.0; 503 | MARKETING_VERSION = 1.0; 504 | PRODUCT_BUNDLE_IDENTIFIER = com.yourcompany.XPCDistributedActorExample.CalculatorXPCService; 505 | PRODUCT_NAME = "$(TARGET_NAME)"; 506 | SKIP_INSTALL = YES; 507 | SWIFT_EMIT_LOC_STRINGS = YES; 508 | SWIFT_VERSION = 5.0; 509 | }; 510 | name = Debug; 511 | }; 512 | ADBB9A742CD3EA5D00BEACA6 /* Release */ = { 513 | isa = XCBuildConfiguration; 514 | buildSettings = { 515 | CODE_SIGN_ENTITLEMENTS = CalculatorXPCService/CalculatorXPCService.entitlements; 516 | CODE_SIGN_STYLE = Automatic; 517 | COMBINE_HIDPI_IMAGES = YES; 518 | CURRENT_PROJECT_VERSION = 1; 519 | ENABLE_HARDENED_RUNTIME = YES; 520 | GENERATE_INFOPLIST_FILE = YES; 521 | INFOPLIST_FILE = CalculatorXPCService/Info.plist; 522 | INFOPLIST_KEY_CFBundleDisplayName = CalculatorXPCService; 523 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 524 | MACOSX_DEPLOYMENT_TARGET = 15.0; 525 | MARKETING_VERSION = 1.0; 526 | PRODUCT_BUNDLE_IDENTIFIER = com.yourcompany.XPCDistributedActorExample.CalculatorXPCService; 527 | PRODUCT_NAME = "$(TARGET_NAME)"; 528 | SKIP_INSTALL = YES; 529 | SWIFT_EMIT_LOC_STRINGS = YES; 530 | SWIFT_VERSION = 5.0; 531 | }; 532 | name = Release; 533 | }; 534 | ADBB9A8A2CD3EB5100BEACA6 /* Debug */ = { 535 | isa = XCBuildConfiguration; 536 | buildSettings = { 537 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 538 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 539 | CODE_SIGN_ENTITLEMENTS = "Calculator App/CalculatorApp.entitlements"; 540 | CODE_SIGN_STYLE = Automatic; 541 | COMBINE_HIDPI_IMAGES = YES; 542 | CURRENT_PROJECT_VERSION = 1; 543 | ENABLE_HARDENED_RUNTIME = YES; 544 | ENABLE_PREVIEWS = YES; 545 | GENERATE_INFOPLIST_FILE = YES; 546 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 547 | LD_RUNPATH_SEARCH_PATHS = ( 548 | "$(inherited)", 549 | "@executable_path/../Frameworks", 550 | ); 551 | MACOSX_DEPLOYMENT_TARGET = 15.1; 552 | MARKETING_VERSION = 1.0; 553 | PRODUCT_BUNDLE_IDENTIFIER = com.yourcompany.XPCDistributedActorExample.CalculatorUI; 554 | PRODUCT_NAME = "$(TARGET_NAME)"; 555 | SWIFT_EMIT_LOC_STRINGS = YES; 556 | SWIFT_VERSION = 5.0; 557 | }; 558 | name = Debug; 559 | }; 560 | ADBB9A8B2CD3EB5100BEACA6 /* Release */ = { 561 | isa = XCBuildConfiguration; 562 | buildSettings = { 563 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 564 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 565 | CODE_SIGN_ENTITLEMENTS = "Calculator App/CalculatorApp.entitlements"; 566 | CODE_SIGN_STYLE = Automatic; 567 | COMBINE_HIDPI_IMAGES = YES; 568 | CURRENT_PROJECT_VERSION = 1; 569 | ENABLE_HARDENED_RUNTIME = YES; 570 | ENABLE_PREVIEWS = YES; 571 | GENERATE_INFOPLIST_FILE = YES; 572 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 573 | LD_RUNPATH_SEARCH_PATHS = ( 574 | "$(inherited)", 575 | "@executable_path/../Frameworks", 576 | ); 577 | MACOSX_DEPLOYMENT_TARGET = 15.1; 578 | MARKETING_VERSION = 1.0; 579 | PRODUCT_BUNDLE_IDENTIFIER = com.yourcompany.XPCDistributedActorExample.CalculatorUI; 580 | PRODUCT_NAME = "$(TARGET_NAME)"; 581 | SWIFT_EMIT_LOC_STRINGS = YES; 582 | SWIFT_VERSION = 5.0; 583 | }; 584 | name = Release; 585 | }; 586 | /* End XCBuildConfiguration section */ 587 | 588 | /* Begin XCConfigurationList section */ 589 | AD189A572D9C2E80008B987F /* Build configuration list for PBXNativeTarget "Calculator" */ = { 590 | isa = XCConfigurationList; 591 | buildConfigurations = ( 592 | AD189A582D9C2E80008B987F /* Debug */, 593 | AD189A592D9C2E80008B987F /* Release */, 594 | ); 595 | defaultConfigurationIsVisible = 0; 596 | defaultConfigurationName = Release; 597 | }; 598 | AD8C4B602CBE9EEB0093E531 /* Build configuration list for PBXProject "XPCServiceExample" */ = { 599 | isa = XCConfigurationList; 600 | buildConfigurations = ( 601 | AD8C4B6E2CBE9EEB0093E531 /* Debug */, 602 | AD8C4B6F2CBE9EEB0093E531 /* Release */, 603 | ); 604 | defaultConfigurationIsVisible = 0; 605 | defaultConfigurationName = Release; 606 | }; 607 | ADBB9A722CD3EA5D00BEACA6 /* Build configuration list for PBXNativeTarget "CalculatorXPCService" */ = { 608 | isa = XCConfigurationList; 609 | buildConfigurations = ( 610 | ADBB9A732CD3EA5D00BEACA6 /* Debug */, 611 | ADBB9A742CD3EA5D00BEACA6 /* Release */, 612 | ); 613 | defaultConfigurationIsVisible = 0; 614 | defaultConfigurationName = Release; 615 | }; 616 | ADBB9A892CD3EB5100BEACA6 /* Build configuration list for PBXNativeTarget "Calculator App" */ = { 617 | isa = XCConfigurationList; 618 | buildConfigurations = ( 619 | ADBB9A8A2CD3EB5100BEACA6 /* Debug */, 620 | ADBB9A8B2CD3EB5100BEACA6 /* Release */, 621 | ); 622 | defaultConfigurationIsVisible = 0; 623 | defaultConfigurationName = Release; 624 | }; 625 | /* End XCConfigurationList section */ 626 | 627 | /* Begin XCLocalSwiftPackageReference section */ 628 | ADBD36FA2CF648D600A14C26 /* XCLocalSwiftPackageReference "../../../XPCDistributedActorSystem" */ = { 629 | isa = XCLocalSwiftPackageReference; 630 | relativePath = ../../../XPCDistributedActorSystem; 631 | }; 632 | /* End XCLocalSwiftPackageReference section */ 633 | 634 | /* Begin XCSwiftPackageProductDependency section */ 635 | AD189A642D9C2EA7008B987F /* XPCDistributedActorSystem */ = { 636 | isa = XCSwiftPackageProductDependency; 637 | package = ADBD36FA2CF648D600A14C26 /* XCLocalSwiftPackageReference "../../../XPCDistributedActorSystem" */; 638 | productName = XPCDistributedActorSystem; 639 | }; 640 | /* End XCSwiftPackageProductDependency section */ 641 | }; 642 | rootObject = AD8C4B5D2CBE9EEB0093E531 /* Project object */; 643 | } 644 | -------------------------------------------------------------------------------- /Examples/XPC Service/XPCServiceExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 6.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "XPCDistributedActorSystem", 8 | platforms: [ 9 | .macOS(.v15), 10 | ], 11 | products: [ 12 | .library(name: "XPCDistributedActorSystem", targets: ["XPCDistributedActorSystem"]), 13 | ], 14 | targets: [ 15 | .target(name: "XPCDistributedActorSystem"), 16 | ] 17 | ) 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # XPCDistributedActorSystem 2 | 3 | An _experimental_ [DistributedActorSystem](https://developer.apple.com/documentation/distributed/distributedactorsystem) for Swift 6 that uses XPC as a transport layer. 4 | 5 | # Supported platforms 6 | 7 | Works on macOS only (because XPC support on iOS is extremely limited). 8 | 9 | Designed for macOS 15, but might work on earlier versions. 10 | 11 | # Known issues 12 | 13 | * ⚠️ Barely tested 14 | * Actor ID assignment needs improvement 15 | 16 | # Usage 17 | 18 | Two example projects are included (for an XPC Service and a Launch Daemon). They are the best starting point to see how this system works. 19 | 20 | To run the examples, you need to set your development team by duplicating the file `Config.xcconfig.template`, renaming it to `Config.xcconfig` and update it with your Development Team Identifier: 21 | 22 | ``` 23 | DEVELOPMENT_TEAM = 24 | ``` 25 | 26 | Short code sample: 27 | 28 | ### In the App: 29 | 30 | ```swift 31 | import XPCDistributedActorSystem 32 | import Calculator 33 | 34 | // Set up the system 35 | let xpc = XPCDistributedActorSystem(mode: .connectingToXPCService(serviceName: yourServiceName), codeSigningRequirement: try .sameTeam) 36 | let calculator = try Calculator.resolve(id: .init(1), using: xpc) 37 | 38 | // Run function in the XPC service and print the result 39 | let result = try await calculator.add(number1, number2) 40 | print(result) 41 | 42 | ``` 43 | 44 | ### In the XPC Service: 45 | 46 | ```swift 47 | import XPCDistributedActorSystem 48 | import Calculator 49 | 50 | let system = XPCDistributedActorSystem(mode: .receivingConnections, codeSigningRequirement: try .sameTeam) 51 | let calculator = Calculator(actorSystem: system) 52 | let listener = try XPCServiceListener(actorSystem: system) 53 | listener.run() 54 | ``` 55 | 56 | ### Required project structure: 57 | 58 | All the types referenced by the distributed actor need to be available to both XPC endpoints – and they have to be *absolutely* identical, because demangling for the distributed actor will fail otherwise. 59 | 60 | The easiest way is to just put the distributed actor and all types required for its functionality inside a Library, and add that Library to both targets. 61 | 62 | # Ideas for the future 63 | 64 | * Implement a way to share status with `@Observable` 65 | 66 | 67 | # Contributing 68 | 69 | Feel free to fork (see [license](LICENSE.txt)) or contribute. 70 | -------------------------------------------------------------------------------- /Sources/XPCDistributedActorSystem/Bundle+FirstXPCService.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public extension Bundle 4 | { 5 | func firstXPCServiceIdentifier() -> String? 6 | { 7 | let xpcPath = self.bundleURL.appending(components: "Contents", "XPCServices", directoryHint: .isDirectory) 8 | 9 | guard 10 | let contents = try? FileManager.default.contentsOfDirectory(at: xpcPath, includingPropertiesForKeys: nil), 11 | let firstXPCService = contents.first(where: { $0.pathExtension == "xpc" }), 12 | let serviceBundle = Bundle(url: firstXPCService) 13 | else { 14 | return nil 15 | } 16 | 17 | return serviceBundle.bundleIdentifier 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Sources/XPCDistributedActorSystem/CodeSigningRequirement.swift: -------------------------------------------------------------------------------- 1 | import Security 2 | import Foundation 3 | 4 | public struct CodeSigningRequirement: Sendable 5 | { 6 | enum Error: Swift.Error, LocalizedError { 7 | case invalidRequirement 8 | 9 | var errorDescription: String? { 10 | switch self { 11 | case .invalidRequirement: 12 | return "Invalid code signing requirement." 13 | } 14 | } 15 | } 16 | 17 | public static var sameTeam: CodeSigningRequirement { 18 | get throws { 19 | let teamID = try SigningInformationExtractor.getCurrentTeamID() 20 | let requirement = "anchor apple generic and certificate leaf[subject.OU] = \"\(teamID)\"" 21 | return CodeSigningRequirement(requirement: requirement) 22 | } 23 | } 24 | 25 | public static let none: CodeSigningRequirement? = nil 26 | 27 | public static func custom(_ requirement: String) throws -> CodeSigningRequirement 28 | { 29 | var requirementRef: SecRequirement? 30 | let status = SecRequirementCreateWithString(requirement as CFString, SecCSFlags(), &requirementRef) 31 | 32 | guard status == errSecSuccess else { 33 | throw Error.invalidRequirement 34 | } 35 | 36 | return .init(requirement: requirement) 37 | } 38 | 39 | let requirement: String 40 | 41 | init(requirement: String) 42 | { 43 | self.requirement = requirement 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/XPCDistributedActorSystem/GenericInvocationDecoder.swift: -------------------------------------------------------------------------------- 1 | import Distributed 2 | import Foundation 3 | 4 | public struct GenericInvocationDecoder: DistributedTargetInvocationDecoder 5 | { 6 | static let decoder = JSONDecoder() 7 | 8 | enum Error : Swift.Error { 9 | case notEnoughArguments 10 | } 11 | 12 | public typealias SerializationRequirement = any Codable 13 | 14 | private let request: InvocationRequest 15 | private var argumentsIterator: Array.Iterator 16 | 17 | init(system: any DistributedActorSystem, request: InvocationRequest) 18 | { 19 | self.argumentsIterator = request.arguments.makeIterator() 20 | self.request = request 21 | Self.decoder.userInfo[.actorSystemKey] = system 22 | } 23 | 24 | public mutating func decodeNextArgument() throws -> Argument 25 | { 26 | guard let data = argumentsIterator.next() else { 27 | throw Error.notEnoughArguments 28 | } 29 | return try Self.decoder.decode(Argument.self, from: data) 30 | } 31 | 32 | public mutating func decodeGenericSubstitutions() throws -> [Any.Type] 33 | { 34 | self.request.genericSubstitutions.compactMap({ _typeByName($0) }) 35 | } 36 | 37 | public mutating func decodeErrorType() throws -> (Any.Type)? 38 | { 39 | guard let errorTypeMangled = self.request.errorType else { return nil } 40 | return _typeByName(errorTypeMangled) 41 | } 42 | 43 | public mutating func decodeReturnType() throws -> (Any.Type)? 44 | { 45 | guard let returnTypeMangled = self.request.returnType else { return nil } 46 | return _typeByName(returnTypeMangled) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Sources/XPCDistributedActorSystem/GenericInvocationEncoder.swift: -------------------------------------------------------------------------------- 1 | import Distributed 2 | import Foundation 3 | 4 | public struct GenericInvocationEncoder: DistributedTargetInvocationEncoder 5 | { 6 | static let encoder = JSONEncoder() 7 | 8 | enum Error: Swift.Error { 9 | case failedToFindMangedName 10 | } 11 | 12 | public typealias SerializationRequirement = any Codable 13 | 14 | var generics: [String] = .init() 15 | var arguments: [Data] = .init() 16 | var returnType: String? = nil 17 | var errorType: String? = nil 18 | 19 | public mutating func recordArgument(_ argument: RemoteCallArgument) throws 20 | { 21 | arguments.append(try Self.encoder.encode(argument.value)) 22 | } 23 | 24 | public mutating func recordGenericSubstitution(_ type: T.Type) throws 25 | { 26 | guard let mangledName = _mangledTypeName(type) else { throw Error.failedToFindMangedName } 27 | generics.append(mangledName) 28 | } 29 | 30 | public mutating func recordReturnType(_ type: R.Type) throws 31 | { 32 | self.returnType = _mangledTypeName(type) 33 | } 34 | 35 | public mutating func recordErrorType(_ type: E.Type) throws 36 | { 37 | self.errorType = _mangledTypeName(type) 38 | } 39 | 40 | public mutating func doneRecording() throws {} 41 | } 42 | -------------------------------------------------------------------------------- /Sources/XPCDistributedActorSystem/InvocationRequest.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Distributed 3 | 4 | public struct InvocationRequest: Codable, Sendable 5 | { 6 | let actorId: XPCDistributedActorSystem.ActorID 7 | let target: String 8 | let arguments: [Data] 9 | let genericSubstitutions: [String] 10 | let returnType: String? 11 | let errorType: String? 12 | 13 | init(actorId: XPCDistributedActorSystem.ActorID, target: String, invocation: GenericInvocationEncoder) 14 | { 15 | self.actorId = actorId 16 | self.target = target 17 | self.arguments = invocation.arguments 18 | self.genericSubstitutions = invocation.generics 19 | self.returnType = invocation.returnType 20 | self.errorType = invocation.errorType 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/XPCDistributedActorSystem/InvocationResponse.swift: -------------------------------------------------------------------------------- 1 | public struct InvocationResponse: Codable where T: Codable 2 | { 3 | var error: String? 4 | var value: T? 5 | } 6 | 7 | extension InvocationResponse where T == Never 8 | { 9 | public init(error: Swift.Error) 10 | { 11 | self.error = String(describing: error) 12 | } 13 | } 14 | 15 | extension InvocationResponse where T == Never 16 | { 17 | public init() {} 18 | } 19 | -------------------------------------------------------------------------------- /Sources/XPCDistributedActorSystem/InvocationResultHandler.swift: -------------------------------------------------------------------------------- 1 | import Distributed 2 | 3 | public struct InvocationResultHandler: DistributedTargetInvocationResultHandler 4 | { 5 | public typealias SerializationRequirement = any Codable 6 | 7 | let xpcConnection: XPCConnection 8 | let requestMessage: XPCMessageWithObject 9 | 10 | init(xpcConnection: XPCConnection, request: XPCMessageWithObject) 11 | { 12 | self.xpcConnection = xpcConnection 13 | self.requestMessage = request 14 | } 15 | 16 | public func onReturn(value: V) async throws 17 | { 18 | let response = InvocationResponse(value: value) 19 | let messageToSend = try XPCMessageWithObject(from: response, replyTo: requestMessage) 20 | try await xpcConnection.reply(with: messageToSend) 21 | } 22 | 23 | public func onReturnVoid() async throws 24 | { 25 | let response = InvocationResponse() 26 | let messageToSend = try XPCMessageWithObject(from: response, replyTo: requestMessage) 27 | try await xpcConnection.reply(with: messageToSend) 28 | } 29 | 30 | public func onThrow(error: Err) async throws where Err: Swift.Error 31 | { 32 | let response = InvocationResponse(error: error) 33 | let messageToSend = try XPCMessageWithObject(from: response, replyTo: requestMessage) 34 | try await xpcConnection.reply(with: messageToSend) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Sources/XPCDistributedActorSystem/LiveActorStorage.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Distributed 3 | import Synchronization 4 | 5 | final class LiveActorStorage: Sendable 6 | { 7 | class Reference 8 | { 9 | weak var actor: (any DistributedActor)? 10 | 11 | init(to actor: (any DistributedActor)? = nil) 12 | { 13 | self.actor = actor 14 | } 15 | } 16 | 17 | let actors: Mutex<[XPCDistributedActorSystem.ActorID:Reference]> = .init([:]) 18 | 19 | func add(_ actor: Act) where Act: DistributedActor, Act.ID == XPCDistributedActorSystem.ActorID 20 | { 21 | actors.withLock { 22 | $0[actor.id] = Reference(to: actor) 23 | } 24 | } 25 | 26 | func get(_ id: XPCDistributedActorSystem.ActorID) -> (any DistributedActor)? 27 | { 28 | actors.withLock { 29 | $0[id]?.actor 30 | } 31 | } 32 | 33 | func get(_ id: XPCDistributedActorSystem.ActorID, as: Act.Type) -> Act? 34 | { 35 | actors.withLock { 36 | $0[id]?.actor as? Act 37 | } 38 | } 39 | 40 | func remove(_ id: XPCDistributedActorSystem.ActorID) 41 | { 42 | actors.withLock { 43 | _ = $0.removeValue(forKey: id) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/XPCDistributedActorSystem/SigningInformationExtractor.swift: -------------------------------------------------------------------------------- 1 | import Security 2 | 3 | struct SigningInformationExtractor 4 | { 5 | enum Error: Swift.Error 6 | { 7 | case secError(OSStatus) 8 | case otherError(String) 9 | 10 | var localizedDescription: String 11 | { 12 | switch self { 13 | case .secError(let status): 14 | if let specificErrorMessage = SecCopyErrorMessageString(status, nil) as? String { 15 | specificErrorMessage 16 | } else { 17 | "Security function failed with status \(status)" 18 | } 19 | case .otherError(let message): 20 | message 21 | } 22 | } 23 | } 24 | 25 | static func withStatusCheck(_ status: OSStatus) throws 26 | { 27 | guard status == errSecSuccess else { 28 | throw Error.secError(status) 29 | } 30 | } 31 | 32 | static func getCurrentTeamID() throws -> String 33 | { 34 | var currentCode: SecCode? 35 | try withStatusCheck(SecCodeCopySelf(SecCSFlags(), ¤tCode)) 36 | guard let currentCode else { 37 | throw Error.otherError("Failed to get current code for unknown reason while trying to extract team ID") 38 | } 39 | 40 | var codeOnDisk: SecStaticCode? 41 | try withStatusCheck(SecCodeCopyStaticCode(currentCode, SecCSFlags(), &codeOnDisk)) 42 | guard let codeOnDisk else { 43 | throw Error.otherError("Failed to get code on disk for unknown reason while trying to extract team ID") 44 | } 45 | 46 | var extractedSigningInformation: CFDictionary? 47 | let signingInformationFlags = SecCSFlags(rawValue: kSecCSSigningInformation) 48 | try withStatusCheck(SecCodeCopySigningInformation(codeOnDisk, signingInformationFlags, &extractedSigningInformation)) 49 | guard let extractedSigningInformation = extractedSigningInformation as? [String: Any], let teamIdentifier = extractedSigningInformation[kSecCodeInfoTeamIdentifier as String] as? String else { 50 | throw Error.otherError("Failed to copy code signing information for unknown reason while trying to extract team ID") 51 | } 52 | 53 | return teamIdentifier 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sources/XPCDistributedActorSystem/XPCActor.swift: -------------------------------------------------------------------------------- 1 | @globalActor public actor XPCActor 2 | { 3 | public static var shared = XPCActor() 4 | } 5 | -------------------------------------------------------------------------------- /Sources/XPCDistributedActorSystem/XPCConnection.swift: -------------------------------------------------------------------------------- 1 | @preconcurrency import XPC 2 | 3 | actor XPCConnection 4 | { 5 | enum State { 6 | case created 7 | case active 8 | case notFound 9 | case invalidCodeSigning 10 | } 11 | 12 | private let connection: xpc_connection_t 13 | private let actorSystem: XPCDistributedActorSystem 14 | private(set) var state: State = .created 15 | 16 | init(incomingConnection connection: sending xpc_connection_t, actorSystem: XPCDistributedActorSystem, codeSigningRequirement: CodeSigningRequirement?) 17 | { 18 | self.init(connection: connection, actorSystem: actorSystem, codeSigningRequirement: codeSigningRequirement) 19 | } 20 | 21 | init(daemonServiceName: String, actorSystem: XPCDistributedActorSystem, codeSigningRequirement: CodeSigningRequirement?) 22 | { 23 | let connection = xpc_connection_create_mach_service(daemonServiceName, nil, UInt64(XPC_CONNECTION_MACH_SERVICE_PRIVILEGED)) 24 | self.init(connection: connection, actorSystem: actorSystem, codeSigningRequirement: codeSigningRequirement) 25 | } 26 | 27 | init(serviceName: String, actorSystem: XPCDistributedActorSystem, codeSigningRequirement: CodeSigningRequirement?) 28 | { 29 | let connection = xpc_connection_create(serviceName, nil) 30 | self.init(connection: connection, actorSystem: actorSystem, codeSigningRequirement: codeSigningRequirement) 31 | } 32 | 33 | private init(connection: sending xpc_connection_t, actorSystem: XPCDistributedActorSystem, codeSigningRequirement: CodeSigningRequirement?) 34 | { 35 | self.connection = connection 36 | self.actorSystem = actorSystem 37 | 38 | if let codeSigningRequirement { 39 | let codeSigningRequirementStatus = xpc_connection_set_peer_code_signing_requirement(connection, codeSigningRequirement.requirement) 40 | guard codeSigningRequirementStatus == 0 else { 41 | Task { 42 | await setState(.invalidCodeSigning) 43 | } 44 | return 45 | } 46 | } 47 | 48 | xpc_connection_set_event_handler(connection, handleEvent) 49 | xpc_connection_activate(connection) 50 | 51 | Task { 52 | await setState(.active) 53 | } 54 | } 55 | 56 | private func setState(_ state: State) 57 | { 58 | self.state = state 59 | } 60 | 61 | public func close() 62 | { 63 | xpc_connection_cancel(connection) 64 | } 65 | 66 | nonisolated func handleEvent(_ event: xpc_object_t) 67 | { 68 | Task { 69 | if event === XPC_ERROR_PEER_CODE_SIGNING_REQUIREMENT { 70 | await self.setState(.invalidCodeSigning) 71 | return 72 | } else if event === XPC_ERROR_CONNECTION_INVALID { 73 | await self.setState(.notFound) 74 | await self.actorSystem.onConnectionInvalidated() 75 | return 76 | } else if event === XPC_ERROR_CONNECTION_INTERRUPTED { 77 | // Interruptions can happen if, for example, the target process exits. However, daemons/agents are usually automatically restarted and the connection will work fine after that without having to recreate or reactivate it. 78 | return 79 | } 80 | 81 | actorSystem.handleIncomingInvocation(connection: self, message: XPCMessageWithObject(raw: event)) 82 | } 83 | } 84 | 85 | deinit 86 | { 87 | xpc_connection_cancel(connection) 88 | } 89 | 90 | func send(_ objectToSend: ObjectToSend, expect: ObjectToReceive.Type) async throws -> sending ObjectToReceive where ObjectToSend: Encodable, ObjectToReceive: Decodable 91 | { 92 | guard state == .active else { throw XPCError(.connectionNotReady) } 93 | 94 | let messageToSend = try XPCMessageWithObject(from: objectToSend) 95 | 96 | let receivedMessage: xpc_object_t = try await withCheckedThrowingContinuation { continuation in 97 | xpc_connection_send_message_with_reply(connection, messageToSend.raw, nil) { message in 98 | continuation.resume(returning: message) 99 | } 100 | } 101 | 102 | // TODO: Implement timeout 103 | 104 | let receivedMessageWithObject = XPCMessageWithObject(raw: receivedMessage) 105 | let extractedObject = try receivedMessageWithObject.extract(ObjectToReceive.self) 106 | return extractedObject 107 | } 108 | 109 | func reply(with messageToSend: sending XPCMessageWithObject) throws 110 | { 111 | guard state == .active else { throw XPCError(.connectionNotReady) } 112 | 113 | xpc_connection_send_message(connection, messageToSend.raw) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Sources/XPCDistributedActorSystem/XPCDaemonListener.swift: -------------------------------------------------------------------------------- 1 | @preconcurrency import XPC 2 | 3 | public actor XPCDaemonListener 4 | { 5 | let actorSystem: XPCDistributedActorSystem 6 | var lastConnection: XPCConnection? 7 | private var listener: xpc_connection_t? 8 | 9 | public init(daemonServiceName: String, actorSystem: XPCDistributedActorSystem) throws 10 | { 11 | self.actorSystem = actorSystem 12 | let listener = xpc_connection_create_mach_service(daemonServiceName, nil, UInt64(XPC_CONNECTION_MACH_SERVICE_LISTENER)) 13 | self.listener = listener 14 | 15 | xpc_connection_set_event_handler(listener) { event in 16 | if event === XPC_ERROR_CONNECTION_INVALID { 17 | print("XPCDaemonListener received XPC_ERROR_CONNECTION_INVALID") 18 | // TODO: Invalidate listener? 19 | return 20 | } else if event === XPC_ERROR_CONNECTION_INTERRUPTED { 21 | print("XPCDaemonListener received XPC_ERROR_CONNECTION_INTERRUPTED") 22 | return 23 | } 24 | 25 | let connection = XPCConnection(incomingConnection: event, actorSystem: actorSystem, codeSigningRequirement: actorSystem.codeSigningRequirement) 26 | Task { 27 | await self.setConnection(connection) 28 | } 29 | } 30 | xpc_connection_activate(listener) 31 | } 32 | 33 | func setConnection(_ connection: XPCConnection) async 34 | { 35 | if let lastConnection { 36 | await lastConnection.close() 37 | } 38 | self.lastConnection = connection 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/XPCDistributedActorSystem/XPCDistributedActorSystem.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Distributed 3 | import Synchronization 4 | 5 | public final class XPCDistributedActorSystem : DistributedActorSystem 6 | { 7 | public enum State: Sendable { 8 | case connecting 9 | case connected 10 | case disconnected 11 | } 12 | 13 | public enum Mode: Sendable { 14 | case receivingConnections 15 | case connectingToDaemon(serviceName: String) 16 | case connectingToXPCService(serviceName: String) 17 | 18 | var canReconnect: Bool { 19 | switch self { 20 | case .connectingToDaemon, .connectingToXPCService: 21 | return true 22 | default: 23 | return false 24 | } 25 | } 26 | } 27 | 28 | enum ProtocolError: Swift.Error, LocalizedError { 29 | case noConnection 30 | case failedToFindValueInResponse 31 | case errorFromRemoteActor(String) 32 | case failedToFindActorForId(ActorID) 33 | 34 | var errorDescription: String? { 35 | switch self { 36 | case .noConnection: 37 | "No active connection has been found" 38 | case .failedToFindValueInResponse: 39 | "Failed to find value in response" 40 | case .errorFromRemoteActor(let string): 41 | "Remote: \(string)" 42 | case .failedToFindActorForId(let actorID): 43 | "Failed to find actor for ID \(actorID)" 44 | } 45 | } 46 | } 47 | 48 | public typealias ActorID = Int 49 | public typealias InvocationEncoder = GenericInvocationEncoder 50 | public typealias InvocationDecoder = GenericInvocationDecoder 51 | public typealias ResultHandler = InvocationResultHandler 52 | public typealias SerializationRequirement = any Codable 53 | 54 | @XPCActor private var xpcConnection: XPCConnection? 55 | 56 | let liveActorStorage = LiveActorStorage() 57 | let nextActorId: Mutex<[ObjectIdentifier:Int]> = .init([:]) 58 | let codeSigningRequirement: CodeSigningRequirement? 59 | let mode: Mode 60 | let state: Mutex = .init(.disconnected) 61 | 62 | public init(mode: Mode, codeSigningRequirement: CodeSigningRequirement?) 63 | { 64 | self.codeSigningRequirement = codeSigningRequirement 65 | self.mode = mode 66 | 67 | connect() 68 | } 69 | 70 | func connect() 71 | { 72 | self.state.withLock { $0 = .connecting } 73 | Task { @XPCActor in 74 | switch mode { 75 | case .receivingConnections: 76 | break 77 | case .connectingToDaemon(serviceName: let serviceName): 78 | self.xpcConnection = XPCConnection(daemonServiceName: serviceName, actorSystem: self, codeSigningRequirement: codeSigningRequirement) 79 | self.state.withLock { $0 = .connected } 80 | case .connectingToXPCService(serviceName: let serviceName): 81 | self.xpcConnection = XPCConnection(serviceName: serviceName, actorSystem: self, codeSigningRequirement: codeSigningRequirement) 82 | self.state.withLock { $0 = .connected } 83 | } 84 | } 85 | } 86 | 87 | nonisolated func setConnection(_ connection: XPCConnection?) 88 | { 89 | Task { @XPCActor in 90 | self.xpcConnection = connection 91 | } 92 | } 93 | 94 | func onConnectionInvalidated() 95 | { 96 | self.state.withLock { $0 = .disconnected } 97 | 98 | // The XPC connection becomes invalid if the specified service couldn't be found in the XPC service namespace. 99 | // Usually, this is because of a misconfiguration. However, there are scenarios where invalidations happen and need to be dealt with. 100 | // Example: After loading a deamon, its XPC listener isn't quite ready when trying to connect immediately. 101 | // In these cases, it's reasonable to try to reconnect. 102 | 103 | if self.mode.canReconnect { 104 | connect() 105 | } 106 | } 107 | 108 | func handleIncomingInvocation(connection: XPCConnection, message: XPCMessageWithObject) 109 | { 110 | Task { 111 | do { 112 | let invocationRequest = try message.extract(InvocationRequest.self) 113 | 114 | guard let localActor = self.liveActorStorage.get(invocationRequest.actorId) else { 115 | throw ProtocolError.failedToFindActorForId(invocationRequest.actorId) 116 | } 117 | 118 | var invocationDecoder = InvocationDecoder(system: self, request: invocationRequest) 119 | 120 | try await self.executeDistributedTarget( 121 | on: localActor, 122 | target: RemoteCallTarget(invocationRequest.target), 123 | invocationDecoder: &invocationDecoder, 124 | handler: ResultHandler(xpcConnection: connection, request: message) 125 | ) 126 | } catch { 127 | let response = InvocationResponse(error: error) 128 | let messageToSend = try XPCMessageWithObject(from: response, replyTo: message) 129 | try await connection.reply(with: messageToSend) 130 | } 131 | } 132 | } 133 | 134 | public func makeInvocationEncoder() -> InvocationEncoder 135 | { 136 | InvocationEncoder() 137 | } 138 | 139 | public func remoteCall(on actor: Act, target: RemoteCallTarget, invocation: inout InvocationEncoder, throwing: Err.Type, returning: Res.Type) async throws -> Res where Act: DistributedActor, Act.ID == ActorID, Err: Error, Res: Codable 140 | { 141 | guard let xpcConnection = await xpcConnection else { throw ProtocolError.noConnection } 142 | 143 | let request = InvocationRequest(actorId: actor.id, target: target.identifier, invocation: invocation) 144 | let response = try await xpcConnection.send(request, expect: InvocationResponse.self) 145 | 146 | if let error = response.error { 147 | throw ProtocolError.errorFromRemoteActor(error) 148 | } 149 | 150 | guard let value = response.value else { 151 | throw ProtocolError.failedToFindValueInResponse 152 | } 153 | 154 | return value 155 | } 156 | 157 | public func remoteCallVoid(on actor: Act, target: RemoteCallTarget, invocation: inout InvocationEncoder, throwing: Err.Type) async throws where Act: DistributedActor, Act.ID == ActorID, Err: Error 158 | { 159 | guard let xpcConnection = await xpcConnection else { throw ProtocolError.noConnection } 160 | 161 | let request = InvocationRequest(actorId: actor.id, target: target.identifier, invocation: invocation) 162 | let response = try await xpcConnection.send(request, expect: InvocationResponse.self) 163 | 164 | if let error = response.error { 165 | throw ProtocolError.errorFromRemoteActor(error) 166 | } 167 | } 168 | 169 | public func actorReady(_ actor: Act) where Act : DistributedActor, ActorID == Act.ID 170 | { 171 | liveActorStorage.add(actor) 172 | } 173 | 174 | public func resolve(id: ActorID, as actorType: Act.Type) throws -> Act? where Act : DistributedActor, ActorID == Act.ID 175 | { 176 | liveActorStorage.get(id, as: actorType.self) 177 | } 178 | 179 | public func assignID(_ actorType: Act.Type) -> ActorID where Act : DistributedActor, Int == Act.ID 180 | { 181 | var id: Int? 182 | 183 | nextActorId.withLock { dictionary in 184 | let nextId = dictionary[ObjectIdentifier(actorType)] ?? 1 185 | dictionary[ObjectIdentifier(actorType)] = nextId + 1 186 | id = nextId 187 | } 188 | 189 | guard let id else { 190 | fatalError("Failed to assign ID") 191 | } 192 | 193 | return id 194 | } 195 | 196 | public func resignID(_ id: XPCDistributedActorSystem.ActorID) 197 | { 198 | liveActorStorage.remove(id) 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /Sources/XPCDistributedActorSystem/XPCError.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import XPC 3 | 4 | public struct XPCError: Error, Codable, Sendable, LocalizedError 5 | { 6 | public enum Category: Codable, Sendable 7 | { 8 | case connectionInterrupted 9 | case connectionInvalid 10 | case terminationImminent 11 | case codeSignatureCheckFailed 12 | case unexpectedMessageType 13 | case failedToGetDataFromXPCDictionary 14 | case failedToCreateReply 15 | case unknown 16 | case connectionNotReady 17 | } 18 | 19 | public let category: Category 20 | public let nativeErrorDescription: String? 21 | 22 | public var errorDescription: String? { 23 | if let nativeErrorDescription { 24 | return nativeErrorDescription 25 | } 26 | 27 | return switch category { 28 | case .connectionInterrupted: 29 | "Connection interrupted" 30 | case .connectionInvalid: 31 | "Connection invalid" 32 | case .terminationImminent: 33 | "Termination imminent" 34 | case .codeSignatureCheckFailed: 35 | "Code signature check failed (or version/artifact mismatch)" 36 | case .unexpectedMessageType: 37 | "Unexpected message type" 38 | case .failedToGetDataFromXPCDictionary: 39 | "Failed to get data from XPC dictionary" 40 | case .failedToCreateReply: 41 | "Failed to create reply" 42 | case .unknown: 43 | "Unknown error" 44 | case .connectionNotReady: 45 | "Connection not ready" 46 | } 47 | } 48 | 49 | init(_ category: Category) 50 | { 51 | self.category = category 52 | self.nativeErrorDescription = nil 53 | } 54 | 55 | init(error: xpc_object_t) 56 | { 57 | let description: String? = if let descriptionFromXpc = xpc_dictionary_get_string(error, XPC_ERROR_KEY_DESCRIPTION) { 58 | String(cString: descriptionFromXpc) 59 | } else { 60 | nil 61 | } 62 | 63 | self.nativeErrorDescription = description 64 | 65 | if error === XPC_ERROR_CONNECTION_INTERRUPTED { 66 | self.category = .connectionInterrupted 67 | } else if error === XPC_ERROR_CONNECTION_INVALID { 68 | self.category = .connectionInvalid 69 | } else if error === XPC_ERROR_TERMINATION_IMMINENT { 70 | self.category = .terminationImminent 71 | } else if error === XPC_ERROR_PEER_CODE_SIGNING_REQUIREMENT { 72 | self.category = .codeSignatureCheckFailed 73 | } else { 74 | self.category = .unknown 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Sources/XPCDistributedActorSystem/XPCMessageWithObject.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | @preconcurrency import XPC 3 | 4 | final class XPCMessageWithObject: Sendable 5 | { 6 | enum Error: Swift.Error { 7 | case failedToConvertData 8 | case failedToCreateMessageDictionary 9 | } 10 | 11 | let raw: xpc_object_t 12 | 13 | init(from objectToSend: T, replyTo request: XPCMessageWithObject? = nil) throws where T: Encodable 14 | { 15 | let encodedObject = try Self.encoder.encode(objectToSend) 16 | 17 | let message = if let request { 18 | xpc_dictionary_create_reply(request.raw) 19 | } else { 20 | xpc_dictionary_create(nil, nil, 0) 21 | } 22 | 23 | guard let message else { 24 | throw Error.failedToCreateMessageDictionary 25 | } 26 | 27 | try encodedObject.withUnsafeBytes { pointer in 28 | guard let rawPointer = pointer.baseAddress else { throw Error.failedToConvertData } 29 | xpc_dictionary_set_data(message, "data", rawPointer, encodedObject.count) 30 | } 31 | 32 | raw = message 33 | } 34 | 35 | init(raw: xpc_object_t) 36 | { 37 | self.raw = raw 38 | } 39 | 40 | func extract(_ type: T.Type) throws -> T where T: Decodable 41 | { 42 | let messageType = xpc_get_type(raw) 43 | 44 | guard messageType != XPC_TYPE_ERROR else { 45 | throw XPCError(error: raw) 46 | } 47 | 48 | guard messageType == XPC_TYPE_DICTIONARY else { 49 | print("Unexpected message type:", messageType, String(cString: xpc_type_get_name(messageType))) 50 | throw XPCError(.unexpectedMessageType) 51 | } 52 | 53 | var dataLength = 0 54 | 55 | guard let dataPointer: UnsafeRawPointer = xpc_dictionary_get_data(raw, "data", &dataLength) else { 56 | throw XPCError(.failedToGetDataFromXPCDictionary) 57 | } 58 | 59 | let data = Data(bytes: dataPointer, count: dataLength) 60 | 61 | let object = try Self.decoder.decode(T.self, from: data) 62 | return object 63 | } 64 | } 65 | 66 | extension XPCMessageWithObject 67 | { 68 | static let encoder = JSONEncoder() 69 | static let decoder = JSONDecoder() 70 | } 71 | -------------------------------------------------------------------------------- /Sources/XPCDistributedActorSystem/XPCServiceListener.swift: -------------------------------------------------------------------------------- 1 | @preconcurrency import XPC 2 | 3 | public actor XPCServiceListener 4 | { 5 | enum Error: Swift.Error { 6 | case previousInstanceExists 7 | } 8 | 9 | static var shared: XPCServiceListener? 10 | 11 | let actorSystem: XPCDistributedActorSystem 12 | var lastConnection: XPCConnection? { 13 | didSet { 14 | actorSystem.setConnection(lastConnection) 15 | } 16 | } 17 | 18 | public init(actorSystem: XPCDistributedActorSystem) throws 19 | { 20 | guard Self.shared == nil else { 21 | throw Error.previousInstanceExists 22 | } 23 | self.actorSystem = actorSystem 24 | Self.shared = self 25 | } 26 | 27 | public nonisolated func run() -> Never 28 | { 29 | xpc_main { connection in 30 | guard let listener = XPCServiceListener.shared else { return } 31 | Task { 32 | let connection = XPCConnection(incomingConnection: connection, actorSystem: listener.actorSystem, codeSigningRequirement: listener.actorSystem.codeSigningRequirement) 33 | await listener.setConnection(connection) 34 | } 35 | } 36 | } 37 | 38 | func setConnection(_ connection: XPCConnection) async 39 | { 40 | if let lastConnection { 41 | await lastConnection.close() 42 | } 43 | self.lastConnection = connection 44 | } 45 | } 46 | --------------------------------------------------------------------------------