'Testing storyboard from within Swift Package

In the middle of factoring code from an app into a swift package and ran into a weird issue while adding test coverage.

I reference the app's main storyboard in the method below, which I've moved into the swift package already...

    /// This method decorates self by setting backgroundColor and adding SKLabelNode with text derived
    /// from `model` parameter. After 3 seconds, the method exits sprite scene to return to view controller
    /// for high scores scene.
    ///
    /// - parameter model: Game Over data model used to derive SKLabelNode text.
    func decorateGameOverScene(from model: GameEndData) {
        backgroundColor = .black
        
        let label = createGameOverLabel(from: model)
        label.run(
            SKAction.sequence([
                                SKAction.wait(forDuration: 3.0),
            SKAction.removeFromParent()]),
            withKey: "FadeoutAction")
        addChild(label)

        DispatchQueue.main.asyncAfter(deadline: .now() + 2.6) { [weak self] in
            let storyboard = UIStoryboard(name: "Main", bundle: nil)
            let vc: UIViewController
            let id = "HighScoreViewController"
            if #available(iOS 13.0, *) {
                vc = storyboard.instantiateViewController(identifier: id)
            } else {
                vc = storyboard.instantiateViewController(withIdentifier: id)
            }

            self?.switchFromSpriteKit(to: vc)
        }
    }

Now this works as expected when I run the app.

When I started adding test coverage I was having trouble accessing the storyboard, because the app didn't have targets for the package and when I tried adding the storyboard to the package I didn't see how to target it.

To solve that I used @testable to reference the app itself in the unit tests. This sort of worked, in that when I run the unit tests in just that file everything is cool and passes.

However, when I run the entire set of unit tests an expectation wait that I've setup (which is really just pausing in another thread to allow a certain amount of time to pass while views are loading on main thread), gets hung up with an error trying to reference the storyboard.

    func waitToFire() {
        let expectation = XCTestExpectation()
        let q = DispatchQueue(label: "Test Queue")
        q.asyncAfter(deadline: .now() + 1.0) {
            expectation.fulfill()
        }
        wait(for: [expectation], timeout: 2.0)
    }

Thread 1: "Could not find a storyboard named 'Main' in bundle NSBundle </Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Agents> (loaded)"

Probably I need to move the storyboard to the package, or make one with the same identifiers for testing, but what really confuses me is why does this only happen when the entire suit of unit tests is run?

I'm looking up how to add the storyboard to the swift package now, but don't get why the tests can pass when their test case is run, but not when all the unit tests are being run... I've checked each test file one at a time and they all pass, in case there was another issue causing the problem, and none of them are hanging. Also the dispatch queue is unique to that method.



Solution 1:[1]

When there are multiple tests running they may be trying to reference the storyboard in parallel which can cause classes to have a problem instantiating.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 cigien