I've found that if you use the PayCardsRecognizer and set up the UI programmatically, you get "UI API called on a background thread" warnings intermittently, probably like 1/5 times, when presenting the view controller.
The warning stack trace is:
=================================================================
Main Thread Checker: UI API called on a background thread: -[UIView layer]
PID: 20583, TID: 4297841, Thread name: (none), Queue name: com.sunsetlakesoftware.GPUImage.openGLESContextQueue, QoS: 0
Backtrace:
4 PayCardsRecognizer 0x0000000108781a50 -[GPUImageView createDisplayFramebuffer] + 188
5 PayCardsRecognizer 0x0000000108781be4 -[GPUImageView setDisplayFramebuffer] + 48
6 PayCardsRecognizer 0x0000000108781f50 __44-[GPUImageView newFrameReadyAtTime:atIndex:]_block_invoke + 72
7 PayCardsRecognizer 0x000000010875633c runSynchronouslyOnVideoProcessingQueue + 100
8 PayCardsRecognizer 0x0000000108781efc -[GPUImageView newFrameReadyAtTime:atIndex:] + 68
9 PayCardsRecognizer 0x00000001086cafec -[GPUImageVideoCamera updateTargetsForVideoCameraUsingCacheTextureAtWidth:height:time:] + 960
10 PayCardsRecognizer 0x00000001086cb584 -[GPUImageVideoCamera processVideoSampleBuffer:] + 1316
11 PayCardsRecognizer 0x00000001086cba50 __74-[GPUImageVideoCamera captureOutput:didOutputSampleBuffer:fromConnection:]_block_invoke + 124
12 libdispatch.dylib 0x000000010a11a338 _dispatch_call_block_and_release + 24
13 libdispatch.dylib 0x000000010a11b730 _dispatch_client_callout + 16
14 libdispatch.dylib 0x000000010a122740 _dispatch_lane_serial_drain + 744
15 libdispatch.dylib 0x000000010a1232e0 _dispatch_lane_invoke + 444
16 libdispatch.dylib 0x000000010a12e6c4 _dispatch_workloop_worker_thread + 1304
17 libsystem_pthread.dylib 0x00000001b3638b74 _pthread_wqthread + 272
18 libsystem_pthread.dylib 0x00000001b363b740 start_wqthread + 8
2020-09-17 10:00:37.740128+1000 MyProjectName[20583:4297841] [reports] Main Thread Checker: UI API called on a background thread: -[UIView layer]
PID: 20583, TID: 4297841, Thread name: (none), Queue name: com.sunsetlakesoftware.GPUImage.openGLESContextQueue, QoS: 0
Backtrace:
4 PayCardsRecognizer 0x0000000108781a50 -[GPUImageView createDisplayFramebuffer] + 188
5 PayCardsRecognizer 0x0000000108781be4 -[GPUImageView setDisplayFramebuffer] + 48
6 PayCardsRecognizer 0x0000000108781f50 __44-[GPUImageView newFrameReadyAtTime:atIndex:]_block_invoke + 72
7 PayCardsRecognizer 0x000000010875633c runSynchronouslyOnVideoProcessingQueue + 100
8 PayCardsRecognizer 0x0000000108781efc -[GPUImageView newFrameReadyAtTime:atIndex:] + 68
9 PayCardsRecognizer 0x00000001086cafec -[GPUImageVideoCamera updateTargetsForVideoCameraUsingCacheTextureAtWidth:height:time:] + 960
10 PayCardsRecognizer 0x00000001086cb584 -[GPUImageVideoCamera processVideoSampleBuffer:] + 1316
11 PayCardsRecognizer 0x00000001086cba50 __74-[GPUImageVideoCamera captureOutput:didOutputSampleBuffer:fromConnection:]_block_invoke + 124
12 libdispatch.dylib 0x000000010a11a338 _dispatch_call_block_and_release + 24
13 libdispatch.dylib 0x000000010a11b730 _dispatch_client_callout + 16
14 libdispatch.dylib 0x000000010a122740 _dispatch_lane_serial_drain + 744
15 libdispatch.dylib 0x000000010a1232e0 _dispatch_lane_invoke + 444
16 libdispatch.dylib 0x000000010a12e6c4 _dispatch_workloop_worker_thread + 1304
17 libsystem_pthread.dylib 0x00000001b3638b74 _pthread_wqthread + 272
18 libsystem_pthread.dylib 0x00000001b363b740 start_wqthread + 8
=================================================================
Main Thread Checker: UI API called on a background thread: -[UIView bounds]
PID: 20583, TID: 4297841, Thread name: (none), Queue name: com.sunsetlakesoftware.GPUImage.openGLESContextQueue, QoS: 0
Backtrace:
4 PayCardsRecognizer 0x0000000108781b0c -[GPUImageView createDisplayFramebuffer] + 376
5 PayCardsRecognizer 0x0000000108781be4 -[GPUImageView setDisplayFramebuffer] + 48
6 PayCardsRecognizer 0x0000000108781f50 __44-[GPUImageView newFrameReadyAtTime:atIndex:]_block_invoke + 72
7 PayCardsRecognizer 0x000000010875633c runSynchronouslyOnVideoProcessingQueue + 100
8 PayCardsRecognizer 0x0000000108781efc -[GPUImageView newFrameReadyAtTime:atIndex:] + 68
9 PayCardsRecognizer 0x00000001086cafec -[GPUImageVideoCamera updateTargetsForVideoCameraUsingCacheTextureAtWidth:height:time:] + 960
10 PayCardsRecognizer 0x00000001086cb584 -[GPUImageVideoCamera processVideoSampleBuffer:] + 1316
11 PayCardsRecognizer 0x00000001086cba50 __74-[GPUImageVideoCamera captureOutput:didOutputSampleBuffer:fromConnection:]_block_invoke + 124
12 libdispatch.dylib 0x000000010a11a338 _dispatch_call_block_and_release + 24
13 libdispatch.dylib 0x000000010a11b730 _dispatch_client_callout + 16
14 libdispatch.dylib 0x000000010a122740 _dispatch_lane_serial_drain + 744
15 libdispatch.dylib 0x000000010a1232e0 _dispatch_lane_invoke + 444
16 libdispatch.dylib 0x000000010a12e6c4 _dispatch_workloop_worker_thread + 1304
17 libsystem_pthread.dylib 0x00000001b3638b74 _pthread_wqthread + 272
18 libsystem_pthread.dylib 0x00000001b363b740 start_wqthread + 8
2020-09-17 10:00:46.642641+1000 MyProjectName[20583:4297841] [reports] Main Thread Checker: UI API called on a background thread: -[UIView bounds]
PID: 20583, TID: 4297841, Thread name: (none), Queue name: com.sunsetlakesoftware.GPUImage.openGLESContextQueue, QoS: 0
Backtrace:
4 PayCardsRecognizer 0x0000000108781b0c -[GPUImageView createDisplayFramebuffer] + 376
5 PayCardsRecognizer 0x0000000108781be4 -[GPUImageView setDisplayFramebuffer] + 48
6 PayCardsRecognizer 0x0000000108781f50 __44-[GPUImageView newFrameReadyAtTime:atIndex:]_block_invoke + 72
7 PayCardsRecognizer 0x000000010875633c runSynchronouslyOnVideoProcessingQueue + 100
8 PayCardsRecognizer 0x0000000108781efc -[GPUImageView newFrameReadyAtTime:atIndex:] + 68
9 PayCardsRecognizer 0x00000001086cafec -[GPUImageVideoCamera updateTargetsForVideoCameraUsingCacheTextureAtWidth:height:time:] + 960
10 PayCardsRecognizer 0x00000001086cb584 -[GPUImageVideoCamera processVideoSampleBuffer:] + 1316
11 PayCardsRecognizer 0x00000001086cba50 __74-[GPUImageVideoCamera captureOutput:didOutputSampleBuffer:fromConnection:]_block_invoke + 124
12 libdispatch.dylib 0x000000010a11a338 _dispatch_call_block_and_release + 24
13 libdispatch.dylib 0x000000010a11b730 _dispatch_client_callout + 16
14 libdispatch.dylib 0x000000010a122740 _dispatch_lane_serial_drain + 744
15 libdispatch.dylib 0x000000010a1232e0 _dispatch_lane_invoke + 444
16 libdispatch.dylib 0x000000010a12e6c4 _dispatch_workloop_worker_thread + 1304
17 libsystem_pthread.dylib 0x00000001b3638b74 _pthread_wqthread + 272
18 libsystem_pthread.dylib 0x00000001b363b740 start_wqthread + 8
I'm using Cocoapods to install the framework, and interestingly I'm not able to reproduce this issue if I use a storyboard to generate the UI. I noticed that your example project doesn't have this issue, and also uses a storyboard, which appears to work there too. If you want to try reproduce this, the view controller I'm using which runs into the issue is:
import PayCardsRecognizer
import UIKit
protocol CreditCardScanPresenting {
func scanned(name: String?, number: String?, expiryMonth: String?, expiryYear: String?)
}
final class CreditCardScanViewController: UIViewController {
private let presenter: CreditCardScanPresenting
private let cancelBag = CancelBag()
private let cardScanContainer = UIView()
private let labelContainer = UIView()
private let titleLabel = UILabel()
private let subtitleLabel = UILabel()
private lazy var recognizer = PayCardsRecognizer(
delegate: self,
resultMode: .async,
container: cardScanContainer,
frameColor: .theme(.primaryColor)
)
init(presenter: CreditCardScanPresenting) {
self.presenter = presenter
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("Init with coder not supported.")
}
override func viewDidLoad() {
super.viewDidLoad()
prepareLayout()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.navigationBar.shadowImage = UIImage()
recognizer.startCamera()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
recognizer.stopCamera()
}
}
private extension CreditCardScanViewController {
func prepareLayout() {
prepareNavigationBar()
prepareCardScanContainer()
prepareTitleLabel()
prepareSubtitleLabel()
prepareLabelContainer()
}
private func prepareNavigationBar() {
navigationItem.largeTitleDisplayMode = .never
navigationItem.leftBarButtonItem = {
let item = UIBarButtonItem(image: .asset(.closeIcon), style: .plain)
item.tintColor = .theme(.primaryColor)
item.tapPublisher
.sink { [weak self] in
self?.dismiss(animated: true, completion: nil)
}
.cancelledBy(cancelBag)
return item
}()
}
private func prepareCardScanContainer() {
view.addSubview(cardScanContainer)
cardScanContainer.constrain([.leading, .trailing], to: view)
cardScanContainer.topAnchor
.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor)
.activate()
}
private func prepareTitleLabel() {
titleLabel.text = LocalizationKey.creditCardScanTitle.localized()
titleLabel.font = .theme(.contentTitle)
titleLabel.numberOfLines = 0
titleLabel.textAlignment = .center
labelContainer.addSubview(titleLabel)
titleLabel.constrain(
[.leading, .trailing, .top],
to: labelContainer,
constants: [
.top: .contentPadding,
.leading: .contentPadding,
.trailing: -.contentPadding
]
)
}
private func prepareSubtitleLabel() {
subtitleLabel.text = LocalizationKey.creditCardScanSubtitle.localized()
subtitleLabel.font = .theme(.contentValue)
subtitleLabel.numberOfLines = 0
subtitleLabel.textAlignment = .center
labelContainer.addSubview(subtitleLabel)
subtitleLabel.constrain(
[.leading, .trailing, .bottom],
to: labelContainer,
constants: [
.bottom: -.contentPadding,
.leading: .contentPadding,
.trailing: -.contentPadding
]
)
subtitleLabel.topAnchor
.constraint(equalTo: titleLabel.bottomAnchor, constant: 8)
.activate()
}
private func prepareLabelContainer() {
view.addSubview(labelContainer)
labelContainer.constrain([.leading, .trailing, .bottom], to: view)
labelContainer.topAnchor
.constraint(equalTo: cardScanContainer.bottomAnchor, constant: 0)
.activate()
}
}
extension CreditCardScanViewController: PayCardsRecognizerPlatformDelegate {
func payCardsRecognizer(_ payCardsRecognizer: PayCardsRecognizer, didRecognize result: PayCardsRecognizerResult) {
presenter.scanned(
name: result.recognizedHolderName,
number: result.recognizedNumber,
expiryMonth: result.recognizedExpireDateMonth,
expiryYear: result.recognizedExpireDateYear
)
}
}
There's a few custom extensions used there for constraints, but you get the general idea.
I did some Googling too and it looks like a similar issue is appearing here:
BradLarson/GPUImage#2512
I'm guessing from the stack trace you guys use the GPUImage library, and might find that useful, I could be misinterpreting it though.
Good luck, and thank you for an awesome card scanning framework!
I've found that if you use the
PayCardsRecognizerand set up the UI programmatically, you get "UI API called on a background thread" warnings intermittently, probably like 1/5 times, when presenting the view controller.The warning stack trace is:
I'm using Cocoapods to install the framework, and interestingly I'm not able to reproduce this issue if I use a storyboard to generate the UI. I noticed that your example project doesn't have this issue, and also uses a storyboard, which appears to work there too. If you want to try reproduce this, the view controller I'm using which runs into the issue is:
There's a few custom extensions used there for constraints, but you get the general idea.
I did some Googling too and it looks like a similar issue is appearing here:
BradLarson/GPUImage#2512
I'm guessing from the stack trace you guys use the
GPUImagelibrary, and might find that useful, I could be misinterpreting it though.Good luck, and thank you for an awesome card scanning framework!