结合下载进度
downloading progress with combine
我尝试使用 combine 和 Alamofire 显示文件下载进度。我有下载器 class
class DataManager: NSObject, DataManagerProtocol {
private(set) var value = 0.0 {
didSet { subject.send(value) }
}
private let subject = PassthroughSubject<Double, Never>()
func increment(by value: Double) {
self.value = value
}
func saveFile(urlString: String, fileName: String) -> AnyPublisher<Double, Never> {
download(urlString: urlString, fileName: fileName)
return subject.eraseToAnyPublisher()
}
private func download(urlString: String, fileName: String) {
AF.download(urlString)
.downloadProgress { [self] progress in
print("Download Progress: \(progress.fractionCompleted)")
increment(by: progress.fractionCompleted)
}
.responseData { response in
if let data = response.value {
print("data recieved")
self.writeToFile(data: data, fileName: fileName)
}
}
}
func writeToFile(data: Data, fileName: String) {
// get path of directory
guard let directory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last else {
return
}
// create file url
let fileurl = directory.appendingPathComponent(filename)
if FileManager.default.fileExists(atPath: fileurl.path) {
if let fileHandle = FileHandle(forWritingAtPath: fileurl.path) {
print("FileExist")
} else {
print("Can't open file to write.")
}
} else {
// if file does not exist write data for the first time
do {
try data.write(to: fileurl, options: .atomic)
} catch {
print("Unable to write in new file.")
}
}
}
}
在控制台中我看到文件下载成功
Download Progress: 0.2707762694368025 Download Progress:
0.30361701168087313 Download Progress: 0.45961053734020857 Download Progress: 0.5088716507063145 Download Progress: 0.5827633207554733
Download Progress: 0.615604062999544 Download Progress:
0.6484448052436146 Download Progress: 0.7798077742198971 Download Progress: 0.8783300009521089 Download Progress: 1.0 data recieved
但是在我的 ViewModel 中我没有看到进度变化的发布
import Combine
final class RecordsListViewModel: ObservableObject {
private var cancellable: AnyCancellable?
private(set) var progress = PassthroughSubject<Double, Never>()
private let dataManager: DataManagerProtocol
init(dataManager: DataManagerProtocol) {
self.dataManager = dataManager
}
func downloadFile() {
cancellable = dataManager.saveFile(urlString: "https://i.artfile.ru/2880x1800_1455670_[www.ArtFile.ru].jpg", fileName: "filename.jpg")
.receive(on: DispatchQueue.main)
.sink { [weak self] completion in
print(completion, "completion")
} receiveValue: { progress in
print(progress, "progress")
}
}
}
问题是在 saveFile 方法中,您在 ViewModel 获取主题之前调用了下载方法。
我建议将 saveFile 方法一分为二。
在数据管理器中:
func getSubscription() -> AnyPublisher<Double, Never> {
return subject.eraseToAnyPublisher()
}
func saveFile(urlString: String, fileName: String) {
download(urlString: urlString, fileName: fileName)
// or just put the contents of the download method here
}
在ViewModel中,先获取DataManager的主题,准备下载:
init(dataManager: DataManagerProtocol) {
self.dataManager = dataManager
cancellable = self.dataManager.getSubscription()
.receive(on: DispatchQueue.main)
.sink { completion in
print(completion, "completion")
} receiveValue: { progress in
print(progress, "progress")
}
}
func downloadFile() {
dataManager.saveFile(urlString: "https://i.artfile.ru/2880x1800_1455670_[www.ArtFile.ru].jpg", fileName: "filename.jpg")
}
我在 Playground 中尝试过,使用 Timer Publisher 来模拟下载。它工作得很好。当然,你需要把DataManagerProtocol改成这样:
protocol DataManagerProtocol {
func saveFile(urlString: String, fileName: String)
func getSubscription() -> AnyPublisher<Double, Never>
}
编辑: 我刚刚找到了另一种解决问题的方法。您只需要更改您提供的代码中的一行:
func saveFile(urlString: String, fileName: String) -> AnyPublisher<Double, Never> {
defer { download(urlString: urlString, fileName: fileName) }
return subject.eraseToAnyPublisher()
}
延迟块将在函数完成之前直接执行,即使在 return 之后。我不确定这是否会使您的代码难以阅读。但我想为了完整起见我应该提到这个选项。
如果有人对 defer 有更多经验,请在评论中告诉我这是否属于误用。
顺便说一句,我想你想将增量方法的内容更改为:
self.value += value
我尝试使用 combine 和 Alamofire 显示文件下载进度。我有下载器 class
class DataManager: NSObject, DataManagerProtocol {
private(set) var value = 0.0 {
didSet { subject.send(value) }
}
private let subject = PassthroughSubject<Double, Never>()
func increment(by value: Double) {
self.value = value
}
func saveFile(urlString: String, fileName: String) -> AnyPublisher<Double, Never> {
download(urlString: urlString, fileName: fileName)
return subject.eraseToAnyPublisher()
}
private func download(urlString: String, fileName: String) {
AF.download(urlString)
.downloadProgress { [self] progress in
print("Download Progress: \(progress.fractionCompleted)")
increment(by: progress.fractionCompleted)
}
.responseData { response in
if let data = response.value {
print("data recieved")
self.writeToFile(data: data, fileName: fileName)
}
}
}
func writeToFile(data: Data, fileName: String) {
// get path of directory
guard let directory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last else {
return
}
// create file url
let fileurl = directory.appendingPathComponent(filename)
if FileManager.default.fileExists(atPath: fileurl.path) {
if let fileHandle = FileHandle(forWritingAtPath: fileurl.path) {
print("FileExist")
} else {
print("Can't open file to write.")
}
} else {
// if file does not exist write data for the first time
do {
try data.write(to: fileurl, options: .atomic)
} catch {
print("Unable to write in new file.")
}
}
}
}
在控制台中我看到文件下载成功
Download Progress: 0.2707762694368025 Download Progress: 0.30361701168087313 Download Progress: 0.45961053734020857 Download Progress: 0.5088716507063145 Download Progress: 0.5827633207554733 Download Progress: 0.615604062999544 Download Progress: 0.6484448052436146 Download Progress: 0.7798077742198971 Download Progress: 0.8783300009521089 Download Progress: 1.0 data recieved
但是在我的 ViewModel 中我没有看到进度变化的发布
import Combine
final class RecordsListViewModel: ObservableObject {
private var cancellable: AnyCancellable?
private(set) var progress = PassthroughSubject<Double, Never>()
private let dataManager: DataManagerProtocol
init(dataManager: DataManagerProtocol) {
self.dataManager = dataManager
}
func downloadFile() {
cancellable = dataManager.saveFile(urlString: "https://i.artfile.ru/2880x1800_1455670_[www.ArtFile.ru].jpg", fileName: "filename.jpg")
.receive(on: DispatchQueue.main)
.sink { [weak self] completion in
print(completion, "completion")
} receiveValue: { progress in
print(progress, "progress")
}
}
}
问题是在 saveFile 方法中,您在 ViewModel 获取主题之前调用了下载方法。 我建议将 saveFile 方法一分为二。
在数据管理器中:
func getSubscription() -> AnyPublisher<Double, Never> {
return subject.eraseToAnyPublisher()
}
func saveFile(urlString: String, fileName: String) {
download(urlString: urlString, fileName: fileName)
// or just put the contents of the download method here
}
在ViewModel中,先获取DataManager的主题,准备下载:
init(dataManager: DataManagerProtocol) {
self.dataManager = dataManager
cancellable = self.dataManager.getSubscription()
.receive(on: DispatchQueue.main)
.sink { completion in
print(completion, "completion")
} receiveValue: { progress in
print(progress, "progress")
}
}
func downloadFile() {
dataManager.saveFile(urlString: "https://i.artfile.ru/2880x1800_1455670_[www.ArtFile.ru].jpg", fileName: "filename.jpg")
}
我在 Playground 中尝试过,使用 Timer Publisher 来模拟下载。它工作得很好。当然,你需要把DataManagerProtocol改成这样:
protocol DataManagerProtocol {
func saveFile(urlString: String, fileName: String)
func getSubscription() -> AnyPublisher<Double, Never>
}
编辑: 我刚刚找到了另一种解决问题的方法。您只需要更改您提供的代码中的一行:
func saveFile(urlString: String, fileName: String) -> AnyPublisher<Double, Never> {
defer { download(urlString: urlString, fileName: fileName) }
return subject.eraseToAnyPublisher()
}
延迟块将在函数完成之前直接执行,即使在 return 之后。我不确定这是否会使您的代码难以阅读。但我想为了完整起见我应该提到这个选项。 如果有人对 defer 有更多经验,请在评论中告诉我这是否属于误用。
顺便说一句,我想你想将增量方法的内容更改为:
self.value += value