Skip to content

Commit 08b9110

Browse files
RMET-3585 ::: Fix Inverted Video Capture (#19)
* fix: replace orientation validation Instead of `UIDevice`'s `deviceOrientation`, use `UIWindowScene`'s `interfaceOrientation`. Not only do we avoid the `faceUp` and `faceDown` validations, but the rotations are similar to `AVCaptureVideoOrientation`'s, with no need to map `landscapeRight` to `landscapeLeft` and vice-versa. References: https://outsystemsrd.atlassian.net/browse/RMET-3585 * chore: refactor unit tests Simplify validity checks. * chore: remove mac catalyst targets Considering we're only targeting mobile devices, these targets are unneeded. * chore: fix pipeline References: https://outsystemsrd.atlassian.net/browse/RMET-3585 * chore: add CHANGELOG entry Fix other versions' CHANGELOG entries. References: https://outsystemsrd.atlassian.net/browse/RMET-3585
1 parent 99a7f95 commit 08b9110

File tree

8 files changed

+79
-52
lines changed

8 files changed

+79
-52
lines changed

.github/workflows/github_actions.yml

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
name: GitHub Actions
22

33
on:
4-
workflow_dispatch:
5-
push:
6-
branches: [ main, development ]
74
pull_request:
8-
branches: [ main, development ]
5+
types: [opened, synchronize, reopened]
96

107
jobs:
118
test:
@@ -14,23 +11,34 @@ jobs:
1411
steps:
1512
- name: Checkout
1613
uses: actions/checkout@v4
14+
1715
- name: Setup Java 17
1816
uses: actions/setup-java@v4
1917
with:
2018
distribution: 'zulu'
2119
java-version: '17'
20+
21+
- name: Link SwiftLint or install it
22+
run: brew link --overwrite swiftlint || brew install swiftlint
23+
24+
- name: Set up XCode
25+
run: sudo xcode-select --switch /Applications/Xcode_15.0.app
26+
2227
- name: Bundle Install
2328
run: bundle install
29+
2430
- name: Unit tests
2531
run: bundle exec fastlane unit_tests
26-
env:
27-
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
32+
2833
- name: Code Coverage
2934
run: bundle exec fastlane coverage
35+
3036
- name: Lint
3137
run: bundle exec fastlane lint
38+
3239
- name: Setup sonarqube
3340
uses: warchant/setup-sonar-scanner@v8
41+
3442
- name: Send to Sonarcloud
3543
run: bundle exec fastlane sonarqube
3644
env:

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
### Fixes
10+
- Fix upside down video capture stream (https://outsystemsrd.atlassian.net/browse/RMET-3585).
11+
12+
## 1.1.0
13+
914
### Chores
1015
- Update `github_actions.yml` file steps versions (https://outsystemsrd.atlassian.net/browse/RMET-2568).
1116
- Set up the repo to release on CocoaPods (https://outsystemsrd.atlassian.net/browse/RMET-3301).
@@ -14,7 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1419
- Add zooming out feature (https://outsystemsrd.atlassian.net/browse/RMET-2986).
1520
- Add zooming in feature (https://outsystemsrd.atlassian.net/browse/RMET-2986).
1621

17-
## [1.0.0]
22+
## 1.0.0
1823

1924
### Features
2025
- Implement iPad user interface (https://outsystemsrd.atlassian.net/browse/RMET-2911).

OSBarcodeLib.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,9 @@
656656
PRODUCT_BUNDLE_IDENTIFIER = com.outsystems.rd.barcode.OSBarcodeLib;
657657
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
658658
SKIP_INSTALL = YES;
659+
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
660+
SUPPORTS_MACCATALYST = NO;
661+
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
659662
SWIFT_EMIT_LOC_STRINGS = YES;
660663
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
661664
SWIFT_VERSION = 5.0;
@@ -693,6 +696,9 @@
693696
PRODUCT_BUNDLE_IDENTIFIER = com.outsystems.rd.barcode.OSBarcodeLib;
694697
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
695698
SKIP_INSTALL = YES;
699+
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
700+
SUPPORTS_MACCATALYST = NO;
701+
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
696702
SWIFT_EMIT_LOC_STRINGS = YES;
697703
SWIFT_VERSION = 5.0;
698704
TARGETED_DEVICE_FAMILY = "1,2";

OSBarcodeLib/Scanner/CameraManager/OSBARCCaptureSessionManager.swift

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -91,24 +91,22 @@ final class OSBARCCaptureSessionManager: OSBARCCameraManager {
9191
Calculate the initial video orientation based on the device and the selection screen orientation.
9292
Subsequent orientation changes are handled by `viewWillTransition(to:with:)`.
9393
*/
94-
let deviceOrientation = UIDevice.current.orientation
95-
var initialVideoOrientation = AVCaptureVideoOrientation(deviceOrientation: deviceOrientation)
96-
97-
/*
98-
If the orientation has to be portrait but the device is not on that mode, orientation is set to `portrait`.
99-
If the orientation has to be landscape but the device is not on that mode, orientation is set to `landscapeRight`.
100-
101-
If the device is set to `flat` orientation, then check screen size to understand it's portrait or landscape.
102-
*/
103-
if self.orientationModel == .portrait, !deviceOrientation.isPortrait {
104-
initialVideoOrientation = .portrait
105-
} else if self.orientationModel == .landscape, !deviceOrientation.isLandscape {
106-
initialVideoOrientation = .landscapeRight
107-
} else if let screenBounds = UIApplication.firstKeyWindowForConnectedScenes?.windowScene?.screen.bounds {
108-
initialVideoOrientation = screenBounds.width > screenBounds.height ? .landscapeRight : .portrait
94+
if videoPreviewLayer.connection?.isVideoOrientationSupported == true,
95+
let interfaceOrientation = UIApplication.firstKeyWindowForConnectedScenes?.windowScene?.interfaceOrientation,
96+
var initialVideoOrientation = AVCaptureVideoOrientation(interfaceOrientation: interfaceOrientation) {
97+
/*
98+
If the orientation has to be portrait but the device is not on that mode, orientation is set to `portrait`.
99+
If the orientation has to be landscape but the device is not on that mode, orientation is set to `landscapeRight`.
100+
*/
101+
102+
if self.orientationModel == .portrait, !interfaceOrientation.isPortrait {
103+
initialVideoOrientation = .portrait
104+
} else if self.orientationModel == .landscape, !interfaceOrientation.isLandscape {
105+
initialVideoOrientation = .landscapeRight
106+
}
107+
108+
videoPreviewLayer.connection?.videoOrientation = initialVideoOrientation
109109
}
110-
111-
videoPreviewLayer.connection?.videoOrientation = initialVideoOrientation ?? .portrait
112110
}
113111
}
114112
}
@@ -144,9 +142,10 @@ final class OSBARCCaptureSessionManager: OSBARCCameraManager {
144142
if let rotationChange = change as? OSBARCCameraRotationChange, let videoPreviewLayer = videoPreview as? AVCaptureVideoPreviewLayer {
145143
videoPreviewLayer.frame = CGRect(x: 0, y: 0, width: rotationChange.value.width, height: rotationChange.value.height)
146144

147-
if let videoPreviewLayerConnection = videoPreviewLayer.connection,
148-
let newVideoOrientation = AVCaptureVideoOrientation(deviceOrientation: UIDevice.current.orientation) {
149-
videoPreviewLayerConnection.videoOrientation = newVideoOrientation
145+
if videoPreviewLayer.connection?.isVideoOrientationSupported == true,
146+
let interfaceOrientation = UIApplication.firstKeyWindowForConnectedScenes?.windowScene?.interfaceOrientation,
147+
let newVideoOrientation = AVCaptureVideoOrientation(interfaceOrientation: interfaceOrientation) {
148+
videoPreviewLayer.connection?.videoOrientation = newVideoOrientation
150149
}
151150
}
152151
}

OSBarcodeLib/Scanner/Extensions/AVCaptureVideoOrientation+CustomInit.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ import UIKit
44
/// Extension that maps a `UIDeviceOrientation` into an `AVCaptureVideoOrientation`.
55
extension AVCaptureVideoOrientation {
66
/// Mapper construtor.
7-
/// - Parameter deviceOrientation: Value to be converted from.
8-
init?(deviceOrientation: UIDeviceOrientation) {
9-
switch deviceOrientation {
7+
/// - Parameter interfaceOrientation: Value to be converted from.
8+
init?(interfaceOrientation: UIInterfaceOrientation) {
9+
switch interfaceOrientation {
1010
case .portrait: self = .portrait
1111
case .portraitUpsideDown: self = .portraitUpsideDown
12-
case .landscapeLeft: self = .landscapeRight
13-
case .landscapeRight: self = .landscapeLeft
14-
default: return nil
12+
case .landscapeLeft: self = .landscapeLeft
13+
case .landscapeRight: self = .landscapeRight
14+
case .unknown: return nil
15+
@unknown default: return nil
1516
}
1617
}
1718
}

OSBarcodeLibTests/OSBARCManagerFactoryTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ final class OSBARCManagerFactoryTests: XCTestCase {
55
func testManagerCreationWithFactoryMethod() {
66
let viewController = UIViewController()
77
let manager = OSBARCManagerFactory.createManager(with: viewController)
8-
XCTAssertTrue(manager as? OSBARCManager != nil)
8+
XCTAssertTrue(manager is OSBARCManager)
99
}
1010
}

OSBarcodeLibTests/OSBARCManagerTests.swift

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,31 +29,43 @@ final class OSBARCManagerTests: XCTestCase {
2929
func testNoAccessToCameraShouldThrowException() async {
3030
self.permissionsBehaviour.error = .anError
3131

32-
do {
33-
_ = try await self.manager.scanBarcode()
34-
XCTFail(OSBARCCommonValues.failMessage)
35-
} catch {
36-
XCTAssertTrue(true)
32+
await self.assertThrowsAsyncError(try await self.manager.scanBarcode()) {
33+
XCTAssertEqual($0 as? OSBARCManagerError, OSBARCManagerError.cameraAccessDenied)
3734
}
3835
}
3936

4037
func testGivenAccessToCameraButCancelledScanShouldThrowException() async {
4138
self.scannerBehaviour.scanCancelled = true
4239

43-
do {
44-
_ = try await self.manager.scanBarcode()
45-
XCTFail(OSBARCCommonValues.failMessage)
46-
} catch {
47-
XCTAssertTrue(true)
40+
await self.assertThrowsAsyncError(try await self.manager.scanBarcode()) {
41+
XCTAssertEqual($0 as? OSBARCManagerError, OSBARCManagerError.scanningCancelled)
4842
}
4943
}
5044

51-
func testGivenAccessToCameraAndSuccessfulScanShouldReturnABarcode() async {
45+
func testGivenAccessToCameraAndSuccessfulScanShouldReturnABarcode() async throws {
46+
let result = try await self.manager.scanBarcode()
47+
XCTAssertEqual(result, OSBARCScannerStubValues.scannedCode)
48+
}
49+
}
50+
51+
private extension OSBARCManagerTests {
52+
func assertThrowsAsyncError<T>(
53+
_ expression: @autoclosure () async throws -> T,
54+
_ message: @autoclosure () -> String = "",
55+
file: StaticString = #filePath,
56+
line: UInt = #line,
57+
_ errorHandler: (_ error: Error) -> Void = { _ in }
58+
) async {
5259
do {
53-
let result = try await self.manager.scanBarcode()
54-
XCTAssertEqual(result, OSBARCScannerStubValues.scannedCode)
60+
_ = try await expression()
61+
let customMessage = message()
62+
if customMessage.isEmpty {
63+
XCTFail("Asynchronous call did not throw an error.", file: file, line: line)
64+
} else {
65+
XCTFail(customMessage, file: file, line: line)
66+
}
5567
} catch {
56-
XCTFail(OSBARCCommonValues.failMessage)
68+
errorHandler(error)
5769
}
5870
}
5971
}
Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
11
struct OSBARCScannerStubValues {
22
static let scannedCode = "Scanned Code"
33
}
4-
5-
struct OSBARCCommonValues {
6-
static let failMessage = "Shouldn't get here"
7-
}

0 commit comments

Comments
 (0)