Add iOS linter & report issues on PR (#634)

* Add iOS linter & report issues on PR

* Fix lint errors

---------

Co-authored-by: Seph Soliman <github@seph.dk>
This commit is contained in:
David Bertet 2024-04-30 16:10:07 -07:00 committed by GitHub
parent 00c461a144
commit a13aa787c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 225 additions and 87 deletions

View File

@ -1,8 +1,13 @@
name: Build
run-name: ⚙️ Build examples on ${{ github.event_name == 'pull_request' && 'PR' || '🌱' }} ${{ github.event_name == 'pull_request' && github.event.number || github.ref_name }}
on:
pull_request:
types: [opened, reopened]
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened]
jobs:
build-example-ios:
name: build-example-ios

View File

@ -1,8 +1,13 @@
name: Linter
run-name: 🩺 Code checks on ${{ github.event_name == 'pull_request' && 'PR' || '🌱' }} ${{ github.event_name == 'pull_request' && github.event.number || github.ref_name }}
on:
pull_request:
types: [opened, reopened]
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened]
jobs:
eslint:
name: ESLint
@ -16,3 +21,29 @@ jobs:
run: yarn bootstrap-linux
- name: Lint
run: yarn lint
ios:
name: Lint iOS
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install dependencies
run: brew bundle
- name: Run swiftlint
run: |
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
updatedFiles=$(git --no-pager diff --name-only --diff-filter=ACMRT ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} -- '*.swift')
if [[ -z "$updatedFiles" ]]; then
echo "No Swift files changed, skipping linting"
exit 0
fi
swiftlint --reporter github-actions-logging -- $updatedFiles
else
swiftlint
fi

21
.swiftlint.yml Normal file
View File

@ -0,0 +1,21 @@
included:
- ios
- example/ios
excluded:
- ios/Pods
- example/ios/Pods
disabled_rules:
- todo
line_length: 160
type_body_length: 400
function_body_length: 50
identifier_name:
excluded:
- on
- x
- y
- at

2
Brewfile Normal file
View File

@ -0,0 +1,2 @@
brew 'swiftlint'

62
Brewfile.lock.json Normal file
View File

@ -0,0 +1,62 @@
{
"entries": {
"brew": {
"swiftlint": {
"version": "0.53.0",
"bottle": {
"rebuild": 0,
"root_url": "https://ghcr.io/v2/homebrew/core",
"files": {
"arm64_sonoma": {
"cellar": ":any_skip_relocation",
"url": "https://ghcr.io/v2/homebrew/core/swiftlint/blobs/sha256:240ccda9de55d948d0c635798079074099bfcb73ffda41428900fdc748aeea7b",
"sha256": "240ccda9de55d948d0c635798079074099bfcb73ffda41428900fdc748aeea7b"
},
"arm64_ventura": {
"cellar": ":any_skip_relocation",
"url": "https://ghcr.io/v2/homebrew/core/swiftlint/blobs/sha256:7b7ceb7896c6833965cc4eac9001255d8adde6c5432045d5a8ab6aea8a9e81d9",
"sha256": "7b7ceb7896c6833965cc4eac9001255d8adde6c5432045d5a8ab6aea8a9e81d9"
},
"arm64_monterey": {
"cellar": ":any_skip_relocation",
"url": "https://ghcr.io/v2/homebrew/core/swiftlint/blobs/sha256:78c2a4c3f4a2f6847b484527b0f0f916da71e3ee29e49890fd44b63fe7b38e26",
"sha256": "78c2a4c3f4a2f6847b484527b0f0f916da71e3ee29e49890fd44b63fe7b38e26"
},
"sonoma": {
"cellar": ":any_skip_relocation",
"url": "https://ghcr.io/v2/homebrew/core/swiftlint/blobs/sha256:abdca78dd8a8bd268053b3be195fe891bb74aef5502ab3a6b871ae0c6bb04540",
"sha256": "abdca78dd8a8bd268053b3be195fe891bb74aef5502ab3a6b871ae0c6bb04540"
},
"ventura": {
"cellar": ":any_skip_relocation",
"url": "https://ghcr.io/v2/homebrew/core/swiftlint/blobs/sha256:be711c707bf3b49fa0dd6e2ae576b309aad620f9b56a2c6e7b1ac5cf35cf652a",
"sha256": "be711c707bf3b49fa0dd6e2ae576b309aad620f9b56a2c6e7b1ac5cf35cf652a"
},
"monterey": {
"cellar": ":any_skip_relocation",
"url": "https://ghcr.io/v2/homebrew/core/swiftlint/blobs/sha256:13487d68a971dbe035019364e19d70641af2a18c06e52925d238685b384a7979",
"sha256": "13487d68a971dbe035019364e19d70641af2a18c06e52925d238685b384a7979"
},
"x86_64_linux": {
"cellar": "/home/linuxbrew/.linuxbrew/Cellar",
"url": "https://ghcr.io/v2/homebrew/core/swiftlint/blobs/sha256:fbbc56fccfcfcd34564feb7325567e2ff3638d3c609396a5c4aa13311c7b26e0",
"sha256": "fbbc56fccfcfcd34564feb7325567e2ff3638d3c609396a5c4aa13311c7b26e0"
}
}
}
}
}
},
"system": {
"macos": {
"sonoma": {
"HOMEBREW_VERSION": "4.2.0-54-g0b804d4",
"HOMEBREW_PREFIX": "/opt/homebrew",
"Homebrew/homebrew-core": "api",
"CLT": "15.1.0.0.1.1700200546",
"Xcode": "15.0",
"macOS": "14.1"
}
}
}
}

View File

@ -25,12 +25,11 @@ import Foundation
resolve: @escaping RCTPromiseResolveBlock,
reject: @escaping RCTPromiseRejectBlock) {
guard let cam = self.camera else {
reject("capture_error", "CKCamera capture() was called but camera view is nil", nil)
reject("capture_error", "CKCamera capture() was called but camera view is nil", nil)
return
}
cam.capture(options as! [String: Any],
onSuccess: { resolve($0) },
onError: { reject("capture_error", $0, nil) })
cam.capture(onSuccess: { resolve($0) },
onError: { reject("capture_error", $0, nil) })
}
@objc func checkDeviceCameraAuthorizationStatus(_ resolve: @escaping RCTPromiseResolveBlock,

View File

@ -28,6 +28,6 @@ protocol CameraProtocol: AnyObject, FocusInterfaceViewDelegate {
func update(scannerFrameSize: CGRect?)
func capturePicture(onWillCapture: @escaping () -> Void,
onSuccess: @escaping (_ imageData: Data, _ thumbnailData: Data?, _ dimensions: CMVideoDimensions) -> (),
onError: @escaping (_ message: String) -> ())
onSuccess: @escaping (_ imageData: Data, _ thumbnailData: Data?, _ dimensions: CMVideoDimensions) -> Void,
onError: @escaping (_ message: String) -> Void)
}

View File

@ -71,7 +71,7 @@ class CameraView: UIView {
}
private func setupCamera() {
if (hasPropBeenSetup && hasPermissionBeenGranted && !hasCameraBeenSetup) {
if hasPropBeenSetup && hasPermissionBeenGranted && !hasCameraBeenSetup {
hasCameraBeenSetup = true
camera.setup(cameraType: cameraType, supportedBarcodeType: scanBarcode && onReadCode != nil ? supportedBarcodeType : [])
}
@ -141,6 +141,7 @@ class CameraView: UIView {
}
// Called once when all props have been set, then every time one is updated
// swiftlint:disable:next cyclomatic_complexity function_body_length
override func didSetProps(_ changedProps: [String]) {
hasPropBeenSetup = true
@ -154,11 +155,11 @@ class CameraView: UIView {
if changedProps.contains("cameraType") || changedProps.contains("torchMode") {
camera.update(torchMode: torchMode)
}
if changedProps.contains("onOrientationChange") {
camera.update(onOrientationChange: onOrientationChange)
}
if changedProps.contains("onZoom") {
camera.update(onZoom: onZoom)
}
@ -219,11 +220,11 @@ class CameraView: UIView {
if changedProps.contains("zoomMode") {
self.update(zoomMode: zoomMode)
}
if changedProps.contains("zoom") {
camera.update(zoom: zoom?.doubleValue)
}
if changedProps.contains("maxZoom") {
camera.update(maxZoom: maxZoom?.doubleValue)
}
@ -231,9 +232,8 @@ class CameraView: UIView {
// MARK: Public
func capture(_ options: [String: Any],
onSuccess: @escaping (_ imageObject: [String: Any]) -> (),
onError: @escaping (_ error: String) -> ()) {
func capture(onSuccess: @escaping (_ imageObject: [String: Any]) -> Void,
onError: @escaping (_ error: String) -> Void) {
camera.capturePicture(onWillCapture: { [weak self] in
// Flash/dim preview to indicate shutter action
DispatchQueue.main.async {
@ -254,12 +254,12 @@ class CameraView: UIView {
}
}, onError: onError)
}
// MARK: - Private Helper
private func update(zoomMode: ZoomMode) {
if zoomMode == .on {
if (zoomGestureRecognizer == nil) {
if zoomGestureRecognizer == nil {
let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(handlePinchToZoomRecognizer(_:)))
addGestureRecognizer(pinchGesture)
zoomGestureRecognizer = pinchGesture
@ -271,13 +271,12 @@ class CameraView: UIView {
}
}
}
private func handleCameraPermission() {
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .authorized:
// The user has previously granted access to the camera.
hasPermissionBeenGranted = true
break
case .notDetermined:
// The user has not yet been presented with the option to grant video access.
AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in
@ -294,11 +293,11 @@ class CameraView: UIView {
private func writeCaptured(imageData: Data,
thumbnailData: Data?,
dimensions: CMVideoDimensions,
onSuccess: @escaping (_ imageObject: [String: Any]) -> (),
onError: @escaping (_ error: String) -> ()) {
onSuccess: @escaping (_ imageObject: [String: Any]) -> Void,
onError: @escaping (_ error: String) -> Void) {
do {
let temporaryImageFileURL = try saveToTmpFolder(imageData)
onSuccess([
"size": imageData.count,
"uri": temporaryImageFileURL.description,

View File

@ -82,7 +82,7 @@ class FocusInterfaceView: UIView {
func update(focusMode: FocusMode) {
if focusMode == .on {
if (focusGestureRecognizer == nil) {
if focusGestureRecognizer == nil {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(focusAndExposeTap(_:)))
addGestureRecognizer(tapGesture)
focusGestureRecognizer = tapGesture
@ -157,17 +157,17 @@ class FocusInterfaceView: UIView {
UIView.animate(withDuration: 0.2, animations: {
self.focusView.frame = focusViewFrame
self.focusView.alpha = 1
}) { _ in
}, completion: { _ in
self.hideFocusViewTimer?.invalidate()
self.hideFocusViewTimer = Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { [weak self] _ in
self.hideFocusViewTimer = Timer.scheduledTimer(withTimeInterval: 2, repeats: false, block: { [weak self] _ in
guard let self else { return }
UIView.animate(withDuration: 0.2, animations: {
self.focusView.alpha = 0
}) { _ in
}, completion: { _ in
self.focusView.isHidden = true
}
}
}
})
})
})
}
}

View File

@ -43,7 +43,7 @@ class PhotoCaptureDelegate: NSObject, AVCapturePhotoCaptureDelegate {
return
}
var thumbnailData: Data? = nil
var thumbnailData: Data?
if let previewPixelBuffer = photo.previewPixelBuffer {
let ciImage = CIImage(cvPixelBuffer: previewPixelBuffer)
let uiImage = UIImage(ciImage: ciImage)

View File

@ -88,6 +88,7 @@ class RatioOverlayView: UIView {
// MARK: - Private
// swiftlint:disable:next function_body_length
private func setOverlayParts() {
guard let ratioData, ratioData.ratio != 0 else {
isHidden = true

View File

@ -3,6 +3,8 @@
// ReactNativeCameraKit
//
// swiftlint:disable file_length
import AVFoundation
import UIKit
import CoreMotion
@ -10,6 +12,7 @@ import CoreMotion
/*
* Real camera implementation that uses AVFoundation
*/
// swiftlint:disable:next type_body_length
class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelegate {
var previewView: UIView { cameraPreview }
@ -32,13 +35,13 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
private var resetFocus: (() -> Void)?
private var focusFinished: (() -> Void)?
private var onBarcodeRead: ((_ barcode: String) -> Void)?
private var scannerFrameSize: CGRect? = nil
private var scannerFrameSize: CGRect?
private var onOrientationChange: RCTDirectEventBlock?
private var onZoomCallback: RCTDirectEventBlock?
private var lastOnZoom: Double?
private var zoom: Double?
private var maxZoom: Double?
private var deviceOrientation = UIDeviceOrientation.unknown
private var motionManager: CMMotionManager?
@ -52,10 +55,11 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
override init() {
super.init()
// In addition to using accelerometer to determine REAL orientation
// we also listen to UI orientation changes (UIDevice does not report rotation if orientation lock is on, so photos aren't rotated correctly)
// When UIDevice reports rotation to the left, UI is rotated right to compensate, but that means we need to re-rotate left to make camera appear correctly (see self.uiOrientationChanged)
// When UIDevice reports rotation to the left, UI is rotated right to compensate, but that means we need to re-rotate left
// to make camera appear correctly (see self.uiOrientationChanged)
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification,
object: UIDevice.current,
@ -75,11 +79,11 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
self.removeObservers()
}
}
motionManager?.stopAccelerometerUpdates()
NotificationCenter.default.removeObserver(self, name: UIDevice.orientationDidChangeNotification, object: UIDevice.current)
UIDevice.current.endGeneratingDeviceOrientationNotifications()
}
@ -94,7 +98,7 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
self.cameraPreview.session = self.session
self.cameraPreview.previewLayer.videoGravity = .resizeAspect
}
self.initializeMotionManager()
// Setup the capture session.
@ -113,13 +117,13 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
// We need to reapply the configuration after starting the camera
self.update(torchMode: self.torchMode)
}
DispatchQueue.main.async {
self.setVideoOrientationToInterfaceOrientation()
}
}
}
private var zoomStartedAt: Double = 1.0
func zoomPinchStart() {
sessionQueue.async {
@ -130,13 +134,13 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
func zoomPinchChange(pinchScale: CGFloat) {
guard !pinchScale.isNaN else { return }
sessionQueue.async {
guard let videoDevice = self.videoDeviceInput?.device else { return }
let desiredZoomFactor = (self.zoomStartedAt / self.defaultZoomFactor(for: videoDevice)) * pinchScale
let zoomForDevice = self.getValidZoom(forDevice: videoDevice, zoom: desiredZoomFactor)
if zoomForDevice != self.normalizedZoom(for: videoDevice) {
// Only trigger zoom changes if it's an uncontrolled component (zoom isn't manually set)
// otherwise it's likely to cause issues inf. loops
@ -147,25 +151,25 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
}
}
}
func update(maxZoom: Double?) {
self.maxZoom = maxZoom
// Re-update zoom value in case the max was increased
self.update(zoom: self.zoom)
}
func update(zoom: Double?) {
sessionQueue.async {
self.zoom = zoom
guard let videoDevice = self.videoDeviceInput?.device else { return }
guard let zoom_ = zoom else { return }
guard let zoom else { return }
let zoomForDevice = self.getValidZoom(forDevice: videoDevice, zoom: zoom_)
let zoomForDevice = self.getValidZoom(forDevice: videoDevice, zoom: zoom)
self.setZoomFor(videoDevice, to: zoomForDevice)
}
}
/**
`desiredZoom` can be nil when we want to notify what the zoom factor really is
*/
@ -174,7 +178,7 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
let cameraZoom = normalizedZoom(for: videoDevice)
let desiredOrCameraZoom = desiredZoom ?? cameraZoom
guard desiredOrCameraZoom > -1.0 else { return }
// ignore duplicate events when zooming to min/max
// but always notify if a desiredZoom wasn't given,
// since that means they wanted to reset setZoom(0.0)
@ -182,11 +186,11 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
if desiredZoom != nil && desiredOrCameraZoom == lastOnZoom {
return
}
lastOnZoom = desiredOrCameraZoom
self.onZoomCallback?(["zoom": desiredOrCameraZoom])
}
func update(onZoom: RCTDirectEventBlock?) {
self.onZoomCallback = onZoom
}
@ -228,11 +232,11 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
}
}
}
func update(onOrientationChange: RCTDirectEventBlock?) {
self.onOrientationChange = onOrientationChange
}
func update(torchMode: TorchMode) {
sessionQueue.async {
self.torchMode = torchMode
@ -300,7 +304,8 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
the main thread and session configuration is done on the session queue.
*/
DispatchQueue.main.async {
let videoPreviewLayerOrientation = self.videoOrientation(from: self.deviceOrientation) ?? self.cameraPreview.previewLayer.connection?.videoOrientation
let videoPreviewLayerOrientation =
self.videoOrientation(from: self.deviceOrientation) ?? self.cameraPreview.previewLayer.connection?.videoOrientation
self.sessionQueue.async {
if let photoOutputConnection = self.photoOutput.connection(with: .video), let videoPreviewLayerOrientation {
@ -365,10 +370,13 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
}
DispatchQueue.main.async {
let visibleRect = scannerFrameSize != nil && scannerFrameSize != .zero ? self.cameraPreview.previewLayer.metadataOutputRectConverted(fromLayerRect: scannerFrameSize!) : nil
var visibleRect: CGRect?
if scannerFrameSize != nil && scannerFrameSize != .zero {
visibleRect = self.cameraPreview.previewLayer.metadataOutputRectConverted(fromLayerRect: scannerFrameSize!)
}
self.sessionQueue.async {
if (self.metadataOutput.rectOfInterest == visibleRect) {
if self.metadataOutput.rectOfInterest == visibleRect {
return
}
@ -457,14 +465,13 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
if session.canAddInput(videoDeviceInput) {
session.addInput(videoDeviceInput)
self.videoDeviceInput = videoDeviceInput
self.resetZoom(forDevice: videoDevice)
} else {
return .sessionConfigurationFailed
}
if session.canAddOutput(photoOutput) {
session.addOutput(photoOutput)
@ -494,11 +501,11 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
private func defaultZoomFactor(for videoDevice: AVCaptureDevice) -> CGFloat {
let fallback = 1.0
guard #available(iOS 13.0, *) else { return fallback }
// Devices that have multiple physical cameras are hidden behind one virtual camera input
// The zoom factor defines what physical camera it actually uses
// The default lens on the native camera app is the wide angle
if var wideAngleIndex = videoDevice.constituentDevices.firstIndex(where: { $0.deviceType == .builtInWideAngleCamera }) {
if let wideAngleIndex = videoDevice.constituentDevices.firstIndex(where: { $0.deviceType == .builtInWideAngleCamera }) {
// .virtualDeviceSwitchOverVideoZoomFactors has the .constituentDevices zoom factor which borders the NEXT device
// so we grab the one PRIOR to the wide angle to get the wide angle's zoom factor
guard wideAngleIndex >= 1 else { return fallback }
@ -507,7 +514,7 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
return fallback
}
private func setZoomFor(_ videoDevice: AVCaptureDevice, to zoom: Double) {
do {
try videoDevice.lockForConfiguration()
@ -523,7 +530,7 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
let defaultZoom = defaultZoomFactor(for: videoDevice)
return videoDevice.videoZoomFactor / defaultZoom
}
private func getValidZoom(forDevice videoDevice: AVCaptureDevice, zoom: Double) -> Double {
let defaultZoom = defaultZoomFactor(for: videoDevice)
let minZoomFactor = videoDevice.minAvailableVideoZoomFactor / defaultZoom
@ -534,7 +541,7 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
let cappedZoom = max(minZoomFactor, min(zoom, maxZoomFactor))
return cappedZoom
}
private func resetZoom(forDevice videoDevice: AVCaptureDevice) {
var zoomForDevice = getValidZoom(forDevice: videoDevice, zoom: 1)
if let zoomPropValue = self.zoom {
@ -594,7 +601,7 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
adjustingFocusObservation = videoDeviceInput?.device.observe(\.isAdjustingFocus,
options: .new,
changeHandler: { [weak self] device, change in
changeHandler: { [weak self] _, change in
guard let self, let isFocusing = change.newValue else { return }
self.isAdjustingFocus(isFocusing: isFocusing)
@ -616,6 +623,7 @@ class RealCamera: NSObject, CameraProtocol, AVCaptureMetadataOutputObjectsDelega
}
private func removeObservers() {
// swiftlint:disable:next notification_center_detachment
NotificationCenter.default.removeObserver(self)
adjustingFocusObservation?.invalidate()

View File

@ -14,6 +14,7 @@ class RealPreviewView: UIView {
// Create an accessor for the right layer type
var previewLayer: AVCaptureVideoPreviewLayer {
// We can safely forcecast here, it can't change at runtime
// swiftlint:disable:next force_cast
return layer as! AVCaptureVideoPreviewLayer
}

View File

@ -48,7 +48,6 @@ class ScannerFrameView: UIView {
laserView.frame = CGRect(x: 2, y: 2, width: frame.size.width - 4, height: 2)
}
UIView.animate(withDuration: 3, delay: 0, options: [.autoreverse, .repeat], animations: {
self.laserView.center = CGPoint(x: self.frame.size.width / 2, y: self.frame.size.height - 3)
})

View File

@ -80,6 +80,10 @@ class ScannerInterfaceView: UIView {
topOverlayView.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: frameRect.origin.y)
leftOverlayView.frame = CGRect(x: 0, y: frameRect.origin.y, width: frameOffset, height: frameHeight)
rightOverlayView.frame = CGRect(x: frameRect.size.width + frameOffset, y: frameRect.origin.y, width: frameOffset, height: frameHeight)
bottomOverlayView.frame = CGRect(x: 0, y: frameRect.origin.y + frameHeight, width: frame.size.width, height: frame.size.height - frameRect.origin.y - frameHeight)
bottomOverlayView.frame = CGRect(
x: 0,
y: frameRect.origin.y + frameHeight,
width: frame.size.width,
height: frame.size.height - frameRect.origin.y - frameHeight)
}
}

View File

@ -31,7 +31,7 @@ class SimulatorCamera: CameraProtocol {
DispatchQueue.main.async {
self.mockPreview.cameraTypeLabel.text = "Camera type: \(cameraType)"
}
// Listen to orientation changes
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification,
@ -39,9 +39,8 @@ class SimulatorCamera: CameraProtocol {
queue: nil,
using: { [weak self] notification in self?.orientationChanged(notification: notification) })
}
private func orientationChanged(notification: Notification) {
guard let device = notification.object as? UIDevice,
let orientation = Orientation(from: device.orientation) else {
@ -50,7 +49,7 @@ class SimulatorCamera: CameraProtocol {
self.onOrientationChange?(["orientation": orientation.rawValue])
}
func cameraRemovedFromSuperview() {
NotificationCenter.default.removeObserver(self, name: UIDevice.orientationDidChangeNotification, object: UIDevice.current)
@ -59,16 +58,16 @@ class SimulatorCamera: CameraProtocol {
func update(onOrientationChange: RCTDirectEventBlock?) {
self.onOrientationChange = onOrientationChange
}
func update(onZoom: RCTDirectEventBlock?) {
self.onZoom = onZoom
}
func setVideoDevice(zoomFactor: Double) {
self.videoDeviceZoomFactor = zoomFactor
self.mockPreview.zoomLabel.text = "Zoom: \(zoomFactor)"
}
private var zoomStartedAt: Double = 1.0
func zoomPinchStart() {
DispatchQueue.main.async {
@ -76,10 +75,10 @@ class SimulatorCamera: CameraProtocol {
self.mockPreview.zoomLabel.text = "Zoom start"
}
}
func zoomPinchChange(pinchScale: CGFloat) {
guard !pinchScale.isNaN else { return }
DispatchQueue.main.async {
let desiredZoomFactor = self.zoomStartedAt * pinchScale
var maxZoomFactor = self.videoDeviceMaxAvailableVideoZoomFactor
@ -87,7 +86,7 @@ class SimulatorCamera: CameraProtocol {
maxZoomFactor = min(maxZoom, maxZoomFactor)
}
let zoomForDevice = max(1.0, min(desiredZoomFactor, maxZoomFactor))
if zoomForDevice != self.videoDeviceZoomFactor {
// Only trigger zoom changes if it's an uncontrolled component (zoom isn't manually set)
// otherwise it's likely to cause issues inf. loops
@ -132,14 +131,14 @@ class SimulatorCamera: CameraProtocol {
self.mockPreview.randomize()
}
}
func update(maxZoom: Double?) {
self.maxZoom = maxZoom
}
func update(zoom: Double?) {
self.zoom = zoom
DispatchQueue.main.async {
var zoomOrDefault = zoom ?? 0
// -1 will reset to zoom default (which is not 1 on modern cameras)
@ -153,7 +152,7 @@ class SimulatorCamera: CameraProtocol {
}
let zoomForDevice = max(1.0, min(zoomOrDefault, maxZoomFactor))
self.setVideoDevice(zoomFactor: zoomForDevice)
// If they wanted to reset, tell them what the default zoom turned out to be
// regardless if it's controlled
if self.zoom == nil || zoom == 0 {
@ -161,7 +160,6 @@ class SimulatorCamera: CameraProtocol {
}
}
}
func isBarcodeScannerEnabled(_ isEnabled: Bool,
supportedBarcodeType: [AVMetadataObject.ObjectType],
@ -169,8 +167,8 @@ class SimulatorCamera: CameraProtocol {
func update(scannerFrameSize: CGRect?) {}
func capturePicture(onWillCapture: @escaping () -> Void,
onSuccess: @escaping (_ imageData: Data, _ thumbnailData: Data?, _ dimensions: CMVideoDimensions) -> (),
onError: @escaping (_ message: String) -> ()) {
onSuccess: @escaping (_ imageData: Data, _ thumbnailData: Data?, _ dimensions: CMVideoDimensions) -> Void,
onError: @escaping (_ message: String) -> Void) {
onWillCapture()
DispatchQueue.main.async {

View File

@ -15,6 +15,7 @@
"clean": "rm -rf dist/",
"test": "jest",
"lint": "yarn eslint -c .eslintrc.js",
"check-ios": "scripts/check-ios.sh",
"release": "yarn clean && yarn build && yarn publish --verbose",
"release:beta": "yarn clean && yarn build && yarn publish --tag beta --verbose",
"release:local": "yarn clean && yarn build && tmp=$(mktemp) && yarn pack --filename $tmp.tar.gz && open -R $tmp.tar.gz",

7
scripts/check-ios.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/sh
if which swiftlint >/dev/null; then
swiftlint lint --quiet --autocorrect && swiftlint lint --quiet
else
echo "warning: SwiftLint not installed, use `brew bundle` to install it"
fi