Reputation: 144
I've found numerous post about the NSArray element failed to match the Swift Array Element type
error. However, I still can't get this to work. I suspect the problem is related to the objective C framework binding I made and is not a casting issue. Among others, one thing I do not understand is why the code work inside a lldb p
instruction while not as an unit test executed within host ios application (I can't test it otherwise as the code requires B-LE which is not available from the iphone simulator).
class onceFirstMuseConnected: IXNMuseListener {
let museManager: IXNMuseManager
let callback: (_ muse: IXNMuse) -> Void
init(museManager: IXNMuseManager, callback: @escaping (_ muse: IXNMuse) -> Void) {
self.museManager = museManager
self.callback = callback
museManager.setMuseListener(self)
museManager.startListening()
}
func museListChanged() {
let muses: [IXNMuse] = museManager.getMuses()
guard muses.count > 0 else {
return
}
let _ = muses.first!.getName() // <<<< `muses.first` fails as well as any other
// kind of array's item access with "Fatal
// error: NSArray element failed to match the
// Swift Array Element type"
}
}
What's weird is I am able to make the line work in lldb as shown on the image below.
edit: As I've got multiple downvote, I would appreciate an explanation on why the lldb line work in comment while the main execution crashes. I assume the lldb line wouldn't work if it was a type conversion issue. None of the related questions I found answer this. I admit I am a swift/objc newbie, there is likely something obvious I don't understand if it is not an issue related to the objc-swift framework mapping process.
The getMuses
function is wrapped by djinni using a module map I wrote to bind a proprietary objective C framework to swift. Here is the module map I wrote. I have had no issue with the other function bindings yet (see comments). Removing the [system]
attribute don't trigger any additional warning.
module Muse [system] {
header "Muse.framework/Headers/Muse.h"
export *
}
The djinni generated declaration:
public func getMuses() -> [IXNMuse]
The original objc declaration:
- (nonnull NSArray<IXNMuse *> *)getMuses;
The definition is unavailable as the framework is proprietary.
The returned pointer is unlikely to be null or to point out a wrong memory address as the lldb-made call to the getName method shown in the screenshot wouldn't give proper result if so.
Upvotes: 1
Views: 182
Reputation: 144
Muse.framework is a static framework. XCTests are built in a bundle embedded in the host app. In my setup, there are 3 targets involved: the framework I develop, the host app for test and the XCTest bundle.
Static framework embed symbols that are used inside the target binaries.
To avoid duplicate symbol issues, static frameworks should only be linked to the target framework**, not the xctest bundle nor the test host app, which transitionally link the symbols by linking the target framework.
To make sure the target framework embed all the symbols of its statically linked framework dependency, the -force_load flag followed by the static dependency path has to be added to the target framework linking options (OTHER_LDFLAGS).
Credits goes to OOPer for help in the question's comment.
For a reason I don't understand, I got a double definition of my IXNMuse class. Swift conflicting between the two definition has been the cause of the NSArray element failed to match the Swift Array Element type
error I had.
The issue disappeared while I did the following procedure:
A. While testing the following code
let muses: NSArray = museManager.getMuses() as NSArray
let muse: IXNMuse = muses.firstObject! as! IXNMuse
I received at runtime
Could not cast value of type 'IXNMuse' (0x103cfa180) to 'IXNMuse' (0x103e965e0).
B. I've had two import Muse
statement, one in my test file, one in one of my framework target source file.
Removing the import Muse
statement from the test file (with the related code). This let the test passes by removing the double IXNMuse definition.
C. I removed the Muse.framework
from the Link Binary with Libraries
of my test target as it was already included in my tested framework target. I put back the import Muse
statement from the test file (with the related code, as I still needed it) I just had removed.
The test still passed even with dual Muse import in both targets source files.
D. I put back the Muse.framework
back to the Link Binary with Libraries
of my test target (I believe it not to be an error to have both targets link the same framework with triangular dependency). I thus came back to a state similar to the one I was into while writing the question at first. The issue didn't reappear and test still passes.
I am still a bit confused why I had the issue at first. It is possible I wasn't exactly in the same state I though I was and did something in the process while changing my linking config I don't remember.
Upvotes: 1