Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
iOS18でQRコードが表示されなくなった🤷
Search
Ryomm
December 06, 2024
Programming
0
120
iOS18でQRコードが表示されなくなった🤷
2024.12.06 KTC×WED×フェンリル 3社合同イベント
Ryomm
December 06, 2024
Tweet
Share
More Decks by Ryomm
See All by Ryomm
なあ兄弟、 余白の意味を考えてから UI実装してくれ!
ktcryomm
0
170
iOSでQRコード生成奮闘記
ktcryomm
2
270
リョムキャットのパーフェクトSwiftネーミング教室
ktcryomm
0
2.1k
Slackを使いこなせ!Slack効率3000倍
ktcryomm
0
1.1k
クソアプリ作って煙突詰めた♪
ktcryomm
0
63
Other Decks in Programming
See All in Programming
AIの弱点、やっぱりプログラミングは人間が(も)勉強しよう / YAPC AI and Programming
kishida
13
5.3k
Private APIの呼び出し方
kishikawakatsumi
3
900
GeistFabrik and AI-augmented software development
adewale
PRO
0
180
Eloquentを使ってどこまでコードの治安を保てるのか?を新人が考察してみた
itokoh0405
0
3.2k
知られているようで知られていない JavaScriptの仕様 4選
syumai
0
630
AIエージェントでのJava開発がはかどるMCPをAIを使って開発してみた / java mcp for jjug
kishida
4
770
Nitro v3
kazupon
2
320
Evolving NEWT’s TypeScript Backend for the AI-Driven Era
xpromx
0
190
モビリティSaaSにおけるデータ利活用の発展
nealle
1
620
JEP 496 と JEP 497 から学ぶ耐量子計算機暗号入門 / Learning Post-Quantum Crypto Basics from JEP 496 & 497
mackey0225
2
480
Developing Specifications - Jakarta EE: a Real World Example
ivargrimstad
0
200
Honoを技術選定したAI要件定義プラットフォームAcsimでの意思決定
codenote
0
260
Featured
See All Featured
Git: the NoSQL Database
bkeepers
PRO
432
66k
Become a Pro
speakerdeck
PRO
30
5.6k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.2k
Build The Right Thing And Hit Your Dates
maggiecrowley
38
2.9k
Fireside Chat
paigeccino
41
3.7k
The Invisible Side of Design
smashingmag
302
51k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
359
30k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
36
6.1k
Code Reviewing Like a Champion
maltzj
527
40k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
253
22k
Designing Experiences People Love
moore
142
24k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.3k
Transcript
None
None
None
None
調査の結果、どうやらこちらのコードでnilが返ってきているっぽい
このコードでは何をしているかというと、こちらの名前の通り…
文字列として渡されたバイナリからQRコードを作っています
ここで、QRコードの仕様について補足、、 QRコードに格納ができるデータはこちらの4種類です。 この中で、8ビットバイトモードを使うとバイナリを格納することが可能です。 バイナリを入れられるということは、日本語以外でもどんな文字列でも入れられるということです。
my routeでは文字列として受け取ったバイナリを16進数バイナリとしてQRコードで読み取れるようにする処理があります。 先ほどのコードでは、このへんで変換を行なっています。
わかりやすさのため、具体的にみていきます。 先ほどのコードを分解するとこのようになっています。
文字列として謎のバイナリXを受け取って、それを16進数バイナリ、つまりData型に変換します。
例えば謎のバイナリXを 6e79dc68 とします。
普通にutf8でData型にすると、元の値がただの文字列として扱われてしまいます。 Data型にした時に、受け取った文字列がカンマが取れるようにしたいので、コネコネします。
まずは文字列を16を基数として変換し、数字として扱えるようにします。 ここではBigNumberというライブラリを使って変換していますが、標準のIntでも似たようなことができます。10進数にすると 1853480040 となりました。 BIntというのは、8バイトの整数を格納できる型です。ここでは普通の整数だと思ってもらって大丈夫です。
続いて、UInt8の配列に変換します。 先ほどのBigNumberライブラリを使うと簡単に取得することができます。 ライブラリ上ではBytesという表記ですが、これはtypealiasで[UInt8]となっています UInt8とは2進数で8ビットまで格納できる型で、一個前の10進数を2進数に変換し、お尻から8桁ずつ切り取って配列にしたものです。頭の足りない分は0で埋めています。
そして、UInt8の配列をData型にするため、文字列とエンコード方式のセットを取得したいです。 そのため、エンコード方式を把握した状態で一旦String型に変換します。
最後に、先ほどエンコードした方法で、今度は文字列をData型にエンコードします
これで、受け取った文字列をData型に変換することができるようになりました! 値が同じままダブルクォートが取れていることがわかります
そう、iOS17までは…
iOS18において、UInt8の配列をStringに変換しようとしたとき、nilが返ってくるようになってしまいました!
Swift6の大きな変更のひとつに、Foundationの実装移行があります。 FoundationにはAppleプラットフォーム向けのと、LinuxなどApple以外のプラットフォーム向けのものがあります。 Swift5では、どちらもCoreFoundationというC言語で作られた内部実装に深く依存しています。 ただ、これはObjCとの親和性が高い実装なので、Swiftで実装し直されました
それが、swift-foundationです。 Swift6のタイミングで、この内部実装が切り替わりました。
そして、iOSにおいて、FoundationはOS内蔵のものが使われていると言われています つまり、iOS18になったタイミングでiOS18に内蔵されたFoundationの参照先がswift-foundationになったことで、iOS18でロジックが壊れる影響が出たと考えられます。 では、Foundationが変わったことで、Stringのinitでどんな差分が発生しているのか詳しく見てみます。 (discordでIcemanさんに教えてもらいました)
問題のコードではUTF-16BEでデコードしています。 UTF-16は0000からFFFFまで全部使う規格なので、基本的にサロゲートペア以外でデコードには失敗しません。 サロゲートペアとは、、 UTF-16においては基本的に2バイトで文字を表現しますが、それだと65535個の文字しか表現できないため、さらに拡張するために編み出されたものです。 D800〜DBFFの上位サロゲートと、DC00〜DFFFの下位サロゲートを組み合わせて表現します。 で、先ほどのUTF-16BEでデコードしようとしていた値を見てみます。 2つ目のdc68が下位サロゲートのコード範囲にあたっています! しかし、ペアとなる上位サロゲートがないためデコードは失敗します (discordでomochimetalさんに教えてもらいました)
前は何かしらの値を無理矢理返却していましたが、新しいfoundationではデコードに失敗した時にnilが返ってくるように仕様が変わっていたのです。
(回想) 100%再現するわけじゃない、という現象の理由は、 たまたまデコードできた時にはQRコードが表示されていたってことか…
変換処理を修正しようと思います。 まず、謎のバイナリXは、なにでエンコードされたものかわかりません。 なので、なんでもいいから一旦デコードして、Stringとそのエンコード方式のペアを取得したいです。
そう、、なんでもいいから一旦デコードしたい…!
名付けて総当たり大作戦!! なんでもいいから一旦デコードできるものを探します!!
ゲットしたStringとエンコード方式を使ってData型に変換します! やったー!変換に成功した!
None
なんか…違和感がある…
…これいる? [UInt8]からData型の変換もっとシンプルにできないの? 総当たりで探し回って一旦String型に変換するのって変じゃない??
[UInt8]からDataへ変換する他の方法を探していると、このような記載を発見しました
None
[UInt8]からStringへの変換処理、全然いらんかったー!
超シンプルにData(byte)でよかった
うわあああこれをQRコード生成メソッドに組み込んで完成!
foundationの内部実装の切り替えの煽りを受けた結果、遠回りした実装になってたことがわかったはなしでした〜
QRコードのデータのモードを判別するにはモード指示子を解読する必要があります。 そしてそれを解読する前にQRコードのマスクパターンを取得します。今回は000なので、こちらの計算式が得られました。
取得したマスクパターンとモード指示子の4ビットの排他論理和を取得します。 これは0100なので、8ビットバイトモードだ!とわかりました