├── .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 |
--------------------------------------------------------------------------------