こんにちは。PayPayフリマでiOSアプリ開発を担当している伊藤(@shizuna_it)です。PayPayフリマYahoo! JAPAN Tech Blogの第二弾です!
PayPayフリマは、誰でも気軽に、安心して個人間取引ができるフリマアプリです。(PayPayアプリの中からも使えます)
PayPayフリマのiOSアプリでは、Swift Package Manager(SwiftPM)でライブラリ管理を行うようにリファクタリングを進めています。その過程で私達はVision.frameworkを活用してクレジットカード番号読み取り機能を独自実装しました。この記事では、実際に起きていた技術負債とその解消のための実装をサンプルコードを交えてご紹介します。
なぜSwiftPM移行を進めているか
Swift Package Managerは、Appleが提供するSwiftのソースコード配布管理ツールで、Xcode11からはXcodeに統合されています。iOS開発では毎年のXcodeメジャーアップデートやApple Siliconの登場など開発環境が大きく変わる場面が多く、サードパーティの管理ツールを利用している場合は新しい環境への対応が困難になる可能性があります。その点、SwiftPMであれば依存関係の解決や更新がGUIで簡単に行えます。
また、SwiftPMを使ったマルチモジュール化を行うことでビルド時間やテスト時間の高速化が見込めました。さらにPayPayフリマではM1搭載Macの利用を検討していたため、特にSwiftPMへの移行のメリットが大きいと感じました。
これらの理由から、PayPayフリマでは開発効率の向上を目的としてSwiftPMへの移行を進めました。
SwiftPM非対応なライブラリが技術負債に
SwiftPMに移行するにあたり、まず利用しているライブラリがSwiftPMに対応しているかを確認しました。
PayPayフリマが移行を試みた時点でSwiftPM登場から数年が経過しており、多くのライブラリが既に対応していました。対応されていないライブラリに関しても、issueやPRを確認するとほとんどのもので今後対応されることが見込めました。しかし、一点課題が発生しました。PayPayフリマにはクレジットカード登録時に番号をカメラで読み取る機能があるのですが、ここで利用していたライブラリがSwiftPM非対応でした。その上数年前でアップデートが止まっており、SwiftPMに関するissueやPRもないため今後対応される見込みがほとんどない状況でした。
このライブラリはクレジットカード読み取りという一部の機能でのみ利用しており改修の影響範囲が少量で済むこと、iOSのフレームワークを利用して独自に実装が可能であったことから、ライブラリを利用せず機能を独自実装することにしました。
クレジットカード番号読み取り機能をVision.frameworkで独自実装する
では実際どのようにしてクレジットカード読み取り機能を独自実装したかを簡単にご紹介します。
クレジットカード読み取り機能は、ユーザーがクレジットカードにカメラをかざすことでクレジットカード番号・有効期限の項目を埋められる機能です。
Vision.frameworkとは、顔認識やテキスト認識など物体認識を簡単に実現できるフレームワークで、Appleが提供しています。こちらはiOS13から利用可能なVisionのVNRecognizeTextRequestを使って実現できます。
まず画像から認識した文字列を取得する具体的な実装をご紹介します。
class TextRecognitionRepository {
private let textRecognitionQueue: DispatchQueue
private var request: VNRecognizeTextRequest?
init(textRecognitionQueue: DispatchQueue) {
self.textRecognitionQueue = textRecognitionQueue
}
func recognize(in image: CIImage, completion: @escaping (([String]) -> Void)) {
request?.cancel()
// 画像分析のリクエストを定義する
request = VNRecognizeTextRequest { (request, error) in
// 処理結果とエラーが返却される
guard let texts = request.results as? [VNRecognizedTextObservation], error == nil else {
completion([])
return
}
// 返却されたテキストのうち上位10件の候補をcompletionで渡す
let results = texts.flatMap({ $0.topCandidates(10).map({ $0.string }) })
DispatchQueue.main.async {
completion(results)
}
}
guard let request = request else { return }
textRecognitionQueue.async {
// 画像解析を処理するオブジェクトにリクエストを渡し実行する
let requestHandler = VNImageRequestHandler(ciImage: image, options: [:])
try? requestHandler.perform([request])
}
}
}
このように少ないコード量で簡単に文字認識をすることができます。
コード上に登場している具体的な実装について一部ご紹介します。
- VNRecognizeTextRequest
- 画像中のテキストを発見し、認識する画像解析リクエスト
- VNRecognizedTextObservation
- VNRecognizeTextRequestで取得される結果
- 画像から認識したテキストの内容や位置情報
- VNRecognizedText
- 1つ1つの認識したテキストオブジェクト
- 文字列、信頼度スコア、文字列の領域情報が含まれる
- VNImageRequestHandler
- 画像に対しての解析リクエストを処理するためのオブジェクト
これらのクラスを利用することで画像から認識した文字を利用するクラスに返却します。
Vision.framework使用時の工夫や注意点
実装時の細かい工夫点は以下の通りです。
- 認識の処理に時間がかかるためMainThreadで実行されないようにすること
- 次のリクエストを処理する際に前回のリクエストをキャンセルできるよう、リクエストをクラス内で保持しておくこと
また、サンプルには記載していませんが、リクエストの設定として処理速度と精度どちらを優先させるかや言語補正を行うか等の指定ができます。このような設定を使うことで、より実際に認識したい内容に合わせた精度の向上が可能です。
今回はこの文字認識技術を利用して、クレジットカード番号と有効期限の認識を独自実装し、SwiftPM移行を実現できました。
ちなみに、先日のWWDCでテキストの認識を簡単に実現できるDataScannerViewControllerが発表されていました。今回はVision.frameworkを利用しましたが、今後はこのような新しい技術を用いることも考えられそうです。
おわりに
この記事ではPayPayフリマのSwiftPM移行にあたり困っていた技術課題をVision.frameworkを利用して解決したことについてご紹介しました。
特にボトルネックとなってしまったライブラリに関して、独自実装することで脱却しSwiftPM移行を進めた部分について詳細にご紹介しましたが、このように一見業務に直結しない内容だっとしても新しい技術を広くキャッチアップしておくことで、困った時の課題解決の引き出しが増えるはずです。今回の記事も皆さんの開発課題解決のヒントになれば幸いです。よろしければPayPayフリマの他のiOS関連記事もご覧ください。
- iOSのBuild In Sound Analysisを使った音声認識で著作権侵害を防ぐ!
- iOSのCompositionalLayoutによるカスタムレイアウト実現と高速化 〜 PayPayフリマのグリッドレイアウトUI
また、PayPayフリマでは一緒にサービスを作ってくれるエンジニアを絶賛募集中です。開発課題を解決しながらより良いプロダクトづくりを一緒に考えていきませんか?
参考文献(外部サイト)
こちらの記事のご感想を聞かせください。
- 学びがある
- わかりやすい
- 新しい視点
ご感想ありがとうございました