Errata for Kotlin Multiplatform by Tutorials 1st Edition

Creating this topic to catch any typos and bugs in the 1st Edition of Kotlin Multiplatform by Tutorials.

1 Like

Chapter 2. Getting started.
There is a mistake in function:

fun formatDateTime(dateTime: LocalDateTime): String {
...
    var hour = dateTime.hour
    var amPm = " am"
    // 3
    // For 12
    if (hour > 12) {
        amPm = " pm"
        hour -= 12
    }

When the 24-hour input value of hours is 0, this fun returns 0 am, but it is expected to be 12 am.
When the 24-hour input value of hours is 12, this fun returns 12 am, but it is expected to be 12 pm.

I can suggest this code:

var hour = dateTime.hour % 12
if (hour == 0) hour = 12
val amPm = if (dateTime.hour < 12) " am" else " pm"
1 Like

Great catch. Will update

1 Like

In section 3 (Developing UI using Compose) the composable AnimatedSwipeDismiss is used. I can’t seem to find that anywhere. It does not get suggested in Android Studio and Google also doesn’t yield any results. Link to the exact location: https://www.raywenderlich.com/books/kotlin-multiplatform-by-tutorials/v1.0/chapters/3-developing-ui-android-jetpack-compose#:~:text=item%20in%20an-,AnimatedSwipeDismiss,-to%20allow%20the

It seems like AnimatedSwipeDismiss is from this gist but this should be linked to from the book or the source code should be included I think: AnimatedSwipeDismiss.kt · GitHub

The code from the gist also doesn’t seem to work completely since import androidx.compose.runtime.onCommit does not exist for some reason.

This class is found in the starter project for this chapter.

1 Like

Oh - I totally missed there being a starter project! Must have skipped the first page of the chapter. Thanks though.

Hey @kevindmoore , the book is great and provides great knowledge, but I found a few issues in the Compose chapter. Please don’t be mad at me ;], i am just a pointing out customer. :upside_down_face:

First of all composable functions should follow PascalCased naming, which is recommended in the official guidelines (androidx/compose/docs/compose-api-guidelines.md at androidx-main · androidx/androidx · GitHub). In chapter 3 there is a composable function with wrong naming:

@Composable
fun emptyComposable() {
}

The next thing that got my attention was LaunchedEffect. You passed 0 as a key, but this variable is initialized in the LaunchedEffect constructor. In the ray’s compose book and official guidelines it was mentioned to pass never-changing constant like Unit or true. Most often I see Unit in examples.

To create an effect that matches the lifecycle of the call site, a never-changing constant like Unit or true is passed as a parameter. In the code above, LaunchedEffect(true) is used. ~ Side-effects in Compose  |  Jetpack Compose  |  Android Developers

LaunchedEffect(0) {
    while (true) {
        time = timezoneHelper.currentTime()
        delay(timeMillis) // Every minute
    }
}

The last thing I would change is AppTheme.kt class. It is a composable function, which role is to pass down to the other composable information about colors/typography … etc. Here is the theme composable used in the project.

@Composable
fun AppTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
    val colors = if (darkTheme) {
        darkColors()
            .copy(
                primary = primaryDarkColor,
                primaryVariant = primaryLightColor,
                secondary = secondaryDarkColor,
                secondaryVariant = secondaryLightColor,
                onPrimary = Color.White,
            )
    } else {
        lightColors()
            .copy(
                primary = primaryColor,
                primaryVariant = primaryLightColor,
                secondary = secondaryColor,
                secondaryVariant = secondaryLightColor,
                onPrimary = Color.Black,
            )
    }
    MaterialTheme(
        colors = colors,
        typography = typography,
        content = content
    )
}

Normally when you create an empty project in android for compose application you will have theme class generated for you. In my opinion it look’s more clear and easier to read because all of your AppTheme variables could be read from the object class which has read only composables.


/**
 * Contains functions to access the current theme values provided at the call site's position in
 * the hierarchy.
 */
object MaterialTheme {
    /**
     * Retrieves the current [ColorScheme] at the call site's position in the hierarchy.
     */
    val colorScheme: ColorScheme
        @Composable
        @ReadOnlyComposable
        get() = LocalColorScheme.current

    /**
     * Retrieves the current [Typography] at the call site's position in the hierarchy.
     */
    val typography: Typography
        @Composable
        @ReadOnlyComposable
        get() = LocalTypography.current
}

This is very useful, because in some cases you have to pass another Theme in your composables. This will only search the closest composable in your tree hierarchy, so you can have many themes in your apps! So to read those values you can do this:

            Text(
                text = text,
                style = MaterialTheme.typography.labelLarge,
            )

One more thing :sweat_smile:
View is usually avoided as a naming scheme in Compose. In chapter 3 there is a MainView composable.
I hope you can fix all the things i have mentioned in the next release. :rocket: :technologist:

Thanks so much for the feedback. I will definitely implement these in the next version.

1 Like

Newer M1 Macs need to run $ brew install cocoapods instead of sudo gem install cocoapods in order to build the project due to a bug with encoding.

Hi
When I’m trying to run the startup project I got this error
10:54 Gradle sync failed: ‘pod install’ command failed with code 1.
Error message:
Please, check that podfile contains following lines in header:
source ‘https://cdn.cocoapods.org
Please, check that each target depended on shared contains following dependencies:
(2 s 248 ms)

Hello trainingtamkin,

Can you please give us a bit more information about your development environment?

For instance:

  • The Operating System of the computer where you’re running the project
  • From which chapter are you running the project
  • Which version of Android Studio are you using?

Thank you

In Chapter 3, we are using a AnimatedSwipeDismiss Composable but it wasn’t included in the Chapter content. I ended up using this gist.
I’m using Compose 1.1. Not sure if in previous versions of Compose that Composable was included in the library? Or maybe I missed something?

Edit: I read in the previous posts that Composable was in the starter projects. Never mind.

Just wondering if maybe adding an asterisk with some of the Composables that are in the starter project for Chapter 3 but not in the Chapter would help with some of the confusion about the missing composables.
I’m using the project I created from the Chapter 2 and so I didn’t start with the starter project. Not a big deal but could help other readers.
Also, I know this book is more about KMM and not really about the declarative frameworks from both Platforms. But maybe adding another appendix section just for those composables not describe in chapter 3 and give a brief overview of them at a high level would be nice especially around the animation APIs in use.
I haven’t read chapter 4 yet but I’m guessing the same pattern might be in use to keep the chapters from being too big.
These are just opinions and I’m really enjoying the book thus far.
Great work!! Thank you!! :grinning:

Thanks so much for the feedback. We really appreciate it. One of the biggest problems I had was trying to keep the chapters from getting huge due to all of the code, so I had to add to the starter projects. Hopefully you can look at the starter or final projects and copy that code.

1 Like

Very small typo in Ch 12:
When updating the desktopApp FeedViewModel for the RSS Feed, it should be setting _items[platform] = newItems to match the parameter of the function. Otherwise it is trying to use the class’s items, which causes an error.

override fun onNewDataAvailable(newItems: List<RWEntry>, platform: PLATFORM, exception: Exception?) {
  Logger.d(TAG, "onNewDataAvailable | platform=$platform items=${items.size}")
  viewModelScope.launch {
    _items[platform] = items
  }
}

This problem doesn’t exist on the androidApp implementation because the parameter is called just items, not newItems

1 Like

Thank you for the heads up! Good catch :slightly_smiling_face:.

We’re going to update it.

When is an update coming?

Hello lenduya,

We’re currently looking into this :slightly_smiling_face:.