macOS XPC(Server) With Swift

ponyo721._.·2025년 2월 8일
post-thumbnail

XPC Server Class

import Foundation

protocol XPCServerUtilDelegate {
    func recvXPCDisconnectEvent()
}

class XPCServerUtil: NSObject, NSXPCListenerDelegate {
    var listener: NSXPCListener?
    var connection: NSXPCConnection?
    
    var serverReciver:XPCServerImpl = XPCServerImpl()
    
    internal func start() {
        NSLog("[XPCServerUtil] start.")
        
        listener = NSXPCListener(
            machServiceName: "com.ponyo.macos.XPCServerWithSwift"
        )
        listener?.delegate = self
        
        NSLog("[XPCServerUtil] resume")
        listener?.resume()
    }
    
    // NSXPCConnection interruptionHandler
    private func connetionInterruptionHandler() {
        NSLog("[XPCServerUtil] The session has interrupted.")
    }
    
    // NSXPCConnection invalidationHandler
    private func connectionInvalidationHandler() {
        NSLog("[XPCServerUtil] The session has invalidated.")
    }
    
    //MARK: - NSXPCListenerDelegate -
    func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
        NSLog("[XPCServerUtil] listener.")
        
        newConnection.exportedInterface = NSXPCInterface(
            with: XPCServerProtocol.self
        )
        newConnection.exportedObject = serverReciver
        
        
        newConnection.remoteObjectInterface = NSXPCInterface(
            with: XPCClientProtocol.self
        )
        
        newConnection.interruptionHandler = connetionInterruptionHandler
        newConnection.invalidationHandler = connectionInvalidationHandler
        
        newConnection.resume()
        
        serverReciver.client = newConnection.remoteObjectProxy as? XPCClientProtocol
        
        self.connection = newConnection
        
        return true
    }
    
    //MARK: - public func -
    
    func sendHello() {
        serverReciver.client?.printClientHello()
    }
    
    func sendJSON() {
        do {
            let user = User(id: 1, name: "John Doe", email: "john@example.com")
            let encoder = JSONEncoder()
            encoder.outputFormatting = .prettyPrinted // 보기 좋게 포맷팅
            let jsonData = try encoder.encode(user)
            
            // JSON 데이터 출력
            if let jsonString = String(data: jsonData, encoding: .utf8) {
                print("JSON String:\n\(jsonString)")
            }
            
            // 원격 객체 호출
            
            serverReciver.client?.sendJSONData(jsonData) { responseData in
                guard let responseData = responseData else {
                    print("No response from XPC")
                    return
                }
                do {
                    let decoder = JSONDecoder()
                    let responseUser = try decoder.decode(User.self, from: responseData)
                    print("Received User from XPC:")
                    print("ID: \(responseUser.id), Name: \(responseUser.name), Email: \(responseUser.email)")
                } catch {
                    print("Failed to decode response:", error)
                }
            }
        } catch {
            print("Failed to encode user to JSON:", error)
        }
    }
}

XPC Protocol Class

import Foundation

//MARK: - XPCClientProtocol -
// target process가 내 process로 보내는 요청
@objc(XPCClientProtocol)
protocol XPCClientProtocol {
    func printClientHello()
    func sendJSONData(_ jsonData: Data, withReply reply: @escaping (Data?) -> Void)
}

//MARK: - XPC Recv Func Implementation -
class XPCClientImpl:NSObject, XPCClientProtocol {
    var server:XPCServerProtocol? = nil
    
    func printClientHello() {
        print("Server Hello")
    }
    
    
    func sendJSONData(_ jsonData: Data, withReply reply: @escaping (Data?) -> Void) {
        do {
            // JSON 데이터를 Swift 객체로 디코딩
            let decoder = JSONDecoder()
            let receivedObject = try decoder.decode([String: String].self, from: jsonData)
            print("Received JSON:", receivedObject)
            
            // 응답으로 보낼 데이터 준비
            let responseObject = ["status": "success", "message": "Hello, \(receivedObject["name"] ?? "Guest")!"]
            let encoder = JSONEncoder()
            let responseData = try encoder.encode(responseObject)
            
            // 응답 전송
            reply(responseData)
        } catch {
            print("Error processing JSON:", error)
            reply(nil)
        }
    }
}


//MARK: - XPCServerProtocol -
@objc(XPCServerProtocol)
protocol XPCServerProtocol {
    func printHello()
}

//MARK: - XPC Recv Func Implementation -
class XPCServerImpl:NSObject, XPCServerProtocol {
    var client:XPCClientProtocol? = nil
    
    func printHello() {
        print("Client Hello")
    }
}

Data Model Class

import Foundation

// Codable을 채택한 데이터 클래스
class User: Codable {
    var id: Int
    var name: String
    var email: String

    init(id: Int, name: String, email: String) {
        self.id = id
        self.name = name
        self.email = email
    }
}

Call Process

@main
let xpcServer:XPCServerUtil = XPCServerUtil()
// xpc client가 붙을 수 있도록 서버 생성 및 대기
xpcServer.start()

// 이후 client와 연결이 되었으면 통신 시작
xpcServer.sendHello()
profile
macOS Native Application Developer

0개의 댓글