realm-swift
realm-swift copied to clipboard
Realm package as a dynamic framework
Problem
We have an SDK which depends on Realm and we distribute it as an XCFramework. When our clients include this xcframework into their projects, they are seeing a lot of logs similar to this one:
(Let's say our SDK name is OurSDK and our client's project is ClientProject)
Class RLMAppConfiguration is implemented in both /.../DerivedData/ClientProject/.../OurSDK.framework/OurSDK (0x112b46fb0) and /.../CoreSimulator/.../ClientProject.app/ClientProject (0x10c9a89a8). One of the two will be used. Which one is undefined.
We understand the reason for this is because Realm is statically linked to both the XCFramework and the Client's project, and having Realm as a dynamic framework would solve the issue.
Also, the app is crashing at some point with the following stacktrace:
*** Terminating app due to uncaught exception 'RLMException', reason: 'null
Exception backtrace:
0 OurSDK 0x000000010dd74bc2 _ZN5realm15InvalidTableRefC2EPKc + 58
1 OurSDK 0x000000010dd74a6c _ZNK5realm13ConstTableRef5checkEv + 62
2 OurSDK 0x000000010dd79511 _ZN5realm9TableView7do_syncEv + 205
3 OurSDK 0x000000010dc785ea _ZN5realm5Query8find_allERKNS_18DescriptorOrderingE + 246
4 OurSDK 0x000000010dbbd9f6 _ZN5realm7Results17ensure_up_to_dateENS0_12EvaluateModeE + 476
5 OurSDK 0x000000010dbbd90c _ZN5realm7Results17ensure_up_to_dateENS0_12EvaluateModeE + 242
6 OurSDK 0x000000010dbc5094 _ZN5realm7Results24evaluate_query_if_neededEb + 58
7 OurSDK 0x000000010d9dee64 -[RLMResults countByEnumeratingWithState:objects:count:] + 49
8 libswiftFoundation.dylib 0x00007fff5999eb82 $s10Foundation25NSFastEnumerationIteratorV4nextypSgyF + 258
9 OurSDK 0x000000010da81abe block_destroy_helper.47 + 4766
10 OurSDK 0x000000010d6845ef block_destroy_helper + 40159
11 OurSDK 0x000000010d661d9d __swift_destroy_boxed_opaque_existential_0 + 4173
12 OurSDK 0x000000010d66130e __swift_destroy_boxed_opaque_existential_0 + 1470
13 OurSDK 0x000000010d69fa88 __swift_memcpy24_8 + 16712
14 OurSDK 0x000000010d6c8bb3 block_destroy_helper + 9123
15 OurSDK 0x000000010d6c7cb7 block_destroy_helper + 5287
16 OurSDK 0x000000010d6cc7da block_destroy_helper + 810
17 OurSDK 0x000000010d838005 __swift_memcpy153_8 + 5797
18 OurSDK 0x000000010d839dce __swift_memcpy153_8 + 13422
19 OurSDK 0x000000010d839dfb __swift_memcpy153_8 + 13467
20 OurSDK 0x000000010de8eb66 __swift_memcpy0_1 + 24822
21 OurSDK 0x000000010de99d3e __swift_memcpy0_1 + 70350
22 OurSDK 0x000000010de99aa5 __swift_memcpy0_1 + 69685
23 OurSDK 0x000000010de78ac5 __swift_memcpy40_8 + 127461
24 OurSDK 0x000000010de99b50 __swift_memcpy0_1 + 69856
25 OurSDK 0x000000010de72a13 __swift_memcpy40_8 + 102707
26 OurSDK 0x000000010de28020 block_destroy_helper + 1072
27 OurSDK 0x000000010de28110 block_destroy_helper + 1312
28 OurSDK 0x000000010de03fb6 __swift_memcpy16_8 + 310
29 OurSDK 0x000000010de03a60 __swift_noop_void_return + 2944
30 OurSDK 0x000000010de6fce0 __swift_memcpy40_8 + 91136
31 OurSDK 0x000000010d83964c __swift_memcpy153_8 + 11500
32 OurSDK 0x000000010d838a61 __swift_memcpy153_8 + 8449
33 OurSDK 0x000000010d83b999 __swift_memcpy153_8 + 20537
34 OurSDK 0x000000010d858153 block_destroy_helper + 48931
35 OurSDK 0x000000010d783fe5 __swift_assign_boxed_opaque_existential_1 + 72437
36 OurSDK 0x000000010d77ea8d __swift_assign_boxed_opaque_existential_1 + 50589
37 OurSDK 0x000000010d7964a8 block_destroy_helper + 1608
38 OurSDK 0x000000010d7787d2 __swift_assign_boxed_opaque_existential_1 + 25314
39 CFNetwork 0x00007fff23e48782 CFNetwork + 30594
40 CFNetwork 0x00007fff23e63ed8 _CFHTTPMessageSetResponseProxyURL + 16299
41 libdispatch.dylib 0x000000010b1d1816 _dispatch_call_block_and_release + 12
42 libdispatch.dylib 0x000000010b1d2a5b _dispatch_client_callout + 8
43 libdispatch.dylib 0x000000010b1d93bd _dispatch_lane_serial_drain + 855
44 libdispatch.dylib 0x000000010b1da123 _dispatch_lane_invoke + 490
45 libdispatch.dylib 0x000000010b1e6601 _dispatch_workloop_worker_thread + 907
46 libsystem_pthread.dylib 0x00007fff701c7074 _pthread_wqthread + 326
47 libsystem_pthread.dylib 0x00007fff701c5ffb start_wqthread + 15'
terminating with uncaught exception of type NSException
Would it be possible to have Realm as a dynamic framework for SPM?
Solution
Probably the solution for this would be to declare two new different products in Package.swift
, like:
...
products: [
.library(
name: "Realm",
targets: ["Realm"]),
.library(
name: "RealmSwift",
targets: ["Realm", "RealmSwift"]),
.library(
name: "Realm-Dynamic",
type: .dynamic,
targets: ["Realm"]),
.library(
name: "RealmSwift-Dynamic",
type: .dynamic,
targets: ["Realm", "RealmSwift"]
)
],
...
I'm not sure about dependencies like RealmDatabase
probably we would need a "RealmDatabase-Dynamic
" as well.
How important is this improvement for you?
Dealbreaker
A XCFramework with a dependency which the consuming app also has as a dependency is a scenario that SPM currently doesn't handle at all. Building the dependency as a dynamic library doesn't fully fix the problem by itself. In the simplest case of the app consuming your xcframework and nothing else, it can work but requires the app to manually ensure that it's always pinning the exact version of Realm your xcframework was built again. In more complicated scenarios (e.g. if they have a second Swift package that relies on Realm), it just can't work.
My recommendation would be to distribute a Realm XCFramework along with your SDK's XCFramework, rather than hoping that the copy of Realm built as part of the app will be ABI-compatible with the one your SDK was built against.
Thanks for your answer @tgoyne! The problem is that the consuming app does not have this dependency itself, only our xcframework has it. In fact, I'm reproducing this with an empty project which only has our sdk as dependency. For some reason it looks like the dependencies from xcframework are installed twice.
Hello again, we are reproducing this crash on any app that has our SDK (XCFramework) integrated via SPM. The weird thing is that this is only happening on simulators, but not on physical devices.
If we install the SDK through CocoaPods, we also receive a crash but with a different error:
Could not cast value of type '__C.RealmSwiftObject.Type' (0x10771ca88) to '__C.RLMObjectBase.Type' (0x10771d318).
Does anybody have any clue how can we get around these crashes? This is completely blocking our SDK clients.
Hi @rserentill let me first try to understand your setup so we can reproduce it, you have RealmSwift through SPM as a dependency in your framework, and then you want to distribute it as a xcframework?.
Hello @dianaafanador3, thank you for answering! Yes, that's correct. Let me clarify that our SDK is not an Xcode project, it's a Swift Package. This swift package has RealmSwift as a dependency. When we need to publish a new SDK version, we archive this swift package for both device and simulator architectures, and we generate an XCFramework with these two archives.
Also, as a workaround to work with dependencies, we have a wrapper target, as suggested in this swift forum thread: https://forums.swift.org/t/issue-with-third-party-dependencies-inside-a-xcframework-through-spm/41977/2
We are also using @_implementationOnly import RealmSwift
in all of our files using Realm.
@rserentill In your comment above I saw you have an empty project with a reproduction of the issue, can you push this a repository?.
@dianaafanador3 is there any way I can share it with you privately? the SDK consumes protected data and it uses client credentials to authenticate to our backend services.
@dianaafanador3 I forgot to mention that the crash is only happening when we include the SDK as an XCFramework. If we add the swift package directly, without a binary, it doesn't crash.
@rserentill you can send it to [email protected]
@dianaafanador3 I have just sent the project to the email address you provided 😊
Hi @rserentill I can run the app and go to the view in question without any crashes when using Xcode 14 on an iPhone 14 simulator.
Hello @leemaguire, thank you for your answer! That's weird.. We can reproduce the crash 100% of the times with this project. In both Xcode 13 and 14. We have also tried with both Intel and M1 chips, same result. So you open the project, click on the red button and you can read the magazine's content without a crash?
@rserentill yep I tap 'Open Magazine' and it shows me content.
@leemaguire Do you have Intel or Apple chip? Just trying to figure out what's different, although we've been able to reproduce it in both. Could you please try to reset SPM cache? This only happens to us if we use the xcframework binary, but it doesn't happen using the package with the source code in it. Maybe you have realm-core project locally and somehow Xcode is recognising it?
I have the Apple chip. I checked the SPM checkouts and its not referencing any local versions of Realm Core. I also just checked it with Xcode 13.4.1 and it runs fine there too.
And you're testing it on Simulator, right?
yeah
Hi all,
any update on this front?
Class RLMAppConfiguration is implemented in both /.../DerivedData/ClientProject/.../OurSDK.framework/OurSDK (0x112b46fb0) and /.../CoreSimulator/.../ClientProject.app/ClientProject (0x10c9a89a8). One of the two will be used. Which one is undefined.
I have exactly this flow:
- RealmSwift through SPM as a dependency in a framework,
- and then I want to distribute it as a xcframework.