mirror of
https://github.com/selfxyz/self.git
synced 2026-01-13 00:28:17 -05:00
119 lines
3.2 KiB
Swift
119 lines
3.2 KiB
Swift
// SPDX-License-Identifier: BUSL-1.1; Copyright (c) 2025 Social Connect Labs, Inc.; Licensed under BUSL-1.1 (see LICENSE); Apache-2.0 from 2029-06-11
|
|
|
|
//
|
|
// QRCodeScannerViewManager.swift
|
|
// OpenPassport
|
|
//
|
|
// Created by Rémi Colin on 07/02/2025.
|
|
//
|
|
|
|
import AVFoundation
|
|
import Foundation
|
|
import React
|
|
|
|
@objc(QRCodeScannerViewManager)
|
|
class QRCodeScannerViewManager: RCTViewManager {
|
|
override static func requiresMainQueueSetup() -> Bool {
|
|
return true
|
|
}
|
|
|
|
override func view() -> UIView! {
|
|
return QRCodeScannerView()
|
|
}
|
|
}
|
|
|
|
class QRCodeScannerView: UIView, AVCaptureMetadataOutputObjectsDelegate {
|
|
var captureSession: AVCaptureSession?
|
|
var previewLayer: AVCaptureVideoPreviewLayer?
|
|
|
|
// This property will hold the callback from JS
|
|
@objc var onQRData: RCTDirectEventBlock?
|
|
|
|
override init(frame: CGRect) {
|
|
super.init(frame: frame)
|
|
initializeScanner()
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
super.init(coder: coder)
|
|
initializeScanner()
|
|
}
|
|
|
|
deinit {
|
|
captureSession?.stopRunning()
|
|
previewLayer?.removeFromSuperlayer()
|
|
captureSession = nil
|
|
previewLayer = nil
|
|
}
|
|
|
|
private func cleanUp() {
|
|
DispatchQueue.main.async { [weak self] in
|
|
guard let self = self else { return }
|
|
self.captureSession?.stopRunning()
|
|
self.previewLayer?.removeFromSuperlayer()
|
|
self.captureSession = nil
|
|
self.previewLayer = nil
|
|
}
|
|
}
|
|
|
|
override func willMove(toSuperview newSuperview: UIView?) {
|
|
if newSuperview == nil {
|
|
cleanUp()
|
|
}
|
|
|
|
super.willMove(toSuperview: newSuperview)
|
|
}
|
|
|
|
func initializeScanner() {
|
|
captureSession = AVCaptureSession()
|
|
guard let videoCaptureDevice = AVCaptureDevice.default(for: .video),
|
|
let videoInput = try? AVCaptureDeviceInput(device: videoCaptureDevice),
|
|
captureSession!.canAddInput(videoInput)
|
|
else {
|
|
return
|
|
}
|
|
captureSession!.addInput(videoInput)
|
|
|
|
let metadataOutput = AVCaptureMetadataOutput()
|
|
if captureSession!.canAddOutput(metadataOutput) {
|
|
captureSession!.addOutput(metadataOutput)
|
|
metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
|
|
metadataOutput.metadataObjectTypes = [.qr]
|
|
} else {
|
|
return
|
|
}
|
|
|
|
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession!)
|
|
previewLayer?.videoGravity = .resizeAspectFill
|
|
previewLayer?.frame = self.layer.bounds
|
|
if let previewLayer = previewLayer {
|
|
self.layer.addSublayer(previewLayer)
|
|
}
|
|
|
|
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
|
|
self?.captureSession?.startRunning()
|
|
}
|
|
}
|
|
|
|
func metadataOutput(
|
|
_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject],
|
|
from connection: AVCaptureConnection
|
|
) {
|
|
if let metadataObject = metadataObjects.first,
|
|
let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject,
|
|
let stringValue = readableObject.stringValue
|
|
{
|
|
DispatchQueue.main.async { [weak self] in
|
|
// Send the scanned QR code data to JS
|
|
self?.onQRData?(["data": stringValue])
|
|
self?.captureSession?.stopRunning()
|
|
}
|
|
}
|
|
}
|
|
|
|
override func layoutSubviews() {
|
|
super.layoutSubviews()
|
|
previewLayer?.frame = self.bounds
|
|
}
|
|
}
|