<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ryan Belz</title>
    <description>The latest articles on DEV Community by Ryan Belz (@rynbelz162).</description>
    <link>https://dev.to/rynbelz162</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F871212%2Fe577eb47-cc5d-401f-9f55-b761bec1a866.jpeg</url>
      <title>DEV Community: Ryan Belz</title>
      <link>https://dev.to/rynbelz162</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rynbelz162"/>
    <language>en</language>
    <item>
      <title>Learning Kotlin Multiplatfom Mobile: Entry 3.5</title>
      <dc:creator>Ryan Belz</dc:creator>
      <pubDate>Tue, 30 Aug 2022 01:47:02 +0000</pubDate>
      <link>https://dev.to/rynbelz162/learning-kotlin-multiplatfom-mobile-entry-35-4d44</link>
      <guid>https://dev.to/rynbelz162/learning-kotlin-multiplatfom-mobile-entry-35-4d44</guid>
      <description>&lt;p&gt;This is a continuation of the last post &lt;a href="https://dev.to/rynbelz162/learning-kmm-entry-3-24am"&gt;Learning KMM: Entry 3&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Instead of installment 4, I went ahead and used my big developer brain and incremented this version number to 3.5 because it does not truly feel like a version 4.&lt;/p&gt;




&lt;h2&gt;
  
  
  📝 Intro
&lt;/h2&gt;

&lt;p&gt;In the last post we went over the Model and Intent part of MVI. In this post we are going to be going over the View part.&lt;/p&gt;

&lt;p&gt;In the previous posts in this series I had already made two basic login screens using both Jetpack Compose and SwiftUI &lt;a href="https://dev.to/rynbelz162/learning-kmm-entry-2-18b"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post is mainly going to be about using MVI Kotlin in tandem with Decompose to create components that I can wire up to my views.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎶 Decompose
&lt;/h2&gt;

&lt;p&gt;So what exactly is Decompose and why do I need to use it? Well you don't exactly need to use Decompose to use the created stores in MVI Kotlin if you are using a declarative UI like Jetpack Compose and SwiftUI. However, Decompose makes it easier to create life-cycle aware components (which is super helpful on android) and comes with a multi-platform router for navigation.&lt;/p&gt;

&lt;p&gt;Because of this you are able to now share even more code between apps (navigation logic) and you have a better separation of concerns between UI and non-UI code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://arkivanov.github.io/Decompose/"&gt;Feel free to read more on the overview of Decompose here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🌳 The Root Component
&lt;/h2&gt;

&lt;p&gt;Each view is going to wire up to a component but all components need to descend from a parent component. The beginning component is going to be the component for the entire application which will be referred to as the "Root" component.&lt;/p&gt;

&lt;p&gt;The root component is also where the shared router navigation is going to live. Inside the root component you can find configurations for children components as well as the current router state of the application.&lt;/p&gt;

&lt;p&gt;Im going to gloss over a bit of the implementation details of the root component to keep the focus on the Login view. I will probably revisit this later when talking about navigation within the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤖 Login Component
&lt;/h2&gt;

&lt;p&gt;Once the scaffolding of the root component is set up we can continue to wire up our Login View to the Login Store.&lt;/p&gt;

&lt;p&gt;Since we will define the initial component of the Root component as the Login Component, we will automatically be directed to this Login View once the application loads.&lt;/p&gt;

&lt;p&gt;Android&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@OptIn(com.arkivanov.decompose.ExperimentalDecomposeApi::class)
@Composable
fun RootContent(component: Root) {
    Children(routerState = component.routerState) {
        when (val child = it.instance) {
            is Root.Child.Login -&amp;gt; LoginScreen(child.component)
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;iOS&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;struct RootView: View {
    @ObservedObject
        private var routerStates: ObservableValue&amp;lt;RouterState&amp;lt;AnyObject, RootChild&amp;gt;&amp;gt;

    init(_ component: Root) {
        self.routerStates = ObservableValue(component.routerState)
    }

    var body: some View {
        let child = self.routerStates.value.activeChild.instance

        switch child {

            case let login as RootChild.Login:
                LoginView(login.component)

            default: EmptyView()
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In both of these logic blocks you can see we are switching the specific type of "Child" configuration to the LoginView or LoginScreen and passing the child component which is our "LoginComponent".&lt;/p&gt;

&lt;p&gt;You can see this login component here&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class LoginComponent(
    componentContext: ComponentContext,
    storeFactory: StoreFactory
) : Login, ComponentContext by componentContext {

    private val store = instanceKeeper.getStore {
        loginStore(storeFactory)
    }

    override val models: Value&amp;lt;Login.Model&amp;gt; = store.asValue().map(stateToModel)

    override fun onLogin() =
        store.accept(Intent.Login(models.value.username, models.value.password))

    override fun onPasswordChanged(password: String) =
        store.accept(Intent.UpdatePassword(password))

    override fun onUsernameChanged(username: String) =
        store.accept(Intent.UpdateUsername(username))
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Essentially what this class is doing is forwarding the actions invoked by the view onto the Login Store from the previous post. The model of the Login Store is also being exposed in a way that can be consumed in both Jetpack Compose and SwiftUI.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎶 Compose Code
&lt;/h2&gt;

&lt;p&gt;Consuming the store is very straight forward using Jetpack Compose and looks like the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LoginScreen(component: Login)
{
    val state by component.models.subscribeAsState()

    // omitting some code for brevity
    ...

    OutlinedTextField(
        value = state.username,
        onValueChange = component::onUsernameChanged
    )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🐣 SwiftUI Code
&lt;/h2&gt;

&lt;p&gt;Consuming the store in SwiftUI is a little more complex than android. It involves some creating of quite a lot of extension code to wrap the observable value, component router state, and exposing the library values from MVI Kotlin and Essenty that we accomplished using gradle in part one of this blog post. Once all of the helper extensions are set up you are left with something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;struct LoginView: View {
    private var component: Login

    @ObservedObject
    private var models: ObservableValue&amp;lt;LoginModel&amp;gt;

    @State private var isSecured: Bool = true

    init(_ component: Login) {
        self.component = component
        self.models = ObservableValue(component.models)
    }


    var body: some View {
        let model = models.value

        let usernameBinding = Binding(get: { model.username }, set: component.onUsernameChanged)
        let passwordBinding = Binding(get: { model.password }, set: component.onPasswordChanged)

        // omitting some code for brevity
        ...

        TextField("Username", text: usernameBinding)
        Button("Login", action: component.onLogin)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🧪 Testing with Napier
&lt;/h2&gt;

&lt;p&gt;Since the action of logging in is not yet wired up yet I figured I would take this time to research a multi-platform logging solution that I could use to at least verify the login action was working as intended.&lt;/p&gt;

&lt;p&gt;After some research I stumbled upon &lt;a href="https://github.com/AAkira/Napier"&gt;Napier&lt;/a&gt; and it was very easy to setup.&lt;/p&gt;

&lt;p&gt;After following the documentation and setting up the initialization calls I added logging to the login button function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private fun logIn(username: String, password:String) : String {
    // Authenticate the user
    // this is probably going to take a while
    // this will also return a auth token
    Napier.i("called log in $username, $password")
    return "AuthToken"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running the android app and fake logging in you can see the following in the logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2022-08-29 21:31:02.520  5005-5005  LoginStoreKt$logIn      com.belzsoftware.voix.android        I  called log in test@test.com, test123
2022-08-29 21:31:35.669  5005-5005  LoginStoreKt$logIn      com.belzsoftware.voix.android        I  called log in yhskhkjsdf, yolo7383
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And after running the iOS app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;08-09 20:58:28.603 💙 INFO logIn.internal + 619 - called log in Dsfsdfsdfsd, sdfsdfsdf
08-09 20:58:28.765 💙 INFO logIn.internal + 619 - called log in Dsfsdfsdfsd, sdfsdfsdf
08-09 20:58:29.503 💙 INFO logIn.internal + 619 - called log in Dsfsdfsdfsd, sdfsdfsdf
08-09 20:58:36.427 💙 INFO logIn.internal + 619 - called log in Dsfsdfsdfsd, sdfsdfsdf
08-09 20:58:42.497 💙 INFO logIn.internal + 619 - called log in Dsfsdfsdfsd, sdfsdfsdfs
08-09 20:58:48.164 💙 INFO logIn.internal + 619 - called log in Dsfsdfsdfsd, test@test.com
08-09 20:59:04.594 💙 INFO logIn.internal + 619 - called log in Dsfsdfsdfsd, test@test.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ⌛️ Closing
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/26u4lOMA8JKSnL9Uk/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/26u4lOMA8JKSnL9Uk/giphy.gif" width="480" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second part of this post was a little speedy so I suggest taking a further look into the &lt;a href="https://github.com/RynBelz162/Voix"&gt;repo&lt;/a&gt;. I figured out most of how to set this up by looking at the sample GitHub repos listed on the decompose example page.&lt;/p&gt;

&lt;p&gt;In the next part of the series I am going to try to get Firebase authentication hooked up to the login view so we can actually get some integration going and not some basic logging. &lt;/p&gt;

&lt;p&gt;I will probably be taking a little break from this project but will be returning eventually as I still want to keep my self-promise of not letting this die off until I feel accomplished.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>mobile</category>
      <category>kmm</category>
    </item>
    <item>
      <title>Learning KMM: Entry 3</title>
      <dc:creator>Ryan Belz</dc:creator>
      <pubDate>Fri, 12 Aug 2022 23:36:58 +0000</pubDate>
      <link>https://dev.to/rynbelz162/learning-kmm-entry-3-24am</link>
      <guid>https://dev.to/rynbelz162/learning-kmm-entry-3-24am</guid>
      <description>&lt;p&gt;So like I mentioned in &lt;a href="https://dev.to/rynbelz162/learning-kotlin-multiplatform-mobile-entry-1-pl9"&gt;Learning KMM: Entry 1&lt;/a&gt;...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;to force myself to not drop this project I am going to attempt to write my experiences while learning using Kotlin Multiplatform Mobile.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I have not forgotten about this, just have been a little &lt;del&gt;lazy&lt;/del&gt; busy 😅.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuxhs0vm1508y3hmkmtew.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuxhs0vm1508y3hmkmtew.gif" alt="lazy gif spongebob"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;🥱 Just a forewarning this is going to be a bit of a longer read than the first two entries and I will be breaking it into two parts.&lt;/p&gt;

&lt;p&gt;⌛️ TLDR: check out the &lt;a href="https://github.com/RynBelz162/Voix" rel="noopener noreferrer"&gt;repository&lt;/a&gt; and read the docs on MVI Kotlin and decompose &lt;a href="https://arkivanov.github.io/MVIKotlin/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  📸 Overview
&lt;/h2&gt;

&lt;p&gt;So to get started, this entry is going to be all about creating reusable components and classes that contain business logic for logging into my barebone views.&lt;/p&gt;

&lt;p&gt;To do this I utilized the following Kotlin packages&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://arkivanov.github.io/MVIKotlin/" rel="noopener noreferrer"&gt;MVI Kotlin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/arkivanov/Essenty" rel="noopener noreferrer"&gt;Essenty&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/AAkira/Napier" rel="noopener noreferrer"&gt;Napier&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now MVI architecture is very new to me and I might explain this a bit like an idiot, but nevertheless I will try my best.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚔️ MVI vs MVVM
&lt;/h2&gt;

&lt;p&gt;MVI stands for Model-View-Intent and is a: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;architectural pattern that utilizes unidirectional data flow&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Meaning that the data only flows in one direction from the model to the view and vice versa. This flow helps maintain a single source of truth and usually contains a single immutable state of the entire view. It, quite like MVVM, usually allows for the model to know nothing about the view.&lt;/p&gt;

&lt;p&gt;MVVM stands for Model-View-ViewModel and is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;structured to separate program logic and user interface controls&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;MVVM is something that I am more familiar with. This pattern usually does not entail having the entire immutable view state inside of a component but, just like stated before, the view model is completely unaware of the view.&lt;/p&gt;

&lt;p&gt;I know from brief reading that some prefer MVVM to MVI as it is less components and classes and easier to get "up and running" to hook views up to business logic components or view models.&lt;/p&gt;

&lt;p&gt;Also from brief research, it seem that there are two popular packages/libraries that are commonly used for both MVI and MVVM in Kotlin multi-platfom mobile projects.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MVI Kotlin by arkivanov&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/icerockdev/moko-mvvm" rel="noopener noreferrer"&gt;moko mvvm by icerock development&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I am going with MVI Kotlin purely on the fact that I know nothing about it so it will be a bigger learning experience.&lt;/p&gt;




&lt;h2&gt;
  
  
  🏗 Setup
&lt;/h2&gt;

&lt;p&gt;So there are a few essential packages to add to your shared gradle file definition to get started.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;listOf(
    iosX64(),
    iosArm64(),
    iosSimulatorArm64()
).forEach {
    it.binaries.framework {
        baseName = "Shared"
        export("com.arkivanov.decompose:decompose:0.6.0")
        export("com.arkivanov.essenty:lifecycle:0.4.1")
        export("com.arkivanov.mvikotlin:mvikotlin-main:3.0.0-beta02")
    }
}

commonMain {
    dependencies {
        implementation("com.arkivanov.mvikotlin:mvikotlin:3.0.0-beta02")
        implementation("com.arkivanov.mvikotlin:rx:3.0.0-beta02")
        implementation("com.arkivanov.mvikotlin:mvikotlin-main:3.0.0-beta02")
        implementation("com.arkivanov.mvikotlin:mvikotlin-extensions-coroutines:3.0.0-beta02")
        implementation("com.arkivanov.decompose:decompose:0.6.0")
        implementation("com.arkivanov.essenty:lifecycle:0.4.1")
        implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.2")
        implementation("io.github.aakira:napier:2.6.1")
    }
}

named("iosMain") {
    dependencies {
        api("com.arkivanov.mvikotlin:mvikotlin:3.0.0-beta02")
        api("com.arkivanov.mvikotlin:rx:3.0.0-beta02")
        api("com.arkivanov.mvikotlin:mvikotlin-main:3.0.0-beta02")
        api("com.arkivanov.mvikotlin:mvikotlin-extensions-coroutines:3.0.0-beta02")
        api("com.arkivanov.decompose:decompose:0.6.0")
        api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.2")
        api("com.arkivanov.essenty:lifecycle:0.4.1")
        api("io.github.aakira:napier:2.6.1")
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are some pretty specific things going on here that need to be pointed out that I ran into. First, you need to make sure the iOS dependencies are marked as "api" instead of "implementation" and to be totally honest with you I do not know why 😅. Second, it is important to make sure you export these three libraries to your shared iOS framework so that you can them in your Swift code. In the Android project you can get away with adding these dependencies straight to the gradle file. Once you have all this set up you should be ready to roll 🎲.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎨 Model creation
&lt;/h2&gt;

&lt;p&gt;The first part of MVI is the model, you can't have MVI without the model!&lt;/p&gt;

&lt;p&gt;In this case the model is actually really simple for just a two input login screen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data class Model(
    val username: String,
    val password: String
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🏪 The Store
&lt;/h2&gt;

&lt;p&gt;The store is where the business logic is going to go and will accept all "intents" and "messages" and output all "labels" and "state".&lt;/p&gt;

&lt;p&gt;With the MVI Kotlin package you can choose either the Reaktive or Coroutine extensions and create a store pretty. I picked Coroutines out of personal preference and experience.&lt;/p&gt;

&lt;p&gt;First for our store we need to define our State.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;internal data class State(
    val username: String = "",
    val password: String = ""
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "Intents" of the store need to be defined. I like to think of "Intents" as actions that "Intend to change the state". In this case there is three.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;One to update the username&lt;/li&gt;
&lt;li&gt;One to update the password&lt;/li&gt;
&lt;li&gt;One update to attempt to login
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;internal sealed interface Intent {
    data class UpdateUsername(val username: String): Intent
    data class UpdatePassword(val password: String): Intent
    data class Login(val username: String, val password: String): Intent
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when an "Intent" gets sent to the store it is executed by the "Executer" which you will see shortly. When the "Intent" is executed it outputs a "Message". This "Message" contains whatever has happened as a result of the "Intent" executing. The output, or "Message", is then sent to a "Reducer" which reduces the state using the "Message". Now this sounds very confusing but it makes a little bit of sense once you see the implementation.&lt;/p&gt;

&lt;p&gt;First the "Store" in all its glory ⭐️&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@OptIn(ExperimentalMviKotlinApi::class)
internal fun loginStore(storeFactory: StoreFactory) : Store&amp;lt;Intent, State, Nothing&amp;gt; =
    storeFactory.create&amp;lt;Intent, Nothing, Msg, State, Nothing&amp;gt;(
        name = "LoginStore",
        initialState = State(),
        executorFactory = coroutineExecutorFactory {
            onIntent&amp;lt;Intent.Login&amp;gt; { intent -&amp;gt;
                val token = logIn(intent.username, intent.password)
                dispatch(Msg.LoggedIn(token))
            }
            onIntent&amp;lt;Intent.UpdateUsername&amp;gt; { dispatch(Msg.UpdateUsername(it.username)) }
            onIntent&amp;lt;Intent.UpdatePassword&amp;gt; { dispatch(Msg.UpdatePassword(it.password)) }
        },
        reducer = { msg -&amp;gt;
            when (msg) {
                is Msg.LoggedIn -&amp;gt; copy()
                is Msg.UpdatePassword -&amp;gt; copy(password = msg.password)
                is Msg.UpdateUsername -&amp;gt; copy(username = msg.username)
            }
        }
    )

private fun logIn(username: String, password:String) : String {
    // Authenticate the user
    // this is probably going to take a while
    // this will also return a auth token
    Napier.i("called log in $username, $password")
    return "AuthToken"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now the "Messages" 📨&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private sealed interface Msg {
    class UpdateUsername(val username: String): Msg
    class UpdatePassword(val password: String): Msg
    class LoggedIn(authToken: String) : Msg
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Final Breakdown:&lt;br&gt;
Maybe an easier way to explain is just walking through someone attempting to log in.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user loads up the login screen with the default state of a new Login data class (Empty string username and password).&lt;/li&gt;
&lt;li&gt;The user types a username and every keystroke sends a "Intent" to the executor.&lt;/li&gt;
&lt;li&gt;Since there is no logic when changing the username we immediately dispatch a "Message" containing the most recent username string.&lt;/li&gt;
&lt;li&gt;The "Reducer" picks up the new username and &lt;em&gt;COPIES&lt;/em&gt; (emphasis copies) the entire state object with the new username.&lt;/li&gt;
&lt;li&gt;The same steps (2-4) is done with the password.&lt;/li&gt;
&lt;li&gt;Once the user has both entered a username and password they might tap a button to log in.&lt;/li&gt;
&lt;li&gt;In the executor for "log in" you can see us simulate some type of business logic hitting an Auth mechanism to validate the username and password and return a "Auth token".&lt;/li&gt;
&lt;li&gt;Once the token is returned we dispatch a message containing the token which could then be either copied into the state or hopefully in the future saved locally.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Since we actually have not wired up our auth (going to use Firebase Auth in an upcoming post hopefully 🤞) I added some logging to just double check what was being sent to the log in function.&lt;/p&gt;

&lt;p&gt;Hopefully this explanation makes a bit of sense to you 🙂.&lt;/p&gt;




&lt;p&gt;And here is where I am going to break into part two (technically part 4) of the series to talk about how to wire up the Login Store to the View.&lt;/p&gt;

&lt;p&gt;Stay tuned for part 4 and as always you can review all code here in the &lt;a href="https://github.com/RynBelz162/Voix" rel="noopener noreferrer"&gt;Voix GitHub repository&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>mobile</category>
      <category>kmm</category>
    </item>
    <item>
      <title>Learning KMM: Entry 2</title>
      <dc:creator>Ryan Belz</dc:creator>
      <pubDate>Wed, 15 Jun 2022 00:11:35 +0000</pubDate>
      <link>https://dev.to/rynbelz162/learning-kmm-entry-2-18b</link>
      <guid>https://dev.to/rynbelz162/learning-kmm-entry-2-18b</guid>
      <description>&lt;p&gt;My second go at learning some more about Kotlin Multiplatform is really not so much going to be about KMM. This post will be about using Jetpack Compose and SwiftUI to create a simple login view that I can later hook up to some shared Kotlin business logic. So let us get into it again 😎.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jetpack Compose (Android) 🤖
&lt;/h2&gt;

&lt;p&gt;I would say I am more familiar with how to create android applications so this won't be too uncomfortable for me. I have, however, very minimal experience using Jetpack Compose to create any sort of UI so there might be a bit of a learning curve.&lt;/p&gt;

&lt;p&gt;I started off by adding the necessary package implementations in my android project gradle file to support.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;🚀 &lt;a href="https://developer.android.com/jetpack/compose"&gt;Jetpack Compose&lt;/a&gt; (with activity support)&lt;/li&gt;
&lt;li&gt;🎨 &lt;a href="https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary"&gt;Material3&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🎼 &lt;a href="https://google.github.io/accompanist/systemuicontroller/"&gt;Accompanist package&lt;/a&gt; (to help color the app/navigation bars)&lt;/li&gt;
&lt;li&gt;🖼 &lt;a href="https://developer.android.com/reference/kotlin/androidx/compose/material/icons/package-summary"&gt;Material Icons&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies {
    implementation(project(":shared"))
    implementation("androidx.appcompat:appcompat:1.4.2")
    implementation("androidx.compose.ui:ui:1.2.0-beta03")
    implementation("androidx.compose.runtime:runtime:1.2.0-beta03")
    implementation("com.google.accompanist:accompanist-systemuicontroller:0.17.0")

    // Tooling support (Previews, etc.)
    implementation("androidx.compose.ui:ui-tooling:1.2.0-beta03")

    // Foundation (Border, Background, Box, Image, Scroll, shapes, animations, etc.)
    implementation("androidx.compose.foundation:foundation:1.2.0-beta03")

    // Material Design 3
    implementation("androidx.compose.material3:material3:1.0.0-alpha13")
    implementation("androidx.compose.material3:material3-window-size-class:1.0.0-alpha13")

    // Material design icons
    implementation("androidx.compose.material:material-icons-extended:1.2.0-beta03")

    // Integration with activities
    implementation("androidx.activity:activity-compose:1.4.0")
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After setting up the gradle file I worked on the colors/theming files needed for Material3. I made use of the handy &lt;a href="https://material-foundation.github.io/material-theme-builder/#/custom"&gt;material theme generator&lt;/a&gt; to generate color and theme kotlin files. After exporting the files I ended up with a composable theme to use at the root main activity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Composable
fun AppTheme(
    useDarkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -&amp;gt; Unit
) {

    val colors = if (!useDarkTheme) {
        LightColors
    } else {
        DarkColors
    }

    val systemUiController = rememberSystemUiController()
    systemUiController.setSystemBarsColor(colors.primary)

    MaterialTheme(
        colorScheme = colors,
        typography = AppTypography,
        content = content
    )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now getting to work on the actual login screen was pretty straight forward. I created a new file to declare my view code. I went with just two outlined inputs and one button, not going too crazy with the styling. This is how my screen turned out.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--R-U5vAOU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wzagr7njb6bjvlbuz13i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--R-U5vAOU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wzagr7njb6bjvlbuz13i.png" alt="Android Login View" width="880" height="1858"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The styling is cookie cutter since most of it is out of the box Material3. The trickiest part about this view was the view password button but this was solved pretty easy though with some google-fu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var password by rememberSaveable { mutableStateOf("") }
var passwordVisible by rememberSaveable { mutableStateOf(false) }

....

OutlinedTextField(
    value = password,
    modifier = Modifier
        .fillMaxWidth()
        .padding(10.dp, 5.dp),
    onValueChange = { password = it },
    label = {
        Text(text = "Password")
    },
    leadingIcon = {
        Icon(Icons.TwoTone.Password, contentDescription = "Password input icon")
    },
    keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
    visualTransformation = if(passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
    trailingIcon = {
        val icon = if(passwordVisible) Icons.TwoTone.Visibility else Icons.TwoTone.VisibilityOff
        val description = if (passwordVisible) "Hide password" else "Show password"

        IconButton(onClick = { passwordVisible = !passwordVisible }) {
            Icon(icon, contentDescription = description)
        }
    }
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Overall I think this is a very nice looking very simple login screen. I enjoyed the experience creating UI code and not using XML android layouts. The preview functionality was also very quick and saved me from reloading the app a ton. Now onto iOS... &lt;/p&gt;

&lt;h2&gt;
  
  
  SwiftUI (iOS) 🍎
&lt;/h2&gt;

&lt;p&gt;As mentioned previously, I have very little experience with native iOS development let alone SwiftUI. So I went with a more straight forward approach to the login page and did not use any third party dependencies for styling or theming.&lt;/p&gt;

&lt;p&gt;For simple color styling I found a very helpful &lt;a href="https://betterprogramming.pub/styling-swiftui-6d4ebd77af2b"&gt;article&lt;/a&gt; which explained how to utilize light/dark colors.&lt;/p&gt;

&lt;p&gt;I created a &lt;code&gt;Colors.xcassets&lt;/code&gt; file and used the color codes from the material 3 theme generator above to plug in here ending up with the following result. I didn't get too crazy with it 😆.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mH8dpVlN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xxmrsn1f9ck16h5r3zlh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mH8dpVlN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xxmrsn1f9ck16h5r3zlh.png" alt="xCode colors" width="880" height="336"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;After I had the colors I created a new SwiftUI file and got to work on the login view. Some specific styling I needed to apply was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Outlined text fields&lt;/li&gt;
&lt;li&gt;Icons in the text fields&lt;/li&gt;
&lt;li&gt;Password view button&lt;/li&gt;
&lt;li&gt;Capsule like primary button&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To get an icon on the left of the text field was easy. I put an Icon and a Textfield in an HStack 😅. To get the rounded outline I had to apply an overlay to the HStack and ended up with something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HStack {
    Image(systemName: "person")
    TextField("Username", text: $username)
}
.padding(10)
.overlay(RoundedRectangle(cornerRadius: 10)
   .strokeBorder(Color("Primary"), style: StrokeStyle(lineWidth: 1.0)))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the primary button I had to clip the button shape and they even have a preset style for Capsule clip shapes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Button("Login", action: {})
                .frame(minWidth: 0, maxWidth: .infinity)
                .padding(10)
                .foregroundColor(.white)
                .background(Color("Primary"))
                .clipShape(Capsule())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In both code snippets you can see me making use of the Color("Primary") from the color asset catalog file from earlier.&lt;/p&gt;

&lt;p&gt;Now onto the hardest part, the view password button. This was very similar to the Jetpack Compose example. All we needed was to keep track of the toggled state and show and hide UI elements based on the state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@State private var password: String = ""
@State private var isSecured: Bool = true

....

HStack {
    Image(systemName: "lock")
    if isSecured {
        SecureField("Password", text: $password)
    } else {
        TextField("Password", text: $password)
    }
    Button(action: { isSecured.toggle() }) {
        Image(systemName: self.isSecured ? "eye.slash" : "eye").foregroundColor(.black)
    }
}
.padding(10)
.overlay(RoundedRectangle(cornerRadius: 10)
    .strokeBorder(Color("Primary"), style: StrokeStyle(lineWidth: 1.0)))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the end of the day this is the screen that I ended up with.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VbdS50Go--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gfj783cgax1cbm8k57ag.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VbdS50Go--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gfj783cgax1cbm8k57ag.png" alt="iOS login screen" width="880" height="1904"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The inputs are not as nice looking as android, but i'm sure if more time was spent we could figure out something a bit more fancy. I think I achieved the goal though so I will leave it for now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overall thoughts 🤔
&lt;/h2&gt;

&lt;p&gt;Comparing this experience to Xamarin I would say it just feels right and let me explain my opinion a bit more. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Compose and SwiftUI declarative UI feels more intuitive than XAML&lt;/li&gt;
&lt;li&gt;The preview functionality was a way smoother experience than "Hot Reload", I had zero problems previewing either screens&lt;/li&gt;
&lt;li&gt;I could easily add functionality like a trailing password view button that in XAML usually ends up with either a custom view or custom renderer. Just a ton more code to implement 🫤.&lt;/li&gt;
&lt;li&gt;Lastly, you just get the feeling that you have all the tools in the toolbox when working natively as opposed to XAML.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now obviously this was a very simple page and I could run into more issues later on but I am pretty happy with the experience so far.&lt;/p&gt;

&lt;p&gt;Next part I will attempt to wire up some business logic using &lt;a href="https://arkivanov.github.io/MVIKotlin/"&gt;MVI Kotlin and Decompose multi-platform libraries&lt;/a&gt;. I've usually used MVVM architecture but MVI looks interesting and this project is really all about learning things I don't really know all that well.&lt;/p&gt;

&lt;p&gt;As always you can find the full code here on my &lt;a href="https://github.com/RynBelz162/Voix"&gt;github&lt;/a&gt; 🧑‍💻&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>mobile</category>
      <category>kmm</category>
    </item>
    <item>
      <title>Learning Kotlin Multiplatform Mobile: Entry 1</title>
      <dc:creator>Ryan Belz</dc:creator>
      <pubDate>Wed, 01 Jun 2022 23:15:17 +0000</pubDate>
      <link>https://dev.to/rynbelz162/learning-kotlin-multiplatform-mobile-entry-1-pl9</link>
      <guid>https://dev.to/rynbelz162/learning-kotlin-multiplatform-mobile-entry-1-pl9</guid>
      <description>&lt;p&gt;So.... to force myself to not drop this project I am going to attempt to write my experiences while learning using &lt;a href="https://kotlinlang.org/lp/mobile/"&gt;Kotlin Multiplatform Mobile&lt;/a&gt;. My GitHub page is starting to become the place where ideas go to die 🫠. &lt;/p&gt;

&lt;blockquote&gt;
&lt;h2&gt;
  
  
  Disclaimer, this is my first ever attempt at documenting really anything but lets get into it.
&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;p&gt;My interest for KMM has stemmed from working with Xamarin Forms. The code sharing is amazing and I absolutely love C# but if you need to add any frills or flair to your UI, well, you better buckle up and start creating those custom renderers 🚧.&lt;/p&gt;

&lt;p&gt;Things that interested me about KMM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Code sharing so you only have to write core code once.

2. Ability to use native cutting edge tech like Jetpack Compose and SwiftUI

3. You can quickly write native code if you need to get the job done. (Kotlin, Swift)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🎉 Here is my experience from 0 to setting up a new project:&lt;br&gt;
&lt;code&gt;&lt;br&gt;
TLDR: 8/10 experience. One minor issue while trying to run the iOS App which was quickly fixed searching stack overflow.&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
I pretty much followed the "Getting started" guide found &lt;a href="https://kotlinlang.org/docs/multiplatform-mobile-getting-started.html"&gt;here&lt;/a&gt; word for word.&lt;/p&gt;

&lt;p&gt;Step 1: 💻 Setting up my environment&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Straight forward enough here, I had already had android studio installed (Electric Eel canary build) on my machine with the Kotlin plugins/language pretty much up to date.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I, however, had to install the Kotlin Multiplatform plugin for android studio.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BPfxhadP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yizoo17hya8vpjl1y8u4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BPfxhadP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yizoo17hya8vpjl1y8u4.png" alt="KMM Plugin installed" width="880" height="639"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 2: 📦 Creating the Project&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After installing the KMM plugin for android studio I followed the prompts to create a new KMM app and had it created and loaded in a quick minute.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TnRRLCGD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uy892347ro823s7tm8yd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TnRRLCGD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uy892347ro823s7tm8yd.png" alt="App creation" width="880" height="659"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 3: 🤖 Running the Android app&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy as can be, waited for the Gradle sync and build to complete and just hit run in the upper right hand corner!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OqvYA5BX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/npj2n9gbqji4yfseehwo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OqvYA5BX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/npj2n9gbqji4yfseehwo.png" alt="Android app running fine" width="880" height="1516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 4: 🍎 Running the iOS App&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Now this one was a bit of a pain... When I first switched the configuration to "iosApp" I noticed a red X mark on the picture. After trying to run the app I was shown this error.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6DDD7Kwf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cgx6ndr98lg9p2k87bfr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6DDD7Kwf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cgx6ndr98lg9p2k87bfr.png" alt="ios red x mark" width="224" height="84"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i7CHBIX9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0p49h1xqk56y3t4jw6sy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i7CHBIX9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0p49h1xqk56y3t4jw6sy.png" alt="ios error" width="880" height="69"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;I had to do some googling but this stack overflow post &lt;br&gt;
&lt;a href="https://stackoverflow.com/questions/71707858/getting-selected-scheme-iosapp-does-not-support-iphonesimulator-error-for"&gt;here&lt;/a&gt; helped to solve the issue. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I closed Android studio and ran the following in my terminal and after that my simulator ran just fine.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd ~/Library/LaunchAgents/
rm com.jetbrains.AppCode.BridgeService.plist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z8GGcVLb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bxxj3ltty7ikeaa8xqi8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z8GGcVLb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bxxj3ltty7ikeaa8xqi8.png" alt="ios app running" width="880" height="1842"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;🎬 Final thoughts:&lt;br&gt;
All in all it was a pretty straight forward experience with iOS being the problem child like it usually is. It is probably less buggy to run the iOS app directly from Xcode but the fix was not too bad. After the fix I was up and running exploring the project structure and generated code.&lt;/p&gt;

&lt;p&gt;Next time I would like to see if I can get something small to work like creating a shared view model. My ultimate goals for the project is to get Firebase Authentication working for both apps and get the app communicating to a Ktor Kotlin backend api for sending and receiving chat messages.&lt;/p&gt;

&lt;p&gt;Thanks for reading 👋 and hopefully this does not join the project graveyard anytime soon 🪦.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>mobile</category>
      <category>kmm</category>
    </item>
  </channel>
</rss>
