如何通过NetService获取打印机URL以创建UIPrinter实例
解决获取AirPrint打印机URL的问题
首先可以明确:你用NetServiceBrowser搜索_ipp._tcp.服务的方式完全适用于查找本地AirPrint打印机,因为AirPrint正是基于IPP(Internet Printing Protocol)协议实现的,这个服务类型就是AirPrint设备的标准标识。
接下来解决核心问题:如何从找到的NetService中提取出能创建UIPrinter的URL。你当前只打印了服务的基本信息,这还不够——需要先解析服务的详细信息(地址、端口、TXT记录),才能构造出正确的IPP URL。
具体步骤和代码示例
在
didFind service代理方法中触发服务解析
当找到服务时,不要只打印信息,调用resolve(withTimeout:)来获取该服务的完整网络信息:func netServiceBrowser(_ browser: NetServiceBrowser, didFind service: NetService, moreComing: Bool) { print("找到打印机服务:\(service.name)") service.resolve(withTimeout: 5.0) // 设置合理的超时时间 }实现服务解析完成的代理方法
当服务解析成功后,在netServiceDidResolveAddress中提取地址、端口和TXT记录里的资源路径(rp字段),然后构造IPP URL:func netServiceDidResolveAddress(_ sender: NetService) { guard let addresses = sender.addresses, !addresses.isEmpty else { print("无法解析服务地址") return } // 提取IP地址(兼容IPv4和IPv6,优先IPv4) var ipAddress: String? for addressData in addresses { var socketAddress = sockaddr_storage() addressData.copyBytes(to: &socketAddress, count: MemoryLayout<sockaddr_storage>.size) if socketAddress.ss_family == UInt8(AF_INET) { let addr = withUnsafePointer(to: &socketAddress) { $0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) { $0.pointee.sin_addr } } var buffer = [CChar](repeating: 0, count: Int(INET_ADDRSTRLEN)) inet_ntop(AF_INET, &addr, &buffer, socklen_t(INET_ADDRSTRLEN)) ipAddress = String(cString: buffer) break } else if socketAddress.ss_family == UInt8(AF_INET6) { let addr = withUnsafePointer(to: &socketAddress) { $0.withMemoryRebound(to: sockaddr_in6.self, capacity: 1) { $0.pointee.sin6_addr } } var buffer = [CChar](repeating: 0, count: Int(INET6_ADDRSTRLEN)) inet_ntop(AF_INET6, &addr, &buffer, socklen_t(INET6_ADDRSTRLEN)) ipAddress = String(cString: buffer) } } guard let ip = ipAddress, let port = sender.port, port > 0 else { print("无法获取IP或端口") return } // 从TXT记录中获取资源路径(rp字段,AirPrint打印机必备) var resourcePath = "/ipp/print" // 默认路径,部分打印机可能不同 if let txtRecords = sender.txtRecord() { let txtDict = NetService.dictionary(fromTXTRecord: txtRecords) if let rpData = txtDict["rp"], let rpString = String(data: rpData, encoding: .utf8) { resourcePath = rpString } } // 构造完整的IPP URL guard let printerUrl = URL(string: "ipp://\(ip):\(port)\(resourcePath)") else { print("构造URL失败") return } // 创建UIPrinter实例 let printer = UIPrinter(url: printerUrl) print("成功创建打印机实例:\(printer.name)") }处理解析失败的情况
别忘了实现netService(_:didNotResolve:)来处理解析失败的场景:func netService(_ sender: NetService, didNotResolve errorDict: [String : NSNumber]) { print("解析服务失败:\(errorDict)") }
补充说明
- 为什么需要解析服务?因为
NetService初始返回的信息只有名称、服务类型和域名,缺少构造URL必需的IP地址、端口和资源路径,这些都需要通过resolve(withTimeout:)来获取。 - TXT记录中的
rp字段:这是IPP服务的资源路径,不同打印机可能略有差异,但大部分AirPrint设备会设置为/ipp/print,所以我们可以用这个作为默认值,再优先取TXT记录里的实际值。
内容的提问来源于stack exchange,提问作者serdaryillar
相关产品推荐
相关产品推荐

