Auto Added by WPeMatico

Compose Multiplatform 1.6.0 – Resources, UI Testing, iOS Accessibility, and Preview Annotation

Compose Multiplatform is a declarative UI framework built by JetBrains that allows developers to share UI implementations across different platforms. The 1.6.0 release brings several powerful features, as well as compatibility with the latest Kotlin version and changes from Google’s latest Jetpack Compose update.

Get Started with Compose Multiplatform

This release of Compose Multiplatform:

  • Revamps the resource management library.
  • Introduces a UI testing API.
  • Adds iOS accessibility support.
  • Brings a host of other features and improvements.

For a description of all notable changes, see our What’s new in Compose Multiplatform 1.6.0 page or check the release notes on GitHub.

Common resources API

The biggest and most anticipated change in Compose Multiplatform 1.6.0 is the improvement of the API for sharing and accessing resources in common Kotlin code. This API now allows you to include and access more types of resources in your Compose Multiplatform applications.

The resources are organized in a number of directories as part of the commonMain source set:

  • composeResources/drawable contains images
  • composeResources/font contains fonts
  • composeResources/values contains strings (in the format of strings.xml)
  • composeResources/files contains any other files.

Compose Multiplatform generates type-safe accessors for all of these resource types (excluding the files directory). For example, after placing a vector image compose-multiplatform.xml in the composeResources/drawable directory, you can access it in your Compose Multiplatform code using the generated Res object:

import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable
import kotlinproject.composeapp.generated.resources.*
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.painterResource

@OptIn(ExperimentalResourceApi::class)
@Composable
fun Logo() {
    Image(
        painterResource(Res.drawable.compose_multiplatform),
        contentDescription = "CMP Logo"
    )
}

The new resources API also allows you to provide variations of the same resource for different use cases, including locale, screen density, or theme. Whether you’re localizing text, changing the colors of your icons in dark mode, or providing alternative images based on screen resolution, you can express these constraints by adding qualifiers to the directory names.

For a closer look at qualifiers for resources, as well as an in-depth overview of the new resources API in Compose Multiplatform 1.6.0, take a look at the official documentation!

Common API for UI testing

UI testing helps you make sure that your application behaves the way it is supposed to. With Compose Multiplatform 1.6.0, we’re introducing an experimental API that allows you to write common UI tests for Compose Multiplatform that validate the behavior of your application’s user interface across the different platforms supported by the framework.

For example, you may want to ensure that a custom component correctly shows an informative string with the proper prefix:

@Composable
fun MyInfoComposable(info: String, modifier: Modifier) {
    Text(modifier = modifier, text = "[IFNO] $info")
}

In the latest version of Compose Multiplatform, you can now use UI tests to validate that the component does indeed correctly prefix the text when rendered. To do so, you can use the same finders, assertions, actions, and matchers as you would with Jetpack Compose on Android. After following the setup instructions from the documentation you can write a test that ensures the prefix is added properly:

import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.runComposeUiTest
...

class InformationTest {
    @OptIn(ExperimentalTestApi::class)
    @Test
    fun shouldPrefixWithInfoTag() = runComposeUiTest {
        setContent {
            MyInfoComposable("Important things!", modifier = Modifier.testTag("info"))
        }
        onNodeWithTag("info").assertTextContains("[INFO]", substring = true)
    }
}

Running this test on any of your target platforms will show you the test results (and in this case, help you spot and correct a typo!):

iOS accessibility support

Compose Multiplatform for iOS now allows people with disabilities to interact with the Compose UI with the same level of comfort as with the native UI:

  • Screen readers and VoiceOver can access the content of the Compose Multiplatform UI.
  • Compose Multiplatform UI supports the same gestures as the native UIs for navigation and interaction.

This is possible because the Compose Multiplatform semantic data is automatically mapped into an accessibility tree. You can also make this data available to Accessibility Services and use it for testing with the XCTest framework.

For details on the implementation and limitations of the current accessibility support, see the documentation page.

@Preview annotation for Fleet

With 1.6.0, Compose Multiplatform introduces the common @Preview annotation (previously available only for Android and Desktop). This annotation is supported by JetBrains Fleet (starting with Fleet 1.31). Add @Preview to your @Composable function, and you’ll be able to open the preview via the gutter icon:

Try it out with a project generated by the Kotlin Multiplatform wizard!

Fleet currently supports the @Preview annotation for @Composable functions without parameters. To use this common annotation, add the experimental compose.components.uiToolingPreview library to your dependencies (as opposed to compose.uiTooling, used for desktop and Android).

Separate platform views for popups, dialogs, and dropdown menus

When mixing Compose Multiplatform with SwiftUI, you may only want to have some widgets of a given screen rendered using Compose. Starting with version 1.6, when you create Dialog, Popup, or Dropdown composables in these scenarios, they can expand beyond the bounds of the individual widget and even expand across the entire screen!

This is also possible for desktop targets, although as an experimental feature for now.

Dialog, Popup, or Dropdown in Compose Multiplatform

Other notable changes

To learn about the rest of the changes included in this release:

What else to read and watch

Continue ReadingCompose Multiplatform 1.6.0 – Resources, UI Testing, iOS Accessibility, and Preview Annotation

Kotlin for WebAssembly Goes Alpha

Kotlin/Wasm, the newest Kotlin Multiplatform target platform, has reached Alpha status! Here’s what you need to know about this change at a glance:

  • JetBrains has promoted Kotlin/Wasm to Alpha, making it ready for you to try for yourself. Your feedback will help shape the future of building web applications with Kotlin!
  • As an Alpha release, Kotlin/Wasm is ready for you to use in pre-production scenarios, but it still has many areas that are works in progress. We rely on the community to help inform and prioritize the decisions influencing Kotlin/Wasm.
  • Compose for Web (currently experimental) is powered by Kotlin/Wasm. Together, the two technologies allow you to create declarative user interfaces for web applications in 100% Kotlin.

Get started with Kotlin/Wasm

WebAssembly: The newest Kotlin Multiplatform target

WebAssembly is establishing itself as the standard compilation target for languages targeting the browser – and beyond! With Kotlin/Wasm, we’re giving you the ability to make use of this new target via Kotlin Multiplatform. We first introduced Kotlin/Wasm in Kotlin 1.8.20 as an experimental technology, and have since improved and refined it.

Because Kotlin is an automatically memory-managed language, it builds on the garbage collection proposal, which recently reached phase 4 (standardization). That means it is now enabled by default in a number of major browsers. For example, recent versions of both Chrome and Firefox can run Kotlin/Wasm applications without any required adjustments. While Safari currently doesn’t support Wasm GC yet, implementation of the required functionality in JavaScriptCore is already underway.

Getting started with Kotlin/Wasm

Join the Kotlin/Wasm community

An easy way to get started with Kotlin/Wasm is to take a look at the Getting Started page. Here, you’ll find an overview of the technology and instructions on how to set up your own Kotlin/Wasm application. You’ll also see projects and links to example projects that show off different facets of Kotlin/Wasm, including those that illustrate how to use it in the browser, together with Compose Multiplatform, and more.

Kotlin/Wasm goes Alpha: explore the technology in detail and see example projects

The Kotlin Playground now also has support for Kotlin/Wasm, meaning you can write your first WebAssembly code snippets right in your browser, and explore what Kotlin/Wasm has to offer.

To help you go beyond your first “Hello World” examples with WebAssembly, we are also bringing the `kotlinx` suite of libraries to Kotlin/Wasm. This includes kotlinx-atomicfu, kotlinx.coroutines, kotlinx.serialization, kotlinx-datetime, and kotlinx-io.

Ktor, the JetBrains framework for building networked applications, is also coming to WebAssembly. With the next release, you’ll be able to use Ktor’s HTTP clients to make network requests right from your Kotlin/Wasm code.

Compose Multiplatform: Powered by Kotlin/Wasm

Kotlin/Wasm isn’t bound to any specific UI framework. It’s a general way of running your Kotlin code in the browser. However, it is the underlying technology for the experimental web target of Compose Multiplatform, the declarative multiplatform UI toolkit by JetBrains based on Google’s Jetpack Compose. Compose Multiplatform for Web uses canvas-based rendering, meaning that you can use the same layouts and components as you would on other platforms. Out of the box, it comes with the Material and Material 3 design components.

With Compose Multiplatform, you can build shared applications that target the most important platforms: Android and iOS, desktop, and – thanks to the power of Kotlin/Wasm – the browser. To start building your own shared UIs, you can generate a project using the Kotlin Multiplatform Web Wizard, which now also experimentally supports the Kotlin/Wasm target.

Performance

WebAssembly is designed from the ground up as a compilation target for languages, meaning the Kotlin compiler can convert your source code into performant WebAssembly bytecode. We regularly run benchmarks on Kotlin/Wasm to ensure its runtime performance. Since Kotlin/Wasm is still in Alpha, the team continues to work on performance improvements, but as you can see, Kotlin/Wasm already outperforms Kotlin/JS in almost all of our macro benchmarks:

Likewise, Compose Multiplatform running on Kotlin/Wasm already shows promising performance characteristics, with execution speed comparable to that of running the same application on the JVM:

AnimatedVisibility: Wasm takes a factor of 0.5 of the time of a JS implementation
LazyGrid: Wasm takes a factor of 0.5 of the time of a JS implementation
VisualEffects: Wasm takes a factor of 0.5 of the time of a JS implementation

These benchmark results come from our testing in a recent version of Google Chrome, but we observed similar results in other browsers we tested.

What’s in the works

As a technology in Alpha, Kotlin/Wasm is evolving rapidly, and the team is busy making improvements and enhancements. As such, there are still a number of areas that are works in progress.

Currently, the debugging support in Kotlin/Wasm is limited, and we’re working on improving its capabilities going forward. We’re also aware that bundle size is an important factor when it comes to targeting the web, and we want to further optimize the outputs generated by the compiler, especially for Compose Multiplatform projects.

As WebAssembly continues evolving, we want to take advantage of new proposals as they arrive – whether that’s stack switching, threading, or others. We’re also working on introducing support for the WebAssembly Component Model to Kotlin, which will enable you to build interoperable Wasm libraries and applications. We’re also still in the process of making Kotlin/Wasm an awesome target for development outside of the browser, including support for WASI, the WebAssembly System Interface. As a part of the WebAssembly Community Group, and by actively collaborating with vendors of WebAssembly VMs, we’re striving to ensure that Kotlin/Wasm provides a great experience no matter where you’re running it.

Our goal is to provide an excellent developer experience for you, and make sure that it meets your requirements in terms of performance and bundle size. As we make progress on these fronts, we’ll make sure to provide you with updates and share more information!

Join the community to get updates and share your feedback!

Join the Kotlin/Wasm community

If you want to connect with the team and other developers excited about Kotlin/Wasm, we invite you to join the discussion on the Kotlin Slack (get your invite here). In the #webassembly channel, you can find discussions about everything Kotlin and WebAssembly.

Kotlin/Wasm is in Alpha, and we want to make sure we continue evolving the technology based on your requirements. Help us help you by reporting problems, telling us about APIs that you feel are missing, and requesting features you’d like to see. You can do so by adding issues to the Kotlin YouTrack project.

We’re excited to take this next step with Kotlin and look forward to seeing what you’ll create with Kotlin/Wasm!

See Kotlin/Wasm in action!

On Tuesday, December 12, 2023, Kotlin/Wasm Team Lead Zalim Bashorov will present a live session entitled Kotlin and WebAssembly: Unleashing Cross-Platform Power on the official Kotlin YouTube channel! To make sure you don’t miss it, sign up to receive notifications.

See also

Continue ReadingKotlin for WebAssembly Goes Alpha

Compose Multiplatform 1.5.10 – The Perfect Time To Get Started

Compose Multiplatform is a declarative UI framework built by JetBrains that allows developers to share their applications’ UIs across different platforms. It enables you to reuse up to 100% of your codebase across Android, iOS, desktop, and web. The framework leverages the code-sharing capabilities of the core Kotlin Multiplatform technology, which just became Stable in Kotlin 1.9.20.

Get Started with Compose Multiplatform

This 1.5.10 release of Compose Multiplatform provides support for Kotlin 1.9.20, offers simplified onboarding, introduces a new portal for documentation, and provides the features listed below:

With these across-the-board improvements, it has never been easier to adopt Compose Multiplatform. Now is a great time for you to start using Compose Multiplatform for cross-platform development, and if you are an existing user, these improvements will make your work more effective than ever.

Easier to get started

If you want to dive into Compose Multiplatform and Kotlin Multiplatform, then we have some great news for you:

  • The Kotlin Multiplatform Web Wizard lets you easily create new projects in a few clicks. The project contains both shared logic and a Compose Multiplatform UI for multiple platforms.
  • New onboarding tutorials are available for Android, iOS, and desktop. These will bring you up to speed on the core concepts of Kotlin Multiplatform and Compose Multiplatform.
  • The Compose Multiplatform documentation has been integrated into the Kotlin Multiplatform Development portal, making it easier and simpler for you to find the information you need.

You can also register for our webinar about getting started with Compose Multiplatform, scheduled for November 28.

New Material 3 components in common code

All Compose Material 3 components are now available in common code. The ModalBottomSheet, ExposedDropdownMenuBox, SearchBar, and DockedSearchBar components were added in this release, which completes the set of components defined in the 1.1.2 version of Compose Material 3.

This is how it will appear on desktop:

New Components on Desktop

And this is how it will look on iOS and Android:

New Components on iOS
                            
New Components on Android

Enhanced TextFields on iOS

This release features multiple enhancements to TextFields on iOS. A new Fast Delete mode is available, handling of double and triple taps has been improved, scroll physics includes the ‘rubber band effect’, and a crossfade animation improves interoperability.

  • Starting with this release, when you hold down Backspace, a new Fast Delete mode activates after the first 21 characters. In this mode each deletion removes two words. This aligns with the default iOS behavior and is what users expect.

Fast Delete mode on iOS

  • Starting with this release, double and triple taps in a TextField are always handled consistently. Double-tapping will always select the current word, while triple-tapping always selects the entire text.

Double and Triple Taps on iOS

  • Compose Multiplatform 1.5.0 introduced support for native scroll physics. This release adds support for overscroll (the “rubber band effect”) within large text fields. Your Compose Multiplatform UI will now provide the bounciness that iOS users expect.

Rubber Band Effect on iOS

Crossfade animation for UIKit interoperability

When you target iOS, Compose Multiplatform allows you to embed composables within a UIKit component hierarchy. This enables you to combine your Kotlin UI components with existing Swift or Objective-C widgets.

Starting with this release, when embedding composables within a UIKit hierarchy, a crossfade animation is applied to the screen as the orientation transitions. This prevents components from temporarily appearing stretched.

Increased compilation speed

In Kotlin 1.9.20, compiler caching and Compose Multiplatform are compatible. So, starting from this release, compiler caching is automatically enabled for Compose Multiplatform applications that use the Kotlin 1.9.20 compiler. Compilation is much faster for all compilations after the initial one.

Caching remains disabled when you use Compose Multiplatform with an earlier version of the Kotlin compiler. Manually setting  kotlin.native.cacheKind to none causes the Gradle compiler plugin to produce an error, so please remove this if present.

Basic support for the K2 compiler

In this release, we introduce partial support for the K2 compiler in Compose Multiplatform. You can use the Compose Compiler and K2 in combination to build basic applications. Full support for K2 is scheduled for the next CMP release, so stay tuned.

Enhanced rendering performance on iOS

In this release, we’ve improved performance on iOS by eliminating any instances of redundant or overly eager processing. In addition, the commands to be run on the GPU for rendering are prepared in a separate thread.

These changes mean that Compose Multiplatform apps running on iOS will have fewer missed frames. Any stutters or lags caused by missed frames will be reduced or removed.

New documentation portal

The new Kotlin Multiplatform Development Portal features tutorials, a FAQ, brand new samples, and other helpful documentation. This means getting started with Compose Multiplatform is easier than ever!

Topics covered include:

Grouping Compose Multiplatform materials with other multiplatform documentation, on a dedicated portal, makes them easier to find and consume. While some documentation remains on the Compose Multiplatform GitHub, our aim is to transfer it to the new portal soon.

Get Started with Compose Multiplatform

Wrapping up

There has never been a better time to get started with Compose Multiplatform! You can use the Compose Material 3 widgets to build beautiful user interfaces that behave consistently on Android, iOS, desktop, and web. Using the Kotlin Multiplatform Web Wizard you can be up and running in minutes, with the new onboarding tutorials to guide you as you build your first Compose Multiplatform applications.

We hope you’ll find building applications with Compose Multiplatform a fun and rewarding experience. Please provide feedback on how you get on. We invite you to join the discussion on the Kotlin Slack in the #compose channel, where you can discuss general topics related to Compose Multiplatform and Jetpack Compose.

What else to read and watch

Continue ReadingCompose Multiplatform 1.5.10 – The Perfect Time To Get Started

Kotlin Multiplatform Is Stable and Production-Ready

In a highly anticipated move, Kotlin Multiplatform, an open-source technology built by JetBrains that allows developers to share code across platforms while retaining the benefits of native programming, has become Stable and is now 100% ready for use in production.

Read on to learn about the evolution of Kotlin Multiplatform (KMP), how it can streamline your development process, and what educational resources are available for you to get the most out of the technology.

Get Started

Share code on your terms, with confidence

Designed with a core focus on flexibility and adaptability, Kotlin Multiplatform (KMP) blurs the boundaries between cross-platform and native development. It allows developers to reuse as much code as they want, write native code if and as needed, and seamlessly integrate shared Kotlin code into any project.

Since the first release of KMP, its flexibility has attracted many prominent companies, such as Netflix, Philips, McDonald’s, 9GAG, and Baidu. Many teams have already been capitalizing on KMP’s ability to be gradually integrated, as well as its low adoption risk. Some of them share isolated critical parts of their existing Kotlin code to improve the stability of their apps. Others, aiming to reuse as much code as possible without compromising app quality, share all application logic across mobile, desktop, web, and TV while maintaining native UIs.

With KMP being widely used in production since its Alpha release, JetBrains has been able to gather extensive feedback and refine the technology to provide a better multiplatform development experience in the Stable version, including the following.

Strict compatibility guarantees

The core of the Kotlin Multiplatform technology consists of many parts, such as compiler support, language features, the libraries API, IDEs, build tooling, and more. All of these cover the most popular code-sharing use-cases and are stable in Kotlin 1.9.20. “Stable” means that we will evolve those parts following strict compatibility rules, so you can confidently use them in your code.

Going beyond the core, KMP also has some more advanced parts that we’re still continuing to work on. Expect/actual classes are an example of such functionality (not to be confused with expect/actual functions, which are fully stable in Kotlin 1.9.20). Such features are explicitly marked as non-stable, and Kotlin will inform you when you’re using them. Please pay attention to the experimentality warnings and the corresponding documentation to make an informed decision about using or not using such features in your projects.

Better build setup user experience

To simplify project setup and maintenance, we’ve introduced the default hierarchy template. This new feature of the Kotlin Gradle plugin automatically configures Kotlin Source Sets for popular scenarios and reduces the amount of boilerplate in typical build scripts.

To help you diagnose build misconfigurations and errors, KMP delivers a couple of improvements in Stable:

  • Approximately 50 diagnostics were added to the Kotlin Gradle plugin, helping to detect common build issues and providing quick tips on how to fix them.
  • Improved output for Gradle errors in Xcode makes build issues in native parts of your project more discoverable.

Build and runtime performance improvements

In addition to other stability and robustness improvements, we’re continuing to improve the performance of KMP:

Thanks to all of these advancements, Kotlin Multiplatform can be used without hesitation for sharing code in production across Android, iOS, desktop (JVM), server-Side (JVM), and web, even in the most conservative usage scenarios.

Get Started

Use the power of the growing Kotlin Multiplatform ecosystem

The evolution of Kotlin Multiplatform isn’t limited to the technology itself, but extends to its thriving ecosystem cultivated by thousands of Kotlin developers worldwide. Since Beta, the number of KMP libraries has multiplied, offering diverse tools for networking, data storage, arithmetics, analytics, and more, enabling swift and confident app development.

The Android team at Google is continuing to support KMP users by providing the experimental multiplatform versions of Jetpack libraries, and they’ve already made the Collections, DataStore, Annotations, and Paging libraries compatible with Kotlin Multiplatform.

The KMP ecosystem also offers a range of tools for the iOS developer experience, with SKIE by Touchlab standing out. It generates Swift code that provides a Swift-friendly API layer, which bridges important Kotlin types, and implements seamless Coroutines interoperability.

Get even more code-sharing freedom with Compose Multiplatform

Compose Multiplatform, a modern declarative cross-platform UI framework by JetBrains, is a significant part of the ever-expanding KMP ecosystem. With Compose Multiplatform, you now have full code-sharing flexibility. Share just one piece of logic, all application logic, or both the logic and the UI, depending on your project’s and team’s requirements:

Compose Multiplatform for Android and desktop (JVM) is already Stable, while support for iOS is currently in Alpha and support for web (Wasm) is Experimental.

Explore the future of Kotlin Multiplatform

JetBrains continues to enhance multiplatform development with Kotlin, aiming to make it the ultimate choice for sharing code across platforms:

  • Future updates for the core Kotlin Multiplatform technology will improve the iOS developer experience, including direct Kotlin-to-Swift interoperability and SwiftPM support.
  • The next milestone for Compose Multiplatform for iOS is Beta support in 2024.
  • JetBrains is actively developing Kotlin/Wasm to facilitate modern web development with KMP, aiming to deliver the Alpha version of Compose Multiplatform for Web (Wasm) next year.

And that’s not all. In the upcoming weeks, get ready for some thrilling announcements regarding the evolution of Kotlin Multiplatform tooling!

Get started with Kotlin Multiplatform

Begin your journey with our extensive, user-friendly educational resources.

Participate in our Kotlin Multiplatform Webinar November Series to get insights directly from JetBrains experts! Explore Kotlin Multiplatform features, build KMP apps with both native and shared UIs, and uncover iOS development tips and tricks:

Register

The new Kotlin Multiplatform Development Portal offers detailed documentation, getting-started guides, and step-by-step tutorials covering all aspects of building apps with Kotlin Multiplatform and Compose Multiplatform:

Get Started

Continue ReadingKotlin Multiplatform Is Stable and Production-Ready

Shining a Spotlight on Kotlin Multiplatform Content Creators

As the Kotlin Multiplatform stable release emerges on the horizon, its ecosystem continues to evolve and flourish, thanks in no small part to the enthusiastic Kotlin community. What’s more, the Compose Multiplatform 1.5.10 release is around the corner, which has only added to the excitement surrounding this technology’s capabilities. With that said, we want to announce the new round of the Kotlin Multiplatform Content Creators Recognition Campaign, highlighting the contributions of those passionate individuals who have been instrumental in keeping the community informed, engaged, and inspired.

Kotlin Multiplatform has captured the imagination of developers worldwide and has motivated content creators to share their how-tos, various articles packed with actionable tips, video tutorials, and sample projects. We want to shine a spotlight on this content that provides valuable insights, guidance, and inspiration to learners, enthusiasts, and professionals in the field.

Let’s introduce the latest round of finalists whose work has stood out in this Recognition Campaign.

Whether you’re a seasoned developer looking to expand your skillset or a newcomer eager to learn more about the Kotlin Multiplatform technology, we encourage you to explore this content, which provides a treasure trove of information.

And if you also have ideas to share, seize the opportunity to become a part of the Kotlin Multiplatform community of content creators! We choose our favorite new content once every three months and highlight it in the official Kotlin resources:

Are you struggling to come up with ideas for content? Use the following list of the most requested Kotlin Multiplatform topics for inspiration:

  • Use cases of what you have shared with Kotlin Multiplatform.
  • Experiments with cutting-edge Kotlin Multiplatform tools and libraries
  • Experience with Compose Multiplatform for iOS.
  • Migrating large projects to Kotlin Multiplatform.

Don’t limit yourself to these topics! Feel free to share your expertise and experience on another subject. Just don’t forget to add the #kotlinmultiplatform tag if the media platform you’re using allows it.

We’re excited to see how our community will continue to shape the Kotlin Multiplatform landscape in the future. Cheers to the creators who keep our community vibrant and thriving!

Continue ReadingShining a Spotlight on Kotlin Multiplatform Content Creators

Compose Multiplatform 1.5.0 Release

As of today, Compose Multiplatform 1.5.0 is available for you to use. It takes the Jetpack Compose declarative UI framework for Kotlin and extends it beyond Android to desktop, iOS, and web. The desktop version is stable, iOS is in Alpha, and support for web is experimental. For a full introduction, see the Compose Multiplatform website

Some highlights from this release are:

  1. The Dialog, Popup, and WindowInsets APIs are now in common code.
  2. On iOS scrolling, resource management and text fields have been improved.
  3. The UI testing framework has been stabilized on desktop.

This release is based on Jetpack Compose 1.5, which focuses on performance improvements. It also builds on the 1.1 release of Material Design 3. This includes new components such as date pickers and time pickers.

Try Compose Multiplatform 1.5.0

Compose Multiplatform supports Dialogs, Popups, and WindowInsets

As of version 1.5, dialogs and popups are available in Compose Multiplatform. Dialogs are used for modal events, where the user makes a choice or enters some data. Popups, meanwhile, are for non-modal behavior, such as offering optional functionality.

In this release, the base types Dialog and Popup, as well as DropdownMenu and AlertDialog, are accessible from within common code. This avoids the need to provide platform-specific functionality.

For example, the Composable below is written entirely in common code:

@Composable
fun CommonDialog() {
   var isDialogOpen by remember { mutableStateOf(false) }
   Button(onClick = { isDialogOpen = true }) {
       Text("Open")
   }
   if (isDialogOpen) {
       AlertDialog(
           onDismissRequest = { },
           confirmButton = {
               Button(onClick = { isDialogOpen = false }) {
                   Text("OK")
               }
           },
           title = { Text("Alert Dialog") },
           text = { Text("Lore ipsum") },
       )
   }
}

This is how it will appear on desktop:

Demonstration of the Dialog Boxes on Desktop

And this is how it will look on Android and iOS:

Demonstration of the Dialog Boxes on Android
            
Demonstration of the Dialog Boxes on iOS

A third feature, available in this release, is the WindowInsets API, which describes how much adjustment is required to prevent your content overlapping with the system UI. From version 1.5, this functionality is included with Compose Multiplatform, and so can be used on both Android and iOS. 

Using the WindowInsets API, you can draw background content via Compose Multiplatform behind the notch. This is done without adding a white line on top of the application. The screenshots below illustrate the difference this makes:

WindowInsets API Before and After in Compose Multiplatform

Improvements on iOS

The iOS platform was the focus of this release, and includes a wide variety of improvements. Scrolling mimics the platform’s look and feel, resource management has been simplified, and text handling is enhanced.

Natural scrolling

In this release, iOS scrolling is adapted to mimic native scrolling. Let’s say we have code where the number and/or size of items to be displayed exceeds the available space:

@Composable
fun NaturalScrolling() {
   val items = (1..30).map { "Item $it" }
   LazyColumn {
       items(items) {
           Text(
               text = it,
               fontSize = 30.sp,
               modifier = Modifier.padding(start = 20.dp)
           )
       }
   }
}

When scrolling, items spring from the edges of the screen, as with native iPhone applications:

Scrolling Springs on iOS

Support for Dynamic Type

The Dynamic Type feature on iOS allows a user to set their preferred font size – larger for ease of viewing or smaller to fit in more content. The text sizing used within an app should be relative to this system setting. 

This feature is now supported in Compose Multiplatform. The increments used in scaling the text are the same as those used in native applications, so the behavior will be identical.

For example, given the following Composable:

@Composable
fun DynamicType() {
   Text("This is some sample text", fontSize = 30.sp)
}

This is what will be displayed when the preferred reading size is set to the minimum:

Dynamic Type feature on iOS in Compose Multiplatform (small text)

Whereas, this is the result when the preferred reading size is at the maximum:

Dynamic Type feature on iOS in Compose Multiplatform (large text)

Support for high-refresh rate displays

In previous versions, the maximum frame rate was 60 FPS. This could result in the UI being slow and laggy on devices with 120Hz screens. From this release, frame rates of up to 120 FPS are supported.

Simplified resource management

From 1.5.0 onwards, any assets in the resources folder of an iOS source set are copied into the application bundle by default. So, for example, if you place an image file into src/commonMain/resources/, it will be copied to the bundle and is usable from your code.

If you are using CocoaPods, you no longer need to configure this behavior in the Gradle build file. You also don’t need to reinvoke podInstall to ensure assets are copied after modification. 

From this release, if you try to configure the behavior explicitly in build scripts (as shown below), then you will receive an error:

kotlin {
    cocoapods {
        extraSpecAttributes["resources"] = "..."
    }
}

For full details, and a guide to migrating existing code, please see this document.

Improved TextField

In earlier releases, there were two situations where entering text could cause unwanted behavior. From this release, TextField has been enhanced to overcome these issues.

Capitalization issues

Firstly, TextField now recognizes when the automatic capitalization of the first letter has been disabled. This is important, for example, when entering passwords. You control this behavior via the keyboardOptions argument.

To illustrate this, examine the Composable below: 

fun TextFieldCapitalization() {
   var text by remember { mutableStateOf("") }
   TextField(
       value = text,
       onValueChange = { text = it },
       keyboardOptions = KeyboardOptions(
           capitalization = KeyboardCapitalization.Sentences,
           autoCorrect = false,
           keyboardType = KeyboardType.Ascii,
       ),
   )
}

The image on the left shows what happens when the capitalization property is set to KeyboardCapitalization.None, whereas the image on the right shows what happens when the value is KeyboardCapitalization.Sentences.

Keyboard Capitalization Sentences with TextField
            
Keyboard Capitalization None

Hardware keyboards

The second situation relates to hardware keyboards. In previous versions, when using a hardware keyboard, pressing Enter resulted in multiple newlines, and pressing Backspace triggered multiple deletions. From this release, these events are now processed correctly.

Improvements on desktop

Stabilized test framework

This release stabilizes support for testing on Compose for desktop. Jetpack Compose provides a set of testing APIs to verify the behavior of your Compose code. These APIs were ported to desktop and available in previous releases, but with limitations. These limitations have now been removed, making it possible to write comprehensive UI tests for your application.

In order to provide an overview of the testing functionality, let’s create and test a simple UI. Here’s our example Composable:

@Composable
fun App() {
   var searchText by remember { mutableStateOf("cats") }
   val searchHistory = remember { mutableStateListOf<String>() }


   Column(modifier = Modifier.padding(30.dp)) {
       TextField(
           modifier = Modifier.testTag("searchText"),
           value = searchText,
           onValueChange = {
               searchText = it
           }
       )
       Button(
           modifier = Modifier.testTag("search"),
           onClick = {
               searchHistory.add("You searched for: $searchText")
           }
       ) {
           Text("Search")
       }
       LazyColumn {
           items(searchHistory) {
               Text(
                   text = it,
                   fontSize = 20.sp,
                   modifier = Modifier.padding(start = 10.dp).testTag("attempt")
               )
           }
       }
   }
}

This creates a simple UI that records search attempts:

Search App Used for Testing

Notice that Modifier.testTag has been used to assign names to TextField, Button and the items in LazyColumn

We can then manipulate the UI within a JUnit test:

class SearchAppTest {
   @get:Rule
   val compose = createComposeRule()


   @Test
   fun `Should display search attempts`() {
       compose.setContent {
           App()
       }


       val testSearches = listOf("cats", "dogs", "fish", "birds")


       for (text in testSearches) {
           compose.onNodeWithTag("searchText").performTextReplacement(text)
           compose.onNodeWithTag("search").performClick()
       }


       val lastAttempt = compose
           .onAllNodesWithTag("attempt")
           .assertCountEquals(testSearches.size)
           .onLast()


       val expectedText = "You searched for: ${testSearches.last()}"
       lastAttempt.assert(hasText(expectedText))
   }
}

Using the Compose-specific JUnit rule we:

  1. Set the content of the UI as the App Composable.
  2. Locate the text field and button via onNodeWithTag
  3. Repeatedly enter sample values into the text field and click the button.
  4. Find all the text nodes that were generated via onAllNodesWithTag.
  5. Assert that the current number of text nodes were created, and acquire the last.
  6. Assert that this last attempt contains the expected message.

Enhanced Swing interop

This release introduces experimental support for improved rendering of compose panels inside Swing components. This prevents transitional rendering issues when panels are being shown, hidden, or resized. It also enables proper layering when combining Swing components and compose panels. A Swing component can now be shown above or beneath a ComposePanel.

To illustrate this, examine the example below:

fun main() {
   System.setProperty("compose.swing.render.on.graphics", "true")
   SwingUtilities.invokeLater {
       val composePanel = ComposePanel().apply {
           setContent {
               Box(modifier = Modifier.background(Color.Black).fillMaxSize())
           }
       }


       val popup = object : JComponent() { ... }


       val rightPanel = JLayeredPane().apply {
           add(composePanel)
           add(popup)
           ...
       }


       val leftPanel = JPanel().apply { background = CYAN }


       val splitter = JSplitPane(..., leftPanel,rightPanel)


       JFrame().apply {
           add(splitter)
           setSize(600, 600)
           isVisible = true
       }
   }
}

In this code, we create and display a Swing JFrame, with the following content:

  1. The JFrame contains a JSplitPane with a vertical divider.
  2. On the left of the split pane is a standard JPanel, colored cyan.
  3. On the right is a JLayeredPane, made up of two layers:
    • A ComposePanel containing a Box composable, colored black
    • A custom Swing component, where the text “Popup” appears within a white rectangle. This is achieved by overriding the paintComponent method.

When the property compose.swing.render.on.graphics is set to true then: 

  • The custom Swing component is displayed on top of the Box Composable. 
  • There are no transitional graphical artifacts as the slider is moved.

Swing Interop Demo Working

If this flag had not been set, the custom component would not be visible, and there could be transitional artifacts as the slider was moved:

Swing Interop Demo Not Working

Please share your feedback about Compose Multiplatform. We invite you to join the discussion on the Kotlin Slack in the #compose channel, where you can discuss general topics related to Compose Multiplatform and Jetpack Compose. In #compose-ios, you can find discussions about Compose Multiplatform for iOS.

Try Compose Multiplatform 1.5.0

What else to read and watch

Continue ReadingCompose Multiplatform 1.5.0 Release

Help Us Improve the Performance of Your Compose Multiplatform Apps

The performance of Compose Multiplatform apps is one of our key priorities at JetBrains. Improving performance includes optimizing start-up time, rendering performance, and binary size, among other things. 

Currently, we’re focusing on enhancing rendering performance and providing smooth, glitchless, and jank-free animation on iOS. Our goal is to ensure that all different Compose apps work on iOS as smoothly as native iOS apps and Android apps. We’ve just released a number of performance improvements in the preview version of Compose Multiplatform 1.5.0, including improved rendering performance on modern 120Hz Apple devices and iOS-like scrolling. We need your feedback in order to keep making this already powerful UI framework even better!

In addition to better performance, this version of Compose Multiplatform also brings other functional enhancements that you may want to try, such as:

  • Support for iOS-specific insets in the WindowInsets API, which can be used from common code.
  • The ability to create Dialog and Popup composables in common code.
  • Support for the Dynamic Type feature that allows choosing the size of visible text in the global settings of Apple operating systems.

We would appreciate it if you could test 1.5.0-beta01 in your projects targeting iOS and share information about any performance issues that still exist.

Why your help is crucial

We have only a very limited number of sample apps, and they don’t cover all real-life cases. As a result, your help is extremely important and will allow us to make sure the technology meets the true performance expectations of your projects, as opposed to just meeting some generic benchmarks.

How to update to Compose Multiplatform 1.5.0-beta01

In the build.gradle.kts of your project, update the version of org.jetbrains.compose to 1.5.0-beta01 and load the Gradle changes.

What is a performance issue

If you notice that a Compose Multiplatform app doesn’t perform on iOS as it does on Android or as if it was a native iOS app, please report the issue, even if you consider it minor. Every issue matters to us!

How to report a performance issue

To help us effectively resolve the issue you encountered, please report a GitHub issue using the template below.

Report a performance issue

Learn more about Compose Multiplatform

Continue ReadingHelp Us Improve the Performance of Your Compose Multiplatform Apps

Compose Multiplatform 1.2 Is Out: New Mouse and Keyboard APIs, ProGuard Support, Online Updates, and More

Starting today, Compose Multiplatform 1.2 is available for you to use! This latest version of the declarative UI framework for Kotlin brings compatibility with the latest Kotlin version and introduces several powerful features for the desktop target. Specifically, here are the highlights:

  • The newly added ProGuard support allows you to minify and obfuscate your application before shipping.
  • Focus management is being improved, resulting in better keyboard navigation through form elements.
  • A reworked API for handling mouse and keyboard input makes it more convenient to implement actions that react to clicking and dragging.
  • We are introducing official support for right-to-left languages (RTL) for layouts and input fields.
  • Swing interoperability for focus management and context menus is becoming more seamless.
  • As an alternative to the Compose Gradle Plugin, you can now package your Desktop application using the third-party tool Hydraulic Conveyor, which makes it easy to build download pages, notarize your application for multiple platforms, and integrate auto-update functionality in your application.

Let’s look at the new features in Compose Multiplatform 1.2 in detail!

Explore the Compose Multiplatform Website

Support for the ProGuard optimizer and obfuscator

Starting with the latest version of Compose Multiplatform, you can now use ProGuard to improve the performance and size of your bundled application without having to provide special configurations. ProGuard applies optimizing techniques to shrink your code, reducing the size of your final binary. It also optimizes your code, which can provide slight performance improvements by removing unnecessary function calls or object allocations. Optionally, it also allows you to apply code obfuscation to your application.

In our tests, we’ve observed that processing an application with ProGuard significantly reduced the final distribution size for our sample applications.

Enabling ProGuard significantly reduces the bundle size of our sample applications, from 136 to 91 MiB for the installed application.

Even without specifying any options in your Gradle configuration for your project, you can use the new runReleaseDistributable and packageRelease<PACKAGE_FORMAT> tasks to process and subsequently run or package your code using ProGuard.

Providing custom ProGuard rules

If your application is using reflection or generates bytecode at runtime, you may need to give ProGuard instructions on which classes should not be removed during the optimization process. You can do so by providing a standard ProGuard configuration file and configuring it in your Gradle build file in the buildTypes.release.proguard block:

compose.desktop {
    application {
        // ...
        buildTypes.release.proguard {
            configurationFiles.from("rules.pro")
        }
    }
}

If you would like to take a closer look at the ProGuard integration provided in Compose Multiplatform, take a look at the example project provided in the GitHub repository.

Improved focus management for keyboard navigation

We have reworked some of the behavior when it comes to managing focus of UI elements, which influences the way users can navigate your elements and inputs via keyboard shortcuts. Both clickable and toggleable elements (with the modifiers Modifier.clickable and Modifier.toggleable) now request focus when clicked, making navigation between focusable items work as users would expect it to (selecting the next focusable item after the one that was clicked). In addition, once an element has been focused, we have also introduced new behaviors that make it easier to use keyboard shortcuts out of the box:

  • Composables marked with Modifier.toggleable can now be toggled on and off using the spacebar.
  • The Slider composable can now be controlled via the arrow keys (incrementing values one step at a time), the Page Up and Page Down keys (moving 10% of the value range at a time), and the Home and End keys (set the value to 0% or 100% of the range respectively).
  • Menu items from DropDownMenu, CursorDropDownMenu, and ContextMenu can now be navigated via the keyboard arrows.

Mouse and keyboard API improvements

We’re also refining Compose for Desktop’s experimental APIs for working with mouse and keyboard input more conveniently. Specifically, we’re introducing the onClick and onDrag modifiers, and making it easier to access modifier keys.

Please note that the mouse and keyboard API is only available on the Desktop target of Compose Multiplatform at the moment.

New onClick modifier

As a replacement for the now deprecated Modifier.mouseClickable, we’re introducing Modifier.onClick. This new modifier gives you fine-grained control over handling clicks of any type using callbacks. Using a PointerMatcher, you can distinguish between different types of clicks (regular, double-click, long click) and the button being used (left mouse button, right mouse button, middle mouse button, etc.). You also have the option to make these callbacks conditional on specific keyboard modifier keys that need to be pressed via the keyboardModifiers lambda parameter.

Box(modifier = Modifier.size(200.dp).background(Color.Yellow)
   .onClick(
       matcher = PointerMatcher.mouse(PointerButton.Secondary), // Right Mouse Button
       keyboardModifiers = { isAltPressed }, // accept clicks only when Alt pressed
       onLongClick = { // optional
           println("Long Click with secondary button and Alt pressed")
       },
       onDoubleClick = { // optional
           println("Double Click with secondary button and Alt pressed")
       },
       onClick = {
           println("Click with secondary button and Alt pressed")
       }
   )
   .onClick { // onClick with all default parameters
       println("Click with primary button (mouse left button)")
   }
) {
   Text(text = "Box with onClick", modifier = Modifier.align(Alignment.Center))
}

If you want to take a look at this API in more detail, we have prepared a tutorial that guides you through using the different options exposed by this modifier, and that also explains how you can chain multiple onClick modifiers to separate different conditions and modifier keys.

New onDrag modifier

Using the onDrag modifier, you can specify a callback that is called on drag events. Just like onClick, it works with a PointerMatcher, meaning you can configure additional constraints, such as which mouse button the drag event should work with. Likewise, you can specify multiple onDrag modifiers that work with different mouse buttons, allowing you to implement different drag behavior for the left and right mouse buttons, for example.

var boxOffset by remember { mutableStateOf(Offset(0f, 0f)) }

Box(modifier = Modifier
   .offset {
       IntOffset(boxOffset.x.toInt(), boxOffset.y.toInt())
   }.size(200.dp).background(Color.Yellow)
   .onDrag(
       matcher = PointerMatcher.mouse(PointerButton.Secondary), // it's Primary by default, can be configured with other pointer buttons such as Secondary, Tertiary, etc.
       onDragStart = { }, // optional
       onDragEnd = { },   // optional
       onDragCancel = { } // optional
   ) {
       println("Drag offset x=${it.x} y=${it.y}")
       boxOffset += it
   }

) {
   Text(text = "Box with onDragn(using right mouse button)", modifier = Modifier.align(Alignment.Center))
}

This also sets this experimental API apart from elements such as the draggable modifier that can be found in the Android target for Compose Multiplatform, which always uses the primary button.

For more information on this API, we have provided a tutorial that includes more extensive usage examples and also illustrates advanced functionality like listening for drag and tap gestures in a PointerInputScope.

Accessing keyboard modifiers

In the context of any composable, you can now use the LocalWindowInfo.current.keyboardModifiers state which provides access to the currently pressed keyboard modifiers (Ctrl, Shift, Alt, or others). Since keyboardModifiers is a state that automatically updates when any modifier keys are pressed or released, composables that are dependent on these keyboard modifiers will recompose.

val windowInfo = LocalWindowInfo.current
var hovered by remember { mutableStateOf(false) }
val color by derivedStateOf {
  if (hovered && windowInfo.keyboardModifiers.isCtrlPressed) {
      Color.Yellow
  } else {
      Color.Gray
  }
}

Box(modifier = Modifier
   .size(200.dp)
   .background(color)
   .onPointerEvent(PointerEventType.Enter) {
       hovered = true
   }.onPointerEvent(PointerEventType.Exit) {
       hovered = false
   }
) {
   Text("Hover me and then press Ctrl", modifier = Modifier.align(Alignment.Center))
}

Support for right-to-left languages and scripts

Starting with version 1.2, Compose for Desktop now supports right-to-left languages. By default, it automatically switches layout and inputs according to the detected system language. If you are implementing behavior that depends on the direction of the script being used, you can use the LocalLayoutDirection CompositionLocal which exposes this information.

Keyboard navigation interoperability and shared context menus with Swing

Combining user interfaces built with Compose for Desktop and those built with Swing becomes a bit more convenient with version 1.2. Using a SwingPanel (which allows you to embed Swing elements in a Compose for Desktop application) or a ComposePanel (embedding Compose for Desktop in your existing Swing applications) now works seamlessly when it comes to keyboard navigation – focusable elements from both UI frameworks now get selected properly when switching via the Tab key.

Tabbing through panels

If you are adding Compose for Desktop to an existing Swing application, you can now use the same context menu for text and text fields across both parts of your application. To do so, you can use the JPopupTextMenu composable.

val localization = LocalLocalization.current
CompositionLocalProvider(
    LocalTextContextMenu provides JPopupTextMenu(owner) { textManager, items ->
        JPopupMenu().apply {
            textManager.cut?.also {
                add(
                    swingItem(localization.cut, Color.RED, KeyEvent.VK_X, it)
                )
            }
            textManager.copy?.also {
                add(
                    swingItem(localization.copy, Color.GREEN, KeyEvent.VK_C, it)
                )
            }
            textManager.paste?.also {
                add(
                    swingItem(localization.paste, Color.BLUE, KeyEvent.VK_V, it)
                )
            }
            textManager.selectAll?.also {
                add(JPopupMenu.Separator())
                add(
                   swingItem(localization.selectAll, Color.BLACK, KeyEvent.VK_A, it)
                )
            }
            ...
        }
    }
) {
    ...
}

We also provide a number of tutorials that provide more details and full samples on Swing interoperability for context menus.

Alternative app distribution with online updates via Conveyor by Hydraulic

With version 1.2, you can use the third-party tool Conveyor by Hydraulic to package your desktop app as an alternative to the Compose Gradle Plugin. Conveyor is free for open-source projects, and is also free for commercial use during its introductory period.

Like with the Compose Gradle Plugin, you can create entirely self-contained applications that come in the native formats of the platforms you are targeting and that come with their own bundled JVM. On top of that, Conveyor adds support for online updates, generating a download page (example), automatic conversion of your icons for the respective platforms, and various other features. Additionally, because Conveyor does not rely on native tooling, you can build, sign, and notarize packages from any operating system.

Conveyor is a great fit for Compose for Desktop, because it drastically simplifies distribution. That makes it smoother to provide online updates and avoid complex multi-platform CI/CD setups. On both Windows and macOS, it also provides extra conveniences for internal or developer-targeted apps: When using the default self-signing mode, the generated download page provides your users with copy-pastable commands to install the app from the terminal (example).

Wrapping up

We hope you enjoy the latest version of Compose Multiplatform and continue to build beautiful UIs for your applications! Some smaller changes didn’t make it into this release post – for a full list of all changes and improvements in Compose Multiplatform 1.2, feel free to take a look at the changelog in the repository.

Starting from version 1.2, Compose Multiplatform comes with support for multiple versions of the Kotlin compiler: The desktop target supports both Kotlin 1.7.10 and Kotlin 1.7.20. The web target currently supports Kotlin 1.7.10, and will provide support for 1.7.20 within the scope of the next minor update.

As usual, the easiest way to get started with this latest version of Compose Multiplatform is to take a look at the official tutorials – or to start exploring on your own using the Kotlin Project Wizard in IntelliJ IDEA. Have fun composing!

Explore the Compose Multiplatform Website

See also

Continue ReadingCompose Multiplatform 1.2 Is Out: New Mouse and Keyboard APIs, ProGuard Support, Online Updates, and More

Compose Multiplatform 1.0 출시!

Kotlin의 선언형 UI 프레임워크인 JetBrains Compose Multiplatform 버전 1.0이 출시되어 프로덕션 사용이 준비되었습니다! 이 프레임워크의 신규 버전이 제공하는 주요 기능은 다음과 같습니다.

  • 데스크톱에서 뛰어난 사용자 인터페이스를 지원하는 Kotlin 앱을 빠르고 효율적으로 개발할 수 있습니다.
  • 에서 모든 브라우저 API와 완벽한 상호 운용성을 갖춘 Compose for Web의 안정적인 DOM API를 사용하여 프로덕션 수준의 동적 웹 환경을 구축할 수 있습니다. Material UI 위젯은 향후 릴리스에서 지원될 예정입니다.
  • 전반적으로 다양한 플랫폼(Android 포함, Jetpack Compose by Google 호환성 사용) 간 전문 기술과 코드를 더 쉽게 공유할 수 있습니다.

하나씩 살펴보겠습니다.

웹사이트 방문

데스크톱용 Kotlin UI

꽤 오랫동안 Kotlin 데스크톱 애플리케이션의 사용자 인터페이스를 개발하려면 기존 Java UI 프레임워크를 사용해야 했습니다. 데스크톱용 모던 UI 개발 스타일을 접목한 Kotlin 라이브러리가 없었기 때문입니다. JetBrains는 Compose Multiplatform을 선보여 변화를 이끌고자 합니다. 이 프레임워크가 Kotlin 앱 UI 개발 경험을 개선하는 방식을 알아보겠습니다.

사용자 인터페이스 개발을 위한 선언형 접근 방식

Compose Multiplatform은 선언형이므로 코드에 앱의 UI 구조가 반영되며, 모델에서 뷰로 데이터 복사 또는 UI 갱신 로직 개발 등의 작업을 고민할 필요가 없습니다. 프레임워크로 모두 처리되므로, UI 개발은 즐거운 경험입니다. 다음 예시의 경우 TextField의 내용이 수정되면 추가 코딩 없이도 Text 라벨 내용이 업데이트됩니다.

var text by remember { mutableStateOf("Hello, World!") }
Column {
   Text(text) //text label
   TextField(text, {text = it}) //text field
}

특히 React 또는 Jetpack Compose by Google과 같은 선언형 UI 프레임워크를 사용해본 경우, Compose Multiplatform을 쉽게 시작할 수 있습니다. Compose Multiplatform은 이와 동일한 많은 기능을 지원하므로 부담 없이 편안하게 사용할 수 있습니다.

하드웨어 가속으로 우수한 런타임 성능

모던 사용자 인터페이스에서 성능이 중요합니다. 따라서 Compose Multiplatform 속도 개선을 위해 최선을 다하고 있습니다. Compose Multiplatform은 Skia를 사용합니다. Skia는 모던 브라우저를 포함하여 성능이 중요한 다수의 애플리케이션에서 사용되는 최적화된 그래픽 라이브러리입니다. 즉, Compose Multiplatform은 DirectX, Metal 및 OpenGL 등 데스크톱의 모든 주요 하드웨어 가속 엔진을 지원합니다. 하드웨어 가속이 지원되지 않는 환경에서는 Compose가 최적화된 소프트웨어 렌더러와 함께 제공됩니다.

미리보기 도구를 통해 반복 주기 단축

UI 개발에서 가장 긴 시간이 소요되는 작업 중 하나는 애플리케이션이 완벽하게 보이도록 다시 빌드하는 과정입니다. Compose Multiplatform IDEA 플러그인을 활용하면 이 과정이 간소화됩니다. 기본 제공 라이브 미리보기 기능을 통해 UI 구성 요소를 세밀하게 조정하고, 애플리케이션을 다시 빌드하거나 시작하지 않고도 여러 개의 반복을 생성할 수 있습니다. 이로써 개발 주기가 대폭 단축됩니다.

자동 애플리케이션 패키징으로 자신 있게 선보이는 데스크톱 앱

사용자에게 애플리케이션을 제공하려면 적절한 개발뿐 아니라 적절한 패키징도 필요합니다. Compose Multiplatform은 이에 대한 지원도 제공합니다. Gradle 플러그인은 MacOS용 서명 및 공증을 비롯하여 msi, dmgdeb 형식으로 애플리케이션 패키징을 지원합니다.

Android 및 Java UI 프레임워크의 Jetpack Compose와 상호 운용성

Google에서 제작한 네이티브 UI 빌드용 Android 모던 툴킷인 Jetpack Compose는 모바일 개발자 사이에서 지속적으로 인기를 끌고 있습니다. Jetpack Compose를 사용해본 경험이 있다면 Compose Multiplatform은 쉽게 사용할 수 있습니다. 두 프레임워크가 API의 상당 부분을 공유하기 때문입니다.

사용자 인터페이스가 일반적인 Java UI 프레임워크로 빌드한 데스크톱 애플리케이션을 다루는 경우, Compose Multiplatform에서 사용하기 위해 코드를 완전히 처음부터 다시 작성할 필요가 없습니다. Compose Multiplatform은 상호 운용성이 매우 뛰어나므로 Compose로 작성된 UI 구성 요소를 기존 Java UI에 추가할 수 있습니다. Compose Multiplatform으로 새로운 앱을 빌드하는 경우에도 기존 Java 컨트롤을 추가할 수 있습니다.

Compose Multiplatform 마법사로 빠른 시작 및 실행

이제 Compose Multiplatform을 매우 빠르게 시작할 수 있습니다. IntelliJ IDEA 2021.1+에서 클릭 몇 번만으로 간단한 Compose Multiplatform 프로젝트 생성이 가능합니다.

Compose 데스크톱 애플리케이션을 보여주는 New Project(새 프로젝트) 마법사 예시

또한 다양한 튜토리얼을 통해 Compose Multiplatform 데스크톱 대상을 알아볼 수 있습니다.

Compose for Web

Compose Multiplatform은 데스크톱 작업 외에도 DOM 작업을 위한 강력한 선언형 Kotlin/JS API를 제공합니다.

또한 다양한 DOM API, CSS-in-JS 기본 지원, SVG 지원, 타입 지정된 입력 등 모던 웹 프레임워크에 필요한 모든 기능을 제공합니다. Compose Multiplatform의 웹 대상은 Kotlin만으로 작성되었으며 Kotlin의 타입 시스템과 관용구를 최대한 활용합니다. 따라서 다른 Kotlin 대상에서 이미 사용된 동일한 개발 워크플로를 활용할 수 있습니다.

멀티플랫폼 지원

Compose Multiplatform을 사용하면 (직접 지원되는) 데스크톱 및 웹 플랫폼으로 대상이 한정되지 않습니다. Google에서 개발한 유명 UI 프레임워크인 Jetpack Compose를 사용해 Android를 대상으로 지정할 수도 있습니다. 두 프레임워크는 공통 API와 Core를 공유하므로, 완벽한 상호 운용성을 제공합니다. 즉, 공통 UI 및 상태 관리 코드를 다시 작성하지 않아도 됩니다. 한 번 작성한 후 필요할 때 여러 플랫폼에서 다시 사용할 수 있습니다.

기존 Android 애플리케이션을 데스크톱이나 웹으로 가져오는 작업도 Compose Multiplatform을 사용하면 매우 간편합니다. Kotlin 프로젝트 하나로 애플리케이션의 모든 대상을 관리할 수 있습니다.

Compose를 통해 여러 플랫폼을 대상으로 하는 앱 빌드를 빠르게 시작하려면 IntelliJ IDEA 2021.1+에서 Kotlin 프로젝트 마법사를 사용해 보세요.

당장 멀티플랫폼 애플리케이션을 개발할 필요가 없더라도, 플랫폼 한 곳에서 습득한 지식과 전문 기술이 다른 플랫폼에서 유용하게 쓰일 수 있습니다.

베타 이후 변경 사항

Compose Multiplatform 1.0 버전은 이 프레임워크를 프로덕션 애플리케이션에서 사용할 준비가 되었는지 확인하는 데 초점을 맞춘 버전입니다. 결과적으로 이번 릴리스에서 중요한 이슈와 버그가 수정되었으며, 품질과 안정성도 개선되었습니다.

실제 프로덕션 경험

Compose Multiplatform은 오늘 출시되었음에도 일부 프로덕션에서 이미 활용된 바 있습니다. 예를 들어, JetBrains는 2021년 초부터 Jetbrains Toolbox App(https://www.jetbrains.com/toolbox-app/)에 Compose Multiplatform을 적용했습니다. JetBrains IDE용 관리 애플리케이션인 Toolbox App은 매월 1,000,000명 이상의 활성 사용자를 보유하며, 4개월 전 C++ 및 Electron에서 Compose Multiplatform으로 완전히 마이그레이션되었습니다.

마무리

Kotlin 개발자는 이제 Compose Multiplatform이라는 강력한 프레임워크를 통해 데스크톱과 웹 애플리케이션 모두에 적합한 아름다운 UI를 생성할 수 있습니다.

지금 바로 Compose Multiplatform을 사용해 보세요! 공식 튜토리얼에서 가장 간편한 사용 방법을 확인할 수 있습니다. IntelliJ IDEA 2021.1+에서 기본으로 제공되는 Kotlin 프로젝트 마법사를 사용하여 첫 Compose Multiplatform 프로젝트를 생성하고 Kotlin으로 선언형 사용자 인터페이스 빌드를 시작하세요.

여러분의 마음에 드셨으면 좋겠습니다!

게시물 원문 작성자

Sebastian Aigner

Continue ReadingCompose Multiplatform 1.0 출시!

JetBrains Toolbox 사례 연구: 100만 사용자를 Kotlin 및 Compose Multiplatform으로 이동

JetBrains의 Toolbox 팀 리더인 Victor Kropp이 데스크톱에서 Kotlin과 Compose Multiplatform을 사용하게 된 사연을 전해 드립니다.

JetBrains Toolbox App은 JetBrains IDE를 사용하여 개발 작업을 수행하기 위한 단일 진입점으로, 도구 및 프로젝트를 위한 제어판 역할을 하며 JetBrains IDE를 빠르고 쉽게 설치 및 업데이트할 수 있게 해줍니다. 2015년 해커톤 프로젝트에서 시작된 이 애플리케이션은 현재 월 100만 명의 실 사용자에게 서비스를 제공하여 JetBrains 제품과 함께 작업 생산성을 높이는 데 도움을 주고 있습니다.

Toolbox 팀이 이 애플리케이션을 C++ 및 JavaScript에서 Kotlin 및 Compose Multiplatform으로 완벽하게 마이그레이션한 후, 코드 작업과 관리를 더 간편하게 수행하고 아티팩트 크기를 줄여 런타임 성능을 개선할 수 있었다는 이야기를 읽어보세요.

JetBrains의 Toolbox 팀을 이끄는 Victor Kropp로부터 직접 들으려면 Talking Kotlin #107을 확인하세요.

Victor씨, JetBrains Toolbox에서 사용하는 아키텍처와 기술 스택을 소개해줄 수 있나요?

Toolbox App은 일반적인 클라이언트-서버 애플리케이션입니다. 이 데스크톱 앱은 서버에서 사용 가능한 도구 목록을 요청하여 사용자에게 보여주고 필요할 경우 JetBrains 제품 업데이트를 다운로드합니다. 애플리케이션의 서버 측 부분은 처음부터 Kotlin으로 구현했습니다. 그러나 데스크톱 애플리케이션은 달랐습니다.

2015년에 JetBrains Toolbox용 데스크톱 앱을 빌드하기 시작했을 때 C++를 사용하여 비즈니스 로직을 구현하고 React 및 HTML/CSS/JS와 함께 Chromium Embedded Framework를 사용하여 사용자 인터페이스를 빌드했습니다. 이러한 선택을 할 당시에는 Kotlin 1.0이 아직 출시되지 않았고 Java 9와 함께 제공된 모듈식 JDK도 없었습니다. 고작 작은 도우미 애플리케이션을 위해 수백 메가바이트의 JRE(Java Runtime Environment)를 번들로 제공할 여유도 없었고 사용자들에게 환경을 수동으로 설정해야 하는 번거로움을 안겨주고 싶지도 않았습니다. 그래서 완전히 다른 접근방식을 선택해야 했죠.

2021년에 이르러 사용자 인터페이스를 React에서 Compose Multiplatform, 더 구체적으로는 Compose for Desktop으로 마이그레이션하여 이 데스크톱 애플리케이션을 100% Kotlin으로 만드는 마지막 단계를 완료했습니다.

Compose for Desktop 웹사이트 방문하기

해당 제품에서 Kotlin과 라이브러리를 어떻게 사용하시나요?

Kotlin으로 마이그레이션을 완료하면서 모든 곳에서 사용하고 있습니다. 사용자 인터페이스를 지원하는 Compose for Desktop 외에도 모든 비동기 작업에 kotlinx.coroutines를 정말 많이 사용합니다. Toolbox App은 수많은 JSON 객체를 처리하므로 이에 따라 저희도 kotlinx.serialization을 (역)직렬화에 사용합니다.

서버 측은 최대한 단순하게 유지됩니다. 사실 여러분이 생각하시는 것과는 달리 이것은 HTTP 서버가 아닙니다. 설치 가능한 도구에 대한 모든 정보와 설명(Toolbox에서는 ‘피드’라고 함)은 정적으로 생성되며 CDN에서 JSON 파일로 제공됩니다. 피드가 자주 변경되지는 않으므로 저희는 TeamCity에서 지속적 배포 파이프라인의 일부로 새 버전의 도구가 출시될 때만 이를 업데이트합니다. 제너레이터는 지원되는 각 제품이 빌드될 때마다 자동으로 트리거되는 ‘빌드 구성'(작업을 나타내는 TeamCity 명칭)으로 호출되는 간단한 명령줄 Kotlin 프로그램입니다. 두 번째 작업에서는 새로 생성된 모든 피드를 주기적으로 병합하고 오래된 피드를 폐기하고 유효성 검사를 수행합니다.

Toolbox 팀이 데스크톱용 애플리케이션 개발에 Kotlin을 사용하기로 결정한 이유는 무엇인가요?

Compose for Desktop으로 이전하기 전에는 Chromium Embedded Framework를 사용하여 Toolbox App용 사용자 인터페이스를 빌드했고 비즈니스 로직에 기본 C++를 사용하여 모든 주요 데스크톱 운영 체제를 쉽게 지원할 수 있었습니다. (그 후로 많은 것이 바뀌었고 2020년에는 모든 C++ 비즈니스 로직을 JVM에서 실행되는 Kotlin으로 옮기기로 결정했습니다.)

2015년 당시에는 프로젝트를 시작하는 데 좋은 선택이었습니다. JetBrains에서 빌드한 웹 UI 구성 요소 라이브러리인 Ring UI의 구성 요소를 재사용할 수 있었습니다. 또한 저희는 웹 개발과 React 작업에 이미 많은 경험을 가지고 있었습니다.

그러나 단점도 없지 않았습니다.

  • Chromium Embedded Framework는 리소스 소비가 많은 것으로 잘 알려져 있습니다. 유휴 상태일 때도 JetBrains Toolbox는 최소 200MiB의 RAM을 사용하곤 했습니다. 또한 창이 보이지 않을 때 전체 프레임워크를 언로드할 수 없었습니다. 사용자가 앱과 상호 작용하려고 할 때 몇 초 정도 지연이 발생하기 때문입니다.
  • 저희에게는 단일 데스크톱 애플리케이션 내부에 완전한 클라이언트-서버 아키텍처가 필요했습니다. 임베디드 웹 UI와 비즈니스 로직은 서로 다른 언어로 서로 다른 사람에 의해 작성되었습니다. 이로 인해 개발 프로세스가 복잡해졌고 애플리케이션 내에서 수 메가바이트의 JSON을 주고 받기 위한 리소스가 필요했으며 이미 가지고 있던 데이터의 (역)직렬화에 CPU 리소스가 소진되었습니다.

이러한 상황은 애플리케이션을 100% Kotlin으로 이전한 후 크게 개선되었습니다.

  • Compose for Desktop은 리소스를 훨씬 적게 사용합니다. Compose 프레임워크는 JavaScript 구현에 비해 더 나은 런타임 성능을 제공하며 백그라운드에서 유휴 상태로 실행될 때 앱에서 사용하는 RAM을 크게 줄일 수 있었습니다.
  • 단일 언어를 사용한다는 것은 모든 개발자가 컨텍스트를 전환하지 않고 처음부터 끝까지 기능을 개발할 수 있음을 의미합니다. 더 빠르고 실수도 적게 발생하며 개발자 간에 원활한 지식 공유가 가능합니다. 또한 전체 애플리케이션이 메모리의 데이터에 대해 동일한 표현을 사용하므로 추가 (역)직렬화 단계가 필요하지 않습니다.

Toolbox에 Kotlin을 도입한 경험에 대해 말씀해 주시겠어요?

저희 앞에는 해결해야 할 많은 과제가 있었습니다. 우선, 만들어진 지 5년이 된 코드베이스를 모든 기능과 문제점을 포함한 상태로 다른 스택으로 마이그레이션해야 했습니다. 애플리케이션의 핵심 요소가 의도대로 작동하도록 모든 유닛 테스트를 마이그레이션했습니다. 그러나 저희 애플리케이션에는 에코시스템마다 확연히 다른 외부 종속 요소가 많이 필요합니다. 어떤 부분은 이전 구현에서 작동하지 않다가 새 구현에서는 작동하기 시작했습니다. 저희가 아무 조치도 취하지 않았는데도 말이죠. 이유는 단순히 새로운 종속 요소가 이를 지원했기 때문입니다. 그러나 당연히 작동하리라 여겼던 다른 것들은 더 이상 작동하지 않았습니다. 공개 릴리스가 출시된 후에도 이러한 차이점을 파악하지 못한 경우도 있었습니다. 두 범주의 사례는 시스템 트레이(메뉴 모음) 아이콘 또는 프록시 서버 및 SSL 인증서와 같은 운영 체제 통합의 다양한 측면들입니다. 다른 한편으로, Toolbox의 ‘Projects‘(프로젝트) 탭에서 IntelliJ IDEA의 프로젝트 검색을 지원하는 코드를 재사용하거나 특정 엔터프라이즈 설정을 탐지하는 것과 같이 JetBrains의 다른 팀이 작성한 Kotlin 코드를 재사용할 수도 있습니다.

저희는 Compose for Desktop를 공식 발표 전부터 사용하기 시작했기 때문에 프레임워크에서 발생하는 문제를 가장 먼저 접하는 경우가 많았습니다. Compose for Desktop의 최초 사용자로서, 저희는 사용 시작과 동시에 모든 종류의 문제를 확인하고 Compose Multiplatform 팀의 동료에게 모든 문제를 보고했습니다. 그들은 많은 도움을 주었고 빠르게 대응했으며 모든 문제를 매우 신속하게 수정했습니다. 심지어 같은 날 수정 사항이 포함된 새 릴리스를 받는 경우도 있었습니다. 매우 인상적이었죠! 또한 Compose를 채택할 때, 그리고 프레임워크에서 어려움을 겪고 있을 때도 많은 도움을 주었습니다.

저희는 이전 설계를 완벽하게 복제할 수 있었습니다. Compose는 언뜻 보기에 HTML/CSS에 비해 더 적은 기본 레이아웃 프리미티브를 제공하는 것 같지만 간단한 수평 및 수직 스택(Compose의 행 및 열)만으로 이미 모든 요구사항의 99%가 충족된다는 사실을 금세 알게 되었습니다. 처음 시작할 때 Compose for Desktop에는 SVG 그래픽 지원과 같은 일부 기능이 여전히 누락되어 있었으나 Compose 팀의 동료들이 고맙게도 이러한 부족한 부분을 매우 신속하게 해결해주었습니다.

처음에는 애플리케이션 전체에서 Compose의 Material 구성 요소를 사용했습니다. 이 구성 요소는 매우 다각적으로 고려하여 만들어졌지만 터치 인터페이스에 중점을 두고 있습니다. 이 때문에 모든 요소에 많은 여백이 있고(손가락으로 쉽게 누를 수 있도록), 마우스오버 상태가 없으며(터치 디스플레이에는 이러한 동작이 없으므로), 매우 눈에 띄는 시각적 터치 피드백이 제공됩니다. 데스크톱에서는 마우스를 가져가면 구성 요소가 반응하고 클릭에 대한 시각적 피드백이 현재 요소에만 영향을 미치는(손가락으로 가려지지 않으므로) 등 상황이 많이 다릅니다. 이 때문에 저희는 Material 구성 요소를 데스크톱에서 더 잘 작동하는 자체 구성 요소로 교체하고 있습니다. 또한 향후에는 구성 요소 라이브러리를 오픈 소스로 공개할 계획이 있으므로 계속 지켜봐 주시기 바랍니다.

Compose for Desktop을 선택하기 전에 다른 UI 프레임워크를 고려하셨나요?

한 가지 대안은 애플리케이션을 완전한 기본 인터페이스로 변환하는 것이었지만, 이렇게 했다면 기능마다 3배는 더 많은 노력이 필요했을 겁니다. 저희는 크로스 플랫폼인 동시에 보기에도 좋으며 Kotlin과 잘 작동하는 프레임워크를 원했습니다.

Swing은 너무 오래되었고 JavaFX는 사용 저변이 아직 부족하다고 느꼈습니다. 이것이 당시에 발표된 지 얼마 되지 않았음에도 불구하고 Compose for Desktop을 선택한 이유입니다. 해당 팀에게 직접적 지원을 받고 피드백이 순환적으로 긴밀하게 이루어진다는 점도 큰 이유를 차지했죠.

Kotlin이 해당 제품에 가져온 가장 큰 이점은 무엇인가요?

일상 작업이 훨씬 간편해졌습니다. 이제 전체 애플리케이션에서 동일한 언어를 사용하므로, 팀의 개발자들이 이전보다 코드와 지식을 더 잘 공유할 수 있게 되었습니다. 또한 C++ 및 JavaScript 대신 Kotlin으로 작성하면서 일하는 재미가 훨씬 커졌습니다!

저희 독자들에게 하고 싶은 조언이나 권장 사항이 있나요?

기존 애플리케이션을 새 프레임워크로 전환하려고 한다면 마이그레이션의 복잡성을 얕잡아보면 안 됩니다. 거의 처음부터 새로운 애플리케이션을 작성하는 것과 다르지 않습니다! 기능뿐만 아니라 의도적이든 아니든 앱 동작의 사소한 뉘앙스까지도 놓치지 않고 다시 구현해야 하거든요.

저는 Compose for Desktop이 2021년에 크로스 플랫폼 데스크톱 애플리케이션을 만드는 주된 방법이 될 것이라고 굳게 믿습니다. 유사한 기술과 비교할 때 Kotlin은 JVM에서 검증된 에코시스템에 대한 액세스를 제공하고 Dart 및 Flutter보다 채택률이 훨씬 높으며 React/JS를 사용하는 Electron보다 훨씬 효율적입니다.

Compose for Desktop으로 첫 데스크톱 앱 빌드하기

Victor Kropp은 JetBrains Toolbox App의 팀장입니다.

게시물 원문 작성자

Sebastian Aigner

Continue ReadingJetBrains Toolbox 사례 연구: 100만 사용자를 Kotlin 및 Compose Multiplatform으로 이동

End of content

No more pages to load