Skip to content

Commit b150f10

Browse files
committed
Better progress reporting
1 parent b2683dc commit b150f10

20 files changed

Lines changed: 105 additions & 76 deletions

Mixin.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,7 @@
719719
94341BFB2862F302009C9147 /* libopusenc.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94341BF72862F302009C9147 /* libopusenc.xcframework */; };
720720
94341C002863530B009C9147 /* libogg.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94341BFF2862F454009C9147 /* libogg.xcframework */; };
721721
9438252725EE697300709B7D /* CacheableAssetFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9438252625EE697300709B7D /* CacheableAssetFileManager.swift */; };
722+
9439116A2A39EA4300CF6DC7 /* DeviceTransferProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 943911692A39EA4300CF6DC7 /* DeviceTransferProgress.swift */; };
722723
94396F2629EB11E300A57833 /* DeviceTransferProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94396F2529EB11E300A57833 /* DeviceTransferProtocol.swift */; };
723724
94396F2829EB475500A57833 /* NWParameters+DeviceTransfer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94396F2729EB475500A57833 /* NWParameters+DeviceTransfer.swift */; };
724725
94396F2A29EBE52400A57833 /* DeviceTransferClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94396F2929EBE52400A57833 /* DeviceTransferClient.swift */; };
@@ -1796,6 +1797,7 @@
17961797
94341BF72862F302009C9147 /* libopusenc.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = libopusenc.xcframework; sourceTree = "<group>"; };
17971798
94341BFF2862F454009C9147 /* libogg.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = libogg.xcframework; sourceTree = "<group>"; };
17981799
9438252625EE697300709B7D /* CacheableAssetFileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheableAssetFileManager.swift; sourceTree = "<group>"; };
1800+
943911692A39EA4300CF6DC7 /* DeviceTransferProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceTransferProgress.swift; sourceTree = "<group>"; };
17991801
94396F2529EB11E300A57833 /* DeviceTransferProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceTransferProtocol.swift; sourceTree = "<group>"; };
18001802
94396F2729EB475500A57833 /* NWParameters+DeviceTransfer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NWParameters+DeviceTransfer.swift"; sourceTree = "<group>"; };
18011803
94396F2929EBE52400A57833 /* DeviceTransferClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceTransferClient.swift; sourceTree = "<group>"; };
@@ -2869,6 +2871,7 @@
28692871
7C07A17C29D8FDBC00D4835C /* NetworkInterface.swift */,
28702872
940B30482A16050D00B45D26 /* NetworkSpeedInspector.swift */,
28712873
94149B422A17B4D5003E9E1A /* NetworkSpeedConditioner.swift */,
2874+
943911692A39EA4300CF6DC7 /* DeviceTransferProgress.swift */,
28722875
);
28732876
path = Utility;
28742877
sourceTree = "<group>";
@@ -5064,6 +5067,7 @@
50645067
9424C64B259246B600FFDAE0 /* main.swift in Sources */,
50655068
945278982626BCD600023A6C /* HighlightableButton.swift in Sources */,
50665069
7BEB5D9F22B79F5500B8B10E /* EmergencyContactLoginVerificationCodeViewController.swift in Sources */,
5070+
9439116A2A39EA4300CF6DC7 /* DeviceTransferProgress.swift in Sources */,
50675071
7CEB736429DBCBE5006FB5B2 /* DeviceTransferSnapshot.swift in Sources */,
50685072
7B59535122672D3500D59DB4 /* TopResultCell.swift in Sources */,
50695073
7CEB736629DBCD6A006FB5B2 /* DeviceTransferSticker.swift in Sources */,

Mixin/Service/DeviceTransfer/DeviceTransferClient.swift

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ final class DeviceTransferClient {
77

88
enum State {
99
case idle
10-
case transfer(progress: Double, speed: String)
10+
case transfer(progress: Float, speed: String) // `progress` is between 0.0 and 1.0
1111
case failed(DeviceTransferError)
12-
case importing(progress: Float)
12+
case importing(progress: Float) // `progress` is between 0.0 and 1.0
1313
case finished
1414
}
1515

@@ -29,13 +29,11 @@ final class DeviceTransferClient {
2929
private weak var statisticsTimer: Timer?
3030

3131
private var fileStream: DeviceTransferFileStream?
32-
33-
// Access counts on main queue
34-
private var processedCount = 0
35-
private var totalCount: Int?
36-
3732
private var messageProcessingObservers: Set<AnyCancellable> = []
3833

34+
// Access on main queue
35+
private var progress = DeviceTransferProgress()
36+
3937
private var opaquePointer: UnsafeMutableRawPointer {
4038
Unmanaged<DeviceTransferClient>.passUnretained(self).toOpaque()
4139
}
@@ -126,7 +124,7 @@ final class DeviceTransferClient {
126124

127125
private func fail(error: DeviceTransferError) {
128126
assert(queue.isCurrent)
129-
Logger.general.info(category: "DeviceTransferClient", message: "Stop: \(error) Processed: \(processedCount) Total: \(totalCount)")
127+
Logger.general.info(category: "DeviceTransferClient", message: "Failed: \(error), progress: \(progress)")
130128
DispatchQueue.main.sync {
131129
self.statisticsTimer?.invalidate()
132130
}
@@ -139,7 +137,7 @@ final class DeviceTransferClient {
139137
state = .failed(error)
140138
}
141139

142-
private func startUpdatingProgressAndSpeed() {
140+
private func startUpdatingStatistics() {
143141
assert(Queue.main.isCurrent)
144142
statisticsTimer?.invalidate()
145143
statisticsTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] timer in
@@ -149,12 +147,7 @@ final class DeviceTransferClient {
149147
return
150148
}
151149
let speed = self.speedInspector.drain()
152-
let progress: Double
153-
if let totalCount = self.totalCount {
154-
progress = Double(self.processedCount) * 100 / Double(totalCount)
155-
} else {
156-
progress = 0
157-
}
150+
let progress = self.progress.fractionCompleted
158151
self.queue.async {
159152
guard case .transfer = self.state else {
160153
DispatchQueue.main.sync(execute: timer.invalidate)
@@ -242,8 +235,8 @@ extension DeviceTransferClient {
242235
Logger.general.info(category: "DeviceTransferClient", message: "Total count: \(count)")
243236
self.state = .transfer(progress: 0, speed: "")
244237
DispatchQueue.main.async {
245-
self.totalCount = count
246-
self.startUpdatingProgressAndSpeed()
238+
self.progress.totalUnitCount = count
239+
self.startUpdatingStatistics()
247240
}
248241
case .finish:
249242
Logger.general.info(category: "DeviceTransferClient", message: "Received finish command")
@@ -279,7 +272,7 @@ extension DeviceTransferClient {
279272
assert(queue.isCurrent)
280273
DispatchQueue.main.sync {
281274
speedInspector.add(byteCount: content.count)
282-
processedCount += 1
275+
progress.completedUnitCount += 1
283276
}
284277

285278
let firstHMACIndex = content.endIndex.advanced(by: -DeviceTransferProtocol.hmacDataCount)
@@ -331,7 +324,7 @@ extension DeviceTransferClient {
331324
DispatchQueue.main.sync {
332325
speedInspector.add(byteCount: content.count)
333326
if isReceivingNewFile {
334-
processedCount += 1
327+
progress.completedUnitCount += 1
335328
}
336329
}
337330

Mixin/Service/DeviceTransfer/DeviceTransferMessageProcessor.swift

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,8 @@ final class DeviceTransferMessageProcessor {
7171
}
7272
}
7373

74-
private var totalCount = 0
75-
private var processedCount = 0
76-
74+
private var processingProgress = DeviceTransferProgress()
7775
private var writingCache: Cache?
78-
7976
private var cacheReadingBuffer = Data(count: Int(bytesPerKiloByte))
8077

8178
// Messages are saved to database in batch. See `messageSavingBatchCount`
@@ -107,7 +104,7 @@ final class DeviceTransferMessageProcessor {
107104
cache.handle.write(encryptedMessage)
108105
cache.wroteCount += encryptedMessage.count
109106
processingQueue.async {
110-
self.totalCount += 1
107+
self.processingProgress.totalUnitCount += 1
111108
}
112109

113110
if cache.isOversized {
@@ -124,14 +121,14 @@ final class DeviceTransferMessageProcessor {
124121

125122
func reportFileReceived() {
126123
processingQueue.async {
127-
self.totalCount += 1
124+
self.processingProgress.totalUnitCount += 1
128125
}
129126
}
130127

131128
func finishProcessing() {
132129
assert(inputQueue.isCurrent)
133130
guard let lastCache = writingCache else {
134-
Logger.general.info(category: "DeviceTransferMessageProcessor", message: "All pending messages are saved")
131+
Logger.general.error(category: "DeviceTransferMessageProcessor", message: "No writing cache on finished")
135132
return
136133
}
137134
writingCache = nil
@@ -141,6 +138,7 @@ final class DeviceTransferMessageProcessor {
141138
try? fileManager.removeItem(at: lastCache.url)
142139
self.savePendingMessages()
143140
self.processFiles()
141+
Logger.general.info(category: "DeviceTransferMessageProcessor", message: "Processing finished with progress: \(self.processingProgress)")
144142
self.progress = 1
145143
}
146144
}
@@ -157,9 +155,7 @@ extension DeviceTransferMessageProcessor {
157155

158156
private func reportProgress() {
159157
assert(processingQueue.isCurrent)
160-
// Divide the count as integer to prevent `progress` from rounding when counts are large
161-
let progress = Float(processedCount * 100 / totalCount) / 100
162-
self.progress = progress
158+
self.progress = processingProgress.fractionCompleted
163159
}
164160

165161
private func savePendingMessages() {
@@ -188,7 +184,7 @@ extension DeviceTransferMessageProcessor {
188184
}
189185

190186
Logger.general.info(category: "DeviceTransferMessageProcessor", message: "Begin processing cache \(cache.index)")
191-
var processedCountOnLastProgressReporting = self.processedCount
187+
var completedCountOnLastProgressReporting = processingProgress.completedUnitCount
192188
while stream.hasBytesAvailable {
193189
guard !isCancelled else {
194190
Logger.general.info(category: "DeviceTransferMessageProcessor", message: "Not processing cache \(cache.index) by cancellation")
@@ -236,9 +232,9 @@ extension DeviceTransferMessageProcessor {
236232
} catch {
237233
Logger.general.error(category: "DeviceTransferMessageProcessor", message: "Decrypt failed: \(error)")
238234
}
239-
processedCount += 1
240-
if processedCount - processedCountOnLastProgressReporting == progressReportingInterval {
241-
processedCountOnLastProgressReporting = processedCount
235+
processingProgress.completedUnitCount += 1
236+
if processingProgress.completedUnitCount - completedCountOnLastProgressReporting == progressReportingInterval {
237+
completedCountOnLastProgressReporting = processingProgress.completedUnitCount
242238
reportProgress()
243239
}
244240
}
@@ -356,7 +352,7 @@ extension DeviceTransferMessageProcessor {
356352
return
357353
}
358354
Logger.general.info(category: "DeviceTransferMessageProcessor", message: "Start processing files")
359-
var processedCountOnLastProgressReporting = processedCount
355+
var processedCountOnLastProgressReporting = processingProgress.completedUnitCount
360356

361357
for case let fileURL as URL in fileEnumerator {
362358
guard !isCancelled else {
@@ -394,9 +390,9 @@ extension DeviceTransferMessageProcessor {
394390
}
395391
}
396392

397-
processedCount += 1
398-
if processedCount - processedCountOnLastProgressReporting == progressReportingInterval {
399-
processedCountOnLastProgressReporting = processedCount
393+
processingProgress.completedUnitCount += 1
394+
if processingProgress.completedUnitCount - processedCountOnLastProgressReporting == progressReportingInterval {
395+
processedCountOnLastProgressReporting = processingProgress.completedUnitCount
400396
reportProgress()
401397
}
402398

Mixin/Service/DeviceTransfer/DeviceTransferServer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ final class DeviceTransferServer {
1212
enum State {
1313
case idle
1414
case listening(hostname: String, port: UInt16)
15-
case transfer(progress: Double, speed: String)
15+
case transfer(progress: Float, speed: String) // `progress` is between 0.0 and 1.0
1616
case closed(ClosedReason)
1717
}
1818

Mixin/Service/DeviceTransfer/DeviceTransferServerDataSource.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ final class DeviceTransferServerDataSource {
2525
// MARK: - Data Count
2626
extension DeviceTransferServerDataSource {
2727

28-
func totalCount() -> Int {
28+
func totalCount() -> Int64 {
2929
assert(!Queue.main.isCurrent)
3030
let messagesCount = MessageDAO.shared.messagesCount()
3131
let attachmentsCount = attachmentsCount()
@@ -46,18 +46,18 @@ extension DeviceTransferServerDataSource {
4646
return total
4747
}
4848

49-
private func attachmentsCount() -> Int {
49+
private func attachmentsCount() -> Int64 {
5050
let folders = AttachmentContainer.Category.allCases.map(\.pathComponent) + ["Transcript"]
51-
let count = folders.reduce(0) { previousCount, folder in
51+
let count: Int64 = folders.reduce(0) { previousCount, folder in
5252
let folderURL = AttachmentContainer.url.appendingPathComponent(folder)
5353
let count = validFileCount(in: folderURL)
5454
return previousCount + count
5555
}
5656
return count
5757
}
5858

59-
private func validFileCount(in url: URL) -> Int {
60-
var count = 0
59+
private func validFileCount(in url: URL) -> Int64 {
60+
var count: Int64 = 0
6161
guard let fileEnumerator = FileManager.default.enumerator(at: url, includingPropertiesForKeys: [.isRegularFileKey], options: [.skipsHiddenFiles, .skipsPackageDescendants]) else {
6262
return 0
6363
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import Foundation
2+
3+
struct DeviceTransferProgress {
4+
5+
// NSProgress rounds `fractionCompleted` as binary, which may not be as
6+
// expected from the view of decimal progress
7+
// e.g. when `totalUnitCount` is `.max / 100`, and `completedUnitCount`
8+
// is `(.max / 100) - 1`, the `fractionCompleted` will be 1.0, which may
9+
// be considered as finished, or leads to misunderstanding
10+
11+
var totalUnitCount: Int64
12+
var completedUnitCount: Int64
13+
14+
var fractionCompleted: Float {
15+
if totalUnitCount == 0 {
16+
return 0
17+
} else {
18+
// Currently provides 4 digits for precision, that is 0.01% ~ 100.0%
19+
return Float(completedUnitCount * 10000 / totalUnitCount) / 10000
20+
}
21+
}
22+
23+
init(totalUnitCount: Int64 = 0, completedUnitCount: Int64 = 0) {
24+
self.totalUnitCount = 0
25+
self.completedUnitCount = 0
26+
}
27+
28+
}
29+
30+
extension DeviceTransferProgress: CustomStringConvertible {
31+
32+
var description: String {
33+
"<DeviceTransferProgress: \(completedUnitCount)/\(totalUnitCount)>"
34+
}
35+
36+
}

Mixin/UserInterface/Controllers/DeviceTransfer/DeviceTransferProgressViewController.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,8 @@ extension DeviceTransferProgressViewController {
194194
}
195195
}
196196

197-
private func updateTitleLabel(with transferProgress: Double, speed: String) {
198-
let progress = String(format: "%.2f", transferProgress)
197+
private func updateTitleLabel(with transferProgress: Float, speed: String) {
198+
let progress = String(format: "%.2f", transferProgress * 100)
199199
switch connection {
200200
case .server:
201201
titleLabel.text = R.string.localizable.transferring_chat_progress(progress)
@@ -204,7 +204,7 @@ extension DeviceTransferProgressViewController {
204204
case .cloud:
205205
break
206206
}
207-
progressView.progress = Float(transferProgress / 100)
207+
progressView.progress = transferProgress
208208
speedLabel.text = speed
209209
}
210210

MixinServices/MixinServices/Database/User/DAO/AppDAO.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ public final class AppDAO: UserDatabaseDAO {
4444
return db.select(with: sql, arguments: [limit])
4545
}
4646

47-
public func appsCount() -> Int {
48-
let count: Int? = db.select(with: "SELECT COUNT(*) FROM apps")
47+
public func appsCount() -> Int64 {
48+
let count: Int64? = db.select(with: "SELECT COUNT(*) FROM apps")
4949
return count ?? 0
5050
}
5151

MixinServices/MixinServices/Database/User/DAO/AssetDAO.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@ public final class AssetDAO: UserDatabaseDAO {
113113
return db.select(with: sql, arguments: [limit])
114114
}
115115

116-
public func assetsCount() -> Int {
117-
let count: Int? = db.select(with: "SELECT COUNT(*) FROM assets")
116+
public func assetsCount() -> Int64 {
117+
let count: Int64? = db.select(with: "SELECT COUNT(*) FROM assets")
118118
return count ?? 0
119119
}
120120

MixinServices/MixinServices/Database/User/DAO/ConversationDAO.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -668,8 +668,8 @@ public final class ConversationDAO: UserDatabaseDAO {
668668
return db.select(with: sql, arguments: [limit])
669669
}
670670

671-
public func conversationsCount() -> Int {
672-
let count: Int? = db.select(with: "SELECT COUNT(*) FROM conversations")
671+
public func conversationsCount() -> Int64 {
672+
let count: Int64? = db.select(with: "SELECT COUNT(*) FROM conversations")
673673
return count ?? 0
674674
}
675675

0 commit comments

Comments
 (0)