Skip to content

UI API called from background thread warnings #48

@MightyFine

Description

@MightyFine

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!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions