Skip to content

Commit

Permalink
Support Xcode 16 (#4066)
Browse files Browse the repository at this point in the history
## Summary
- Fixes StripeConnect and StripeCardScan to compile on Xcode 16 
- Adds Bitrise tests for Xcode 16 to ensure it compiles
- Updates OCMock to an Xcode 16 compatible version

Note: OCMock doesn't work with SPM unless you add the dependency via
commit hash as opposed to release version. We're using the commit hash
that corresponds to v3.9.4:
- https://github.com/erikdoe/ocmock/releases/tag/v3.9.4
- erikdoe/ocmock#500 (comment)

## Motivation
https://jira.corp.stripe.com/browse/MXMOBILE-2808

## Testing
- Verified this branch passes tests
- Created a branch without the Connect fixes and verified it fails on
CI: #4065

Manually tested CardScan example app:


https://github.com/user-attachments/assets/cd9448d1-ca23-4396-82f4-05baa5430ffa
  • Loading branch information
mludowise-stripe authored Sep 30, 2024
1 parent 5f07c01 commit c8cf5d2
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 23 deletions.
4 changes: 1 addition & 3 deletions Stripe.xcworkspace/xcshareddata/swiftpm/Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/erikdoe/ocmock",
"state" : {
"branch" : "master",
"revision" : "05cbc9d934e84ad32530fa53fd7d29c7966d5828"
"revision" : "2c0bfd373289f4a7716db5d6db471640f91a6507"
}
},
{
Expand All @@ -40,4 +39,3 @@
],
"version" : 2
}

Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,34 @@
ReferencedContainer = "container:Stripe3DS2/Stripe3DS2.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "41D17A3F2C5A73A6007C6EE6"
BuildableName = "StripeConnect.framework"
BlueprintName = "StripeConnect"
ReferencedContainer = "container:StripeConnect/StripeConnect.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "NO"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "41D17A492C5A73A7007C6EE6"
BuildableName = "StripeConnectTests.xctest"
BlueprintName = "StripeConnectTests"
ReferencedContainer = "container:StripeConnect/StripeConnect.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
Expand Down
4 changes: 2 additions & 2 deletions Stripe/Stripe.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -2264,8 +2264,8 @@
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/erikdoe/ocmock";
requirement = {
branch = master;
kind = branch;
kind = revision;
revision = 2c0bfd373289f4a7716db5d6db471640f91a6507;
};
};
/* End XCRemoteSwiftPackageReference section */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,9 +356,9 @@ class ScanBaseViewController: UIViewController, AVCaptureVideoDataOutputSampleBu
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
ScanBaseViewController.isAppearing = true
/// Set beginning of scan session
// Set beginning of scan session
ScanAnalyticsManager.shared.setScanSessionStartTime(time: Date())
/// Check and log torch availability
// Check and log torch availability
ScanAnalyticsManager.shared.logTorchSupportTask(
supported: videoFeed.hasTorchAndIsAvailable()
)
Expand Down Expand Up @@ -413,22 +413,20 @@ class ScanBaseViewController: UIViewController, AVCaptureVideoDataOutputSampleBu
from connection: AVCaptureConnection
) {
if self.machineLearningSemaphore.wait(timeout: .now()) == .success {
ScanBaseViewController.machineLearningQueue.async {
self.captureOutputWork(sampleBuffer: sampleBuffer)
guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer),
let fullCameraImage = pixelBuffer.cgImage() else {
self.machineLearningSemaphore.signal()
return
}
}
}

func captureOutputWork(sampleBuffer: CMSampleBuffer) {
guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
return
}

guard let fullCameraImage = pixelBuffer.cgImage() else {
return
ScanBaseViewController.machineLearningQueue.async { [weak self] in
self?.captureOutputWork(fullCameraImage: fullCameraImage)
self?.machineLearningSemaphore.signal()
}
}
}

func captureOutputWork(fullCameraImage: CGImage) {
// confirm videoGravity settings in previewView. Calculations based on .resizeAspectFill
DispatchQueue.main.async {
assert(self.previewView?.videoPreviewLayer.videoGravity == .resizeAspectFill)
Expand Down Expand Up @@ -472,9 +470,9 @@ class ScanBaseViewController: UIViewController, AVCaptureVideoDataOutputSampleBu
// MARK: - OcrMainLoopComplete logic
func complete(creditCardOcrResult: CreditCardOcrResult) {
ocrMainLoop()?.mainLoopDelegate = nil
/// Stop the previewing when we are done
// Stop the previewing when we are done
self.previewView?.videoPreviewLayer.session?.stopRunning()
/// Log total frames processed
// Log total frames processed
ScanAnalyticsManager.shared.logMainLoopImageProcessedRepeatingTask(
.init(executions: self.getScanStats().scans)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,22 @@ import UIKit
/// Protocol used to dependency inject `UIApplication.open` for use in tests
protocol ApplicationURLOpener {
func canOpenURL(_ url: URL) -> Bool
func open(_ url: URL, options: [UIApplication.OpenExternalURLOptionsKey: Any], completionHandler completion: ((Bool) -> Void)?)
func open(
_ url: URL,
options: [UIApplication.OpenExternalURLOptionsKey: Any],
completionHandler completion: OpenCompletionHandler?
)
}

extension ApplicationURLOpener {
// The signature of the `completionHandler` in UIApplication.open changed
// in Xcode 16
#if compiler(>=6.0)
typealias OpenCompletionHandler = @MainActor @Sendable (Bool) -> Void
#else
typealias OpenCompletionHandler = (Bool) -> Void
#endif

func openIfPossible(_ url: URL) {
guard canOpenURL(url) else {
// TODO: MXMOBILE-2491 Log as analytics when url can't be opened.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,13 +157,13 @@ class MockNavigationAction: WKNavigationAction {

class MockURLOpener: ApplicationURLOpener {
var canOpenURLOverride: ((_ url: URL) -> Bool)?
var openURLOverride: ((_ url: URL, _ options: [UIApplication.OpenExternalURLOptionsKey: Any], _ completion: ((Bool) -> Void)?) -> Void)?
var openURLOverride: ((_ url: URL, _ options: [UIApplication.OpenExternalURLOptionsKey: Any], _ completion: OpenCompletionHandler?) -> Void)?

func canOpenURL(_ url: URL) -> Bool {
canOpenURLOverride?(url) ?? false
}

func open(_ url: URL, options: [UIApplication.OpenExternalURLOptionsKey: Any], completionHandler completion: ((Bool) -> Void)?) {
func open(_ url: URL, options: [UIApplication.OpenExternalURLOptionsKey: Any], completionHandler completion: OpenCompletionHandler?) {
openURLOverride?(url, options, completion)
}
}
31 changes: 31 additions & 0 deletions bitrise.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ stages:
workflows:
- framework-tests: {}
- test-builds-xcode-15: {}
- test-builds-xcode-16: {}
- test-builds-vision: {}
- install-tests-non-carthage: {}
- lint-tests: {}
Expand All @@ -53,7 +54,9 @@ stages:
workflows:
- framework-tests-no-mocks: {}
- test-builds-xcode-15: {}
- test-builds-xcode-16: {}
- test-builds-xcode-15-release: {}
- test-builds-xcode-16-release: {}
- test-builds-vision: {}
- deploy-docs: {}
- install-tests-non-carthage: {}
Expand All @@ -71,6 +74,7 @@ stages:
stage-nightly-all:
workflows:
- test-builds-xcode-15-release: {}
- test-builds-xcode-16-release: {}
- framework-tests-no-mocks: {}
- check-docs: {}
- legacy-tests-15: {}
Expand Down Expand Up @@ -348,6 +352,21 @@ workflows:
bitrise.io:
stack: osx-xcode-15.0.x
machine_type_id: g2-m1.8core
test-builds-xcode-16:
steps:
- xcode-build-for-test@2:
inputs:
- scheme: AllStripeFrameworks
- destination: generic/platform=iOS Simulator
- deploy-to-bitrise-io@2: {}
envs:
- DEFAULT_TEST_DEVICE: platform=iOS Simulator,name=iPhone 15,OS=18.0
before_run:
- prep_all
meta:
bitrise.io:
stack: osx-xcode-16.0.x
machine_type_id: g2-m1.8core
test-builds-xcode-15-release:
steps:
- script@1:
Expand All @@ -360,6 +379,18 @@ workflows:
bitrise.io:
stack: osx-xcode-15.0.x
machine_type_id: g2-m1.8core
test-builds-xcode-16-release:
steps:
- script@1:
inputs:
- content: xcodebuild build -workspace "Stripe.xcworkspace" -scheme "AllStripeFrameworks" -configuration "Release" -sdk "iphonesimulator" | xcpretty
title: Build release builds
before_run:
- prep_all
meta:
bitrise.io:
stack: osx-xcode-16.0.x
machine_type_id: g2-m1.8core
test-builds-vision:
steps:
- xcode-build-for-test@2:
Expand Down

0 comments on commit c8cf5d2

Please sign in to comment.