Changes
4 changed files (+106/-113)
-
-
@@ -28,7 +28,7 @@ };/* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ C3490F962DB36DC40030476A /* PlacCore.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = PlacCore.xcframework; path = "../old_core/zig-out/xcframeworks/PlacCore.xcframework"; sourceTree = "<group>"; }; C3490F962DB36DC40030476A /* PlacCore.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = PlacCore.xcframework; path = "../core/zig-out/xcframeworks/PlacCore.xcframework"; sourceTree = "<group>"; }; C3EABC712DB1170400F786D6 /* plac.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = plac.app; sourceTree = BUILT_PRODUCTS_DIR; }; C3EABC7F2DB1170700F786D6 /* placTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = placTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C3EABC892DB1170700F786D6 /* placUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = placUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
-
-
-
@@ -0,0 +1,78 @@// Copyright 2025 Shota FUJI // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // SPDX-License-Identifier: Apache-2.0 import PlacKit import os let logger = Logger() class Server { var ptr: UnsafeMutablePointer<PlacKit.plac_discovery_server> var id: String { return String(cString: ptr.pointee.id) } var name: String { return String(cString: ptr.pointee.name) } var version: String { return String(cString: ptr.pointee.version) } init(ptr: UnsafeMutablePointer<PlacKit.plac_discovery_server>) { self.ptr = ptr } deinit { PlacKit.plac_discovery_server_release(ptr) } } class ScanResult { var ptr: UnsafeMutablePointer<PlacKit.plac_discovery_scan_result> var code: PlacKit.plac_discovery_scan_result_code { return ptr.pointee.code } var entries: [Server] init(ptr: UnsafeMutablePointer<PlacKit.plac_discovery_scan_result>) { self.ptr = ptr var entries: [Server] = [] let current = ptr.pointee.servers_ptr?.pointee guard var current = current else { logger.warning("servers_ptr is null") self.entries = [] return } for _ in 0..<ptr.pointee.servers_len { entries.append(Server(ptr: PlacKit.plac_discovery_server_retain(current))) current = current.successor() } self.entries = entries } deinit { PlacKit.plac_discovery_scan_result_release(ptr) } }
-
-
-
@@ -14,47 +14,23 @@ // limitations under the License.// // SPDX-License-Identifier: Apache-2.0 import PlacCore import PlacKit import SwiftUI import os struct FoundServer { let id: String let name: String let version: String let ptr: UnsafeMutablePointer<plac_app_server_selector_entry> } enum DiscoveryResult { case loading case loaded([FoundServer]) case failed(plac_app_server_selector_state) case loaded([Server]) case failed(PlacKit.plac_discovery_scan_result_code) case null_pointer } // https://oleb.net/blog/2015/06/c-callbacks-in-swift/ class Context<T> { private(set) var item: T init(_ item: T) { self.item = item } func mutate(_ mutation: (inout T) -> Void) { mutation(&item) } } struct ServerDiscoveryScene: Scene { @Environment(\.scenePhase) private var scenePhase @State private var discovery: DiscoveryResult = .loading private var app: UnsafeMutablePointer<plac_app> init(_ app: UnsafeMutablePointer<plac_app>) { self.app = app } private let logger = Logger() private let queue = DispatchQueue(label: "plac.server-discovery") var body: some Scene { WindowGroup {
-
@@ -70,19 +46,17 @@ }} case .failed(let plac_scan_result_code): switch plac_scan_result_code { case PLAC_APP_SERVER_SELECTOR_ERR_SOCKET: case PlacKit.PLAC_DISCOVERY_SCAN_RESULT_SOCKET_ERROR: Text("Unable to operate on network socket") case PLAC_APP_SERVER_SELECTOR_ERR_UNEXPECTED: case PlacKit.PLAC_DISCOVERY_SCAN_RESULT_UNKNOWN: Text("Unexpected error") case PLAC_APP_SERVER_SELECTOR_ERR_THREAD_SPAWN: Text("Unable to spawn worker thread") case PLAC_APP_SERVER_SELECTOR_ERR_OUT_OF_MEMORY: case PlacKit.PLAC_DISCOVERY_SCAN_RESULT_OUT_OF_MEMORY: Text("Out of memory") case PLAC_APP_SERVER_SELECTOR_ERR_SOCKET_PERMISSION: case PlacKit.PLAC_DISCOVERY_SCAN_RESULT_SOCKET_PERMISSION_DENIED: Text( "Permission rejected for network socket operation" ) case PLAC_APP_SERVER_SELECTOR_ERR_NETWORK_UNAVAILABLE: case PlacKit.PLAC_DISCOVERY_SCAN_RESULT_SOCKET_PERMISSION_DENIED: Text("Network unavailable") default: Text("Unexpected error (platform misbehaving)")
-
@@ -94,65 +68,27 @@ }} .onChange(of: scenePhase, initial: true) { if self.scenePhase == .active { let context = Unmanaged.passRetained(Context(self)) plac_app_server_selector_on_change( app.pointee.server_selector, { ptr in queue.async { let ptr = PlacKit.plac_discovery_scan() guard let ptr = ptr else { logger.error("Failed to scan servers: Out of memory") DispatchQueue.main.async { let logger = Logger() guard let ptr = ptr else { logger.error( "Exiting server selector callback because context is NULL" ) return } discovery = .null_pointer } return } let ctx: Context<ServerDiscoveryScene> = Unmanaged.fromOpaque(ptr) .takeUnretainedValue() ctx.mutate { this in switch this.app.pointee.server_selector.pointee.state { case PLAC_APP_SERVER_SELECTOR_LOADING, PLAC_APP_SERVER_SELECTOR_REFRESHING, PLAC_APP_SERVER_SELECTOR_NOT_LOADED: this.discovery = .loading case PLAC_APP_SERVER_SELECTOR_LOADED: var servers: [FoundServer] = [] var current = this.app.pointee.server_selector!.pointee .entries! for _ in 0...this.app.pointee.server_selector.pointee.entries_len { if let server = current.pointee { servers.append( FoundServer( id: String(cString: server.pointee.id), name: String(cString: server.pointee.name), version: String(cString: server.pointee.version), ptr: server ) ) } current = current.successor() } let result = ScanResult(ptr: ptr) this.discovery = .loaded(servers) default: this.discovery = .failed( this.app.pointee.server_selector.pointee.state ) } } if result.code != PlacKit.PLAC_DISCOVERY_SCAN_RESULT_OK { DispatchQueue.main.async { discovery = .failed(result.code) } }, context.toOpaque() ) return } plac_app_server_selector_load(self.app.pointee.server_selector) discovery = .loaded(result.entries) } } } }
-
-
-
@@ -14,32 +14,11 @@ // limitations under the License.// // SPDX-License-Identifier: Apache-2.0 import PlacCore import SwiftUI @main struct placApp: App { private var core: Core = Core() var body: some Scene { ServerDiscoveryScene(self.core.app) } } class Core { var app: UnsafeMutablePointer<plac_app> init() { if let app = plac_app_new() { self.app = app } else { fatalError( "Failed to start Plac core library (returned NULL handle, possibly out of memory)" ) } } deinit { plac_app_destroy(self.app) ServerDiscoveryScene() } }
-