Run SwiftFormat in GH Action (#28)
This commit is contained in:
parent
168ba054d1
commit
64bb6f6a2a
35
.github/workflows/validate-ios.yml
vendored
35
.github/workflows/validate-ios.yml
vendored
@ -13,33 +13,30 @@ on:
|
|||||||
- 'ios/**'
|
- 'ios/**'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
SwiftLint:
|
||||||
name: SwiftLint
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Run SwiftLint GitHub Action (--strict)
|
- name: Run SwiftLint GitHub Action (--strict)
|
||||||
uses: norio-nomura/action-swiftlint@3.2.1
|
uses: norio-nomura/action-swiftlint@master
|
||||||
with:
|
with:
|
||||||
args: --strict
|
args: --strict
|
||||||
env:
|
env:
|
||||||
# DIFF_BASE: ${{ github.base_ref }}
|
# DIFF_BASE: ${{ github.base_ref }}
|
||||||
WORKING_DIRECTORY: ios
|
WORKING_DIRECTORY: ios
|
||||||
# TODO: Figure out how to run SwiftFormat in a GitHub action
|
SwiftFormat:
|
||||||
# SwiftFormat:
|
runs-on: macOS-latest
|
||||||
# name: SwiftFormat
|
defaults:
|
||||||
# description: 'https://github.com/nicklockwood/SwiftFormat'
|
run:
|
||||||
# runs-on: ubuntu-latest
|
working-directory: ./ios
|
||||||
# defaults:
|
steps:
|
||||||
# run:
|
- uses: actions/checkout@v2
|
||||||
# working-directory: ./ios
|
|
||||||
# steps:
|
|
||||||
# - uses: actions/checkout@v2
|
|
||||||
|
|
||||||
# - name: Format Swift code
|
- name: Install SwiftFormat
|
||||||
# run: swiftformat --verbose .
|
run: brew install swiftformat
|
||||||
# working-directory: ${{env.working-directory}}
|
|
||||||
|
|
||||||
# - name: Verify formatted code is unchanged
|
- name: Format Swift code
|
||||||
# run: git diff --exit-code HEAD
|
run: swiftformat --verbose .
|
||||||
# working-directory: ${{env.working-directory}}
|
|
||||||
|
- name: Verify formatted code is unchanged
|
||||||
|
run: git diff --exit-code HEAD
|
||||||
|
1
ios/.swift-version
Normal file
1
ios/.swift-version
Normal file
@ -0,0 +1 @@
|
|||||||
|
5.2
|
@ -1,3 +1,14 @@
|
|||||||
--allman false
|
--allman false
|
||||||
--indent 2
|
--indent 2
|
||||||
--exclude Pods,Generated
|
--exclude Pods,Generated
|
||||||
|
|
||||||
|
--disable andOperator
|
||||||
|
--disable redundantReturn
|
||||||
|
--disable wrapMultilineStatementBraces
|
||||||
|
|
||||||
|
--enable organizeDeclarations
|
||||||
|
--lifecycle didSetProps,requiresMainQueueSetup,view,methodQueue,getCameraView
|
||||||
|
|
||||||
|
--enable markTypes
|
||||||
|
|
||||||
|
--enable isEmpty
|
||||||
|
@ -18,7 +18,6 @@ opt_in_rules:
|
|||||||
- last_where
|
- last_where
|
||||||
- reduce_boolean
|
- reduce_boolean
|
||||||
- reduce_into
|
- reduce_into
|
||||||
- sorted_first_last
|
|
||||||
- yoda_condition
|
- yoda_condition
|
||||||
- vertical_whitespace_opening_braces
|
- vertical_whitespace_opening_braces
|
||||||
- vertical_whitespace_closing_braces
|
- vertical_whitespace_closing_braces
|
||||||
|
@ -8,10 +8,14 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
// MARK: - PermissionError
|
||||||
|
|
||||||
enum PermissionError: String {
|
enum PermissionError: String {
|
||||||
case microphone = "microphone-permission-denied"
|
case microphone = "microphone-permission-denied"
|
||||||
case camera = "camera-permission-denied"
|
case camera = "camera-permission-denied"
|
||||||
|
|
||||||
|
// MARK: Internal
|
||||||
|
|
||||||
var code: String {
|
var code: String {
|
||||||
return rawValue
|
return rawValue
|
||||||
}
|
}
|
||||||
@ -26,6 +30,8 @@ enum PermissionError: String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - ParameterError
|
||||||
|
|
||||||
enum ParameterError {
|
enum ParameterError {
|
||||||
case invalid(unionName: String, receivedValue: String)
|
case invalid(unionName: String, receivedValue: String)
|
||||||
case unsupportedOS(unionName: String, receivedValue: String, supportedOnOs: String)
|
case unsupportedOS(unionName: String, receivedValue: String, supportedOnOs: String)
|
||||||
@ -33,6 +39,8 @@ enum ParameterError {
|
|||||||
case unsupportedInput(inputDescriptor: String)
|
case unsupportedInput(inputDescriptor: String)
|
||||||
case invalidCombination(provided: String, missing: String)
|
case invalidCombination(provided: String, missing: String)
|
||||||
|
|
||||||
|
// MARK: Internal
|
||||||
|
|
||||||
var code: String {
|
var code: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .invalid:
|
case .invalid:
|
||||||
@ -64,6 +72,8 @@ enum ParameterError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - DeviceError
|
||||||
|
|
||||||
enum DeviceError: String {
|
enum DeviceError: String {
|
||||||
case configureError = "configuration-error"
|
case configureError = "configuration-error"
|
||||||
case noDevice = "no-device"
|
case noDevice = "no-device"
|
||||||
@ -74,6 +84,8 @@ enum DeviceError: String {
|
|||||||
case focusNotSupported = "focus-not-supported"
|
case focusNotSupported = "focus-not-supported"
|
||||||
case notAvailableOnSimulator = "camera-not-available-on-simulator"
|
case notAvailableOnSimulator = "camera-not-available-on-simulator"
|
||||||
|
|
||||||
|
// MARK: Internal
|
||||||
|
|
||||||
var code: String {
|
var code: String {
|
||||||
return rawValue
|
return rawValue
|
||||||
}
|
}
|
||||||
@ -100,12 +112,16 @@ enum DeviceError: String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - FormatError
|
||||||
|
|
||||||
enum FormatError {
|
enum FormatError {
|
||||||
case invalidFps(fps: Int)
|
case invalidFps(fps: Int)
|
||||||
case invalidHdr
|
case invalidHdr
|
||||||
case invalidFormat
|
case invalidFormat
|
||||||
case invalidPreset(preset: String)
|
case invalidPreset(preset: String)
|
||||||
|
|
||||||
|
// MARK: Internal
|
||||||
|
|
||||||
var code: String {
|
var code: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .invalidFormat:
|
case .invalidFormat:
|
||||||
@ -133,10 +149,14 @@ enum FormatError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - SessionError
|
||||||
|
|
||||||
enum SessionError {
|
enum SessionError {
|
||||||
case cameraNotReady
|
case cameraNotReady
|
||||||
case audioSessionSetupFailed(reason: String)
|
case audioSessionSetupFailed(reason: String)
|
||||||
|
|
||||||
|
// MARK: Internal
|
||||||
|
|
||||||
var code: String {
|
var code: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .cameraNotReady:
|
case .cameraNotReady:
|
||||||
@ -156,6 +176,8 @@ enum SessionError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - CaptureError
|
||||||
|
|
||||||
enum CaptureError {
|
enum CaptureError {
|
||||||
case invalidPhotoFormat
|
case invalidPhotoFormat
|
||||||
case recordingInProgress
|
case recordingInProgress
|
||||||
@ -165,6 +187,8 @@ enum CaptureError {
|
|||||||
case invalidPhotoCodec
|
case invalidPhotoCodec
|
||||||
case unknown(message: String? = nil)
|
case unknown(message: String? = nil)
|
||||||
|
|
||||||
|
// MARK: Internal
|
||||||
|
|
||||||
var code: String {
|
var code: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .invalidPhotoFormat:
|
case .invalidPhotoFormat:
|
||||||
@ -204,9 +228,13 @@ enum CaptureError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - SystemError
|
||||||
|
|
||||||
enum SystemError: String {
|
enum SystemError: String {
|
||||||
case noManager = "no-camera-manager"
|
case noManager = "no-camera-manager"
|
||||||
|
|
||||||
|
// MARK: Internal
|
||||||
|
|
||||||
var code: String {
|
var code: String {
|
||||||
return rawValue
|
return rawValue
|
||||||
}
|
}
|
||||||
@ -219,6 +247,8 @@ enum SystemError: String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - CameraError
|
||||||
|
|
||||||
enum CameraError: Error {
|
enum CameraError: Error {
|
||||||
case permission(_ id: PermissionError)
|
case permission(_ id: PermissionError)
|
||||||
case parameter(_ id: ParameterError)
|
case parameter(_ id: ParameterError)
|
||||||
@ -229,6 +259,8 @@ enum CameraError: Error {
|
|||||||
case system(_ id: SystemError)
|
case system(_ id: SystemError)
|
||||||
case unknown(message: String? = nil)
|
case unknown(message: String? = nil)
|
||||||
|
|
||||||
|
// MARK: Internal
|
||||||
|
|
||||||
var code: String {
|
var code: String {
|
||||||
switch self {
|
switch self {
|
||||||
case let .permission(id: id):
|
case let .permission(id: id):
|
||||||
|
@ -8,9 +8,10 @@
|
|||||||
|
|
||||||
import AVFoundation
|
import AVFoundation
|
||||||
|
|
||||||
|
// MARK: - TakePhotoOptions
|
||||||
|
|
||||||
struct TakePhotoOptions {
|
struct TakePhotoOptions {
|
||||||
var videoCodec: AVVideoCodecType?
|
// MARK: Lifecycle
|
||||||
var qualityPrioritization: String?
|
|
||||||
|
|
||||||
init(fromDictionary dictionary: NSDictionary) {
|
init(fromDictionary dictionary: NSDictionary) {
|
||||||
if let videoCodec = dictionary.value(forKey: "videoCodec") as? String {
|
if let videoCodec = dictionary.value(forKey: "videoCodec") as? String {
|
||||||
@ -18,6 +19,11 @@ struct TakePhotoOptions {
|
|||||||
}
|
}
|
||||||
qualityPrioritization = dictionary.value(forKey: "qualityPrioritization") as? String
|
qualityPrioritization = dictionary.value(forKey: "qualityPrioritization") as? String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Internal
|
||||||
|
|
||||||
|
var videoCodec: AVVideoCodecType?
|
||||||
|
var qualityPrioritization: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
extension CameraView {
|
extension CameraView {
|
||||||
|
@ -29,72 +29,10 @@ import UIKit
|
|||||||
private let propsThatRequireReconfiguration = ["cameraId", "enableDepthData", "enableHighResolutionCapture", "enablePortraitEffectsMatteDelivery", "preset", "onCodeScanned", "scannableCodes"]
|
private let propsThatRequireReconfiguration = ["cameraId", "enableDepthData", "enableHighResolutionCapture", "enablePortraitEffectsMatteDelivery", "preset", "onCodeScanned", "scannableCodes"]
|
||||||
private let propsThatRequireDeviceReconfiguration = ["fps", "hdr", "lowLightBoost", "colorSpace"]
|
private let propsThatRequireDeviceReconfiguration = ["fps", "hdr", "lowLightBoost", "colorSpace"]
|
||||||
|
|
||||||
|
// MARK: - CameraView
|
||||||
|
|
||||||
final class CameraView: UIView {
|
final class CameraView: UIView {
|
||||||
// pragma MARK: Exported Properties
|
// MARK: Lifecycle
|
||||||
// props that require reconfiguring
|
|
||||||
@objc var cameraId: NSString?
|
|
||||||
@objc var enableDepthData = false
|
|
||||||
@objc var enableHighResolutionCapture: NSNumber? // nullable bool
|
|
||||||
@objc var enablePortraitEffectsMatteDelivery = false
|
|
||||||
@objc var preset: String?
|
|
||||||
@objc var scannableCodes: [String]?
|
|
||||||
// props that require format reconfiguring
|
|
||||||
@objc var format: NSDictionary?
|
|
||||||
@objc var fps: NSNumber?
|
|
||||||
@objc var hdr: NSNumber? // nullable bool
|
|
||||||
@objc var lowLightBoost: NSNumber? // nullable bool
|
|
||||||
@objc var colorSpace: NSString?
|
|
||||||
// other props
|
|
||||||
@objc var isActive = false
|
|
||||||
@objc var torch = "off"
|
|
||||||
@objc var zoom: NSNumber = 0.0 // in percent
|
|
||||||
// events
|
|
||||||
@objc var onInitialized: RCTDirectEventBlock?
|
|
||||||
@objc var onError: RCTDirectEventBlock?
|
|
||||||
@objc var onCodeScanned: RCTBubblingEventBlock?
|
|
||||||
@objc var enableZoomGesture: Bool = false {
|
|
||||||
didSet {
|
|
||||||
if enableZoomGesture {
|
|
||||||
addPinchGestureRecognizer()
|
|
||||||
} else {
|
|
||||||
removePinchGestureRecognizer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var isReady: Bool = false
|
|
||||||
var isRunning: Bool {
|
|
||||||
return captureSession.isRunning
|
|
||||||
}
|
|
||||||
|
|
||||||
// pragma MARK: Private Properties
|
|
||||||
/// The serial execution queue for the camera preview layer (input stream) as well as output processing (take photo, record video, process metadata/barcodes)
|
|
||||||
internal let queue = DispatchQueue(label: "com.mrousavy.camera-queue", qos: .userInteractive, attributes: [], autoreleaseFrequency: .inherit, target: nil)
|
|
||||||
private let captureSession = AVCaptureSession()
|
|
||||||
internal var videoDeviceInput: AVCaptureDeviceInput?
|
|
||||||
internal var audioDeviceInput: AVCaptureDeviceInput?
|
|
||||||
internal var photoOutput: AVCapturePhotoOutput?
|
|
||||||
internal var movieOutput: AVCaptureMovieFileOutput?
|
|
||||||
internal var metadataOutput: AVCaptureMetadataOutput?
|
|
||||||
// CameraView+TakePhoto
|
|
||||||
internal var photoCaptureDelegates: [PhotoCaptureDelegate] = []
|
|
||||||
// CameraView+RecordVideo
|
|
||||||
internal var recordingDelegateResolver: RCTPromiseResolveBlock?
|
|
||||||
internal var recordingDelegateRejecter: RCTPromiseRejectBlock?
|
|
||||||
// CameraView+Zoom
|
|
||||||
internal var pinchGestureRecognizer: UIPinchGestureRecognizer?
|
|
||||||
internal var pinchScaleOffset: CGFloat = 1.0
|
|
||||||
|
|
||||||
// pragma MARK: Setup
|
|
||||||
override class var layerClass: AnyClass {
|
|
||||||
return AVCaptureVideoPreviewLayer.self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convenience wrapper to get layer as its statically known type.
|
|
||||||
var videoPreviewLayer: AVCaptureVideoPreviewLayer {
|
|
||||||
// swiftlint:disable force_cast
|
|
||||||
return layer as! AVCaptureVideoPreviewLayer
|
|
||||||
}
|
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
@ -114,26 +52,6 @@ final class CameraView: UIView {
|
|||||||
object: captureSession)
|
object: captureSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func removeFromSuperview() {
|
|
||||||
captureSession.stopRunning()
|
|
||||||
super.removeFromSuperview()
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc
|
|
||||||
func sessionRuntimeError(notification: Notification) {
|
|
||||||
guard let error = notification.userInfo?[AVCaptureSessionErrorKey] as? AVError else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if isActive {
|
|
||||||
// restart capture session after an error occured
|
|
||||||
queue.async {
|
|
||||||
self.captureSession.startRunning()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
invokeOnError(.unknown(message: error.localizedDescription), cause: error as NSError)
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(*, unavailable)
|
@available(*, unavailable)
|
||||||
required init?(coder _: NSCoder) {
|
required init?(coder _: NSCoder) {
|
||||||
fatalError("init(coder:) is not implemented.")
|
fatalError("init(coder:) is not implemented.")
|
||||||
@ -188,6 +106,159 @@ final class CameraView: UIView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Internal
|
||||||
|
|
||||||
|
// pragma MARK: Setup
|
||||||
|
override class var layerClass: AnyClass {
|
||||||
|
return AVCaptureVideoPreviewLayer.self
|
||||||
|
}
|
||||||
|
|
||||||
|
// pragma MARK: Exported Properties
|
||||||
|
// props that require reconfiguring
|
||||||
|
@objc var cameraId: NSString?
|
||||||
|
@objc var enableDepthData = false
|
||||||
|
@objc var enableHighResolutionCapture: NSNumber? // nullable bool
|
||||||
|
@objc var enablePortraitEffectsMatteDelivery = false
|
||||||
|
@objc var preset: String?
|
||||||
|
@objc var scannableCodes: [String]?
|
||||||
|
// props that require format reconfiguring
|
||||||
|
@objc var format: NSDictionary?
|
||||||
|
@objc var fps: NSNumber?
|
||||||
|
@objc var hdr: NSNumber? // nullable bool
|
||||||
|
@objc var lowLightBoost: NSNumber? // nullable bool
|
||||||
|
@objc var colorSpace: NSString?
|
||||||
|
// other props
|
||||||
|
@objc var isActive = false
|
||||||
|
@objc var torch = "off"
|
||||||
|
@objc var zoom: NSNumber = 0.0 // in percent
|
||||||
|
// events
|
||||||
|
@objc var onInitialized: RCTDirectEventBlock?
|
||||||
|
@objc var onError: RCTDirectEventBlock?
|
||||||
|
@objc var onCodeScanned: RCTBubblingEventBlock?
|
||||||
|
var isReady = false
|
||||||
|
// pragma MARK: Private Properties
|
||||||
|
/// The serial execution queue for the camera preview layer (input stream) as well as output processing (take photo, record video, process metadata/barcodes)
|
||||||
|
internal let queue = DispatchQueue(label: "com.mrousavy.camera-queue", qos: .userInteractive, attributes: [], autoreleaseFrequency: .inherit, target: nil)
|
||||||
|
internal var videoDeviceInput: AVCaptureDeviceInput?
|
||||||
|
internal var audioDeviceInput: AVCaptureDeviceInput?
|
||||||
|
internal var photoOutput: AVCapturePhotoOutput?
|
||||||
|
internal var movieOutput: AVCaptureMovieFileOutput?
|
||||||
|
internal var metadataOutput: AVCaptureMetadataOutput?
|
||||||
|
// CameraView+TakePhoto
|
||||||
|
internal var photoCaptureDelegates: [PhotoCaptureDelegate] = []
|
||||||
|
// CameraView+RecordVideo
|
||||||
|
internal var recordingDelegateResolver: RCTPromiseResolveBlock?
|
||||||
|
internal var recordingDelegateRejecter: RCTPromiseRejectBlock?
|
||||||
|
// CameraView+Zoom
|
||||||
|
internal var pinchGestureRecognizer: UIPinchGestureRecognizer?
|
||||||
|
internal var pinchScaleOffset: CGFloat = 1.0
|
||||||
|
|
||||||
|
@objc var enableZoomGesture = false {
|
||||||
|
didSet {
|
||||||
|
if enableZoomGesture {
|
||||||
|
addPinchGestureRecognizer()
|
||||||
|
} else {
|
||||||
|
removePinchGestureRecognizer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var isRunning: Bool {
|
||||||
|
return captureSession.isRunning
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience wrapper to get layer as its statically known type.
|
||||||
|
var videoPreviewLayer: AVCaptureVideoPreviewLayer {
|
||||||
|
// swiftlint:disable force_cast
|
||||||
|
return layer as! AVCaptureVideoPreviewLayer
|
||||||
|
}
|
||||||
|
|
||||||
|
override func removeFromSuperview() {
|
||||||
|
captureSession.stopRunning()
|
||||||
|
super.removeFromSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
func sessionRuntimeError(notification: Notification) {
|
||||||
|
guard let error = notification.userInfo?[AVCaptureSessionErrorKey] as? AVError else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if isActive {
|
||||||
|
// restart capture session after an error occured
|
||||||
|
queue.async {
|
||||||
|
self.captureSession.startRunning()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
invokeOnError(.unknown(message: error.localizedDescription), cause: error as NSError)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal final func setTorchMode(_ torchMode: String) {
|
||||||
|
guard let device = videoDeviceInput?.device else {
|
||||||
|
return invokeOnError(.session(.cameraNotReady))
|
||||||
|
}
|
||||||
|
guard var torchMode = AVCaptureDevice.TorchMode(withString: torchMode) else {
|
||||||
|
return invokeOnError(.parameter(.invalid(unionName: "TorchMode", receivedValue: torch)))
|
||||||
|
}
|
||||||
|
if !captureSession.isRunning {
|
||||||
|
torchMode = .off
|
||||||
|
}
|
||||||
|
if device.torchMode == torchMode {
|
||||||
|
// no need to run the whole lock/unlock bs
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !device.hasTorch || !device.isTorchAvailable {
|
||||||
|
if torchMode == .off {
|
||||||
|
// ignore it, when it's off and not supported, it's off.
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
// torch mode is .auto or .on, but no torch is available.
|
||||||
|
return invokeOnError(.device(.torchUnavailable))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
try device.lockForConfiguration()
|
||||||
|
device.torchMode = torchMode
|
||||||
|
if torchMode == .on {
|
||||||
|
try device.setTorchModeOn(level: 1.0)
|
||||||
|
}
|
||||||
|
device.unlockForConfiguration()
|
||||||
|
} catch let error as NSError {
|
||||||
|
return invokeOnError(.device(.configureError), cause: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pragma MARK: Event Invokers
|
||||||
|
internal final func invokeOnError(_ error: CameraError, cause: NSError? = nil) {
|
||||||
|
ReactLogger.log(level: .error, message: error.localizedDescription, alsoLogToJS: true)
|
||||||
|
guard let onError = self.onError else { return }
|
||||||
|
|
||||||
|
var causeDictionary: [String: Any]?
|
||||||
|
if let cause = cause {
|
||||||
|
causeDictionary = [
|
||||||
|
"code": cause.code,
|
||||||
|
"domain": cause.domain,
|
||||||
|
"message": cause.localizedDescription,
|
||||||
|
"details": cause.userInfo,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
onError([
|
||||||
|
"code": error.code,
|
||||||
|
"message": error.message,
|
||||||
|
"cause": causeDictionary ?? NSNull(),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
internal final func invokeOnInitialized() {
|
||||||
|
ReactLogger.log(level: .info, message: "Camera onInitialized()", alsoLogToJS: true)
|
||||||
|
guard let onInitialized = self.onInitialized else { return }
|
||||||
|
onInitialized([String: Any]())
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
private let captureSession = AVCaptureSession()
|
||||||
|
|
||||||
// pragma MARK: Session, Device and Format Configuration
|
// pragma MARK: Session, Device and Format Configuration
|
||||||
/**
|
/**
|
||||||
Configures the Capture Session.
|
Configures the Capture Session.
|
||||||
@ -196,7 +267,7 @@ final class CameraView: UIView {
|
|||||||
isReady = false
|
isReady = false
|
||||||
|
|
||||||
#if targetEnvironment(simulator)
|
#if targetEnvironment(simulator)
|
||||||
return invokeOnError(.device(.notAvailableOnSimulator))
|
return invokeOnError(.device(.notAvailableOnSimulator))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
guard cameraId != nil else {
|
guard cameraId != nil else {
|
||||||
@ -295,7 +366,7 @@ final class CameraView: UIView {
|
|||||||
}
|
}
|
||||||
captureSession.addOutput(photoOutput!)
|
captureSession.addOutput(photoOutput!)
|
||||||
if videoDeviceInput!.device.position == .front {
|
if videoDeviceInput!.device.position == .front {
|
||||||
photoOutput!.mirror()
|
photoOutput!.mirror()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Video Output
|
// Video Output
|
||||||
@ -308,7 +379,7 @@ final class CameraView: UIView {
|
|||||||
}
|
}
|
||||||
captureSession.addOutput(movieOutput!)
|
captureSession.addOutput(movieOutput!)
|
||||||
if videoDeviceInput!.device.position == .front {
|
if videoDeviceInput!.device.position == .front {
|
||||||
movieOutput!.mirror()
|
movieOutput!.mirror()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Barcode Scanning
|
// Barcode Scanning
|
||||||
@ -422,66 +493,4 @@ final class CameraView: UIView {
|
|||||||
return invokeOnError(.device(.configureError), cause: error)
|
return invokeOnError(.device(.configureError), cause: error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal final func setTorchMode(_ torchMode: String) {
|
|
||||||
guard let device = videoDeviceInput?.device else {
|
|
||||||
return invokeOnError(.session(.cameraNotReady))
|
|
||||||
}
|
|
||||||
guard var torchMode = AVCaptureDevice.TorchMode(withString: torchMode) else {
|
|
||||||
return invokeOnError(.parameter(.invalid(unionName: "TorchMode", receivedValue: torch)))
|
|
||||||
}
|
|
||||||
if !captureSession.isRunning {
|
|
||||||
torchMode = .off
|
|
||||||
}
|
|
||||||
if device.torchMode == torchMode {
|
|
||||||
// no need to run the whole lock/unlock bs
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !device.hasTorch || !device.isTorchAvailable {
|
|
||||||
if torchMode == .off {
|
|
||||||
// ignore it, when it's off and not supported, it's off.
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
// torch mode is .auto or .on, but no torch is available.
|
|
||||||
return invokeOnError(.device(.torchUnavailable))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
try device.lockForConfiguration()
|
|
||||||
device.torchMode = torchMode
|
|
||||||
if torchMode == .on {
|
|
||||||
try device.setTorchModeOn(level: 1.0)
|
|
||||||
}
|
|
||||||
device.unlockForConfiguration()
|
|
||||||
} catch let error as NSError {
|
|
||||||
return invokeOnError(.device(.configureError), cause: error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pragma MARK: Event Invokers
|
|
||||||
internal final func invokeOnError(_ error: CameraError, cause: NSError? = nil) {
|
|
||||||
ReactLogger.log(level: .error, message: error.localizedDescription, alsoLogToJS: true)
|
|
||||||
guard let onError = self.onError else { return }
|
|
||||||
|
|
||||||
var causeDictionary: [String: Any]?
|
|
||||||
if let cause = cause {
|
|
||||||
causeDictionary = [
|
|
||||||
"code": cause.code,
|
|
||||||
"domain": cause.domain,
|
|
||||||
"message": cause.localizedDescription,
|
|
||||||
"details": cause.userInfo
|
|
||||||
]
|
|
||||||
}
|
|
||||||
onError([
|
|
||||||
"code": error.code,
|
|
||||||
"message": error.message,
|
|
||||||
"cause": causeDictionary ?? NSNull(),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
internal final func invokeOnInitialized() {
|
|
||||||
ReactLogger.log(level: .info, message: "Camera onInitialized()", alsoLogToJS: true)
|
|
||||||
guard let onInitialized = self.onInitialized else { return }
|
|
||||||
onInitialized([String: Any]())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -11,17 +11,15 @@ import Foundation
|
|||||||
|
|
||||||
@objc(CameraViewManager)
|
@objc(CameraViewManager)
|
||||||
final class CameraViewManager: RCTViewManager {
|
final class CameraViewManager: RCTViewManager {
|
||||||
// pragma MARK: Setup
|
// MARK: Lifecycle
|
||||||
override final func view() -> UIView! {
|
|
||||||
return CameraView()
|
|
||||||
}
|
|
||||||
|
|
||||||
override static func requiresMainQueueSetup() -> Bool {
|
override static func requiresMainQueueSetup() -> Bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override var methodQueue: DispatchQueue! {
|
// pragma MARK: Setup
|
||||||
return DispatchQueue.main
|
override final func view() -> UIView! {
|
||||||
|
return CameraView()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getCameraView(withTag tag: NSNumber) -> CameraView {
|
private func getCameraView(withTag tag: NSNumber) -> CameraView {
|
||||||
@ -29,6 +27,12 @@ final class CameraViewManager: RCTViewManager {
|
|||||||
return bridge.uiManager.view(forReactTag: tag) as! CameraView
|
return bridge.uiManager.view(forReactTag: tag) as! CameraView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Internal
|
||||||
|
|
||||||
|
override var methodQueue: DispatchQueue! {
|
||||||
|
return DispatchQueue.main
|
||||||
|
}
|
||||||
|
|
||||||
// pragma MARK: Exported Functions
|
// pragma MARK: Exported Functions
|
||||||
@objc
|
@objc
|
||||||
final func startRecording(_ node: NSNumber, options: NSDictionary, onRecordCallback: @escaping RCTResponseSenderBlock) {
|
final func startRecording(_ node: NSNumber, options: NSDictionary, onRecordCallback: @escaping RCTResponseSenderBlock) {
|
||||||
@ -65,7 +69,7 @@ final class CameraViewManager: RCTViewManager {
|
|||||||
guard let movieOutput = component.movieOutput else {
|
guard let movieOutput = component.movieOutput else {
|
||||||
throw CameraError.session(SessionError.cameraNotReady)
|
throw CameraError.session(SessionError.cameraNotReady)
|
||||||
}
|
}
|
||||||
return movieOutput.availableVideoCodecTypes.map { $0.descriptor }
|
return movieOutput.availableVideoCodecTypes.map(\.descriptor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,26 +80,10 @@ final class CameraViewManager: RCTViewManager {
|
|||||||
guard let photoOutput = component.photoOutput else {
|
guard let photoOutput = component.photoOutput else {
|
||||||
throw CameraError.session(SessionError.cameraNotReady)
|
throw CameraError.session(SessionError.cameraNotReady)
|
||||||
}
|
}
|
||||||
return photoOutput.availablePhotoCodecTypes.map { $0.descriptor }
|
return photoOutput.availablePhotoCodecTypes.map(\.descriptor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final func getAllDeviceTypes() -> [AVCaptureDevice.DeviceType] {
|
|
||||||
var deviceTypes: [AVCaptureDevice.DeviceType] = []
|
|
||||||
if #available(iOS 13.0, *) {
|
|
||||||
deviceTypes.append(.builtInTripleCamera)
|
|
||||||
deviceTypes.append(.builtInDualWideCamera)
|
|
||||||
deviceTypes.append(.builtInUltraWideCamera)
|
|
||||||
}
|
|
||||||
if #available(iOS 11.1, *) {
|
|
||||||
deviceTypes.append(.builtInTrueDepthCamera)
|
|
||||||
}
|
|
||||||
deviceTypes.append(.builtInDualCamera)
|
|
||||||
deviceTypes.append(.builtInWideAngleCamera)
|
|
||||||
deviceTypes.append(.builtInTelephotoCamera)
|
|
||||||
return deviceTypes
|
|
||||||
}
|
|
||||||
|
|
||||||
// pragma MARK: View Manager funcs
|
// pragma MARK: View Manager funcs
|
||||||
@objc
|
@objc
|
||||||
final func getAvailableCameraDevices(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
final func getAvailableCameraDevices(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
||||||
@ -104,7 +92,7 @@ final class CameraViewManager: RCTViewManager {
|
|||||||
return discoverySession.devices.map {
|
return discoverySession.devices.map {
|
||||||
return [
|
return [
|
||||||
"id": $0.uniqueID,
|
"id": $0.uniqueID,
|
||||||
"devices": $0.physicalDevices.map { $0.deviceType.descriptor },
|
"devices": $0.physicalDevices.map(\.deviceType.descriptor),
|
||||||
"position": $0.position.descriptor,
|
"position": $0.position.descriptor,
|
||||||
"name": $0.localizedName,
|
"name": $0.localizedName,
|
||||||
"hasFlash": $0.hasFlash,
|
"hasFlash": $0.hasFlash,
|
||||||
@ -155,4 +143,22 @@ final class CameraViewManager: RCTViewManager {
|
|||||||
resolve(result.descriptor)
|
resolve(result.descriptor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
private final func getAllDeviceTypes() -> [AVCaptureDevice.DeviceType] {
|
||||||
|
var deviceTypes: [AVCaptureDevice.DeviceType] = []
|
||||||
|
if #available(iOS 13.0, *) {
|
||||||
|
deviceTypes.append(.builtInTripleCamera)
|
||||||
|
deviceTypes.append(.builtInDualWideCamera)
|
||||||
|
deviceTypes.append(.builtInUltraWideCamera)
|
||||||
|
}
|
||||||
|
if #available(iOS 11.1, *) {
|
||||||
|
deviceTypes.append(.builtInTrueDepthCamera)
|
||||||
|
}
|
||||||
|
deviceTypes.append(.builtInDualCamera)
|
||||||
|
deviceTypes.append(.builtInWideAngleCamera)
|
||||||
|
deviceTypes.append(.builtInTelephotoCamera)
|
||||||
|
return deviceTypes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ extension AVCaptureDevice.Format {
|
|||||||
|
|
||||||
func toDictionary() -> [String: Any] {
|
func toDictionary() -> [String: Any] {
|
||||||
var dict: [String: Any] = [
|
var dict: [String: Any] = [
|
||||||
"videoStabilizationModes": videoStabilizationModes.map { $0.descriptor },
|
"videoStabilizationModes": videoStabilizationModes.map(\.descriptor),
|
||||||
"autoFocusSystem": autoFocusSystem.descriptor,
|
"autoFocusSystem": autoFocusSystem.descriptor,
|
||||||
"photoHeight": highResolutionStillImageDimensions.height,
|
"photoHeight": highResolutionStillImageDimensions.height,
|
||||||
"photoWidth": highResolutionStillImageDimensions.width,
|
"photoWidth": highResolutionStillImageDimensions.width,
|
||||||
@ -31,7 +31,7 @@ extension AVCaptureDevice.Format {
|
|||||||
"minISO": minISO,
|
"minISO": minISO,
|
||||||
"fieldOfView": videoFieldOfView,
|
"fieldOfView": videoFieldOfView,
|
||||||
"maxZoom": videoMaxZoomFactor,
|
"maxZoom": videoMaxZoomFactor,
|
||||||
"colorSpaces": supportedColorSpaces.map { $0.descriptor },
|
"colorSpaces": supportedColorSpaces.map(\.descriptor),
|
||||||
"supportsVideoHDR": isVideoHDRSupported,
|
"supportsVideoHDR": isVideoHDRSupported,
|
||||||
"supportsPhotoHDR": false,
|
"supportsPhotoHDR": false,
|
||||||
"frameRateRanges": videoSupportedFrameRateRanges.map {
|
"frameRateRanges": videoSupportedFrameRateRanges.map {
|
||||||
|
@ -9,11 +9,11 @@
|
|||||||
import AVFoundation
|
import AVFoundation
|
||||||
|
|
||||||
extension AVCaptureMovieFileOutput {
|
extension AVCaptureMovieFileOutput {
|
||||||
func mirror() {
|
func mirror() {
|
||||||
connections.forEach { (connection) in
|
connections.forEach { connection in
|
||||||
if connection.isVideoMirroringSupported {
|
if connection.isVideoMirroringSupported {
|
||||||
connection.isVideoMirrored = true
|
connection.isVideoMirrored = true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,11 +9,11 @@
|
|||||||
import AVFoundation
|
import AVFoundation
|
||||||
|
|
||||||
extension AVCapturePhotoOutput {
|
extension AVCapturePhotoOutput {
|
||||||
func mirror() {
|
func mirror() {
|
||||||
connections.forEach { (connection) in
|
connections.forEach { connection in
|
||||||
if connection.isVideoMirroringSupported {
|
if connection.isVideoMirroringSupported {
|
||||||
connection.isVideoMirrored = true
|
connection.isVideoMirrored = true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,10 @@ import AVFoundation
|
|||||||
|
|
||||||
private var delegatesReferences: [NSObject] = []
|
private var delegatesReferences: [NSObject] = []
|
||||||
|
|
||||||
|
// MARK: - PhotoCaptureDelegate
|
||||||
|
|
||||||
class PhotoCaptureDelegate: NSObject, AVCapturePhotoCaptureDelegate {
|
class PhotoCaptureDelegate: NSObject, AVCapturePhotoCaptureDelegate {
|
||||||
private let promise: Promise
|
// MARK: Lifecycle
|
||||||
|
|
||||||
required init(promise: Promise) {
|
required init(promise: Promise) {
|
||||||
self.promise = promise
|
self.promise = promise
|
||||||
@ -19,6 +21,8 @@ class PhotoCaptureDelegate: NSObject, AVCapturePhotoCaptureDelegate {
|
|||||||
delegatesReferences.append(self)
|
delegatesReferences.append(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Internal
|
||||||
|
|
||||||
func photoOutput(_: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
|
func photoOutput(_: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
|
||||||
defer {
|
defer {
|
||||||
delegatesReferences.removeAll(where: { $0 == self })
|
delegatesReferences.removeAll(where: { $0 == self })
|
||||||
@ -66,4 +70,8 @@ class PhotoCaptureDelegate: NSObject, AVCapturePhotoCaptureDelegate {
|
|||||||
return promise.reject(error: .capture(.unknown(message: error.localizedDescription)), cause: error as NSError)
|
return promise.reject(error: .capture(.unknown(message: error.localizedDescription)), cause: error as NSError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
private let promise: Promise
|
||||||
}
|
}
|
||||||
|
@ -17,4 +17,4 @@ This folder contains the iOS-platform-specific code for react-native-vision-came
|
|||||||
|
|
||||||
It is recommended that you work on the code using the Example project (`example/ios/VisionCameraExample.xcworkspace`), since that always includes the React Native header files, plus you can easily test changes that way.
|
It is recommended that you work on the code using the Example project (`example/ios/VisionCameraExample.xcworkspace`), since that always includes the React Native header files, plus you can easily test changes that way.
|
||||||
|
|
||||||
You can however still edit the library project here by opening `VisionCamera.xcodeproj`.
|
You can however still edit the library project here by opening `VisionCamera.xcodeproj`, this has the advantage of **automatically formatting your Code** (swiftformat) and **showing you Linter errors** (swiftlint) when trying to build (<kbd>⌘</kbd>+<kbd>B</kbd>).
|
||||||
|
@ -8,18 +8,21 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
// MARK: - Promise
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a JavaScript Promise instance. `reject()` and `resolve()` should only be called once.
|
* Represents a JavaScript Promise instance. `reject()` and `resolve()` should only be called once.
|
||||||
*/
|
*/
|
||||||
class Promise {
|
class Promise {
|
||||||
private let resolver: RCTPromiseResolveBlock
|
// MARK: Lifecycle
|
||||||
private let rejecter: RCTPromiseRejectBlock
|
|
||||||
|
|
||||||
init(resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
|
init(resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
|
||||||
self.resolver = resolver
|
self.resolver = resolver
|
||||||
self.rejecter = rejecter
|
self.rejecter = rejecter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Internal
|
||||||
|
|
||||||
func reject(error: CameraError, cause: NSError?) {
|
func reject(error: CameraError, cause: NSError?) {
|
||||||
rejecter(error.code, error.message, cause)
|
rejecter(error.code, error.message, cause)
|
||||||
}
|
}
|
||||||
@ -35,6 +38,11 @@ class Promise {
|
|||||||
func resolve() {
|
func resolve() {
|
||||||
resolve(nil)
|
resolve(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
private let resolver: RCTPromiseResolveBlock
|
||||||
|
private let rejecter: RCTPromiseRejectBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -10,6 +10,8 @@ import Foundation
|
|||||||
|
|
||||||
let context = "Camera"
|
let context = "Camera"
|
||||||
|
|
||||||
|
// MARK: - ReactLogger
|
||||||
|
|
||||||
enum ReactLogger {
|
enum ReactLogger {
|
||||||
static func log(level: RCTLogLevel, message: String, alsoLogToJS: Bool = false, file: String = #file, lineNumber: Int = #line) {
|
static func log(level: RCTLogLevel, message: String, alsoLogToJS: Bool = false, file: String = #file, lineNumber: Int = #line) {
|
||||||
RCTDefaultLogFunction(level, RCTLogSource.native, file, lineNumber as NSNumber, "\(context): \(message)")
|
RCTDefaultLogFunction(level, RCTLogSource.native, file, lineNumber as NSNumber, "\(context): \(message)")
|
||||||
|
@ -14,9 +14,10 @@ import AVFoundation
|
|||||||
// once the delegate has been triggered once.
|
// once the delegate has been triggered once.
|
||||||
private var delegateReferences: [NSObject] = []
|
private var delegateReferences: [NSObject] = []
|
||||||
|
|
||||||
|
// MARK: - RecordingDelegateWithCallback
|
||||||
|
|
||||||
class RecordingDelegateWithCallback: NSObject, AVCaptureFileOutputRecordingDelegate {
|
class RecordingDelegateWithCallback: NSObject, AVCaptureFileOutputRecordingDelegate {
|
||||||
private let callback: RCTResponseSenderBlock // (video?, error?) => void
|
// MARK: Lifecycle
|
||||||
private let resetTorchMode: () -> Void
|
|
||||||
|
|
||||||
init(callback: @escaping RCTResponseSenderBlock, resetTorchMode: @escaping () -> Void) {
|
init(callback: @escaping RCTResponseSenderBlock, resetTorchMode: @escaping () -> Void) {
|
||||||
self.callback = callback
|
self.callback = callback
|
||||||
@ -25,6 +26,8 @@ class RecordingDelegateWithCallback: NSObject, AVCaptureFileOutputRecordingDeleg
|
|||||||
delegateReferences.append(self)
|
delegateReferences.append(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Internal
|
||||||
|
|
||||||
func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from _: [AVCaptureConnection], error: Error?) {
|
func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from _: [AVCaptureConnection], error: Error?) {
|
||||||
defer {
|
defer {
|
||||||
self.resetTorchMode()
|
self.resetTorchMode()
|
||||||
@ -37,4 +40,9 @@ class RecordingDelegateWithCallback: NSObject, AVCaptureFileOutputRecordingDeleg
|
|||||||
let seconds = CMTimeGetSeconds(output.recordedDuration)
|
let seconds = CMTimeGetSeconds(output.recordedDuration)
|
||||||
return callback([["path": outputFileURL.absoluteString, "duration": seconds, "size": output.recordedFileSize], NSNull()])
|
return callback([["path": outputFileURL.absoluteString, "duration": seconds, "size": output.recordedFileSize], NSNull()])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
private let callback: RCTResponseSenderBlock // (video?, error?) => void
|
||||||
|
private let resetTorchMode: () -> Void
|
||||||
}
|
}
|
||||||
|
@ -196,6 +196,7 @@
|
|||||||
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "VisionCamera" */;
|
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "VisionCamera" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
B81F6C7625E515810008974A /* Run SwiftLint */,
|
B81F6C7625E515810008974A /* Run SwiftLint */,
|
||||||
|
B80D6CAB25F770FE006F2CB7 /* Run SwiftFormat */,
|
||||||
58B511D71A9E6C8500147676 /* Sources */,
|
58B511D71A9E6C8500147676 /* Sources */,
|
||||||
58B511D81A9E6C8500147676 /* Frameworks */,
|
58B511D81A9E6C8500147676 /* Frameworks */,
|
||||||
58B511D91A9E6C8500147676 /* CopyFiles */,
|
58B511D91A9E6C8500147676 /* CopyFiles */,
|
||||||
@ -242,6 +243,24 @@
|
|||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
|
|
||||||
/* Begin PBXShellScriptBuildPhase section */
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
B80D6CAB25F770FE006F2CB7 /* Run SwiftFormat */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Run SwiftFormat";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "if which swiftformat >/dev/null; then\n swiftformat .\nelse\n echo \"warning: SwiftFormat not installed, download from https://github.com/nicklockwood/SwiftFormat\"\nfi\n";
|
||||||
|
};
|
||||||
B81F6C7625E515810008974A /* Run SwiftLint */ = {
|
B81F6C7625E515810008974A /* Run SwiftLint */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@ -258,7 +277,7 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "if which swiftlint >/dev/null; then\n swiftlint autocorrect && swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
|
shellScript = "if which swiftlint >/dev/null; then\n swiftlint --fix && swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
|
||||||
};
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
|
@ -35,10 +35,14 @@
|
|||||||
"example": "yarn --cwd example",
|
"example": "yarn --cwd example",
|
||||||
"pods": "cd example && pod-install --quiet",
|
"pods": "cd example && pod-install --quiet",
|
||||||
"bootstrap": "yarn example && yarn && yarn pods",
|
"bootstrap": "yarn example && yarn && yarn pods",
|
||||||
"ktlint-fix": "ktlint -F android/**/*.kt*",
|
"ktlint": "scripts/ktlint.sh",
|
||||||
"swiftlint-fix": "cd ios && swiftlint autocorrect",
|
"swiftlint": "scripts/swiftlint.sh",
|
||||||
|
"swiftformat": "scripts/swiftformat.sh",
|
||||||
"docs": "cd docs && yarn build"
|
"docs": "cd docs && yarn build"
|
||||||
},
|
},
|
||||||
|
"pre-commit": [
|
||||||
|
"swiftformat"
|
||||||
|
],
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react-native",
|
"react-native",
|
||||||
"ios",
|
"ios",
|
||||||
@ -81,6 +85,7 @@
|
|||||||
"eslint-plugin-react-native": "^3.10.0",
|
"eslint-plugin-react-native": "^3.10.0",
|
||||||
"jest": "^26.0.1",
|
"jest": "^26.0.1",
|
||||||
"pod-install": "^0.1.0",
|
"pod-install": "^0.1.0",
|
||||||
|
"pre-commit": "^1.2.2",
|
||||||
"prettier": "^2.2.1",
|
"prettier": "^2.2.1",
|
||||||
"react": "17.0.1",
|
"react": "17.0.1",
|
||||||
"react-native": "0.63.4",
|
"react-native": "0.63.4",
|
||||||
|
7
scripts/ktlint.sh
Executable file
7
scripts/ktlint.sh
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if which ktlint >/dev/null; then
|
||||||
|
cd android && ktlint -F ./**/*.kt*
|
||||||
|
else
|
||||||
|
echo "warning: KTLint not installed, download from https://github.com/pinterest/ktlint"
|
||||||
|
fi
|
7
scripts/swiftformat.sh
Executable file
7
scripts/swiftformat.sh
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if which swiftformat >/dev/null; then
|
||||||
|
cd ios && swiftformat .
|
||||||
|
else
|
||||||
|
echo "warning: SwiftFormat not installed, download from https://github.com/nicklockwood/SwiftFormat"
|
||||||
|
fi
|
7
scripts/swiftlint.sh
Executable file
7
scripts/swiftlint.sh
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if which swiftlint >/dev/null; then
|
||||||
|
cd ios && swiftlint --fix && swiftlint
|
||||||
|
else
|
||||||
|
echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint"
|
||||||
|
fi
|
45
yarn.lock
45
yarn.lock
@ -2,11 +2,6 @@
|
|||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
"@actions/core@^1.2.0":
|
|
||||||
version "1.2.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.2.6.tgz#a78d49f41a4def18e88ce47c2cac615d5694bf09"
|
|
||||||
integrity sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA==
|
|
||||||
|
|
||||||
"@babel/code-frame@7.12.11":
|
"@babel/code-frame@7.12.11":
|
||||||
version "7.12.11"
|
version "7.12.11"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f"
|
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f"
|
||||||
@ -1043,13 +1038,6 @@
|
|||||||
minimatch "^3.0.4"
|
minimatch "^3.0.4"
|
||||||
strip-json-comments "^3.1.1"
|
strip-json-comments "^3.1.1"
|
||||||
|
|
||||||
"@firmnav/eslint-github-actions-formatter@^1.0.1":
|
|
||||||
version "1.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/@firmnav/eslint-github-actions-formatter/-/eslint-github-actions-formatter-1.0.1.tgz#dbedcc4d8a799faf9b709417981039819980aab0"
|
|
||||||
integrity sha512-KbhZwNPFuwoRWspUfoJISOeGZHGSm7tvdOC+uOUlbcY9LNmusRHHmBcq3KaorvW9WmmiOS/2EOo0nafFZ0gpEQ==
|
|
||||||
dependencies:
|
|
||||||
"@actions/core" "^1.2.0"
|
|
||||||
|
|
||||||
"@hapi/address@2.x.x":
|
"@hapi/address@2.x.x":
|
||||||
version "2.1.4"
|
version "2.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
|
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
|
||||||
@ -3035,7 +3023,7 @@ concat-map@0.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||||
|
|
||||||
concat-stream@^1.6.0:
|
concat-stream@^1.4.7, concat-stream@^1.6.0:
|
||||||
version "1.6.2"
|
version "1.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
|
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
|
||||||
integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
|
integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
|
||||||
@ -3289,7 +3277,7 @@ cosmiconfig@^5.0.5, cosmiconfig@^5.1.0:
|
|||||||
js-yaml "^3.13.1"
|
js-yaml "^3.13.1"
|
||||||
parse-json "^4.0.0"
|
parse-json "^4.0.0"
|
||||||
|
|
||||||
cross-spawn@^5.1.0:
|
cross-spawn@^5.0.1, cross-spawn@^5.1.0:
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
|
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
|
||||||
integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=
|
integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=
|
||||||
@ -7112,6 +7100,11 @@ os-name@4.0.0:
|
|||||||
macos-release "^2.2.0"
|
macos-release "^2.2.0"
|
||||||
windows-release "^4.0.0"
|
windows-release "^4.0.0"
|
||||||
|
|
||||||
|
os-shim@^0.1.2:
|
||||||
|
version "0.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917"
|
||||||
|
integrity sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc=
|
||||||
|
|
||||||
os-tmpdir@^1.0.0, os-tmpdir@~1.0.2:
|
os-tmpdir@^1.0.0, os-tmpdir@~1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
|
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
|
||||||
@ -7436,6 +7429,15 @@ posix-character-classes@^0.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
|
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
|
||||||
integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
|
integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
|
||||||
|
|
||||||
|
pre-commit@^1.2.2:
|
||||||
|
version "1.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/pre-commit/-/pre-commit-1.2.2.tgz#dbcee0ee9de7235e57f79c56d7ce94641a69eec6"
|
||||||
|
integrity sha1-287g7p3nI15X95xW186UZBpp7sY=
|
||||||
|
dependencies:
|
||||||
|
cross-spawn "^5.0.1"
|
||||||
|
spawn-sync "^1.0.15"
|
||||||
|
which "1.2.x"
|
||||||
|
|
||||||
prelude-ls@^1.2.1:
|
prelude-ls@^1.2.1:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
||||||
@ -8480,6 +8482,14 @@ source-map@^0.7.3:
|
|||||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
|
||||||
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
|
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
|
||||||
|
|
||||||
|
spawn-sync@^1.0.15:
|
||||||
|
version "1.0.15"
|
||||||
|
resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476"
|
||||||
|
integrity sha1-sAeZVX63+wyDdsKdROih6mfldHY=
|
||||||
|
dependencies:
|
||||||
|
concat-stream "^1.4.7"
|
||||||
|
os-shim "^0.1.2"
|
||||||
|
|
||||||
spdx-correct@^3.0.0:
|
spdx-correct@^3.0.0:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"
|
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"
|
||||||
@ -9381,6 +9391,13 @@ which-module@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
|
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
|
||||||
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
|
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
|
||||||
|
|
||||||
|
which@1.2.x:
|
||||||
|
version "1.2.14"
|
||||||
|
resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5"
|
||||||
|
integrity sha1-mofEN48D6CfOyvGs31bHNsAcFOU=
|
||||||
|
dependencies:
|
||||||
|
isexe "^2.0.0"
|
||||||
|
|
||||||
which@^1.2.9:
|
which@^1.2.9:
|
||||||
version "1.3.1"
|
version "1.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
||||||
|
Loading…
Reference in New Issue
Block a user