こんにちは、 yu です。2024年9月から10月までの約1ヶ月間、STORES レジ・予約チームの iOS エンジニアとして Join させていただきました。
インターン期間中のアウトプットとして、学んだことや取り組んだタスクについて記事を書いてきます。
初めてのタスク
この前にも2つ軽いタスクはありましたが、初めにアサインしていただいた大きめのタスクは、カメラが意図せず回転してしまう問題の修正でした。
STORES レジアプリには、バーコードを読み込んだりするためのスキャン機能が存在しています。そのスキャン機能を使用中にカメラが意図せず回転してしまうとのこと。これに悩まされていて、カメラの向きが変わってもスキャンができる能力を身につけたレジのエンジニアさんもいらっしゃいました。
現状
STORES レジアプリは横向き (Landscape Left/Right) のみに対応していますが、デバイスの向きによってカメラが回転してしまい、下の動画のように平面にしたときにもカメラの向きが変わってしまいます。
原因調査
まず、カメラの向きを制御している既存コードを見てみると、そこでは UIDeviceOrientation
の rawValue
の値を AVCaptureVideoOrientation
に適用して向きを制御しているようでした。
guard let videoOrientation = AVCaptureVideoOrientation(rawValue: UIDevice.current.orientation.rawValue) else { return }
UIDeviceOrientation
は UI の向きとは独立していて、レジアプリのように横向きにしか対応していなくても常にデバイスの向きを返してきます。
ということで UI の向きを取るにはどうすればいいか調べてみると…
ありました。 UIInterfaceOrientation
です。
developer.apple.com
原因をまとめてみるとこのような感じになります。
- カメラは UIDeviceOrientation に基づいて回転している。
- UIは InterfaceOrientation に基づいて回転している。
それぞれ別の値に基づいて回転しているため、意図しない回転が起こっているということです。
解決
原因がわかったので実装をしていきますが至ってかんたんでした。
UIWindowScene
から UIInterfaceOrientation
を取得し、それを AVCaptureVideoOrientation
に変換するだけです。
let window = UIWindow(frame: UIScreen.main.bounds) guard let interfaceOrientation = window.windowScene?.interfaceOrientation else { return } guard let videoOrientation = AVCaptureVideoOrientation(interfaceOrientation: interfaceOrientation) else { return }
結果、無事意図せずカメラの向きが変わることなくスキャンできるようになりました。
プリンターを有線接続に対応させる
次にアサインしていただいたタスクは、レシート印刷用のプリンターを有線接続に対応させるというものでした。
私はインターンに入る前の受け入れ面談などで、ハードウェアと連携させる部分の実装をしたいとリクエストをしており、それを実現していただきました。
STORES レジではさまざまなレシートプリンターに対応していますが、中でもスター精密社製のプリンターの mC-Print3 というプリンターに関するタスクです。
背景
1年ほど前まで、公式にはサポートしていなかったものの mC-Print3 は有線接続ができていたそうです。
しかし、スター精密社製の SDK をアップデートしたところ有線接続ができなくなってしまったため、今回、有線接続機能を復活させ、公式にサポートするところまで私が担当することとなりました。
原因
1年ほど前の調査段階で、スター精密社製の SDK で有線接続と無線接続を自動で切り替えるとクラッシュが発生するところまでは分かっていました。
そこで、「SDK の最新バージョンでこの問題が修正されているかもしれない」という淡い期待を抱いてアップデートしてみましたが、状況は変わらずでした。
結果、自動切り替え機能を使わずにどうにかして手動で切り替える必要がありそうです。
解決
アプリ側では、プリンターが有線と無線のどちらで接続されているかを把握する術がありません。
ということで、下記のようなフローで有線接続に対応しました。
有線と無線接続の両方に対応しているデバイスの型番を登録
両方に対応していなければ、指定されたインターフェースで接続
両方に対応していれば、一旦 Bluetooth で接続を試みる
接続できれば、今までどおり Bluetooth で接続
接続できなければ、USB で再度接続を試みる
接続できればOK
そして、有線接続を復活させることができました。
実際に動いている様子がこちらです。
ただ、実際には接続中に接続先を切り替えると、切り替え後アプリを再起動しないとプリントができないという問題もありました。しかし、そちらは技術的に対応が難しそうであったため、 FAQ に記載することで対処しました。
設計に参加
今まで開発関連の話をさせていただきましたが、実は新機能の設計にも関わらせていただきました。
私は設計ミーティングの途中から参加させていただいたため、始めの方は「どのような状況で使用されてどうなれば正解なのか」といったドメイン知識を得ることに必死でした。
ある程度理解した後、機能の基盤となるデータベースやバックエンドの仕様検討の話が多くなっていきました。今までモバイル中心の開発をしてバックエンドは BaaS に頼ってきたため、自分の知識の無さを実感するようになりました。
バックエンドには Ruby と GraphQL が使用されていたため、クライアント側も密接に関わる GraphQL について自習をしたことで、徐々にミーティング中の話の解像度を高めていくことができました。
また、今まで私はとあるインターン先のインターンチームのリーダーとして、新規のアプリ開発をした経験がありますが、その当時も要件定義や設計から行った経験があります。
しかし、実際に複雑な機能の設計を経験してみて、関数の定義や責務、命名など、ひとつひとつ時間をかけて詰めていっていたため、設計はこんなに時間のかかる大変な作業なのだと実感しました。
設計に時間をかけるからこそ、実装時のコスト削減や将来的な負債の可能性を下げることができるのだと感じました。
当時設計を担当していたときは初めての経験だったため、詰めが甘い状態で開発を始めてしまい手戻りが発生したりしたため、その当時の私に設計の大切さを伝えたいです…
インターンに参加してみて
今回1ヶ月間インターンに参加した結果、ハードウェアに触れることができたことはもちろん、Swiftを用いて本格的な開発ができ、普段気づかないような鋭いレビューを頂いたり、スクラム開発を本格的に体感できたりするなど、技術的に刺激的な1ヶ月間でした。
また、設計や機能の企画、開発の効率化などについての議論に参加させていただくことができ、技術以外の部分でも非常に成長できました。
この1ヶ月間、週3日で稼働させていただき、トータル40コミットと、9つの Pull Requests をマージできました。今現在も最後のタスクに取り組んでいるので、最後までやり通せるよう頑張ります。
メンターさんをはじめ、お世話になった方々本当にありがとうございました!