Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to dynamically extend an NSProgress hierarchy after progress has started?

Tags:

ios

nsprogress

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);
}
like image 915
exokernel Avatar asked Apr 21 '14 17:04

exokernel


1 Answers

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.

like image 82
exokernel Avatar answered Sep 22 '22 19:09

exokernel