- 所有公开 API 改为 completion handler (Result<T, Error>) - 状态通知改为 DuooomiBleSDKDelegate 协议 - 移除 Sendable、@MainActor、AsyncStream、CheckedContinuation - 网络请求改为 URLSession.dataTask 回调 - BLE 通信改为 delegate + 闭包模式 - deployment target 降至 iOS 12.2 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
69 lines
2.5 KiB
Swift
69 lines
2.5 KiB
Swift
import Foundation
|
||
|
||
/// 文件传输服务:下载文件并通过协议发送二进制数据
|
||
final class FileTransferService {
|
||
private let protocolService: BleProtocolService
|
||
|
||
init(protocolService: BleProtocolService) {
|
||
self.protocolService = protocolService
|
||
}
|
||
|
||
/// 传输文件到设备
|
||
func transferFile(
|
||
fileUri: String,
|
||
commandType: CommandType,
|
||
onProgress: ((Double) -> Void)? = nil,
|
||
completion: @escaping (Result<Void, Error>) -> Void
|
||
) {
|
||
BleLog.i("Load file: \(fileUri)", "Transfer")
|
||
loadFileData(from: fileUri) { [weak self] result in
|
||
switch result {
|
||
case .failure(let error):
|
||
completion(.failure(error))
|
||
case .success(let data):
|
||
BleLog.d("File loaded: size=\(data.count) bytes", "Transfer")
|
||
self?.protocolService.send(
|
||
type: commandType.rawValue,
|
||
data: data,
|
||
onProgress: onProgress,
|
||
completion: completion
|
||
)
|
||
}
|
||
}
|
||
}
|
||
|
||
private func loadFileData(from uri: String, completion: @escaping (Result<Data, Error>) -> Void) {
|
||
guard let url = URL(string: uri) else {
|
||
completion(.failure(DuooomiBleError.transferFailed("Invalid URL: \(uri)")))
|
||
return
|
||
}
|
||
|
||
if url.isFileURL {
|
||
do {
|
||
let data = try Data(contentsOf: url)
|
||
completion(.success(data))
|
||
} catch {
|
||
completion(.failure(DuooomiBleError.transferFailed("Failed to read local file: \(error.localizedDescription)")))
|
||
}
|
||
return
|
||
}
|
||
|
||
URLSession.shared.dataTask(with: url) { data, response, error in
|
||
if let error = error {
|
||
completion(.failure(DuooomiBleError.transferFailed("Download failed: \(error.localizedDescription)")))
|
||
return
|
||
}
|
||
guard let httpResponse = response as? HTTPURLResponse,
|
||
(200...299).contains(httpResponse.statusCode) else {
|
||
completion(.failure(DuooomiBleError.transferFailed("Failed to download file: \(uri)")))
|
||
return
|
||
}
|
||
guard let data = data, !data.isEmpty else {
|
||
completion(.failure(DuooomiBleError.transferFailed("Empty download response")))
|
||
return
|
||
}
|
||
completion(.success(data))
|
||
}.resume()
|
||
}
|
||
}
|