Sync attributes should also be exposed in async API (Was: Partially synchronous interface is odd)
Howdy, client-hinters!
Per a conversation around a Blink I2S thread regarding this feature, I was looking at the DOM API as proposed here:
https://github.com/WICG/ua-client-hints/pull/48/files
It appears that the example code in the Explainer (which, surprisingly, has IDL in it?) hasn't been updated to match, and I don't see an example code that uses the APIs in any of the proposed use-cases section. At a minimum, this isn't great Explainer hygeine.
If I'm reading the new IDL correctly, this example code:
const uaData = navigator.userAgentData;
const brands = uaData.brands; // [ {brand: "Google Chrome", version: "84"}, {brand: "Chromium", version: "84"} ]
const mobileness = uaData.mobile; // false
(async ()=>{
// `getHighEntropyValues()` returns a Promise, so needs to be `await`ed on.
const highEntropyValues = await uaData.getHighEntropyValues(
["platform", "platformVersion", "architecture", "model", "uaFullVersion"]);
const platform = highEntropyValues.platform; // "Mac OS X"
const platformVersion = highEntropyValues.platformVersion; //"10_15_4"
const architecture = highEntropyValues.architecture; // "Intel"
const model = highEntropyValues.model; // ""
const uaFullVersion = highEntropyValues.uaFullVersion; // "84.0.4113.0"
})();
Becomes:
(async () => {
//
// async entrypoint
//
const ua = await navigator.getUserAgent();
//
// sync accessors from async return value
//
// Plural return value but singular spelling?
const brand = ua.brand; // [ {brand: "Chrome", version: "84"} ]
const isMobile = ua.mobile;
//
// async accessors from async return value
//
// Singular async accessors, singular return value
const platform = await ua.getPlatform(); // { brand: "Win", version: "10" }
// What does this return for 32bit apps on a 64bit system, ala low-end Android?
const arch = await ua.getArchitecture(); // "ARM64"
// Singular async accessor without `get` prefix?
const model = await ua.model(); // ""
// UA full version no longer available anywhere?
})();
At a minimum, this is pretty confusing. A few paths to consider:
- Ensure that all async accessors have
getprefixes - Ensure that, should a sync accessor be needed for some properties, an async version is available so that callers don't need to always understand which is which
- Make array/object return values align with plurality of accessor names
Lastly, it'd be great to see the explainer work through examples to justify the various design choices (why are some things promoted to sync access, e.g.?).
I suspect you may be looking in the wrong place. The following example code you included (below...) is the latest API shape, and there are no plans to modify that.
const uaData = navigator.userAgentData;
const brands = uaData.brands; // [ {brand: "Google Chrome", version: "84"}, {brand: "Chromium", version: "84"} ]
const mobileness = uaData.mobile; // false
(async ()=>{
// `getHighEntropyValues()` returns a Promise, so needs to be `await`ed on.
const highEntropyValues = await uaData.getHighEntropyValues(
["platform", "platformVersion", "architecture", "model", "uaFullVersion"]);
const platform = highEntropyValues.platform; // "Mac OS X"
const platformVersion = highEntropyValues.platformVersion; //"10_15_4"
const architecture = highEntropyValues.architecture; // "Intel"
const model = highEntropyValues.model; // ""
const uaFullVersion = highEntropyValues.uaFullVersion; // "84.0.4113.0"
})();
https://github.com/WICG/ua-client-hints/pull/48/files
This PR was since overridden by https://github.com/WICG/ua-client-hints/pull/70
Explainer (which, surprisingly, has IDL in it?)
Good point. I can remove the IDL from the explainer.
Lastly, it'd be great to see the explainer work through examples to justify the various design choices
Yeah, I can add explanations on that front
Thanks for that.
I'm not sure it has net reduced my confusion. The following might be wrong too as I feel mightily confused.
It looks like there's a single async getter (uaData.getHighEntropyValues()) that's async, but the UADataValues doesn't also include brands or mobile, which means the developer still needs to remember which to use when/where.
Assuming the rest gets cleaned up, perhaps adding brands and mobile to UADataValues -- in addition to surfacing at the top level -- solves the issue? It would appear to.
Thoughts?
It looks like there's a single async getter (
uaData.getHighEntropyValues()) that's async, but theUADataValuesdoesn't also includebrandsormobile, which means the developer still needs to remember which to use when/where.
Indeed, userAgentData contains 2 sync attributes for brands and mobile and a getter for the higher entropy values.
perhaps adding
brandsandmobiletoUADataValues-- in addition to surfacing at the top level -- solves the issue? It would appear to.
OK, happy to add them as well if you think it'll make it clearer for developers.
I'm -1 on including redundant information. If you want a single object, you can use JavaScript's built-in abilities to combine two objects into one. More commonly, I think it'll be important to separate the high-entry values from the low-entropy ones.
@domenic - thanks for the feedback!
I renamed the issue and marked as an enhancement, as this seems non-blocking for the initial version.
I'm too lazy to dig up the CL, but we did end up implementing and shipped what @slightlyoff suggested some years ago:
That is, the sync low-entropy hints are also available via the async API:
await uaData.getHighEntropyValues(["mobile", "brands", "platform"])