Can anyone tell me how I'd use Koin to inject the ...
# multiplatform
i
Can anyone tell me how I'd use Koin to inject the same instance of a repository between my app and a widget for iOS?
j
Any service locator or dependency injector you use isn't magic. It is just retaining instances for you and automatically connecting the internal dependency graph for you. The solution to sharing a dependency via some library like this is the same solution as sharing the dependency itself. If you cannot share an instance of any class between those two then you cannot share the injector (which provides you with the repository instance).
👍 1
p
Jake doesn't explicitly say this (but I believe he's hinting at it), widgets on iOS run in a totally separate process so you cannot share in-memory data between the app and widget (at least as far as I understand). We've had some success sharing a room file, sadly the multiprocess data store is not kmp yet (idk if that is on the roadmap).
👍 1
i
I see, thank you. So I can't read from datastore within my widget to get the data from a cache that my app edits?
Because I tried doing so but it looked like it was creating two separate instances of datastore as well. Which I guess I'm misunderstanding something fundamental about how datastore works because I'm saving user Preferences in datastore, and I wanted my widget to read from it. If I'm creating two separate instances of "UserRepository", shouldn't it at least still be reading from the same datastore?
Also @jw , woke up in disbelief it was actually you who responded to my message. Just want to say I'm a huge fan and you really inspired me alot throughout my career. Thank you to both of you, @Pearce Keesling for answering my question.
p
You can read from the same datastore file in both targets I just don't think that you'll get correct reactivity between the two processes. So if you write to the datastore in the app target it might not update the flow in the widget target. Technically that might not be a real problem since widgets tend to be "one-shot" in that you build the timeline all in one go so there is not as much reactivity involved. One caveat to keep in mind for both room/datastore is that the documentation will point you to pick a file path that is not shared between the two targets. You'll need to create an app group and then use that to get a path to a folder which both targets can read from. For example we use this
Copy code
fun getDataFolderPath() = requireNotNull(NSFileManager.defaultManager.containerURLForSecurityApplicationGroupIdentifier("group.com.your.identifier")).path
i
So could that be why it's acting as if it's two separate datastores? Here's how I'm initializing it:
Copy code
actual class DatastoreFactory {
    actual fun createDatastore(): DataStore<Preferences> {
        return initDataStore {
            val directory = NSFileManager.defaultManager.URLForDirectory(
                directory = NSDocumentDirectory,
                inDomain = NSUserDomainMask,
                appropriateForURL = null,
                create = false,
                error = null
            )
            requireNotNull(directory).path() + "/${Constants.DATASTORE_FILE_NAME}"
        }
    }
}
Seems like you're using the App group which is so that apps can read from the same files. But I didn't think widgets and the app would have different directories
p
It has been a while since I went through this process, but go ahead and log out the
director.path()
on both targets and make sure they are the same. If I remember correctly they were in different locations for us
👍 1
i
Sweet, okay I'll try that
p
And I think app groups are always required to share data between targets https://michael-kiley.medium.com/sharing-object-data-between-an-ios-app-and-its-widget-a0a1af499c31
Now that your data is ready to be written and read externally, we need access to shared storage for that data. We will accomplish this by creating an App Group and adding both the app and the widget to it. Normally, iOS isolates each app into its own container; App Groups are the operating system’s way of letting apps access shared containers and communicate through interprocess communication, which is exactly what you will need for your app and widget.
That's just the result of a random google though so take it with a grain of salt
👍 1
i
I sometimes am getting an error in the that says ComposeApp is not found. I was trying to see if there's an issue on the Gradle side but I haven't touched anything since the initial configuration of the project. It was working before but now I'm not sure what happened.
p
I'd probably need a little more context to know what is going on
i
I created the widget extension and I'm trying to import the ComposeApp static library that houses all my common code but when I add the import, it just keeps saying ComposeApp not found. Works perfectly fine for the actually app
p
Have you added the ComposeApp static library as a dependency for that target?
Each target has its own set of dependencies
i
I was thinking that might be the case but I don't even see that as an option. Let me screenshot
p
How are you adding your shared code to the app target? If it is a Build Phases step then you'll have to add another matching build step to the widget target
We do it using cocoapods since it is a hassle to get both of those build phases to cooperate well
(Fwiw I am not an iOS expert at all, just fumbled around enough to get our app to build)
👍 1
i
Copy code
listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach { iosTarget ->
        iosTarget.binaries.framework {
            baseName = "ComposeApp"
            isStatic = true
        }
    }
This was the default implementation
Then let me check xcode
Here's what I see under frameworks and libraries
And then the script there is under the app target
no script like that for the widget target
(Fwiw I am not an iOS expert at all, just fumbled around enough to get our app to build)
No worries! I really appreciate your help. I am an Android developer just fumbling around too lol
p
no script like that for the widget target
You might want to try adding an equivalent under the widget target
i
I can't get the script working but basically if I comment out all the ComposeApp references and then build the mobile app, the widget extension recognizes the ComposeApp. So I'm guessing it is the script, that's the problem
👍 1