I've tried to use XCTKeyPathExpectation in an async environment and could not get it to work. The error I'm getting is confusing me a lot. Because the error stated is not really an error in my opinion...
I've created a very simple test to see if I did something wrong. Using the following two classes:
TestMock.swift:
import Foundation
@testable import UnitTests
final class TestMock: NSObject {
    @objc private(set) var testCalled: Bool = false
    func test() {
        self.testCalled = true
    }
}
UnitTestsTests.swift:
import XCTest
@testable import UnitTests
final class UnitTestsTests: XCTestCase {
    var testMock: TestMock!
    override func setUpWithError() throws {
        self.testMock = TestMock()
        // Put setup code here. This method is called before the invocation of each test method in the class.
    }
    override func tearDownWithError() throws {
        self.testMock = nil
        try super.tearDownWithError()
        // Put teardown code here. This method is called after the invocation of each test method in the class.
    }
    func testExample() throws {
        let expectation = XCTKeyPathExpectation(keyPath: \TestMock.testCalled,
                                                observedObject: self.testMock,
                                                expectedValue: true)
        self.testMock.test()
        self.wait(for: [expectation], timeout: 1.0)
    }
}
It gives me the error: testExample(): Asynchronous wait failed: Exceeded timeout of 1 seconds, with unfulfilled expectations: "Expect value of 'Swift.ReferenceWritableKeyPath<UnitTestsTests.TestMock, Swift.Bool>' of <UnitTestsTests.TestMock: 0x600003bfc090> to be 'true', was 'true'".
The only thing I can think of is that is comparing a Swift.Bool with an Objective-C Bool. But not sure how to fix this.
 
                        
It appears that I needed to add the word 'dynamic' to the declaration of testCalled.