Say I have some hierarchy of NSProgress
objects. For simplicity say a root progress with 2 children. If progress has already started can I add another child progress to the hierarchy and expect the right thing to happen? For example, say I start with 2 children, so the totalUnitCount
of the root is 2
and the pendingUnitCount
is 2
. Once the first child is complete the root progress' fractionCompleted
will be 0.5
. If I then add another child progress the root progress' fractionCompleted
does update to ~0.33
as I would like but the root's totalUnitCount
remains at 2
, as does the pendingUnitCount
. I can update the totalUnitCount
manually but this throws the root's fractionCompleted
off. Also, there seems to be no way to update the pendingUnitCount
without calling becomeCurrentWithPendingUnitCount:
. Was NSProgress
not designed for this kind of use or is there a "proper" way to do this?
My use case is that I have an application that can upload files to a remote server. The user may select some files and hit a button to start uploading them. While they are uploading the user sees the overall progress of the upload job. Now, if the user wants to select some more photos to upload while the uploads are already in progress I want to update the progress to reflect the new total. For example, he is currently uploading 2 files, 1 has already completed, while they are uploading he adds 2 more to upload. The progress should adjust itself. Total unit count should now be 4 and there should be 3 items remaining to upload. Seems like this would be a common use case to me.
Here's a unit test I wrote to play with dynamically extending an NSProgress
hierarchy. All the asserts in the test below pass but if you check the root's totalUnitCount
it always remains 2 and there seems to be no way to tell how many items remain to be completed from the root progress. The root progress is the one that the UI would observe so it is important that changes deeper in the hierarchy propagate up, including totalUnitCount
, etc.
- (void)testDynamicNSProgress
{
NSProgress *root = [NSProgress progressWithTotalUnitCount:2];
[root becomeCurrentWithPendingUnitCount:2];
NSProgress *child1 = [NSProgress progressWithTotalUnitCount:1];
NSProgress *child2 = [NSProgress progressWithTotalUnitCount:1];
XCTAssertEqual(root.totalUnitCount, 2);
XCTAssertEqual(root.completedUnitCount, 0);
child1.completedUnitCount++;
XCTAssertEqual(ceil(root.fractionCompleted * 100), 50);
NSProgress *child3 = [NSProgress progressWithTotalUnitCount:1];
NSProgress *child4 = [NSProgress progressWithTotalUnitCount:1];
XCTAssertEqual(ceil(root.fractionCompleted * 100), 25);
child2.completedUnitCount++;
XCTAssertEqual(ceil(root.fractionCompleted * 100), 50);
child3.completedUnitCount++;
XCTAssertEqual(ceil(root.fractionCompleted * 100), 75);
child4.completedUnitCount++;
XCTAssertEqual(ceil(root.fractionCompleted * 100), 100);
__unused NSProgress *child5 = [NSProgress progressWithTotalUnitCount:1];
XCTAssertEqual(ceil(root.fractionCompleted * 100), 80);
}
I filed a bug with Apple and this was their response:
Engineering has determined that this issue behaves as intended based on the following information:
The reason is that we cannot update the parent's completed unit count while any member of a 'group' of children (those attached to it as child progresses while a progress is current) is unfinished. Once all members of the group are finished, we detach that group from the parent and increment the parent's completed unit count. This preserves correct accounting of completed work. The localized description method accounts for this, which is why it gives a different result than the completedUnitCount property.
If you want to track the completion of these children more closely, then you can have the parent become current more frequently with a smaller unit count. Then, when the children are completed, the parent will update its completed unit count more frequently.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With