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
110
iOS18でQRコードが表示されなくなった🤷
2024.12.06 KTC×WED×フェンリル 3社合同イベント
Ryomm
December 06, 2024
Tweet
Share
More Decks by Ryomm
See All by Ryomm
iOSでQRコード生成奮闘記
ktcryomm
2
250
リョムキャットのパーフェクトSwiftネーミング教室
ktcryomm
0
2.1k
Slackを使いこなせ!Slack効率3000倍
ktcryomm
0
1.1k
クソアプリ作って煙突詰めた♪
ktcryomm
0
61
Other Decks in Programming
See All in Programming
CSC305 Lecture 06
javiergs
PRO
0
230
Web Components で実現する Hotwire とフロントエンドフレームワークの橋渡し / Bridging with Web Components
da1chi
3
2.4k
Django Ninja による API 開発効率化とリプレースの実践
kashewnuts
0
1.3k
CSC509 Lecture 04
javiergs
PRO
0
300
バッチ処理を「状態の記録」から「事実の記録」へ
panda728
PRO
0
160
2分台で1500examples完走!爆速CIを支える環境構築術 - Kaigi on Rails 2025
falcon8823
3
3.6k
Domain-centric? Why Hexagonal, Onion, and Clean Architecture Are Answers to the Wrong Question
olivergierke
2
840
Web フロントエンドエンジニアに開かれる AI Agent プロダクト開発 - Vercel AI SDK を観察して AI Agent と仲良くなろう! #FEC余熱NIGHT
izumin5210
3
530
タスクの特性や不確実性に応じた最適な作業スタイルの選択(ペアプロ・モブプロ・ソロプロ)と実践 / Optimal Work Style Selection: Pair, Mob, or Solo Programming.
honyanya
3
170
dynamic!
moro
10
7.8k
私達はmodernize packageに夢を見るか feat. go/analysis, go/ast / Go Conference 2025
kaorumuta
2
560
コードとあなたと私の距離 / The Distance Between Code, You, and I
hiro_y
0
160
Featured
See All Featured
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
229
22k
It's Worth the Effort
3n
187
28k
Site-Speed That Sticks
csswizardry
11
900
A Modern Web Designer's Workflow
chriscoyier
697
190k
Documentation Writing (for coders)
carmenintech
75
5.1k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
15
1.7k
Bash Introduction
62gerente
615
210k
Side Projects
sachag
455
43k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
34
6.1k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
7
900
Faster Mobile Websites
deanohume
310
31k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.2k
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ビットバイトモードだ!とわかりました