bytesTotal property
The artifact's size. Present on UploadEvent.progress and on the UploadState.transferring line.
Beside bytesSent rather than instead of a percentage, so the fraction is derivable rather than asserted — a consumer that wants two decimal places is not held to whatever this package rounded to.
Floor that fraction; do not round it. A chunk is 1 MiB and an
artifact is not a whole number of them, so the last-but-one line is a
few kibibytes short of the total and (sent * 100 / total).round() is
100 with bytes still in flight. A finished cell and an
almost-finished one then look identical, which is the confusion this
stream exists to remove. Measured on a 2 101 248-byte artifact: the
second progress line is 2 097 152, which is 99.805%.
And do not read 100% as finished, however it is computed. bytesSent == bytesTotal means the store has the bytes and nothing more: on Play
the run is still to reach UploadState.committing, and on the App Store
it is still to reach UploadState.processing, which is five to fifteen
minutes long. UploadEvent.result is the line that says the run
finished, and it is the only one that does.
And stop showing it once state leaves UploadState.transferring.
The bytes have landed by then and the store is working on what it already
holds, so a percentage beside UploadState.accepting,
UploadState.committing or UploadState.processing describes something
that has finished — committing 99% reads as a transfer one percent
short of done, and a reader waits for a number that will never move. Those
states are not fractions of anything; let the state name stand alone.
The ordering makes the obvious version of that wrong, and nothing
about the ordering looks like a hazard until it is hit: a progress line
arrives after UploadState.accepting, because the final chunk is
acknowledged by the response that announcement was written ahead of. So a
consumer that hides the number on a non-transferring state and re-shows it
on any progress line flickers it back on for exactly one line. Let the
last state line own that judgment and let progress say only how far —
a progress line is silent about what kind of work is going on.
All three are the same rule AppStoreUploadEvent follows by having no
bytesSent at all: a display must not imply a state the data does not
support. Each was reported by the first consumer, from real runs against
these events — the first two from the arithmetic, the third from reading
a rendered frame.
Implementation
final int? bytesTotal;