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
88
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
230
リョムキャットのパーフェクトSwiftネーミング教室
ktcryomm
0
2k
Slackを使いこなせ!Slack効率3000倍
ktcryomm
0
990
クソアプリ作って煙突詰めた♪
ktcryomm
0
59
Other Decks in Programming
See All in Programming
Create a website using Spatial Web
akkeylab
0
310
Blazing Fast UI Development with Compose Hot Reload (droidcon New York 2025)
zsmb
1
250
Select API from Kotlin Coroutine
jmatsu
1
190
Webの外へ飛び出せ NativePHPが切り拓くPHPの未来
takuyakatsusa
2
440
20250613-SSKMvol.15
diostray
0
100
NPOでのDevinの活用
codeforeveryone
0
450
AIプログラマーDevinは PHPerの夢を見るか?
shinyasaita
1
170
なぜ「共通化」を考え、失敗を繰り返すのか
rinchoku
1
590
生成AIコーディングとの向き合い方、AIと共創するという考え方 / How to deal with generative AI coding and the concept of co-creating with AI
seike460
PRO
1
340
Azure AI Foundryではじめてのマルチエージェントワークフロー
seosoft
0
140
設計やレビューに悩んでいるPHPerに贈る、クリーンなオブジェクト設計の指針たち
panda_program
6
1.7k
Beyond Portability: Live Migration for Evolving WebAssembly Workloads
chikuwait
0
400
Featured
See All Featured
The Cult of Friendly URLs
andyhume
79
6.5k
Embracing the Ebb and Flow
colly
86
4.7k
Agile that works and the tools we love
rasmusluckow
329
21k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
48
5.4k
Done Done
chrislema
184
16k
Typedesign – Prime Four
hannesfritz
42
2.7k
Adopting Sorbet at Scale
ufuk
77
9.4k
Why Our Code Smells
bkeepers
PRO
337
57k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
34
5.9k
Unsuck your backbone
ammeep
671
58k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.7k
Optimising Largest Contentful Paint
csswizardry
37
3.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ビットバイトモードだ!とわかりました