Search code examples
iosqr-codepassbookwalletapple-wallet

Why do some Apple Wallet generated QR codes contain so much additional junk?


I am trying to create an Apple Wallet pass for SMART Health Card QR codes.

The content of the QR code will be something like the following dummy data, and it appears to be generated with a medium error correction level:

shc:/567629095243206034602924374044603122295953265460346029254077280433602870286471674522280928613331456437653141590640220306450459085643550341424541364037063665417137241236380304375622046737407532323925433443326057360106452931531270742428395038692212766728666731266342087422573776302062041022437658685343255820002167287607585708105505622752282407670809680507692361773323356634342439664440596761410443377667202663224433674530596175400038397052612140292974753658337372662132066669047253044469405210524536242721550377673434280323045475690310233670562227414567090555653507636250537239522776211205312561442568282012726838630039087127042463716936535535602928393065580072763158437500341209546904210458383257586630101033123422114008776058732325243477645920113037325929083272452732223707055550412927584543582550667760036577724025621136525340592771740903663844771261692077697211447057562509437029626707254539002011763240720310114260256672645965627243654061066553770056003044082967606162724306592273682223412466107335331229606157521057357572327529693965670332063208596309543400076452696835713027450728663529345234666377297208583525543653527774072234735706452828641140633528387577054371703966706421520708254156041170353656054471407636552612616834377244090406554327122559623453686207006139712936404138601156656945315611255669116044703333731263580306106975715411702932060511012768634011703371553353213365032550756476005853005224547339310064671161682376335069647622323339523133724171327531702738363650063527592633763908656123314363227707566731311074

Using most standard QR code generators, this gives the following (correct, expected) QR code.

Correct, expected QR code

In my pass.json file, I have the following segment for the QR code:

*snip* "barcode":{"message":"shc:\/567629095243206034602924374044603122295953265460346029254077280433602870286471674522280928613331456437653141590640220306450459085643550341424541364037063665417137241236380304375622046737407532323925433443326057360106452931531270742428395038692212766728666731266342087422573776302062041022437658685343255820002167287607585708105505622752282407670809680507692361773323356634342439664440596761410443377667202663224433674530596175400038397052612140292974753658337372662132066669047253044469405210524536242721550377673434280323045475690310233670562227414567090555653507636250537239522776211205312561442568282012726838630039087127042463716936535535602928393065580072763158437500341209546904210458383257586630101033123422114008776058732325243477645920113037325929083272452732223707055550412927584543582550667760036577724025621136525340592771740903663844771261692077697211447057562509437029626707254539002011763240720310114260256672645965627243654061066553770056003044082967606162724306592273682223412466107335331229606157521057357572327529693965670332063208596309543400076452696835713027450728663529345234666377297208583525543653527774072234735706452828641140633528387577054371703966706421520708254156041170353656054471407636552612616834377244090406554327122559623453686207006139712936404138601156656945315611255669116044703333731263580306106975715411702932060511012768634011703371553353213365032550756476005853005224547339310064671161682376335069647622323339523133724171327531702738363650063527592633763908656123314363227707566731311074","format":"PKBarcodeFormatQR","messageEncoding":"iso-8859-1"} *snip*

When this pass is added to Apple Wallet, I get the following QR code in the Wallet app:

QR code actually displayed by Apple Wallet

This technically does appear to encode the same data. However, because the QR code in an Apple Wallet pass is so small and the code is so dense with unnecessary junk, I haven't had much luck getting any QR code readers to actually read it from my device's screen, like how a Passbook pass would normally be used.

I didn't find any way to set the error correction level in Passbook, but when I tried generating the QR code using different error correction levels to check, even the highest error correction level wouldn't produce a QR code like the one my Apple Wallet produced. My guess, given how repetitive the right 2/3 of that second code looks, is that it's null padding, but I'm not sure.

What's happening here, and how can I fix it so that my passes contain the QR code from the first example without all the additional junk? If it isn't possible to fix, is there a way for me to just embed the image of the correct QR code on the pass but have it displayed large enough to scan?

========

Update: removing just the shc:/ header seemed to pruduce a QR code that looks closer to what is expected; although this header is necessary and so this is not a solution, I'm guessing it means Wallet is just having a hard time encoding it effectively.


Solution

  • The code in Apple Wallet looks very different because it uses an inefficient method to encode the data in question.

    Apple Wallet (PassKit) likely uses the CIQRCodeGenerator Core Image filter to generate QR codes. The filter automatically chooses the most suitable encoding mode based on the given data, and it chose binary/bytes mode in this case. Because the vast majority of the SMART Health Cards data is expressed in numbers (which only carry ~3.3 bits of information per digit), encoding it in binary mode (8 bits/character) is rather inefficient. This is why the QR code looks much denser yet repetitive.

    When you removed shc:/, the resulting QR code seemed more "correct" because the filter was then able to encode in pure numeric mode. Funny enough, if you changed shc:/ to all uppercase, the filter would encode in alphanumeric mode, which is still more efficient than binary mode for this data.

    Some other QR code generators could handle this better as they are able to segment the data for maximum efficiency. In fact, the framework protocol described as muchbinary mode for shc:/ and the optional chunk prefix, then numeric mode for the rest.

    Unfortunately, there doesn't seem to be a way to fix this within Apple Wallet, at least as of iOS 14. The Core Image filter is simply not flexible enough to encode data using multiple modes, and PassKit itself still leaves a lot to be desired.