SwiftUPnP 是一个基于 Swift 的库,为开发者提供了一套全面的 API,用于实现 UPnP(通用即插即用)和 OpenHome 协议。借助 SwiftUPnP,开发者可以将 UPnP 和 OpenHome 支持集成到他们的 iOS、macOS 和 tvOS 应用中,从而允许用户发现和控制网络上的音频设备。
SwiftUPnP 实现了完整的 UPnP 和 OpenHome 服务集,包括事件处理。这意味着开发者可以创建能够处理 UPnP 和 OpenHome 事件的实时应用,例如设备状态的变化或网络中新设备的添加。借助 SwiftUPnP,开发者可以通过订阅设备服务并在事件发生时接收通知来实现事件处理。
总的来说,SwiftUPnP 对于想要在其应用中添加 UPnP 和 OpenHome 支持的开发者来说是一个有用的工具。该库提供了一种直接而高效的方式来创建音频流媒体和设备控制应用,使用户可以轻松发现和控制网络上的音频设备。
使 SwiftUPnP 在现代 Swift 环境中易于使用的关键特性
SwiftUPnP 库的最低要求是
SSDP 发现使用 CocoaAsyncSocket 完成。 也支持使用标准的 Apple Network 框架,但是当另一个应用也在监听 UPnP 设备时,此方法存在已知问题。 仅发现设备,可用的源根据设备的描述加载。(设备在网络上的)出现和消失通过 UPnPRegistery 中定义的 Combine 发布器 deviceAdded 和 deviceRemoved 发布。 当设备在 subject 上发布时,它已完全加载所有包含的服务定义。
private let openHomeRegistry = UPnPRegistry.shared
private var cancellables = Set<AnyCancellable>()
public init() {
openHomeRegistry.deviceAdded
.sink {
print("Detected device \($0.deviceDefinition.device.friendlyName) of type \($0.deviceType)")
}
.store(in: &cancellables)
openHomeRegistry.deviceRemoved
.sink {
print("Removed device \($0.deviceDefinition.device.friendlyName) of type \($0.deviceType)")
}
.store(in: &cancellables)
}
public func startListening() {
// A list of device types can be passed to discover. The default devices types used when nothing is specified:
// urn:schemas-upnp-org:device:MediaServer:1
// urn:linn-co-uk:device:Source:1
// urn:av-openhome-org:device:Source:1
try? openHomeRegistry.startDiscovery()
}
public func stopListening() {
openHomeRegistry.stopDiscovery()
}
或者,您可以循环遍历 AsyncStream
private let openHomeRegistry = UPnPRegistry.shared
public init() {
Task {
for await device in openHomeRegistry.deviceAddedStream {
print("Detected device \(device.deviceDefinition.device.friendlyName) of type \(device.deviceType)")
}
}
Task {
for await device in openHomeRegistry.deviceRemovedStream {
print("Removed device \(device.deviceDefinition.device.friendlyName) of type \(device.deviceType)")
}
}
}
public func startListening() {
// A list of device types can be passed to discover. The default devices types used when nothing is specified:
// urn:schemas-upnp-org:device:MediaServer:1
// urn:linn-co-uk:device:Source:1
// urn:av-openhome-org:device:Source:1
try? openHomeRegistry.startDiscovery()
}
public func stopListening() {
openHomeRegistry.stopDiscovery()
}
要在 iOS 上使用设备发现,您需要向 Apple 请求进行多播请求的权限。 页面 https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_networking_multicast 包含一个链接,您可以在其中为您的应用请求此权限。 在获得此权限后,您需要将以下键添加到您的应用的 .plist 文件中
<key>com.apple.developer.networking.multicast</key>
<true/>
UPnP 动作和响应是强类型的,没有键值对。 这是通过 Codable 结构体完成的。 所有动作调用都实现为异步函数。
let device: UPnPDevice
try? await device.openHomeVolume1Service?.setVolume(value: 50)
每个服务实现都有一个 Combine 发布器 stateSubject。 当服务通过 subscribeToEvents() 订阅状态更改时,这些事件将作为强类型结构体在 stateSubject 上传递。 为了接收状态更改,将运行一个小型的 web 服务器 (Swifter)。
let device: UPnPDevice
var cancellables = Set<AnyCancellable>()
if let service = device.openHomeVolume1Service {
service.stateSubject
.sink {
print("Received volume change, volume = \($0.volume ?? -1)")
}
.store(in: &cancellables)
Task {
await service.subscribeToEvents()
}
}
或者,您可以循环遍历 AsyncStream
let device: UPnPDevice
if let service = device.openHomeVolume1Service {
Task {
for await volumeChange in service.stateChangeStream {
print("Received volume change, volume = \(volumeChange.volume ?? -1)")
}
}
Task {
await service.subscribeToEvents()
}
}
包含一个命令行工具,用于从基于 xml 的文件生成基于 swift 的服务实现。 这用于生成 AV Profile 和 OpenHome Profile 文件夹中的 swift 源代码。 代码生成包括为允许值、所有定义的动作和状态更改创建枚举。
所有标准 UPnP 服务和状态更改的完整实现
所有标准 OpenHome 服务和状态更改的完整实现
该软件包可以使用 Swift Package Manager 包含。
SwiftUPnP 在 MIT 许可证下发布。
SwiftUPnP 使用以下软件包