All Projects → icerockdev → Moko Resources

icerockdev / Moko Resources

Licence: apache-2.0
Resources access for mobile (android & ios) Kotlin Multiplatform development

Programming Languages

kotlin
9241 projects

Projects that are alternatives of or similar to Moko Resources

Audiovideocodec
一款视频录像机,支持AudioRecord录音、MediaCodec输出AAC、MediaMuxer合成音频视频并输出mp4,支持自动对焦、屏幕亮度调节、录制视频时长监听、手势缩放调整焦距等
Stars: ✭ 113 (-20.42%)
Mutual labels:  gradle-plugin
Jib
🏗 Build container images for your Java applications.
Stars: ✭ 11,370 (+7907.04%)
Mutual labels:  gradle-plugin
Gson Plugin
辅助 Gson 库的 gradle 插件,防止 Json 数据解析类型异常。
Stars: ✭ 133 (-6.34%)
Mutual labels:  gradle-plugin
Jaop
jaop is a gradle plugin base on javassist&asm for android aop
Stars: ✭ 115 (-19.01%)
Mutual labels:  gradle-plugin
Clojurephant
Clojure and Clojurescript support for Gradle
Stars: ✭ 118 (-16.9%)
Mutual labels:  gradle-plugin
Bytex
ByteX is a bytecode plugin platform based on Android Gradle Transform API and ASM. 字节码插件开发平台
Stars: ✭ 2,140 (+1407.04%)
Mutual labels:  gradle-plugin
Gradle Build Properties Plugin
Keep your secrets secret. External build properties support for your Gradle scripts.
Stars: ✭ 110 (-22.54%)
Mutual labels:  gradle-plugin
Kotlin Gradle Plugin Template
🐘 A template to let you started with custom Gradle Plugins + Kotlin in a few seconds
Stars: ✭ 141 (-0.7%)
Mutual labels:  gradle-plugin
Reckon
Infer a project's version from your Git repository.
Stars: ✭ 124 (-12.68%)
Mutual labels:  gradle-plugin
Yguard
The open-source Java obfuscation tool working with Ant and Gradle by yWorks - the diagramming experts
Stars: ✭ 130 (-8.45%)
Mutual labels:  gradle-plugin
Fat Aar Android
A gradle plugin that merge dependencies into the final aar file works with AGP 3.+
Stars: ✭ 2,076 (+1361.97%)
Mutual labels:  gradle-plugin
Okbuck
OkBuck is a gradle plugin that lets developers utilize the Buck build system on a gradle project.
Stars: ✭ 1,513 (+965.49%)
Mutual labels:  gradle-plugin
Gradle Eclipse Aar Plugin
Gradle plugin to use Android AAR libraries on Eclipse.
Stars: ✭ 127 (-10.56%)
Mutual labels:  gradle-plugin
Godot
Keep track of how much time you spend on Gradle builds
Stars: ✭ 113 (-20.42%)
Mutual labels:  gradle-plugin
Findbugs Android
Gradle plugin that creates FindBugs reports for android projects
Stars: ✭ 133 (-6.34%)
Mutual labels:  gradle-plugin
Licensetoolsplugin
Gradle plugin to check library licenses and generate license pages for Android
Stars: ✭ 113 (-20.42%)
Mutual labels:  gradle-plugin
Gradle Css Plugin
Gradle plugin for working with CSS
Stars: ✭ 125 (-11.97%)
Mutual labels:  gradle-plugin
Gradle Pitest Plugin
Gradle plugin for PIT Mutation Testing
Stars: ✭ 144 (+1.41%)
Mutual labels:  gradle-plugin
Gradle Nexus Staging Plugin
Automatize releasing Gradle projects to Maven Central.
Stars: ✭ 132 (-7.04%)
Mutual labels:  gradle-plugin
Forma
Meta build system with Android and Gradle support.
Stars: ✭ 127 (-10.56%)
Mutual labels:  gradle-plugin

moko-resources
GitHub license Download kotlin-version

Mobile Kotlin resources

This is a Kotlin MultiPlatform library that provides access to the resources on iOS & Android with the support of the default system localization.

Table of Contents

Features

  • Strings, Plurals, Images, Fonts, Files to access the corresponding resources from common code;
  • Colors with light/dark mode support;
  • StringDesc for lifecycle-aware access to resources and unified localization on both platforms;
  • Static iOS frameworks support;
  • FatFrameworkWithResourcesTask Gradle task.

Requirements

  • Gradle version 6.0+
  • Android API 16+
  • iOS version 9.0+

Versions

  • kotlin 1.3.50
    • 0.1.0
    • 0.2.0
    • 0.3.0
    • 0.4.0
  • kotlin 1.3.60
    • 0.5.0
  • kotlin 1.3.61
    • 0.6.0
    • 0.6.1
    • 0.6.2
    • 0.7.0
    • 0.8.0
  • kotlin 1.3.70
    • 0.9.0
  • kotlin 1.3.71
    • 0.9.1
  • kotlin 1.3.72
    • 0.10.0
    • 0.10.1
    • 0.11.0
    • 0.11.1
    • 0.12.0
  • kotlin 1.4.0
    • 0.13.0
    • 0.13.1
  • kotlin 1.4.21
    • 0.13.2
    • 0.14.0
    • 0.15.0

Installation

root build.gradle

buildscript {
    repositories {
        maven { url = "https://dl.bintray.com/icerockdev/plugins" }
    }

    dependencies {
        classpath "dev.icerock.moko:resources-generator:0.15.0"
    }
}


allprojects {
    repositories {
        maven { url = "https://dl.bintray.com/icerockdev/moko" }
    }
}

project build.gradle

apply plugin: "dev.icerock.mobile.multiplatform-resources"

dependencies {
    commonMainApi("dev.icerock.moko:resources:0.15.0")
}

multiplatformResources {
    multiplatformResourcesPackage = "org.example.library" // required
    iosBaseLocalizationRegion = "en" // optional, default "en"
    multiplatformResourcesSourceSet = "commonClientMain"  // optional, default "commonMain"
}

ios-app Info.plist:

<key>CFBundleLocalizations</key>
<array>
    <string>en</string>
    <string>ru</string>
</array>

in array should be added all used languages.

Static kotlin frameworks support

If project configured with static framework output (for example by org.jetbrains.kotlin.native.cocoapods plugin) in Xcode project should be added Build Phase (at end of list) with script:

"$SRCROOT/../gradlew" -p "$SRCROOT/../" :yourframeworkproject:copyFrameworkResourcesToApp \
    -Pmoko.resources.PLATFORM_NAME=$PLATFORM_NAME \
    -Pmoko.resources.CONFIGURATION=$CONFIGURATION \
    -Pmoko.resources.BUILT_PRODUCTS_DIR=$BUILT_PRODUCTS_DIR \
    -Pmoko.resources.CONTENTS_FOLDER_PATH=$CONTENTS_FOLDER_PATH

Please replace :yourframeworkproject to kotlin project gradle path, and set correct relative path ($SRCROOT/../ in example).
This phase will copy resources into application, because static frameworks can't have resources.

To disable warnings about static framework in gradle set flag:

multiplatformResources {
    disableStaticFrameworkWarning = true
}

With Pods dependencies in Kotlin

When you use org.jetbrains.kotlin.native.cocoapods plugin and also kotlin module depends to Pods - you also need to pass extra properties:

"$SRCROOT/../gradlew" -p "$SRCROOT/../" :shared:copyFrameworkResourcesToApp \
    -Pmoko.resources.PLATFORM_NAME=$PLATFORM_NAME \
    -Pmoko.resources.CONFIGURATION=$CONFIGURATION \
    -Pmoko.resources.BUILT_PRODUCTS_DIR=$BUILT_PRODUCTS_DIR \
    -Pmoko.resources.CONTENTS_FOLDER_PATH=$CONTENTS_FOLDER_PATH\
    -Pkotlin.native.cocoapods.target=$KOTLIN_TARGET \
    -Pkotlin.native.cocoapods.configuration=$CONFIGURATION \
    -Pkotlin.native.cocoapods.cflags="$OTHER_CFLAGS" \
    -Pkotlin.native.cocoapods.paths.headers="$HEADER_SEARCH_PATHS" \
    -Pkotlin.native.cocoapods.paths.frameworks="$FRAMEWORK_SEARCH_PATHS"

and setup extra build settings in your xcode target:

'KOTLIN_TARGET[sdk=iphonesimulator*]' => 'ios_x64'
'KOTLIN_TARGET[sdk=iphoneos*]' => 'ios_arm64'

here example of changes

Usage

Example 1 - simple localization string

The first step is a create a file strings.xml in commonMain/resources/MR/base with the following content:

<?xml version="1.0" encoding="UTF-8" ?>
<resources>
    <string name="my_string">My default localization string</string>
</resources>

Next - create a file strings.xml with localized strings in commonMain/resource/MR/<languageCode>. Here's an example of creating commonMain/resource/MR/ru for a Russian localization:

<?xml version="1.0" encoding="UTF-8" ?>
<resources>
    <string name="my_string">Моя строка локализации по умолчанию</string>
</resources>

After adding the resources we can call a gradle sync or execute a gradle task generateMRcommonMain. This will generate a MR class containing MR.strings.my_string, which we can use in commonMain:

fun getMyString(): StringDesc {
  return StringDesc.Resource(MR.strings.my_string)
}

After this we can use our functions on the platform side:
Android:

val string = getMyString().toString(context = this)

iOS:

let string = getMyString().localized()

Note: StringDesc is a multiple-source container for Strings: in StringDesc we can use a resource, plurals, formatted variants, or raw string. To convert StringDesc to String on Android call toString(context) (a context is required for the resources usage), on iOS - call localized().

MR directly from native side

Android:

val string = MR.strings.my_string.desc().toString(context = this)

iOS:

let string = MR.strings.my_string.desc().localized()

Get resourceId for Jetpack Compose / SwiftUI

Android:

val resId = MR.strings.my_string.resourceId

for example in Compose:

text = stringResource(id = MR.strings.email.resourceId)

iOS:

LocalizedStringKey(MR.strings.email.resourceId)

Note: more info in issue #126.

Example 2 - formatted localization string

In commonMain/resources/MR/base/strings.xml add:

<?xml version="1.0" encoding="UTF-8" ?>
<resources>
    <string name="my_string_formatted">My format '%s'</string>
</resources>

Then add the localized values for other languages like in example #1. Now create the following function in commonMain:

fun getMyFormatDesc(input: String): StringDesc {
  return StringDesc.ResourceFormatted(MR.strings.my_string_formatted, input)
}

To create formatted strings from resources you can also use extension format:

fun getMyFormatDesc(input: String): StringDesc {
  return MR.strings.my_string_formatted.format(input)
}

Now add support on the platform side like in example #1:
Android:

val string = getMyFormatDesc("hello").toString(context = this)

iOS:

let string = getMyFormatDesc(input: "hello").localized()

Example 3 - plural string

The first step is to create a file plurals.xml in commonMain/resources/MR/base with the following content:

<?xml version="1.0" encoding="UTF-8" ?>
<resources>
    <plural name="my_plural">
        <item quantity="zero">zero</item>
        <item quantity="one">one</item>
        <item quantity="two">two</item>
        <item quantity="few">few</item>
        <item quantity="many">many</item>
        <item quantity="other">other</item>
    </plural>
</resources>

Then add the localized values for other languages like in example #1.
Next, create a function in commonMain:

fun getMyPluralDesc(quantity: Int): StringDesc {
  return StringDesc.Plural(MR.plurals.my_plural, quantity)
}

Now add support on the platform side like in example #1:
Android:

val string = getMyPluralDesc(10).toString(context = this)

iOS:

let string = getMyPluralDesc(quantity: 10).localized()

Example 4 - plural formatted string

The first step is to create file plurals.xml in commonMain/resources/MR/base with the following content:

<?xml version="1.0" encoding="UTF-8" ?>
<resources>
    <plural name="my_plural">
        <item quantity="zero">no items</item>
        <item quantity="one">%d item</item>
        <item quantity="two">%d items</item>
        <item quantity="few">%d items</item>
        <item quantity="many">%d items</item>
        <item quantity="other">%d items</item>
    </plural>
</resources>

Then add the localized values for other languages like in example #1.
Next, create a function in commonMain:

fun getMyPluralFormattedDesc(quantity: Int): StringDesc {
  // we pass quantity as selector for correct plural string and for pass quantity as argument for formatting
  return StringDesc.PluralFormatted(MR.plurals.my_plural, quantity, quantity)  
}

To create formatted plural strings from resources you can also use extension format:

fun getMyPluralFormattedDesc(quantity: Int): StringDesc {
  // we pass quantity as selector for correct plural string and for pass quantity as argument for formatting
  return MR.plurals.my_plural.format(quantity, quantity)  
}

And like in example #1, add the platform-side support:
Android:

val string = getMyPluralFormattedDesc(10).toString(context = this)

iOS:

let string = getMyPluralFormattedDesc(quantity: 10).localized()

Example 5 - pass raw string or resource

If we already use some resources as a placeholder value, we can use StringDesc to change the string source:

fun getUserName(user: User?): StringDesc {
  if(user != null) {
    return StringDesc.Raw(user.name)
  } else {
    return StringDesc.Resource(MR.strings.name_placeholder)
  }  
}

And just like in example 1 usage on platform side:
Android:

val string1 = getUserName(user).toString(context = this) // we got name from User model
val string2 = getUserName(null).toString(context = this) // we got name_placeholder from resources

iOS:

let string1 = getUserName(user: user).localized() // we got name from User model
let string2 = getUserName(user: null).localized() // we got name_placeholder from resources

Example 6 - Select localization in runtime

You can force StringDesc to use preferred localization in common code:

StringDesc.localeType = StringDesc.LocaleType.Custom("es")

and return to system behaviour (when localization depends on device settings):

StringDesc.localeType = StringDesc.LocaleType.System()

Example 7 - pass image

Image resources directory is commonMain/resources/MR/images with support of nested directories.
Image name should be end with one of:

  • @0.75x - android ldpi;
  • @1x - android mdpi, ios 1x;
  • @1.5x - android hdpi;
  • @2x - android xhdpi, ios 2x;
  • @3x - android xxhdpi, ios 3x;
  • @4x - android xxxhdpi. Supported png and jpg resources for now.

If we add to commonMain/resources/MR/images files:

We got autogenerated MR.images.home_black_18 ImageResource in code, that we can use:

  • Android: imageView.setImageResource(image.drawableResId)
  • iOS: imageView.image = image.toUIImage()

You can get images by their name too

in commonMain create a Resources.kt file with the content below

fun getImageByFileName(name: String): ImageResource {
  val fallbackImage = MR.images.transparent
  return MR.images.getImageByFileName(name) ?: fallbackImage
}
  • Android: imageView.setImageResource(getDrawableByFileName("image_name"))
  • iOS: imageView.image = ResourcesKt.getDrawableByFileName(name: "image_name").toUIImage()!

Example 8 - pass font

Fonts resources directory is commonMain/resources/MR/fonts.
Font name should be this pattern: <fontFamily>-<fontStyle> like:

  • Raleway-Bold.ttf
  • Raleway-Regular.ttf
  • Raleway-Italic.ttf Supported only ttf resources for now.

If we add to commonMain/resources/MR/fonts files:

  • Raleway-Bold.ttf
  • Raleway-Regular.ttf
  • Raleway-Italic.ttf

We got autogenerated MR.fonts.Raleway.italic, MR.fonts.Raleway.regular, MR.fonts.Raleway.bold FontResource in code, that we can use:

  • Android: textView.typeface = font.getTypeface(context = this)
  • iOS: textView.font = font.uiFont(withSize: 14.0)

Example 9 - pass colors

Colors resources directory is commonMain/resources/MR/colors.
Colors files is xml with format:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="valueColor">#B02743FF</color>
    <color name="referenceColor">@color/valueColor</color>
    <color name="themedColor">
        <light>0xB92743FF</light>
        <dark>7CCFEEFF</dark>
    </color>
    <color name="themedReferenceColor">
        <light>@color/valueColor</light>
        <dark>@color/referenceColor</dark>
    </color>
</resources>

If you want use one color without light/dark theme selection:

<color name="valueColor">#B02743FF</color>

If you want use value of other color - use references:

<color name="referenceColor">@color/valueColor</color>

If you want different colors in light/dark themes:

<color name="themedColor">
    <light>0xB92743FF</light>
    <dark>7CCFEEFF</dark>
</color>

Also themed colors can be referenced too:

<color name="themedReferenceColor">
    <light>@color/valueColor</light>
    <dark>@color/referenceColor</dark>
</color>

Colors available in common code insode MR.colors.** as ColorResource.
ColorResource can be ColorResource.Single - simple color without theme selection.
And can be ColorResource.Themed with colors for each mode.

You can read colors value from common code:

val color: Color = MR.colors.valueColor.color

but if you use ColorResource.Themed you can get current theme color only from platfrom side. Android:

val color: Color = MR.colors.valueColor.getColor(context = this)

iOS:

val color: UIColor = MR.colors.valueColor.getColor(UIScreen.main.traitCollection.userInterfaceStyle)

// If your SwiftUI View can not handle the run time dark/light mode changes for colors
// add this line on top of the View it will make it aware of dark/light mode changes 
@Environment(\.colorScheme) var colorScheme

Gradle task for creating Fat Framework with resources

If you want to create Fat Framework for iOS with all resources from KMP Gradle module you should use extended Gradle task FatFrameworkWithResourcesTask. There is example of FatFrameworkWithResourcesTask task using for the mpp-library module of the Sample. In the end of the sample/mpp-library/build.gradle.kts file:

kotlin {
    tasks.register("debugFatFramework", dev.icerock.gradle.generator.FatFrameworkWithResourcesTask::class) {
        baseName = "multiplatform"

        val targets = mapOf(
            "iosX64" to kotlin.targets.getByName<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>("iosX64"),
            "iosArm64" to kotlin.targets.getByName<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>("iosArm64")
        )

        from(
            targets.toList().map {
                it.second.binaries.getFramework("MultiPlatformLibrary", org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType.DEBUG)
            }
        )
    }
}

Then just launch task:

./gradlew :sample:mpp-library:debugFatFramework

Samples

Please see more examples in the sample directory.

Sample mpp-hierarhical contains usage of org.jetbrains.kotlin.native.cocoapods plugin and unit tests with resources usage.

Set Up Locally

Contributing

All development (both new features and bug fixes) is performed in the develop branch. This way master always contains the sources of the most recently released version. Please send PRs with bug fixes to the develop branch. Documentation fixes in the markdown files are an exception to this rule. They are updated directly in master.

The develop branch is pushed to master on release.

For more details on contributing please see the contributing guide.

License

Copyright 2019 IceRock MAG Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].