I have a project with 100+ gradle modules. Got a K...
# multiplatform
u
I have a project with 100+ gradle modules. Got a KMM proof of concept going, and now I'd like to start adding kmm support one by one. However ios devs tell me, there is a limit to the number of .frameworks in a single app Is this true? I cannot have say 50 kmm modules in a given ios app?
e
you want all your Kotlin modules to be built into a single monolithic framework for iOS
u
right, but why? they say you would have like max 6 frameworks, which sounds odd to me as our android project has 200
e
JVM is traditionally lazy-loaded, so adding libraries is somewhat "free": you only pay the cost for what you actually execute. however iOS is not, and frameworks are either linked at build time (if static) or startup (if dynamic). there isn't a hard limit, but it increases app launch time. https://bpoplauschi.github.io/2021/10/25/Advanced-static-vs-dynamic-libraries-and-frameworks.html
u
does static increase app start time as well?
per article it seems dynamic is slowest app startup and riskiest so why bother, build time?
e
to an extent, yes static increases app start time, but mostly as an effect that the larger an app binary is, the longer it takes to load
u
then I'm confused .. if app startup corelates with binary size, then why is it preffered to have less bigger frameworks
e
dynamic linking has its uses, but I think the biggest reason for iOS is that in previous versions, Swift required dynamic linking
maybe you're still supporting those
u
im probably confused on the difference between static and dynamic .. if dynamic is linked at runtime, is it sort of like thin vs far jars on jvm? i.e. bundled with os vs bundled with app?
e
everything on JVM is dynamic whether you have fat jars or not
u
in terms of when classes are loaded?
e
JVM will link incrementally as each class is loaded (on first use), yes
u
I see im familiar with that .. so ios dynamic framework is wholy loaded eagerly at app start?
e
depends
but typically yes
on native, binaries are loaded directly into memory, and then fixups need to be performed (due to differences such as load address) across the whole thing
u
therefore static linking "pretends" that the framework code is my own source?
e
effectively yes
u
so the downside is paying a penalty of long clean build time?
btw why is static faster to start, when as you said, on native everything is loaded into memory, regardless if its 3rd party or 1st party code .. I read it as code needs to be loaded, options are static vs dynamic, dynamic is loaded eagerly at app start, well then when is static loaded? somehow sooner? I would get if dynamic were lazy loaded like on jvm, but since its eager, doenst make much sense to me
e
static linking: linking into the binary at build time dynamic linking: linking into running executable there are multiple ways of using dyld (the dynamic linker), such as declaring in the program headers that these libraries/frameworks are required, which will cause them to be loaded immediately. you can avoid that, but need to be careful because there's no safety guards against calling a function in a library that hasn't yet been loaded (will crash)
frameworks default to dynamic because that's what makes sense on macos, where system frameworks are shared between all running executables and re-use the same memory (mostly, aside from fixups and other writable sections). static frameworks didn't even exist until recent iOS versions. static libraries were available since ~forever, but they don't support having resources
https://kotlinlang.org/docs/multiplatform-dsl-reference.html#native-targets Kotlin/Native can produce both static and dynamic frameworks
u
wait, dynamic framework binary is not included in the app binary? just expected to be in the platform? like android shim jar?
e
both
a .app/.ipa can contain internal frameworks, and the system has frameworks
u
but say app dynamically links Alamofire 3.2.1, then this code is physically able to be shared by different apps on the system?
e
no, because of iOS's sandboxing. it is possible on macos though
u
okay so, does it make sense to have dynamic frameworks on ios at all? seems only downsides
e
the biggest reason is historical
but in any case, to bring this back to Kotlin: you still want all of your Kotlin code to be exported as a single umbrella framework, regardless of static or dynamic
u
where the precedens is what, dynamic?
e
yes
because K/N follows a closed-world compilation model, you can only share Kotlin objects within a single library/framework
(same as basically everything that targets native)
Objective-C/Swift are in a bit of a privileged position because of how they're built into the system
u
and I would do that how, in terms of gradle, just have a :umbrella module which includes all other gradle modules necessary for the app business wise, and only expose umbrella.framework to the ios app?
e
yes
with re-exports of other modules as necessary, documented in links above
u
because, cost of a module loaded is mostly amortized, so 1 big with size of 1 is better than 2 with size of 0.5?
e
no. because if you have two frameworks, the type of a kotlin object in one framework is not the same as the type of a kotlin object in the other framework, even if they were compiled from the same shared module
u
oh, I see
btw if I have the .framework file, does it imply if its static or dynamic, or can it be both
also, given macOS and dynamic framework .. I would bundle the binary with app binary, i.e. it is physically there, but if some other app would already load the exactly same framework, only a single "instance" of framework is in the memory ..i.e. its only a runtime optimization (which is not the same as thin vs fat jars, where you optimize binary physical size)
e
it's one or the other
I believe memory is only shared if it is mapped from the same physical file
(memory deduplication does exist on some platforms, but I am not aware of it being a thing on macos)
u
yes but just by having .framewkrk file, I cannot tell? Would I need to inspect some internals? Or is it how it is used where the difference is? i.e. same .framework file can be both be used statically in app A and dynamically in app B?
e
you have to inspect the binary file inside the framework to determine if it is static or dynamic
u
I see btw when a pure ios project, no kotlin, pulls in dependencies via cocoapods or spm, those are dynamic. frameworks by default?
e
depends on the dependency
u
why, doesnt cocoapods by default pull in sources via git and compile on your machine?
e
it allows the dependency to declare whether it should be built as static or dynamic
u
but regardless of static or dynamic, it's built on my machine, right? Or can it just "add" a prebuilt binary as well? which is what kmm uses?
e
yes
u
btw why do they do that? why no "maven" hosting pre built binaries? historic reasons?
e
an Xcode project doesn't know how to consume artifacts from Maven
it doesn't really know anything outside of local dependencies, which is how we ended up with third-party solutions like cocoapods
u
yes but for sake of argument, let's say cocoapods is gradle, there is the maven bit still missing, and all cocoapods dependencies I've seen are pulled in via link to github repo, seems odd to me they didn't do that part, so much cpu wasted?
SPM should host the pre built binaries, right?
e
cocoapods piggy-backed off of the free-ness of GitHub instead of building their own infrastructure
I haven't used SPM but it does seem to support a more traditional package registry
u
okay but the actual linking config, that is a xcode thing, right? xcode knows nothing about cocoapods, right?
e
right. pod just puts everything into place for xcode to find
u
okay so say I have a monorepo, which's point is to share code between android and ios (and lets say ios has no 3rd party libaries) .. or lets just say a pure ios codebase, lets say its two apps, Uber and Uber-Rider, and they want to share code -- then there is no need to use cocoapods, right?
e
you can still use cocoapods with an private git repo
u
yes but let's say its a monorepo
would you still use cocoapod to dependency manage internal frameworks?
e
if you're already using pods for external dependencies then yes
u
why
e
so you can uniformly manage all the pods, both your app's direct dependencies and your internal frameworks' dependencies
u
I see, but don't you then need to
pod install
anytime you change a public api in the shared code?
e
I don't work on iOS but as far as I know, not with local pods
u
I do a little bit, hence my queries on kmm, but maybe a its a xcode thing, that I dont see latest stuff
do you by chance have any insight on how would say the Uber and UberRider projects looks like? To me it kind of feels like ios doesn't want such granular modules like android does, I could imagine that those two on android would consist of 300+ modules
or is it just a feeling? (that they would like to have 10 at most)
btw, the issue of having many dynamic frameworks - assuming pure ios environment - would that be able to be workedaround via the
umbrella
approach from kmm as well?