1

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.

lldb works

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.

nuKs
  • 134
  • 1
  • 1
  • 10
  • I'm assuming you cannot cast IXNMuse to _. Try to use let object = muses.first as? IXNMuse – tereks Apr 02 '18 at 03:35
  • Unfortunately this is not working, I get the same error. – nuKs Apr 02 '18 at 17:51
  • Also, Quick Help (alt + click on `first`) recognize the type IXNMuse (it states `var first: IXNMuse? { get }` in declaration) – nuKs Apr 02 '18 at 17:53
  • I had to wrote the module map for the muse library (wrapped from objective c framework). My target is a swift framework. I am wondering if the issue may be related as I don't have the issue in similar non-framework code, but I have to admit I am a complete newbie with both languages so this may be completely unrelated as well. – nuKs Apr 02 '18 at 17:56
  • Please show the definition of `getMuses()`. The error happens when any of the element of the result array is non-`IXNMuse`. – OOPer Apr 02 '18 at 18:10
  • I do not have access to the definition of getMuses as the objective c framework is proprietary. The declaration is automatically generated as `public func getMuses() -> [IXNMuse]` by xcode (djinni) from the module map I wrote. The true objc declaration is `- (nonnull NSArray *)getMuses;` (cf. http://ios.choosemuse.com/_i_x_n_muse_manager_8h_source.html). Perhaps there is something I don't understand in the pointer mapping process. – nuKs Apr 02 '18 at 18:18
  • I modified the question to state additional facts suggesting the item is indeed an IXNMuse memory-wise. – nuKs Apr 02 '18 at 19:04
  • 1
    Thanks for adding the declarations. But no info included which I can find some clue to solve your issue. What do you get if you put `print(type(of: (muses as NSArray).firstObject!))` before the line causing the crash? – OOPer Apr 02 '18 at 19:16
  • Your help has been greatly appreciated! It shows `IXNMuse`, I swear it does :-) I did a few more attempts based on your suggestion, and I also get: `Could not cast value of type 'IXNMuse' (0x103cfa180) to 'IXNMuse' (0x103e965e0).` It seems I have two IXNMuse type. – nuKs Apr 02 '18 at 19:30
  • Seems you are very near to what causes this issue. I'm afraid I cannot be much help of you, but really hope you can solve it soon. – OOPer Apr 02 '18 at 19:36

1 Answers1

1

Edited answer

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).

Original Answer

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.

Fix procedure

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.

Conclusion

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.

nuKs
  • 134
  • 1
  • 1
  • 10