11import Foundation
2- import GitHubCopilotService
32import Logger
4- import SuggestionModel
5- import XPCShared
63
7- public struct AsyncXPCService {
8- public var connection : NSXPCConnection { service. connection }
9- let service : XPCService
4+ public enum XPCExtensionServiceError : Swift . Error , LocalizedError {
5+ case failedToGetServiceEndpoint
6+ case failedToCreateXPCConnection
7+ case xpcServiceError( Error )
108
11- init ( service: XPCService ) {
12- self . service = service
9+ public var errorDescription : String ? {
10+ switch self {
11+ case . failedToGetServiceEndpoint:
12+ return " Failed to get service endpoint "
13+ case . failedToCreateXPCConnection:
14+ return " Failed to create XPC connection "
15+ case let . xpcServiceError( error) :
16+ return " XPC Service error: \( error. localizedDescription) "
17+ }
18+ }
19+ }
20+
21+ @XPCServiceActor
22+ public class XPCExtensionService {
23+ var service : XPCService ?
24+ var connection : NSXPCConnection ? { service? . connection }
25+ let logger : Logger
26+ let bridge : XPCCommunicationBridge
27+
28+ nonisolated
29+ public init ( logger: Logger ) {
30+ self . logger = logger
31+ bridge = XPCCommunicationBridge ( logger: logger)
1332 }
1433
1534 public func getXPCServiceVersion( ) async throws -> ( version: String , build: String ) {
16- try await withXPCServiceConnected ( connection : connection ) {
35+ try await withXPCServiceConnected {
1736 service, continuation in
1837 service. getXPCServiceVersion { version, build in
1938 continuation. resume ( ( version, build) )
@@ -22,7 +41,7 @@ public struct AsyncXPCService {
2241 }
2342
2443 public func getXPCServiceAccessibilityPermission( ) async throws -> Bool {
25- try await withXPCServiceConnected ( connection : connection ) {
44+ try await withXPCServiceConnected {
2645 service, continuation in
2746 service. getXPCServiceAccessibilityPermission { isGranted in
2847 continuation. resume ( isGranted)
@@ -32,15 +51,13 @@ public struct AsyncXPCService {
3251
3352 public func getSuggestedCode( editorContent: EditorContent ) async throws -> UpdatedContent ? {
3453 try await suggestionRequest (
35- connection,
3654 editorContent,
3755 { $0. getSuggestedCode }
3856 )
3957 }
4058
4159 public func getNextSuggestedCode( editorContent: EditorContent ) async throws -> UpdatedContent ? {
4260 try await suggestionRequest (
43- connection,
4461 editorContent,
4562 { $0. getNextSuggestedCode }
4663 )
@@ -50,7 +67,6 @@ public struct AsyncXPCService {
5067 -> UpdatedContent ?
5168 {
5269 try await suggestionRequest (
53- connection,
5470 editorContent,
5571 { $0. getPreviousSuggestedCode }
5672 )
@@ -60,7 +76,6 @@ public struct AsyncXPCService {
6076 -> UpdatedContent ?
6177 {
6278 try await suggestionRequest (
63- connection,
6479 editorContent,
6580 { $0. getSuggestionAcceptedCode }
6681 )
@@ -70,7 +85,6 @@ public struct AsyncXPCService {
7085 -> UpdatedContent ?
7186 {
7287 try await suggestionRequest (
73- connection,
7488 editorContent,
7589 { $0. getSuggestionRejectedCode }
7690 )
@@ -80,7 +94,6 @@ public struct AsyncXPCService {
8094 -> UpdatedContent ?
8195 {
8296 try await suggestionRequest (
83- connection,
8497 editorContent,
8598 { $0. getRealtimeSuggestedCode }
8699 )
@@ -90,14 +103,13 @@ public struct AsyncXPCService {
90103 -> UpdatedContent ?
91104 {
92105 try await suggestionRequest (
93- connection,
94106 editorContent,
95107 { $0. getPromptToCodeAcceptedCode }
96108 )
97109 }
98110
99111 public func toggleRealtimeSuggestion( ) async throws {
100- try await withXPCServiceConnected ( connection : connection ) {
112+ try await withXPCServiceConnected {
101113 service, continuation in
102114 service. toggleRealtimeSuggestion { error in
103115 if let error {
@@ -111,7 +123,7 @@ public struct AsyncXPCService {
111123
112124 public func prefetchRealtimeSuggestions( editorContent: EditorContent ) async {
113125 guard let data = try ? JSONEncoder ( ) . encode ( editorContent) else { return }
114- try ? await withXPCServiceConnected ( connection : connection ) { service, continuation in
126+ try ? await withXPCServiceConnected { service, continuation in
115127 service. prefetchRealtimeSuggestions ( editorContent: data) {
116128 continuation. resume ( ( ) )
117129 }
@@ -120,15 +132,13 @@ public struct AsyncXPCService {
120132
121133 public func chatWithSelection( editorContent: EditorContent ) async throws -> UpdatedContent ? {
122134 try await suggestionRequest (
123- connection,
124135 editorContent,
125136 { $0. chatWithSelection }
126137 )
127138 }
128139
129140 public func promptToCode( editorContent: EditorContent ) async throws -> UpdatedContent ? {
130141 try await suggestionRequest (
131- connection,
132142 editorContent,
133143 { $0. promptToCode }
134144 )
@@ -139,14 +149,13 @@ public struct AsyncXPCService {
139149 editorContent: EditorContent
140150 ) async throws -> UpdatedContent ? {
141151 try await suggestionRequest (
142- connection,
143152 editorContent,
144153 { service in { service. customCommand ( id: id, editorContent: $0, withReply: $1) } }
145154 )
146155 }
147156
148157 public func postNotification( name: String ) async throws {
149- try await withXPCServiceConnected ( connection : connection ) {
158+ try await withXPCServiceConnected {
150159 service, continuation in
151160 service. postNotification ( name: name) {
152161 continuation. resume ( ( ) )
@@ -157,7 +166,7 @@ public struct AsyncXPCService {
157166 public func send< M: ExtensionServiceRequestType > (
158167 requestBody: M
159168 ) async throws -> M . ResponseBody {
160- try await withXPCServiceConnected ( connection : connection ) { service, continuation in
169+ try await withXPCServiceConnected { service, continuation in
161170 do {
162171 let requestBodyData = try JSONEncoder ( ) . encode ( requestBody)
163172 service. send ( endpoint: M . endpoint, requestBody: requestBodyData) { data, error in
@@ -186,61 +195,76 @@ public struct AsyncXPCService {
186195 }
187196}
188197
189- struct NoDataError : Error { }
198+ extension XPCExtensionService : XPCServiceDelegate {
199+ func connectionDidInterrupt( ) async {
200+ // do nothing
201+ }
190202
191- struct AutoFinishContinuation < T> {
192- var continuation : AsyncThrowingStream < T , Error > . Continuation
203+ func connectionDidInvalidate( ) async {
204+ service = nil
205+ }
206+ }
193207
194- func resume( _ value: T ) {
195- continuation. yield ( value)
196- continuation. finish ( )
208+ extension XPCExtensionService {
209+ private func updateEndpoint( _ endpoint: NSXPCListenerEndpoint ) {
210+ service = XPCService (
211+ kind: . anonymous( endpoint: endpoint) ,
212+ interface: NSXPCInterface ( with: XPCServiceProtocol . self) ,
213+ logger: logger
214+ )
215+ service? . delegate = self
197216 }
198217
199- func reject( _ error: Error ) {
200- if ( error as NSError ) . code == - 100 {
201- continuation. finish ( throwing: CancellationError ( ) )
218+ private func withXPCServiceConnected< T> (
219+ _ fn: @escaping ( XPCServiceProtocol , AutoFinishContinuation < T > ) -> Void
220+ ) async throws -> T {
221+ if let service, let connection = service. connection {
222+ do {
223+ return try await XPCShared . withXPCServiceConnected ( connection: connection, fn)
224+ } catch {
225+ throw XPCExtensionServiceError . xpcServiceError ( error)
226+ }
202227 } else {
203- continuation. finish ( throwing: error)
228+ guard let endpoint = try await bridge. launchExtensionServiceIfNeeded ( )
229+ else { throw XPCExtensionServiceError . failedToGetServiceEndpoint }
230+ updateEndpoint ( endpoint)
231+
232+ if let service, let connection = service. connection {
233+ do {
234+ return try await XPCShared . withXPCServiceConnected ( connection: connection, fn)
235+ } catch {
236+ throw XPCExtensionServiceError . xpcServiceError ( error)
237+ }
238+ } else {
239+ throw XPCExtensionServiceError . failedToCreateXPCConnection
240+ }
204241 }
205242 }
206- }
207243
208- func withXPCServiceConnected< T> (
209- connection: NSXPCConnection ,
210- _ fn: @escaping ( XPCServiceProtocol , AutoFinishContinuation < T > ) -> Void
211- ) async throws -> T {
212- let stream : AsyncThrowingStream < T , Error > = AsyncThrowingStream { continuation in
213- let service = connection. remoteObjectProxyWithErrorHandler {
214- continuation. finish ( throwing: $0)
215- } as! XPCServiceProtocol
216- fn ( service, . init( continuation: continuation) )
217- }
218- return try await stream. first ( where: { _ in true } ) !
219- }
220-
221- func suggestionRequest(
222- _ connection: NSXPCConnection ,
223- _ editorContent: EditorContent ,
224- _ fn: @escaping ( any XPCServiceProtocol ) -> ( Data , @escaping ( Data ? , Error ? ) -> Void ) -> Void
225- ) async throws -> UpdatedContent ? {
226- let data = try JSONEncoder ( ) . encode ( editorContent)
227- return try await withXPCServiceConnected ( connection: connection) {
228- service, continuation in
229- fn ( service) ( data) { updatedData, error in
230- if let error {
231- continuation. reject ( error)
232- return
233- }
234- do {
235- if let updatedData {
236- let updatedContent = try JSONDecoder ( )
237- . decode ( UpdatedContent . self, from: updatedData)
238- continuation. resume ( updatedContent)
239- } else {
240- continuation. resume ( nil )
244+ private func suggestionRequest(
245+ _ editorContent: EditorContent ,
246+ _ fn: @escaping ( any XPCServiceProtocol ) -> ( Data , @escaping ( Data ? , Error ? ) -> Void )
247+ -> Void
248+ ) async throws -> UpdatedContent ? {
249+ let data = try JSONEncoder ( ) . encode ( editorContent)
250+ return try await withXPCServiceConnected {
251+ service, continuation in
252+ fn ( service) ( data) { updatedData, error in
253+ if let error {
254+ continuation. reject ( error)
255+ return
256+ }
257+ do {
258+ if let updatedData {
259+ let updatedContent = try JSONDecoder ( )
260+ . decode ( UpdatedContent . self, from: updatedData)
261+ continuation. resume ( updatedContent)
262+ } else {
263+ continuation. resume ( nil )
264+ }
265+ } catch {
266+ continuation. reject ( error)
241267 }
242- } catch {
243- continuation. reject ( error)
244268 }
245269 }
246270 }
0 commit comments