Kotlin

Kotlin

Kotlin is an open-source, statically typed programming language backed by JetBrains. Kotlin combines OO and functional features and is focused on interoperability, safety, clarity, and tooling support.
山岸  英樹

山岸 英樹

1656759900

Android開発者向けの5つの便利なKotlin拡張機能

Kotlin拡張機能とは何ですか?

Kotlinは、クラスから継承したり、などのデザインパターンを使用したりすることなく、クラスまたはインターフェイスを新しい機能で拡張する機能を提供しますDecorator。これは、拡張機能と呼ばれる特別な宣言を介して行われます。

たとえば、String主にコードでクラスを使用しました。一部のプロパティをに含めたい場合がありますが、含まれString classません。この場合、KotlinExtensionメソッドを使用すると可能になります。

String文字列内のすべての文字をアルファベット順に並べ替える1つのプロパティが必要だとします。Extensionそのための関数を作ってみましょう。

fun String.sortAlphabetically() = toCharArray().apply { sort() }

拡張機能の機能とは何か、そしてそれらがどのように見えるかを理解していただければ幸いです。

1. EditText onChanged Text

テキストの編集の変更は、モバイルアプリケーションで実行する重要なタスクです。どのテキストが変更されたかを確認します。テキストの使用例の1つはEditText.onChange、3〜4文字の文字が検索を開始したときにボタンを押さずに、検索バーで何かを検索することです。テキストに変更がある場合、関数は文字列で変更されたテキストを返します。

fun EditText.onChange(textChanged: ((String) -> Unit)) {
    this.addTextChangedListener(object : TextWatcher {
        override fun afterTextChanged(s: Editable?) {

        }

        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            textChanged.invoke(s.toString())
        }
    })
}

2.トーストを表示

この拡張機能を使用すると、ActivitiesまたはFragmentのどこでも、またはを渡すことができる場所ならどこでもtoastを呼び出すことができますContext。電話をかけるだけshowToast(MESSAGE_YOU_WANT_TO_SHOW)で、デフォルトではトーストの持続時間はになりますToast.LENGTH_SHORT

fun Context.showToast(message: String?, length: Int = Toast.LENGTH_SHORT) {
    message?.let {
        Toast.makeText(this, it, length).show()
    }
}


//Usage
showToast("Hello World👋")

3.スナックバーを表示する

Snackbarモバイルアプリの画面下部に簡単なメッセージを表示するために使用されます。トーストの一種です。でSnackBar、要件がある場合はアクションボタンがある場合があります。

fun View.showSnackMessage(
    message: String?,
    anchorView: View? = null,
    backgroundColor: Int,
    textColor: Int,
    length: Int = Snackbar.LENGTH_SHORT
) {
    message?.let {
        try {
            val snack = Snackbar.make(this, it, length)
            snack.setBackgroundTint(ContextCompat.getColor(context, backgroundColor))
            snack.setTextColor(ContextCompat.getColor(context, textColor))
            snack.anchorView = anchorView
            snack.show()
        } catch (ex: Exception) {
            ex.showLog()
        }
    }
}

4.インターネットが利用可能かどうかを確認します

ファイルのアップロードやアプリでインターネットからのあらゆる種類のデータの取得などのAPIリクエストを行う際には、インターネットが利用可能である必要があります。インターネットが利用できない場合、アプリはクラッシュします。

1つのシナリオで、Androidプロジェクトに複数のアクティビティとフラグメントがあり、インターネットが多くのポイントで利用可能であることを確認する必要がある場合、接続マネージャーを数回呼び出す必要があります。これは、プロジェクトで使用する拡張機能が1つあるためです。 。

それもあなたを助けます。

fun Context.isNetworkAvailable(): Boolean {
    val manager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        val capabilities = manager.getNetworkCapabilities(manager.activeNetwork)
        if (capabilities != null) {
            if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
                return true
            } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
                return true
            } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
                return true
            }
        }
    } else {
        try {
            val activeNetworkInfo = manager.activeNetworkInfo
            if (activeNetworkInfo != null && activeNetworkInfo.isConnected) {
                return true
            }
        } catch (e: Exception) {
            e.showLog()
        }
    }
    return false
}


//Usage
//For example, In the MainActivity, how to call this?
if(isNetworkAvailable()){
  //Perform your task
}else{
   showToast("Internet is not available, Try Later")
}   
  

インターネット接続チェック拡張機能

5.ビューの可視性を制御する

Androidで、取得したデータに応じてビューを表示または非表示にする場合。特定のビューの表示を非表示にしたい場合があります。たとえば、を使用ProgressBarすると、一部のデータがバックエンドまたはローカルソースから取得しているときにのみプログレスバーを表示したいとします。データがフェッチされたら、プログレスバーを非表示にします。

Androidでこのアクションを実行する一般的な方法はbinding.loadingProgressBar = View.GONE、毎回実行する必要があります。Viewこれで、クラスを使用して1つの拡張関数を作成するだけで、ニーズを満たすことができます。

fun View.show() {
    visibility = View.VISIBLE
}

fun View.hide() {
    visibility = View.GONE
}

fun View.invisible() {
    visibility = View.INVISIBLE
}

//Removing visibility of ProgressBar
//Using Extension
binding.loadingPb.hide()
//Without Extension
binding.loadingPb.visibility = View.GONE

結論

それで全部です!あなたが何かを学んだことを願っています。疑問がある場合は、コメントしてください。私はあなたの問題を喜んで解決します。

ありがとう、そして乾杯!

ソース:https ://betterprogramming.pub/5-useful-kotlin-extensions-for-android-developers-485f7c9ad7e8

#kotlin #android #web-development 

Android開発者向けの5つの便利なKotlin拡張機能

5 Useful Kotlin Extensions for Android Developers

What is a Kotlin Extension?

Kotlin provides the ability to extend a class or an interface with new functionality without having to inherit from the class or use design patterns such as Decorator. This is done via special declarations called extensions.

For example, you have used the String class mostly in your code. It may happen that you want some properties to be included in the String class , but they won’t. In this case, it is possible when using the Kotlin Extension method.

Suppose you want a String having one property to sort all the characters in the string alphabetically. Let’s make an Extension function for that.

fun String.sortAlphabetically() = toCharArray().apply { sort() }

I hope you get the idea of what exactly extensions' functions are and what they look like.

See more at: https://betterprogramming.pub/5-useful-kotlin-extensions-for-android-developers-485f7c9ad7e8

#kotlin #android #web-development 

5 Useful Kotlin Extensions for Android Developers
Hong  Nhung

Hong Nhung

1656684965

5 Tiện ích Mở Rộng Kotlin Hữu ích Cho Nhà Phát Triển Android

Phần mở rộng Kotlin là gì?

Kotlin cung cấp khả năng mở rộng một lớp hoặc một giao diện với chức năng mới mà không cần phải kế thừa từ lớp hoặc sử dụng các mẫu thiết kế như Decorator. Điều này được thực hiện thông qua các khai báo đặc biệt được gọi là phần mở rộng.

Ví dụ, bạn đã sử dụng Stringlớp chủ yếu trong mã của mình. Có thể xảy ra trường hợp bạn muốn một số thuộc tính được đưa vào String class, nhưng chúng sẽ không. Trong trường hợp này, có thể thực hiện được khi sử dụng phương pháp Mở rộng Kotlin.

Giả sử bạn muốn Stringcó một thuộc tính để sắp xếp tất cả các ký tự trong chuỗi theo thứ tự bảng chữ cái. Hãy tạo một Extensionhàm cho điều đó.

fun String.sortAlphabetically() = toCharArray().apply { sort() }

Tôi hy vọng bạn hiểu được các chức năng chính xác của tiện ích mở rộng là gì và chúng trông như thế nào.

1. EditText onChanged Text

Thay đổi văn bản chỉnh sửa là nhiệm vụ chính cần thực hiện trong ứng dụng di động. Kiểm tra xem văn bản nào đã được thay đổi. Một trường hợp sử dụng của EditText.onChangevăn bản là tìm kiếm thứ gì đó trong thanh tìm kiếm mà không cần nhấn nút chỉ khi có 3–4 ký tự ở đó bắt đầu tìm kiếm. Khi có thay đổi trong văn bản, hàm sẽ trả về văn bản đã thay đổi trong chuỗi.

fun EditText.onChange(textChanged: ((String) -> Unit)) {
    this.addTextChangedListener(object : TextWatcher {
        override fun afterTextChanged(s: Editable?) {

        }

        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            textChanged.invoke(s.toString())
        }
    })
}

2. Hiển thị bánh mì nướng

Với chức năng mở rộng này, bạn có thể gọi bánh mì nướng ở bất kỳ đâu trong Hoạt động hoặc Phân đoạn hoặc bất kỳ nơi nào bạn có thể vượt qua Context. Bạn chỉ cần gọi showToast(MESSAGE_YOU_WANT_TO_SHOW)và theo mặc định, thời lượng bánh mì nướng sẽ là Toast.LENGTH_SHORT.

fun Context.showToast(message: String?, length: Int = Toast.LENGTH_SHORT) {
    message?.let {
        Toast.makeText(this, it, length).show()
    }
}


//Usage
showToast("Hello World👋")

3. Hiển thị SnackBar

Snackbarđược sử dụng để hiển thị thông báo ngắn gọn ở cuối màn hình trên ứng dụng dành cho thiết bị di động của bạn. Đó là một loại thay thế cho bánh mì nướng. Trong SnackBar, bạn có thể có một nút hành động nếu có yêu cầu.

fun View.showSnackMessage(
    message: String?,
    anchorView: View? = null,
    backgroundColor: Int,
    textColor: Int,
    length: Int = Snackbar.LENGTH_SHORT
) {
    message?.let {
        try {
            val snack = Snackbar.make(this, it, length)
            snack.setBackgroundTint(ContextCompat.getColor(context, backgroundColor))
            snack.setTextColor(ContextCompat.getColor(context, textColor))
            snack.anchorView = anchorView
            snack.show()
        } catch (ex: Exception) {
            ex.showLog()
        }
    }
}

4. Kiểm tra xem Internet có khả dụng không

Trong khi thực hiện bất kỳ yêu cầu API nào như tải lên tệp hoặc nhận bất kỳ loại dữ liệu nào từ internet trong ứng dụng của chúng tôi, chúng tôi cần đảm bảo rằng Internet phải khả dụng. Nếu Internet không khả dụng, thì ứng dụng sẽ bị treo.

Giả sử trong một tình huống có nhiều hoạt động và phân đoạn trong dự án Android của bạn và bạn cần kiểm tra Internet có khả dụng ở nhiều thời điểm hay không, sau đó bạn cần gọi trình quản lý kết nối nhiều lần, vì có một chức năng mở rộng mà tôi sử dụng trong dự án của mình. .

Nó cũng sẽ giúp bạn.

fun Context.isNetworkAvailable(): Boolean {
    val manager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        val capabilities = manager.getNetworkCapabilities(manager.activeNetwork)
        if (capabilities != null) {
            if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
                return true
            } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
                return true
            } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
                return true
            }
        }
    } else {
        try {
            val activeNetworkInfo = manager.activeNetworkInfo
            if (activeNetworkInfo != null && activeNetworkInfo.isConnected) {
                return true
            }
        } catch (e: Exception) {
            e.showLog()
        }
    }
    return false
}


//Usage
//For example, In the MainActivity, how to call this?
if(isNetworkAvailable()){
  //Perform your task
}else{
   showToast("Internet is not available, Try Later")
}   
  

Phần mở rộng kiểm tra kết nối Internet

5. Kiểm soát khả năng hiển thị của VIEWS

Trong Android, khi chúng ta muốn hiển thị hoặc ẩn chế độ xem tùy theo dữ liệu mà chúng ta nhận được. Có một số trường hợp khi chúng tôi muốn ẩn khả năng hiển thị của bất kỳ chế độ xem cụ thể nào. Ví dụ: bằng cách sử dụng, ProgressBarchúng tôi muốn hiển thị thanh tiến trình chỉ khi một số dữ liệu đang trong quá trình lấy từ phần phụ trợ hoặc nguồn cục bộ. Khi dữ liệu được tìm nạp, chúng tôi chỉ muốn ẩn thanh tiến trình.

Thông lệ chung để thực hiện hành động này trong Android là binding.loadingProgressBar = View.GONEnó cần phải thực hiện mọi lúc. Bây giờ, chúng ta có thể chỉ cần tạo một hàm mở rộng với Viewlớp và nó sẽ đáp ứng nhu cầu của chúng ta.

fun View.show() {
    visibility = View.VISIBLE
}

fun View.hide() {
    visibility = View.GONE
}

fun View.invisible() {
    visibility = View.INVISIBLE
}

//Removing visibility of ProgressBar
//Using Extension
binding.loadingPb.hide()
//Without Extension
binding.loadingPb.visibility = View.GONE

Sự kết luận

Đó là tất cả! Tôi hy vọng bạn đã học được một cái gì đó. Trong trường hợp có bất kỳ nghi ngờ, chỉ cần bình luận! Tôi rất vui khi giải quyết các vấn đề của bạn.

Cảm ơn và chúc mừng!

Nguồn: https://betterprogramming.pub/5-useful-kotlin-extensions-for-android-developers-485f7c9ad7e8

#kotlin #android #web-development 

5 Tiện ích Mở Rộng Kotlin Hữu ích Cho Nhà Phát Triển Android
郝 玉华

郝 玉华

1656681242

5 个对 Android 开发者有用的 Kotlin 扩展

什么是 Kotlin 扩展?

Kotlin 提供了使用新功能扩展类或接口的能力,而无需从类继承或使用Decorator. 这是通过称为扩展的特殊声明完成的。

例如,您String主要在代码中使用了该类。您可能希望某些属性包含在 中String class,但它们不会。在这种情况下,可以使用 Kotlin 扩展方法。

假设您想要String一个属性来按字母顺序对字符串中的所有字符进行排序。让我们Extension为此创建一个函数。

fun String.sortAlphabetically() = toCharArray().apply { sort() }

我希望您了解扩展的功能到底是什么以及它们的外观。

1.EditText onChanged 文本

编辑文本更改是在移动应用程序中执行的一项关键任务。检查是否更改了哪些文本。文本的一个用例EditText.onChange是在搜索栏中搜索某些内容,而无需在 3-4 个字符开始搜索时按下按钮。当文本发生更改时,该函数将返回字符串中更改的文本。

fun EditText.onChange(textChanged: ((String) -> Unit)) {
    this.addTextChangedListener(object : TextWatcher {
        override fun afterTextChanged(s: Editable?) {

        }

        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            textChanged.invoke(s.toString())
        }
    })
}

2. 展示吐司

使用此扩展功能,您可以在活动或片段中的任何位置或您可以传递Context. 您只需拨打电话showToast(MESSAGE_YOU_WANT_TO_SHOW),默认情况下,吐司持续时间为Toast.LENGTH_SHORT.

fun Context.showToast(message: String?, length: Int = Toast.LENGTH_SHORT) {
    message?.let {
        Toast.makeText(this, it, length).show()
    }
}


//Usage
showToast("Hello World👋")

3. 显示 SnackBar

Snackbar用于在您的移动应用程序屏幕底部显示简短消息。它是吐司的一种替代品。在SnackBar中,如果有需要,您可能会有一个操作按钮。

fun View.showSnackMessage(
    message: String?,
    anchorView: View? = null,
    backgroundColor: Int,
    textColor: Int,
    length: Int = Snackbar.LENGTH_SHORT
) {
    message?.let {
        try {
            val snack = Snackbar.make(this, it, length)
            snack.setBackgroundTint(ContextCompat.getColor(context, backgroundColor))
            snack.setTextColor(ContextCompat.getColor(context, textColor))
            snack.anchorView = anchorView
            snack.show()
        } catch (ex: Exception) {
            ex.showLog()
        }
    }
}

4. 检查互联网是否可用

在我们的应用程序中发出任何 API 请求(例如上传文件或从 Internet 获取任何类型的数据)时,我们需要确保 Internet 必须可用。如果互联网不可用,则应用程序将崩溃。

假设在一种情况下,您的 Android 项目中有多个活动和片段,并且您需要检查互联网在很多时候是否可用,然后您需要多次调用连接管理器,因为我在项目中使用了一个扩展功能.

它也会帮助你。

fun Context.isNetworkAvailable(): Boolean {
    val manager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        val capabilities = manager.getNetworkCapabilities(manager.activeNetwork)
        if (capabilities != null) {
            if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
                return true
            } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
                return true
            } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
                return true
            }
        }
    } else {
        try {
            val activeNetworkInfo = manager.activeNetworkInfo
            if (activeNetworkInfo != null && activeNetworkInfo.isConnected) {
                return true
            }
        } catch (e: Exception) {
            e.showLog()
        }
    }
    return false
}


//Usage
//For example, In the MainActivity, how to call this?
if(isNetworkAvailable()){
  //Perform your task
}else{
   showToast("Internet is not available, Try Later")
}   
  

互联网连接检查扩展

5. 控制 VIEWS 的可见性

在Android中,当我们想根据我们得到的数据显示或隐藏视图时。在某些情况下,我们想要隐藏任何特定视图的可见性。例如,使用ProgressBar我们希望仅在某些数据正在从后端或本地源获取的过程中显示进度条。获取数据后,我们只想让进度条不可见。

在 Android 中执行此操作的一般做法是binding.loadingProgressBar = View.GONE,每次都需要执行。现在,我们可以简单地使用该类创建一个扩展函数View,它将满足我们的需求。

fun View.show() {
    visibility = View.VISIBLE
}

fun View.hide() {
    visibility = View.GONE
}

fun View.invisible() {
    visibility = View.INVISIBLE
}

//Removing visibility of ProgressBar
//Using Extension
binding.loadingPb.hide()
//Without Extension
binding.loadingPb.visibility = View.GONE

结论

就这样!我希望你学到了一些东西。如有任何疑问,请发表评论!我很乐意解决您的问题。

谢谢,欢呼!

来源:https ://betterprogramming.pub/5-useful-kotlin-extensions-for-android-developers-485f7c9ad7e8

#kotlin #android #web-development 

5 个对 Android 开发者有用的 Kotlin 扩展

5 Extensões Kotlin úteis Para Desenvolvedores android

O que é uma extensão Kotlin?

Kotlin fornece a capacidade de estender uma classe ou uma interface com novas funcionalidades sem ter que herdar da classe ou usar padrões de design como Decorator. Isso é feito por meio de declarações especiais chamadas extensões.

Por exemplo, você usou a Stringclasse principalmente em seu código. Pode acontecer que você queira que algumas propriedades sejam incluídas no String class, mas elas não serão. Nesse caso, é possível ao usar o método Kotlin Extension.

Suponha que você queira Stringter uma propriedade para classificar todos os caracteres na string em ordem alfabética. Vamos fazer uma Extensionfunção para isso.

fun String.sortAlphabetically() = toCharArray().apply { sort() }

Espero que você tenha uma ideia de quais são exatamente as funções das extensões e como elas se parecem.

1. Editar texto em texto alterado

Uma alteração de texto de edição é uma tarefa importante a ser executada no aplicativo móvel. Verificando se o texto foi alterado. Um caso de uso de EditText.onChangetexto é pesquisar algo na barra de pesquisa, sem pressionar um botão apenas quando 3 a 4 caracteres estiverem lá, comece a pesquisar. Quando houver uma alteração no texto, a função retornará o texto alterado na string.

fun EditText.onChange(textChanged: ((String) -> Unit)) {
    this.addTextChangedListener(object : TextWatcher {
        override fun afterTextChanged(s: Editable?) {

        }

        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            textChanged.invoke(s.toString())
        }
    })
}

2. Mostrar brinde

Com esta função de extensão, você pode chamar o toast em qualquer lugar em Atividades ou Fragmento ou em qualquer lugar em que possa passar o arquivo Context. Você só precisa chamar showToast(MESSAGE_YOU_WANT_TO_SHOW)e, por padrão, a duração do brinde será Toast.LENGTH_SHORT.

fun Context.showToast(message: String?, length: Int = Toast.LENGTH_SHORT) {
    message?.let {
        Toast.makeText(this, it, length).show()
    }
}


//Usage
showToast("Hello World👋")

3. Mostrar SnackBar

Snackbaré usado para mostrar a breve mensagem na parte inferior da tela em seu aplicativo móvel. É uma espécie de substituto para torradas. Em SnackBar, você pode ter um botão de ação se houver um requisito.

fun View.showSnackMessage(
    message: String?,
    anchorView: View? = null,
    backgroundColor: Int,
    textColor: Int,
    length: Int = Snackbar.LENGTH_SHORT
) {
    message?.let {
        try {
            val snack = Snackbar.make(this, it, length)
            snack.setBackgroundTint(ContextCompat.getColor(context, backgroundColor))
            snack.setTextColor(ContextCompat.getColor(context, textColor))
            snack.anchorView = anchorView
            snack.show()
        } catch (ex: Exception) {
            ex.showLog()
        }
    }
}

4. Verifique se a Internet está disponível

Ao fazer qualquer solicitação de API, como fazer upload de um arquivo ou obter qualquer tipo de dados da Internet em nosso aplicativo, precisamos garantir que a Internet esteja disponível. Se a internet não estiver disponível, o aplicativo travará.

Suponha que em um cenário existem várias atividades e fragmentos em seu projeto Android e você precisa verificar se a internet está disponível em muitos pontos, então você precisa chamar o gerenciador de conectividade várias vezes, para isso existe uma função de extensão que eu uso no meu projeto .

Vai te ajudar também.

fun Context.isNetworkAvailable(): Boolean {
    val manager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        val capabilities = manager.getNetworkCapabilities(manager.activeNetwork)
        if (capabilities != null) {
            if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
                return true
            } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
                return true
            } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
                return true
            }
        }
    } else {
        try {
            val activeNetworkInfo = manager.activeNetworkInfo
            if (activeNetworkInfo != null && activeNetworkInfo.isConnected) {
                return true
            }
        } catch (e: Exception) {
            e.showLog()
        }
    }
    return false
}


//Usage
//For example, In the MainActivity, how to call this?
if(isNetworkAvailable()){
  //Perform your task
}else{
   showToast("Internet is not available, Try Later")
}   
  

Extensão de verificação de conectividade com a Internet

5. Controlar a visibilidade das VISUALIZAÇÕES

No Android, quando queremos mostrar ou ocultar a visualização de acordo com os dados que obtemos. Existem algumas circunstâncias em que queremos ocultar a visibilidade de qualquer visualização específica. Por exemplo, usando ProgressBarqueremos mostrar a barra de progresso apenas quando alguns dados estão em processo de obtenção do back-end ou fonte local. Uma vez que os dados são buscados, queremos apenas tornar a barra de progresso invisível.

A prática geral para executar essa ação no Android é binding.loadingProgressBar = View.GONE, ela precisa ser executada sempre. Agora, podemos simplesmente criar uma função de extensão com a Viewclasse e ela atenderá às nossas necessidades.

fun View.show() {
    visibility = View.VISIBLE
}

fun View.hide() {
    visibility = View.GONE
}

fun View.invisible() {
    visibility = View.INVISIBLE
}

//Removing visibility of ProgressBar
//Using Extension
binding.loadingPb.hide()
//Without Extension
binding.loadingPb.visibility = View.GONE

Conclusão

Isso é tudo! Espero que você tenha aprendido alguma coisa. Qualquer dúvida é só comentar! Ficarei feliz em resolver seus problemas.

Obrigado, e aplausos!

Fonte: https://betterprogramming.pub/5-useful-kotlin-extensions-for-android-developers-485f7c9ad7e8

#kotlin #android #web-development 

5 Extensões Kotlin úteis Para Desenvolvedores android

5 Extensiones útiles De Kotlin Para Desarrolladores De Android

¿Qué es una extensión de Kotlin?

Kotlin brinda la capacidad de extender una clase o una interfaz con nueva funcionalidad sin tener que heredar de la clase o usar patrones de diseño como Decorator. Esto se hace a través de declaraciones especiales llamadas extensiones.

Por ejemplo, ha usado la Stringclase principalmente en su código. Puede suceder que desee que se incluyan algunas propiedades en el String class, pero no lo harán. En este caso, es posible al usar el método Kotlin Extension.

Suponga que desea Stringtener una propiedad para ordenar alfabéticamente todos los caracteres de la cadena. Hagamos una Extensionfunción para eso.

fun String.sortAlphabetically() = toCharArray().apply { sort() }

Espero que tengas una idea de cuáles son exactamente las funciones de las extensiones y cómo se ven.

1. EditText onChangeText

Un cambio de texto de edición es una tarea clave para realizar en la aplicación móvil. Comprobando si se ha cambiado qué texto. Un caso de uso de EditText.onChangetexto es buscar algo en la barra de búsqueda, sin presionar un botón, justo cuando hay 3 o 4 caracteres que comienzan a buscar. Cuando haya un cambio en el texto, la función devolverá el texto modificado en la cadena.

fun EditText.onChange(textChanged: ((String) -> Unit)) {    this.addTextChangedListener(object : TextWatcher {        override fun afterTextChanged(s: Editable?) {        }        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {            textChanged.invoke(s.toString())        }    })}

2. Mostrar tostadas

Con esta función de extensión, puede llamar al brindis en cualquier lugar, ya sea en Actividades o Fragmento, o en cualquier lugar donde pueda pasar el archivo Context. Solo necesita llamar showToast(MESSAGE_YOU_WANT_TO_SHOW)y, de forma predeterminada, la duración del brindis será Toast.LENGTH_SHORT.

fun Context.showToast(message: String?, length: Int = Toast.LENGTH_SHORT) {    message?.let {        Toast.makeText(this, it, length).show()    }}//UsageshowToast("Hello World👋")

3. Mostrar SnackBar

Snackbarse utiliza para mostrar el breve mensaje en la parte inferior de la pantalla de su aplicación móvil. Es una especie de sustituto de las tostadas. En SnackBar, puede tener un botón de acción si hay un requisito.

fun View.showSnackMessage(    message: String?,    anchorView: View? = null,    backgroundColor: Int,    textColor: Int,    length: Int = Snackbar.LENGTH_SHORT) {    message?.let {        try {            val snack = Snackbar.make(this, it, length)            snack.setBackgroundTint(ContextCompat.getColor(context, backgroundColor))            snack.setTextColor(ContextCompat.getColor(context, textColor))            snack.anchorView = anchorView            snack.show()        } catch (ex: Exception) {            ex.showLog()        }    }}

4. Compruebe si Internet está disponible

Al realizar cualquier solicitud de API, como cargar un archivo u obtener cualquier tipo de datos de Internet en nuestra aplicación, debemos asegurarnos de que Internet debe estar disponible. Si Internet no está disponible, la aplicación se bloqueará.

Supongamos que en un escenario hay múltiples actividades y fragmentos en su proyecto de Android y necesita verificar que Internet esté disponible en muchos puntos, luego debe llamar al administrador de conectividad varias veces, para eso hay una función de extensión que uso en mi proyecto .

También te ayudará.

fun Context.isNetworkAvailable(): Boolean {    val manager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {        val capabilities = manager.getNetworkCapabilities(manager.activeNetwork)        if (capabilities != null) {            if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {                return true            } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {                return true            } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {                return true            }        }    } else {        try {            val activeNetworkInfo = manager.activeNetworkInfo            if (activeNetworkInfo != null && activeNetworkInfo.isConnected) {                return true            }        } catch (e: Exception) {            e.showLog()        }    }    return false}//Usage//For example, In the MainActivity, how to call this?if(isNetworkAvailable()){  //Perform your task}else{   showToast("Internet is not available, Try Later")}     

Extensión de verificación de conectividad a Internet

5. Controlar la visibilidad de las VISTAS

En Android, cuando queremos mostrar u ocultar la vista según los datos que obtengamos. Hay algunas circunstancias en las que queremos ocultar la visibilidad de una vista específica. Por ejemplo, ProgressBarqueremos mostrar la barra de progreso solo cuando algunos datos están en proceso de obtenerse del backend o fuente local. Una vez que se obtienen los datos, solo queremos que la barra de progreso sea invisible.

La práctica general para realizar esta acción en Android es binding.loadingProgressBar = View.GONEque debe realizarse cada vez. Ahora, simplemente podemos crear una función de extensión con la Viewclase y satisfará nuestras necesidades.

fun View.show() {    visibility = View.VISIBLE}fun View.hide() {    visibility = View.GONE}fun View.invisible() {    visibility = View.INVISIBLE}//Removing visibility of ProgressBar//Using Extensionbinding.loadingPb.hide()//Without Extensionbinding.loadingPb.visibility = View.GONE

Conclusión

¡Eso es todo! Espero que hayas aprendido algo. Ante cualquier duda, ¡comenta! Estaré feliz de resolver sus problemas.

¡Gracias y ánimo!

Fuente: https://betterprogramming.pub/5-useful-kotlin-extensions-for-android-developers-485f7c9ad7e8

#kotlin #android #web-development 

5 Extensiones útiles De Kotlin Para Desarrolladores De Android
Thierry  Perret

Thierry Perret

1656677289

5 Extensions Kotlin Utiles Pour Les Développeurs Android

Qu'est-ce qu'une extension Kotlin ?

Kotlin offre la possibilité d'étendre une classe ou une interface avec de nouvelles fonctionnalités sans avoir à hériter de la classe ou à utiliser des modèles de conception tels que Decorator. Cela se fait via des déclarations spéciales appelées extensions.

Par exemple, vous avez utilisé la Stringclasse principalement dans votre code. Il peut arriver que vous vouliez que certaines propriétés soient incluses dans le String class, mais elles ne le seront pas. Dans ce cas, c'est possible en utilisant la méthode d'extension Kotlin.

Supposons que vous souhaitiez Stringavoir une propriété pour trier tous les caractères de la chaîne par ordre alphabétique. Créons une Extensionfonction pour cela.

fun String.sortAlphabetically() = toCharArray().apply { sort() }

J'espère que vous avez une idée de ce que sont exactement les fonctions des extensions et à quoi elles ressemblent.

1. EditText onChanged Text

Un changement de texte d'édition est une tâche clé à effectuer dans l'application mobile. Vérifier si quel texte a été modifié. Un cas d'utilisation du EditText.onChangetexte consiste à rechercher quelque chose dans la barre de recherche, sans appuyer sur un bouton juste au moment où 3 à 4 caractères sont là pour commencer la recherche. Lorsqu'il y aura un changement dans le texte, la fonction renverra le texte modifié dans la chaîne.

fun EditText.onChange(textChanged: ((String) -> Unit)) {
    this.addTextChangedListener(object : TextWatcher {
        override fun afterTextChanged(s: Editable?) {

        }

        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            textChanged.invoke(s.toString())
        }
    })
}

2. Afficher le toast

Avec cette fonction d'extension, vous pouvez appeler toast n'importe où dans Activités ou Fragment ou n'importe où vous pouvez passer le Context. Il vous suffit d'appeler showToast(MESSAGE_YOU_WANT_TO_SHOW)et par défaut, la durée du toast sera de Toast.LENGTH_SHORT.

fun Context.showToast(message: String?, length: Int = Toast.LENGTH_SHORT) {
    message?.let {
        Toast.makeText(this, it, length).show()
    }
}


//Usage
showToast("Hello World👋")

3. Afficher SnackBar

Snackbarest utilisé pour afficher le bref message en bas de l'écran de votre application mobile. C'est une sorte de substitut aux toasts. Dans SnackBar, vous pouvez avoir un bouton d'action s'il y a une exigence.

fun View.showSnackMessage(
    message: String?,
    anchorView: View? = null,
    backgroundColor: Int,
    textColor: Int,
    length: Int = Snackbar.LENGTH_SHORT
) {
    message?.let {
        try {
            val snack = Snackbar.make(this, it, length)
            snack.setBackgroundTint(ContextCompat.getColor(context, backgroundColor))
            snack.setTextColor(ContextCompat.getColor(context, textColor))
            snack.anchorView = anchorView
            snack.show()
        } catch (ex: Exception) {
            ex.showLog()
        }
    }
}

4. Vérifiez si Internet est disponible

Lors de toute demande d'API, comme le téléchargement d'un fichier ou l'obtention de tout type de données d'Internet dans notre application, nous devons nous assurer qu'Internet doit être disponible. Si Internet n'est pas disponible, l'application plantera.

Supposons que dans un scénario, il y ait plusieurs activités et fragments dans votre projet Android et que vous deviez vérifier qu'Internet est disponible à de nombreux points, puis vous devez appeler le gestionnaire de connectivité plusieurs fois, pour cela il y a une fonction d'extension que j'utilise dans mon projet .

Cela vous aidera aussi.

fun Context.isNetworkAvailable(): Boolean {
    val manager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        val capabilities = manager.getNetworkCapabilities(manager.activeNetwork)
        if (capabilities != null) {
            if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
                return true
            } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
                return true
            } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
                return true
            }
        }
    } else {
        try {
            val activeNetworkInfo = manager.activeNetworkInfo
            if (activeNetworkInfo != null && activeNetworkInfo.isConnected) {
                return true
            }
        } catch (e: Exception) {
            e.showLog()
        }
    }
    return false
}


//Usage
//For example, In the MainActivity, how to call this?
if(isNetworkAvailable()){
  //Perform your task
}else{
   showToast("Internet is not available, Try Later")
}   
  

Extension de vérification de la connectivité Internet

5. Contrôler la visibilité des VUES

Dans Android, lorsque nous voulons afficher ou masquer la vue en fonction des données que nous obtenons. Dans certaines circonstances, nous souhaitons masquer la visibilité d'une vue spécifique. Par exemple, en utilisant, ProgressBarnous souhaitons afficher la barre de progression uniquement lorsque certaines données sont en cours d'obtention depuis le backend ou la source locale. Une fois les données récupérées, nous voulons simplement rendre la barre de progression invisible.

La pratique générale pour effectuer cette action dans Android est binding.loadingProgressBar = View.GONEqu'elle doit être exécutée à chaque fois. Maintenant, nous pouvons simplement créer une fonction d'extension avec la Viewclasse, et elle répondra à nos besoins.

fun View.show() {
    visibility = View.VISIBLE
}

fun View.hide() {
    visibility = View.GONE
}

fun View.invisible() {
    visibility = View.INVISIBLE
}

//Removing visibility of ProgressBar
//Using Extension
binding.loadingPb.hide()
//Without Extension
binding.loadingPb.visibility = View.GONE

Conclusion

C'est tout! J'espère que vous avez appris quelque chose. En cas de doute, il suffit de commenter ! Je serai heureux de résoudre vos problèmes.

Merci et bravo !

Source : https://betterprogramming.pub/5-useful-kotlin-extensions-for-android-developers-485f7c9ad7e8

#kotlin #android #web-development 

5 Extensions Kotlin Utiles Pour Les Développeurs Android

Moshi: A Modern JSON Library for Kotlin and Java

Moshi

Moshi is a modern JSON library for Android, Java and Kotlin. It makes it easy to parse JSON into Java and Kotlin classes:

Note: The Kotlin examples of this README assume use of either Kotlin code gen or KotlinJsonAdapterFactory for reflection. Plain Java-based reflection is unsupported on Kotlin classes.

Java

String json = ...;

Moshi moshi = new Moshi.Builder().build();
JsonAdapter<BlackjackHand> jsonAdapter = moshi.adapter(BlackjackHand.class);

BlackjackHand blackjackHand = jsonAdapter.fromJson(json);
System.out.println(blackjackHand);

Kotlin

val json: String = ...

val moshi: Moshi = Moshi.Builder().build()
val jsonAdapter: JsonAdapter<BlackjackHand> = moshi.adapter<BlackjackHand>()

val blackjackHand = jsonAdapter.fromJson(json)
println(blackjackHand)

And it can just as easily serialize Java or Kotlin objects as JSON:

Java

BlackjackHand blackjackHand = new BlackjackHand(
    new Card('6', SPADES),
    Arrays.asList(new Card('4', CLUBS), new Card('A', HEARTS)));

Moshi moshi = new Moshi.Builder().build();
JsonAdapter<BlackjackHand> jsonAdapter = moshi.adapter(BlackjackHand.class);

String json = jsonAdapter.toJson(blackjackHand);
System.out.println(json);

Kotlin

val blackjackHand = BlackjackHand(
    Card('6', SPADES),
    listOf(Card('4', CLUBS), Card('A', HEARTS))
  )

val moshi: Moshi = Moshi.Builder().build()
val jsonAdapter: JsonAdapter<BlackjackHand> = moshi.adapter<BlackjackHand>()

val json: String = jsonAdapter.toJson(blackjackHand)
println(json)

Built-in Type Adapters

Moshi has built-in support for reading and writing Java’s core data types:

  • Primitives (int, float, char...) and their boxed counterparts (Integer, Float, Character...).
  • Arrays, Collections, Lists, Sets, and Maps
  • Strings
  • Enums

It supports your model classes by writing them out field-by-field. In the example above Moshi uses these classes:

Java

class BlackjackHand {
  public final Card hidden_card;
  public final List<Card> visible_cards;
  ...
}

class Card {
  public final char rank;
  public final Suit suit;
  ...
}

enum Suit {
  CLUBS, DIAMONDS, HEARTS, SPADES;
}

Kotlin

class BlackjackHand(
  val hidden_card: Card,
  val visible_cards: List<Card>,
  ...
)

class Card(
  val rank: Char,
  val suit: Suit
  ...
)

enum class Suit {
  CLUBS, DIAMONDS, HEARTS, SPADES;
}

to read and write this JSON:

{
  "hidden_card": {
    "rank": "6",
    "suit": "SPADES"
  },
  "visible_cards": [
    {
      "rank": "4",
      "suit": "CLUBS"
    },
    {
      "rank": "A",
      "suit": "HEARTS"
    }
  ]
}

The Javadoc catalogs the complete Moshi API, which we explore below.

Custom Type Adapters

With Moshi, it’s particularly easy to customize how values are converted to and from JSON. A type adapter is any class that has methods annotated @ToJson and @FromJson.

For example, Moshi’s default encoding of a playing card is verbose: the JSON defines the rank and suit in separate fields: {"rank":"A","suit":"HEARTS"}. With a type adapter, we can change the encoding to something more compact: "4H" for the four of hearts or "JD" for the jack of diamonds:

Java

class CardAdapter {
  @ToJson String toJson(Card card) {
    return card.rank + card.suit.name().substring(0, 1);
  }

  @FromJson Card fromJson(String card) {
    if (card.length() != 2) throw new JsonDataException("Unknown card: " + card);

    char rank = card.charAt(0);
    switch (card.charAt(1)) {
      case 'C': return new Card(rank, Suit.CLUBS);
      case 'D': return new Card(rank, Suit.DIAMONDS);
      case 'H': return new Card(rank, Suit.HEARTS);
      case 'S': return new Card(rank, Suit.SPADES);
      default: throw new JsonDataException("unknown suit: " + card);
    }
  }
}

Kotlin

class CardAdapter {
  @ToJson fun toJson(card: Card): String {
    return card.rank + card.suit.name.substring(0, 1)
  }

  @FromJson fun fromJson(card: String): Card {
    if (card.length != 2) throw JsonDataException("Unknown card: $card")

    val rank = card[0]
    return when (card[1]) {
      'C' -> Card(rank, Suit.CLUBS)
      'D' -> Card(rank, Suit.DIAMONDS)
      'H' -> Card(rank, Suit.HEARTS)
      'S' -> Card(rank, Suit.SPADES)
      else -> throw JsonDataException("unknown suit: $card")
    }
  }
}

Register the type adapter with the Moshi.Builder and we’re good to go.

Java

Moshi moshi = new Moshi.Builder()
    .add(new CardAdapter())
    .build();

Kotlin

val moshi = Moshi.Builder()
    .add(CardAdapter())
    .build()

Voilà:

{
  "hidden_card": "6S",
  "visible_cards": [
    "4C",
    "AH"
  ]
}

Another example

Note that the method annotated with @FromJson does not need to take a String as an argument. Rather it can take input of any type and Moshi will first parse the JSON to an object of that type and then use the @FromJson method to produce the desired final value. Conversely, the method annotated with @ToJson does not have to produce a String.

Assume, for example, that we have to parse a JSON in which the date and time of an event are represented as two separate strings.

{
  "title": "Blackjack tournament",
  "begin_date": "20151010",
  "begin_time": "17:04"
}

We would like to combine these two fields into one string to facilitate the date parsing at a later point. Also, we would like to have all variable names in CamelCase. Therefore, the Event class we want Moshi to produce like this:

Java

class Event {
  String title;
  String beginDateAndTime;
}

Kotlin

class Event(
  val title: String,
  val beginDateAndTime: String
)

Instead of manually parsing the JSON line per line (which we could also do) we can have Moshi do the transformation automatically. We simply define another class EventJson that directly corresponds to the JSON structure:

Java

class EventJson {
  String title;
  String begin_date;
  String begin_time;
}

Kotlin

class EventJson(
  val title: String,
  val begin_date: String,
  val begin_time: String
)

And another class with the appropriate @FromJson and @ToJson methods that are telling Moshi how to convert an EventJson to an Event and back. Now, whenever we are asking Moshi to parse a JSON to an Event it will first parse it to an EventJson as an intermediate step. Conversely, to serialize an Event Moshi will first create an EventJson object and then serialize that object as usual.

Java

class EventJsonAdapter {
  @FromJson Event eventFromJson(EventJson eventJson) {
    Event event = new Event();
    event.title = eventJson.title;
    event.beginDateAndTime = eventJson.begin_date + " " + eventJson.begin_time;
    return event;
  }

  @ToJson EventJson eventToJson(Event event) {
    EventJson json = new EventJson();
    json.title = event.title;
    json.begin_date = event.beginDateAndTime.substring(0, 8);
    json.begin_time = event.beginDateAndTime.substring(9, 14);
    return json;
  }
}

Kotlin

class EventJsonAdapter {
  @FromJson
  fun eventFromJson(eventJson: EventJson): Event {
    return Event(
      title = eventJson.title,
      beginDateAndTime = "${eventJson.begin_date} ${eventJson.begin_time}"
    )
  }

  @ToJson
  fun eventToJson(event: Event): EventJson {
    return EventJson(
      title = event.title,
      begin_date = event.beginDateAndTime.substring(0, 8),
      begin_time = event.beginDateAndTime.substring(9, 14),
    )
  }
}

Again we register the adapter with Moshi.

Java

Moshi moshi = new Moshi.Builder()
    .add(new EventJsonAdapter())
    .build();

Kotlin

val moshi = Moshi.Builder()
    .add(EventJsonAdapter())
    .build()

We can now use Moshi to parse the JSON directly to an Event.

Java

JsonAdapter<Event> jsonAdapter = moshi.adapter(Event.class);
Event event = jsonAdapter.fromJson(json);

Kotlin

val jsonAdapter = moshi.adapter<Event>()
val event = jsonAdapter.fromJson(json)

Adapter convenience methods

Moshi provides a number of convenience methods for JsonAdapter objects:

  • nullSafe()
  • nonNull()
  • lenient()
  • failOnUnknown()
  • indent()
  • serializeNulls()

These factory methods wrap an existing JsonAdapter into additional functionality. For example, if you have an adapter that doesn't support nullable values, you can use nullSafe() to make it null safe:

Java

String dateJson = "\"2018-11-26T11:04:19.342668Z\"";
String nullDateJson = "null";

// Hypothetical IsoDateDapter, doesn't support null by default
JsonAdapter<Date> adapter = new IsoDateDapter();

Date date = adapter.fromJson(dateJson);
System.out.println(date); // Mon Nov 26 12:04:19 CET 2018

Date nullDate = adapter.fromJson(nullDateJson);
// Exception, com.squareup.moshi.JsonDataException: Expected a string but was NULL at path $

Date nullDate = adapter.nullSafe().fromJson(nullDateJson);
System.out.println(nullDate); // null

Kotlin

val dateJson = "\"2018-11-26T11:04:19.342668Z\""
val nullDateJson = "null"

// Hypothetical IsoDateDapter, doesn't support null by default
val adapter: JsonAdapter<Date> = IsoDateDapter()

val date = adapter.fromJson(dateJson)
println(date) // Mon Nov 26 12:04:19 CET 2018

val nullDate = adapter.fromJson(nullDateJson)
// Exception, com.squareup.moshi.JsonDataException: Expected a string but was NULL at path $

val nullDate = adapter.nullSafe().fromJson(nullDateJson)
println(nullDate) // null

In contrast to nullSafe() there is nonNull() to make an adapter refuse null values. Refer to the Moshi JavaDoc for details on the various methods.

Parse JSON Arrays

Say we have a JSON string of this structure:

[
  {
    "rank": "4",
    "suit": "CLUBS"
  },
  {
    "rank": "A",
    "suit": "HEARTS"
  }
]

We can now use Moshi to parse the JSON string into a List<Card>.

Java

String cardsJsonResponse = ...;
Type type = Types.newParameterizedType(List.class, Card.class);
JsonAdapter<List<Card>> adapter = moshi.adapter(type);
List<Card> cards = adapter.fromJson(cardsJsonResponse);

Kotlin

val cardsJsonResponse: String = ...
// We can just use a reified extension!
val adapter = moshi.adapter<List<Card>>()
val cards: List<Card> = adapter.fromJson(cardsJsonResponse)

Fails Gracefully

Automatic databinding almost feels like magic. But unlike the black magic that typically accompanies reflection, Moshi is designed to help you out when things go wrong.

JsonDataException: Expected one of [CLUBS, DIAMONDS, HEARTS, SPADES] but was ANCHOR at path $.visible_cards[2].suit
  at com.squareup.moshi.JsonAdapters$11.fromJson(JsonAdapters.java:188)
  at com.squareup.moshi.JsonAdapters$11.fromJson(JsonAdapters.java:180)
  ...

Moshi always throws a standard java.io.IOException if there is an error reading the JSON document, or if it is malformed. It throws a JsonDataException if the JSON document is well-formed, but doesn’t match the expected format.

Built on Okio

Moshi uses Okio for simple and powerful I/O. It’s a fine complement to OkHttp, which can share buffer segments for maximum efficiency.

Borrows from Gson

Moshi uses the same streaming and binding mechanisms as Gson. If you’re a Gson user you’ll find Moshi works similarly. If you try Moshi and don’t love it, you can even migrate to Gson without much violence!

But the two libraries have a few important differences:

  • Moshi has fewer built-in type adapters. For example, you need to configure your own date adapter. Most binding libraries will encode whatever you throw at them. Moshi refuses to serialize platform types (java.*, javax.*, and android.*) without a user-provided type adapter. This is intended to prevent you from accidentally locking yourself to a specific JDK or Android release.
  • Moshi is less configurable. There’s no field naming strategy, versioning, instance creators, or long serialization policy. Instead of naming a field visibleCards and using a policy class to convert that to visible_cards, Moshi wants you to just name the field visible_cards as it appears in the JSON.
  • Moshi doesn’t have a JsonElement model. Instead it just uses built-in types like List and Map.
  • No HTML-safe escaping. Gson encodes = as \u003d by default so that it can be safely encoded in HTML without additional escaping. Moshi encodes it naturally (as =) and assumes that the HTML encoder – if there is one – will do its job.

Custom field names with @Json

Moshi works best when your JSON objects and Java or Kotlin classes have the same structure. But when they don't, Moshi has annotations to customize data binding.

Use @Json to specify how Java fields or Kotlin properties map to JSON names. This is necessary when the JSON name contains spaces or other characters that aren’t permitted in Java field or Kotlin property names. For example, this JSON has a field name containing a space:

{
  "username": "jesse",
  "lucky number": 32
}

With @Json its corresponding Java or Kotlin class is easy:

Java

class Player {
  String username;
  @Json(name = "lucky number") int luckyNumber;

  ...
}

Kotlin

class Player {
  val username: String
  @Json(name = "lucky number") val luckyNumber: Int

  ...
}

Because JSON field names are always defined with their Java or Kotlin fields, Moshi makes it easy to find fields when navigating between Java or Koltin and JSON.

Alternate type adapters with @JsonQualifier

Use @JsonQualifier to customize how a type is encoded for some fields without changing its encoding everywhere. This works similarly to the qualifier annotations in dependency injection tools like Dagger and Guice.

Here’s a JSON message with two integers and a color:

{
  "width": 1024,
  "height": 768,
  "color": "#ff0000"
}

By convention, Android programs also use int for colors:

Java

class Rectangle {
  int width;
  int height;
  int color;
}

Kotlin

class Rectangle(
  val width: Int,
  val height: Int,
  val color: Int
)

But if we encoded the above Java or Kotlin class as JSON, the color isn't encoded properly!

{
  "width": 1024,
  "height": 768,
  "color": 16711680
}

The fix is to define a qualifier annotation, itself annotated @JsonQualifier:

Java

@Retention(RUNTIME)
@JsonQualifier
public @interface HexColor {
}

Kotlin

@Retention(RUNTIME)
@JsonQualifier
annotation class HexColor

Next apply this @HexColor annotation to the appropriate field:

Java

class Rectangle {
  int width;
  int height;
  @HexColor int color;
}

Kotlin

class Rectangle(
  val width: Int,
  val height: Int,
  @HexColor val color: Int
)

And finally define a type adapter to handle it:

Java

/** Converts strings like #ff0000 to the corresponding color ints. */
class ColorAdapter {
  @ToJson String toJson(@HexColor int rgb) {
    return String.format("#%06x", rgb);
  }

  @FromJson @HexColor int fromJson(String rgb) {
    return Integer.parseInt(rgb.substring(1), 16);
  }
}

Kotlin

/** Converts strings like #ff0000 to the corresponding color ints.  */
class ColorAdapter {
  @ToJson fun toJson(@HexColor rgb: Int): String {
    return "#%06x".format(rgb)
  }

  @FromJson @HexColor fun fromJson(rgb: String): Int {
    return rgb.substring(1).toInt(16)
  }
}

Use @JsonQualifier when you need different JSON encodings for the same type. Most programs shouldn’t need this @JsonQualifier, but it’s very handy for those that do.

Omitting fields

Some models declare fields that shouldn’t be included in JSON. For example, suppose our blackjack hand has a total field with the sum of the cards:

Java

public final class BlackjackHand {
  private int total;

  ...
}

Kotlin

class BlackjackHand(
  private val total: Int,

  ...
)

By default, all fields are emitted when encoding JSON, and all fields are accepted when decoding JSON. Prevent a field from being included by annotating them with @Json(ignore = true).

Java

public final class BlackjackHand {
  @Json(ignore = true)
  private int total;

  ...
}

Kotlin

class BlackjackHand(...) {
  @Json(ignore = true)
  var total: Int = 0

  ...
}

These fields are omitted when writing JSON. When reading JSON, the field is skipped even if the JSON contains a value for the field. Instead, it will get a default value. In Kotlin, these fields must have a default value if they are in the primary constructor.

Note that you can also use Java’s transient keyword or Kotlin's @Transient annotation on these fields for the same effect.

Default Values & Constructors

When reading JSON that is missing a field, Moshi relies on the Java or Kotlin or Android runtime to assign the field’s value. Which value it uses depends on whether the class has a no-arguments constructor.

If the class has a no-arguments constructor, Moshi will call that constructor and whatever value it assigns will be used. For example, because this class has a no-arguments constructor the total field is initialized to -1.

Note: This section only applies to Java reflections.

public final class BlackjackHand {
  private int total = -1;
  ...

  private BlackjackHand() {
  }

  public BlackjackHand(Card hidden_card, List<Card> visible_cards) {
    ...
  }
}

If the class doesn’t have a no-arguments constructor, Moshi can’t assign the field’s default value, even if it’s specified in the field declaration. Instead, the field’s default is always 0 for numbers, false for booleans, and null for references. In this example, the default value of total is 0!

public final class BlackjackHand {
  private int total = -1;
  ...

  public BlackjackHand(Card hidden_card, List<Card> visible_cards) {
    ...
  }
}

This is surprising and is a potential source of bugs! For this reason consider defining a no-arguments constructor in classes that you use with Moshi, using @SuppressWarnings("unused") to prevent it from being inadvertently deleted later:

public final class BlackjackHand {
  private int total = -1;
  ...

  @SuppressWarnings("unused") // Moshi uses this!
  private BlackjackHand() {
  }

  public BlackjackHand(Card hidden_card, List<Card> visible_cards) {
    ...
  }
}

Composing Adapters

In some situations Moshi's default Java-to-JSON conversion isn't sufficient. You can compose adapters to build upon the standard conversion.

In this example, we turn serialize nulls, then delegate to the built-in adapter:

Java

class TournamentWithNullsAdapter {
  @ToJson void toJson(JsonWriter writer, Tournament tournament,
      JsonAdapter<Tournament> delegate) throws IOException {
    boolean wasSerializeNulls = writer.getSerializeNulls();
    writer.setSerializeNulls(true);
    try {
      delegate.toJson(writer, tournament);
    } finally {
      writer.setLenient(wasSerializeNulls);
    }
  }
}

Kotlin

class TournamentWithNullsAdapter {
  @ToJson fun toJson(writer: JsonWriter, tournament: Tournament?,
    delegate: JsonAdapter<Tournament?>) {
    val wasSerializeNulls: Boolean = writer.getSerializeNulls()
    writer.setSerializeNulls(true)
    try {
      delegate.toJson(writer, tournament)
    } finally {
      writer.setLenient(wasSerializeNulls)
    }
  }
}

When we use this to serialize a tournament, nulls are written! But nulls elsewhere in our JSON document are skipped as usual.

Moshi has a powerful composition system in its JsonAdapter.Factory interface. We can hook in to the encoding and decoding process for any type, even without knowing about the types beforehand. In this example, we customize types annotated @AlwaysSerializeNulls, which an annotation we create, not built-in to Moshi:

Java

@Target(TYPE)
@Retention(RUNTIME)
public @interface AlwaysSerializeNulls {}

Kotlin

@Target(TYPE)
@Retention(RUNTIME)
annotation class AlwaysSerializeNulls

Java

@AlwaysSerializeNulls
static class Car {
  String make;
  String model;
  String color;
}

Kotlin

@AlwaysSerializeNulls
class Car(
  val make: String?,
  val model: String?,
  val color: String?
)

Each JsonAdapter.Factory interface is invoked by Moshi when it needs to build an adapter for a user's type. The factory either returns an adapter to use, or null if it doesn't apply to the requested type. In our case we match all classes that have our annotation.

Java

static class AlwaysSerializeNullsFactory implements JsonAdapter.Factory {
  @Override public JsonAdapter<?> create(
      Type type, Set<? extends Annotation> annotations, Moshi moshi) {
    Class<?> rawType = Types.getRawType(type);
    if (!rawType.isAnnotationPresent(AlwaysSerializeNulls.class)) {
      return null;
    }

    JsonAdapter<Object> delegate = moshi.nextAdapter(this, type, annotations);
    return delegate.serializeNulls();
  }
}

Kotlin

class AlwaysSerializeNullsFactory : JsonAdapter.Factory {
  override fun create(type: Type, annotations: Set<Annotation>, moshi: Moshi): JsonAdapter<*>? {
    val rawType: Class<*> = type.rawType
    if (!rawType.isAnnotationPresent(AlwaysSerializeNulls::class.java)) {
      return null
    }
    val delegate: JsonAdapter<Any> = moshi.nextAdapter(this, type, annotations)
    return delegate.serializeNulls()
  }
}

After determining that it applies, the factory looks up Moshi's built-in adapter by calling Moshi.nextAdapter(). This is key to the composition mechanism: adapters delegate to each other! The composition in this example is simple: it applies the serializeNulls() transform on the delegate.

Composing adapters can be very sophisticated:

An adapter could transform the input object before it is JSON-encoded. A string could be trimmed or truncated; a value object could be simplified or normalized.

An adapter could repair the output object after it is JSON-decoded. It could fill-in missing data or discard unwanted data.

The JSON could be given extra structure, such as wrapping values in objects or arrays.

Moshi is itself built on the pattern of repeatedly composing adapters. For example, Moshi's built-in adapter for List<T> delegates to the adapter of T, and calls it repeatedly.

Precedence

Moshi's composition mechanism tries to find the best adapter for each type. It starts with the first adapter or factory registered with Moshi.Builder.add(), and proceeds until it finds an adapter for the target type.

If a type can be matched multiple adapters, the earliest one wins.

To register an adapter at the end of the list, use Moshi.Builder.addLast() instead. This is most useful when registering general-purpose adapters, such as the KotlinJsonAdapterFactory below.

Kotlin

Moshi is a great JSON library for Kotlin. It understands Kotlin’s non-nullable types and default parameter values. When you use Kotlin with Moshi you may use reflection, codegen, or both.

Reflection

The reflection adapter uses Kotlin’s reflection library to convert your Kotlin classes to and from JSON. Enable it by adding the KotlinJsonAdapterFactory to your Moshi.Builder:

val moshi = Moshi.Builder()
    .addLast(KotlinJsonAdapterFactory())
    .build()

Moshi’s adapters are ordered by precedence, so you should use addLast() with KotlinJsonAdapterFactory, and add() with your custom adapters.

The reflection adapter requires the following additional dependency:

<dependency>
  <groupId>com.squareup.moshi</groupId>
  <artifactId>moshi-kotlin</artifactId>
  <version>1.12.0</version>
</dependency>
implementation("com.squareup.moshi:moshi-kotlin:1.13.0")

Note that the reflection adapter transitively depends on the kotlin-reflect library which is a 2.5 MiB .jar file.

Codegen

Moshi’s Kotlin codegen support can be used as an annotation processor (via kapt) or Kotlin SymbolProcessor (KSP). It generates a small and fast adapter for each of your Kotlin classes at compile-time. Enable it by annotating each class that you want to encode as JSON:

@JsonClass(generateAdapter = true)
data class BlackjackHand(
  val hidden_card: Card,
  val visible_cards: List<Card>
)

The codegen adapter requires that your Kotlin types and their properties be either internal or public (this is Kotlin’s default visibility).

Kotlin codegen has no additional runtime dependency. You’ll need to enable kapt or KSP and then add the following to your build to enable the annotation processor:

KSP

plugins {
  id("com.google.devtools.ksp").version("1.6.10-1.0.4") // Or latest version of KSP
}

dependencies {
  ksp("com.squareup.moshi:moshi-kotlin-codegen:1.13.0")
}

Kapt

<dependency>
  <groupId>com.squareup.moshi</groupId>
  <artifactId>moshi-kotlin-codegen</artifactId>
  <version>1.12.0</version>
  <scope>provided</scope>
</dependency>
kapt("com.squareup.moshi:moshi-kotlin-codegen:1.13.0")

Limitations

If your Kotlin class has a superclass, it must also be a Kotlin class. Neither reflection or codegen support Kotlin types with Java supertypes or Java types with Kotlin supertypes. If you need to convert such classes to JSON you must create a custom type adapter.

The JSON encoding of Kotlin types is the same whether using reflection or codegen. Prefer codegen for better performance and to avoid the kotlin-reflect dependency; prefer reflection to convert both private and protected properties. If you have configured both, generated adapters will be used on types that are annotated @JsonClass(generateAdapter = true).

Download

Download the latest JAR or depend via Maven:

<dependency>
  <groupId>com.squareup.moshi</groupId>
  <artifactId>moshi</artifactId>
  <version>1.13.0</version>
</dependency>

or Gradle:

implementation("com.squareup.moshi:moshi:1.13.0")

Snapshots of the development version are available in Sonatype's snapshots repository.

R8 / ProGuard

Moshi contains minimally required rules for its own internals to work without need for consumers to embed their own. However if you are using reflective serialization and R8 or ProGuard, you must add keep rules in your proguard configuration file for your reflectively serialized classes.

Enums

Annotate enums with @JsonClass(generateAdapter = false) to prevent them from being removed/obfuscated from your code by R8/ProGuard.

Download Details:
Author: square
Source Code: https://github.com/square/moshi
License: Apache-2.0 license

#java #JSON #kotlin

Moshi: A Modern JSON Library for Kotlin and Java

Creación De Aplicaciones Móviles Multiplataforma Con Kotlin Multiplatf

Imagina que eres un desarrollador experto de Android con una idea para una aplicación increíble. Confía en que puede crear fácilmente una aplicación para Android, pero no está tan seguro acerca de iOS.

Si tiene experiencia en el desarrollo de interfaces de usuario móviles para Android, probablemente pueda seguir algunos tutoriales de SwiftUI y salir adelante sin problemas. Pero, ¿qué pasa con el núcleo de la aplicación? Incluso si tiene experiencia con el desarrollo de iOS, volver a escribir el mismo núcleo para iOS puede ser redundante.

Entonces, ¿cómo puede ejecutar su idea con una curva de aprendizaje mínima? Ingrese a Kotlin Multiplatform Mobile o KMM.

Según su sitio web oficial , "Kotlin Multiplatform Mobile (KMM) es un SDK diseñado para simplificar el desarrollo de aplicaciones móviles multiplataforma".

Con KMM, puede escribir el núcleo de su aplicación en Kotlin y usarlo en aplicaciones de Android e iOS. Solo necesita escribir un código específico de la plataforma como UI.

En esta publicación, aprenderemos KMM mediante la creación de una pequeña aplicación para tomar notas con operaciones de bases de datos locales. También veremos cómo se puede reutilizar la lógica comercial común, como las operaciones de base de datos. Pero primero, debemos aclarar algunos requisitos previos.

Configuración del entorno

Primero, necesitamos instalar Android Studio . Si queremos probar el lado de iOS, también necesitaremos instalar Xcode . Podemos compilar y ejecutar la aplicación de Android con código KMM sin Xcode.

A continuación, debemos asegurarnos de tener instalado el último complemento de Kotlin. Vaya a Android Studio Tools y luego coloque el cursor sobre Kotlin . A continuación, haga clic en Configurar actualizaciones del complemento de Kotlin seguido de Verificar de nuevo . Puedes ver una imagen a continuación.

Después de eso, busque el complemento KMM en la sección Complemento del menú Preferencias . Haga clic en Instalar el complemento Kotlin Multiplatform Mobile e instálelo.

No hace falta decir que también necesitamos tener instalado JDK . Tenga cuidado con esto, ya que puede ser un poco complicado. Es posible que debamos configurar la ruta de instalación de JDK en Rutas del sistema para que esto funcione. Afortunadamente, Stack Overflow tiene recursos y respuestas para cualquier problema.

Si tiene que trabajar con SQLDelight , asegúrese de instalar este complemento también para que sea más fácil trabajar con él.

Creación de un proyecto KMM

Ahora que el entorno está configurado, creemos un proyecto KMM.

Si ya tiene un proyecto de Android/iOS que desea ampliar con KMM, deberá agregar el módulo KMM a su proyecto de Android, reestructurar el código para mover el código común a módulos compartidos y configurar el proyecto de iOS para que se vincule al Marco KMM. Puede hacer referencia a este proceso aquí , pero ignoremos este caso complicado al comenzar.

Primero, creemos un nuevo proyecto en Android Studio yendo a Archivo -> Nuevo -> Nuevo proyecto . Luego, seleccione Aplicación multiplataforma de Kotlin . Esto aparecerá si finalizamos la configuración del entorno con éxito:

A continuación, complete el nombre del paquete y la ruta del proyecto.

El tercer paso es nuevo y específico de KMM. Aquí, podemos cambiar el nombre de Android, iOS o los nombres de los módulos compartidos. Dado que estamos probando KMM por primera vez, mantengamos estas opciones iguales.

Habilite la casilla de verificación Agregar pruebas de muestra para el módulo compartido si desea probar las ejecuciones de prueba y seleccione Marco regular para la distribución del marco iOS. Debe tener un aspecto como este:

¡Ahora, el proyecto estará listo en poco tiempo! Una vez configurado, podemos cambiar la Vista de archivos de proyecto de Android a Proyecto y ver la estructura completa. Tenga en cuenta que han sucedido muchas cosas detrás de escena, que discutiremos a continuación.

Estructura del proyecto KMM

Un proyecto KMM tiene tres módulos: Android, compartido e iOS.

Módulo Android

El módulo de Android es nuestro módulo de aplicación normal que se integra en una aplicación de Android. En nuestro caso, lo llamamos androidApp.

módulo compartido

Este es un módulo multiplataforma de Kotlin que se compila en una biblioteca de Android y un marco de iOS. El módulo compartido contiene todo el código reutilizable común para la aplicación. Hay tres partes dentro de él: commonMain, androidMainy iosMain.

commonMainno es específico de la plataforma y contiene todo el código que puede ejecutarse directamente en ambas plataformas. Hay bibliotecas de Kotlin que son compatibles con KMM en ciertos casos de uso. Por ejemplo, usaremos SQLDelight para bases de datos o Ktor para llamadas de red.

En androidMain, algún código en el módulo común puede necesitar una API o un comportamiento diferente. Este módulo contiene el código específico de Android.

iosMaines androidMainla contraparte de . Contiene el código específico de iOS de commonMain. Tener androidMainy iosMaindentro del módulo compartido puede parecer contradictorio, pero quedará claro una vez que hagamos un poco de trabajo práctico.

módulo iOS

Este módulo es un proyecto de Xcode que se compila como una aplicación de iOS y consume nuestro módulo KMM compartido como marco. Si recuerda, seleccionamos Regular Framework para iOS en el tercer paso de la sección anterior.

Tenga en cuenta que aunque este módulo se crea dentro de nuestro directorio raíz del proyecto, no está conectado a ninguna otra parte del proyecto (excepto para consumir el módulo compartido).

¿Cómo sabe el proyecto Xcode sobre el marco compartido?

Como acabamos de ver, el módulo compartido se compila en un marco de iOS y luego lo consume la aplicación de iOS dentro del proyecto raíz. Pero, ¿cómo lo sabe? ¿Necesitamos conectarlos manualmente? ¡No!

Cuando crea un nuevo proyecto KMM en Android Studio, configura automáticamente las rutas de compilación para el marco y otras configuraciones requeridas.

Hay una tarea gradle llamada embedAndSignAppleFrameworkForXcodeque se ejecuta cada vez que se crea la aplicación iOS para generar un nuevo marco.

Práctica con Kotlin Multiplatform Mobile

Para solidificar los conceptos anteriores y ver KMM en acción, construiremos una pequeña aplicación. Específicamente, construiremos una base de datos en el dispositivo y realizaremos algunas operaciones utilizando un código común para Android e iOS.

Específicamente para Android, tendremos una pantalla de inicio con una lista de notas recuperadas de una base de datos en el dispositivo y un CTA para agregar nuevas notas, como se ve a continuación:

Si el usuario hace clic en Agregar , la aplicación lo llevará a una nueva pantalla para agregar una nueva nota y guardarla en la base de datos. La aplicación actualizará la primera pantalla con nuevos datos, como este:

Para iOS, sin embargo, solo tendremos una pantalla en la que borraremos la base de datos en cada lanzamiento, insertaremos nuevas notas automatizadas con marca de tiempo y las buscaremos y mostraremos en una pantalla:

Descargo de responsabilidad: no soy un desarrollador de iOS. Dado que nuestro objetivo aquí es demostrar cómo se puede ejecutar el código de lógica empresarial común en ambas plataformas, realizar las tres operaciones en iOS debería ser suficiente para el propósito de este artículo.

El proyecto se puede clonar desde mi repositorio de GitHub o puede seguirlo y crear un nuevo proyecto. Puede seguir los pasos de la sección "Crear un proyecto KMM" de este artículo y crear una nueva aplicación Android KMM.

Agregar dependencias

No hay muchas dependencias externas para este proyecto, por lo que podemos concentrarnos en cómo se estructuran las dependencias.

Comenzando con el nivel de proyecto build.gradle, esto necesita las dependencias habituales relacionadas con gradle junto con las dependencias relacionadas con SQLDelight.

classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21")
classpath("com.android.tools.build:gradle:7.1.3")
classpath("com.squareup.sqldelight:gradle-plugin:$sqlDelightVersion")

El nivel de la aplicación build.gradletambién tiene todos los elementos estándar, pero hay un requisito adicional. Tenemos que agregar una dependencia en el módulo compartido en el dependenciesbloque:

implementation(project(":shared"))

El módulo compartido build.gradletiene todos los cambios principales:

plugins {
   kotlin("multiplatform")
   id("com.android.library")
   id("com.squareup.sqldelight")
}

kotlin {
   android()


   listOf(
       iosX64(),
       iosArm64(),
       iosSimulatorArm64()
   ).forEach {
       it.binaries.framework {
           baseName = "shared"
       }
   }

   val coroutinesVersion = "1.6.1"
   val ktorVersion = "1.6.1"
   val sqlDelightVersion: String by project

   sourceSets {
       val commonMain by getting {
           dependencies {
               implementation("com.squareup.sqldelight:runtime:$sqlDelightVersion")
           }
       }
       val commonTest by getting {
           dependencies {
               implementation(kotlin("test"))
           }
       }
       val androidMain by getting {
           dependencies {
               implementation("com.squareup.sqldelight:android-driver:$sqlDelightVersion")
           }
       }
       val androidTest by getting
       val iosX64Main by getting
       val iosArm64Main by getting
       val iosSimulatorArm64Main by getting
       val iosMain by creating {
           dependsOn(commonMain)
           iosX64Main.dependsOn(this)
           iosArm64Main.dependsOn(this)
           iosSimulatorArm64Main.dependsOn(this)
           dependencies {
               implementation("com.squareup.sqldelight:native-driver:$sqlDelightVersion")
           }
       }
       val iosX64Test by getting
       val iosArm64Test by getting
       val iosSimulatorArm64Test by getting
       val iosTest by creating {
           dependsOn(commonTest)
           iosX64Test.dependsOn(this)
           iosArm64Test.dependsOn(this)
           iosSimulatorArm64Test.dependsOn(this)
       }
   }
}

android {
   compileSdk = 31
   sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
   defaultConfig {
       minSdk = 21
       targetSdk = 31
   }
}

sqldelight {
   database("KmmDemoDB") {
       packageName = "com.outliers.kmmdemo.shared.cache"
   }
}

Con la excepción de la declaración de dependencias externas, todo lo demás se agrega automáticamente al crear un nuevo proyecto KMM. Sin embargo, necesitamos comprender varios bloques para saber dónde agregar dependencias.

Comenzando con el pluginsbloque, observe que hemos aplicado el librarycomplemento junto con el multiplatformcomplemento agregado automáticamente. El sqldelightcomplemento es para nuestro SDK de base de datos y es posible que no sea necesario para otros proyectos y, por lo tanto, debe agregarse manualmente.

SQLDelight también necesita el último bloque para definir el nombre de la base de datos y el paquete/ruta donde se definen las consultas. Las dependencias necesarias para el código escrito en el androidMainmódulo compartido se declaran en el androidMainbloque interior sourceSets. Lo mismo se aplica para iosMainy commonMain.

Ahora veamos el código fuente. El quid aquí es el módulo compartido. Configuraremos la base de datos SDK, SQLDelight.

Primero, cree una ruta de directorio sqldelight/your-package-name/shared/cachedentro de la commonMaincarpeta del módulo compartido, donde your-package-nameestá el nombre del paquete que definió al crear el proyecto (en este caso, la ruta es sqldelight/com/outliers/kmmdemo/shared/cache).

Aquí, necesitamos crear un archivo <your database name>.sqque contenga todas nuestras consultas SQL, como inserto select all. Para esta demostración, nombré el archivo KmmDemoDB.sq.

El nombre de la base de datos y el nombre del paquete deben coincidir con lo que hemos definido en el sqldelightbloque en shared/build.gradle. SQLDelight usa este archivo para generar código correspondiente a las consultas proporcionadas en ~/shared/build/generated/sqldelight.

Si encuentra esto confuso, tómese un tiempo para navegar por el proyecto e identificar los archivos que estamos discutiendo. Puede estar tranquilo con el hecho de que esto solo es específico de la biblioteca SQLDelight y, por lo tanto, puede aprenderse a fondo de su documentación.

Para ejecutar realmente las consultas SQL en el código común de Kotlin, necesitamos un objeto llamado SqlDriver. Pero esto SqlDriverse crea utilizando diferentes API para plataformas Android e iOS.

Aquí viene el papel de androidMainy iosMain. Primero definiremos una clase llamada DatabaseDriverFactory, declarándola como expecten conjunto fuente común ( commonMain):

expect class DatabaseDriverFactory {
   fun createDriver(): SqlDriver
}

La expectpalabra clave le dice al compilador que busque una implementación real de esta clase en el conjunto de fuentes específico de la plataforma ( androidMainy iosMain) en el mismo paquete ( commonMain/com/outliers/kmmdemo/shared/cache) en todos los conjuntos de fuentes.

Esto permite el uso de API específicas de la plataforma para crear SqlDriver, después de lo cual todo es igual. Así es como se ven las implementaciones reales de androidMainy iosMain:

actual class DatabaseDriverFactory(private val context: Context) {
   actual fun createDriver(): SqlDriver {
       return AndroidSqliteDriver(KmmDemoDB.Schema, context, "notes.db")
   }
}

actual class DatabaseDriverFactory {
   actual fun createDriver(): SqlDriver {
       return NativeSqliteDriver(KmmDemoDB.Schema, "notes.db")
   }
}

Tenga en cuenta que usamos diferentes API para crear y devolver una instancia de SqlDriver, a saber, AndroidSqliteDrivery NativeSqliteDriver. Estas API están disponibles para androidMainy a iOSMaintravés de las diferentes dependencias declaradas para cada fuente en el build.gradlearchivo del módulo compartido.

Ahora, crearemos una clase contenedora llamada Databaseen el mismo paquete que crea internamente un SqlDriverobjeto usando DatabaseDriverFactoryy expone funciones para realizar operaciones de base de datos.

class Database(databaseDriverFactory: DatabaseDriverFactory) {
   private val database = KmmDemoDB(databaseDriverFactory.createDriver())
   private val dbQuery = database.kmmDemoDBQueries

   internal fun getAllNotes(): List<Note> {
       return dbQuery.selectAllNotes().executeAsList()
   }

   internal fun getLastNote(): Note {
       return dbQuery.selectLastNote().executeAsOne()
   }

   internal fun insertNote(title: String, body: String?) {
       return dbQuery.insertNote(title, body)
   }

   internal fun deleteAll() {
       return dbQuery.deleteAll()
   }
}

A continuación, creemos otra clase raíz de SDK. Esto crea el Databaseobjeto y expone todas las operaciones. Este se convertirá en nuestro punto de entrada a la biblioteca compartida para aplicaciones de Android/iOS.

class KmmSDK(dbDriverFactory: DatabaseDriverFactory) {
   private val database: Database = Database(dbDriverFactory)

   fun getAllNotes(): List<Note> {
       return database.getAllNotes()
   }

   fun getLastNote(): Note {
       return database.getLastNote()
   }

   fun insertNote(title: String, body: String?) {
       database.insertNote(title, body)
   }

   fun deleteAll() {
       database.deleteAll()
   }
}

Esta es la clase que las aplicaciones de Android e iOS instanciarán y realizarán el trabajo principal. Esta clase toma una instancia de DatabseDriverFactory. Por lo tanto, cuando iOS instancia esta clase, el compilador seleccionará la implementación de iosMain.

Ahora, todo lo que tiene que hacer es importar KmmSDKen Android o shareden iOS y llamar a sus métodos, como se muestra arriba. El resto es todo el desarrollo de la interfaz de usuario, con el que estamos demasiado familiarizados. Pase la lista de notas a un RecyclerViewAdaptery muéstrela en la pantalla. También se implementa una lógica similar para iOS.

Conclusión

¡Uf! Hemos cubierto mucho terreno aquí. Pero, todavía hay una cosa para reflexionar: si hemos escrito todo nuestro código en Kotlin, ¿cómo lo entiende iOS?

Es por el compilador de Kotlin. El compilador de Kotlin primero convierte el código de Kotlin en una representación intermedia (código de bytes para JVM de Android), que, a su vez, se convierte en el código nativo de la plataforma.

¡Y eso es! Hemos cubierto la mayoría de las cosas que uno necesita para comenzar a trabajar con Kotlin Multiplatform.

Esta historia se publicó originalmente en https://blog.logrocket.com/building-cross-platform-mobile-apps-kotlin-multiplatform/

#mobile-apps #kotlin 

Creación De Aplicaciones Móviles Multiplataforma Con Kotlin Multiplatf
坂本  篤司

坂本 篤司

1654990140

Kotlin Multiplatformを使用したクロスプラットフォームモバイルアプリの構築

あなたがキラーアプリのアイデアを持ったAndroidのエキスパートであると想像してみてください。Android用のアプリを簡単に作成できると確信していますが、iOSについてはあまり確信がありません。

Android用のモバイルUIの開発経験がある場合は、おそらくいくつかのSwiftUIチュートリアルに従って、問題なく実行できます。しかし、アプリのコアはどうですか?iOS開発の経験がある場合でも、iOS用に同じコアを書き直すことは冗長になる可能性があります。

では、最小限の学習曲線でアイデアを実行するにはどうすればよいでしょうか。Kotlin Multiplatform Mobile、またはKMMを入力します。

彼らの公式ウェブサイトによると、「Kotlin Multiplatform Mobile(KMM)は、クロスプラットフォームモバイルアプリケーションの開発を簡素化するために設計されたSDKです。」

KMMを使用すると、アプリのコアをKotlinで記述し、AndroidアプリケーションとiOSアプリケーションの両方で使用できます。UIのようなプラットフォーム固有のコードを記述するだけで済みます。

この投稿では、ローカルデータベース操作を使用して小さなメモを取るアプリケーションを構築することにより、KMMについて学習します。また、データベース操作などの一般的なビジネスロジックを再利用する方法についても説明します。ただし、最初に、いくつかの前提条件をクリアする必要があります。

環境設定

まず、AndroidStudioをインストールする必要があります。iOS側を試してみたい場合は、Xcodeもインストールする必要があります。Xcodeを使用せずにKMMコードを使用してAndroidアプリをビルドして実行できます。

次に、最新のKotlinプラグインがインストールされていることを確認する必要があります。Android Studio Toolsに移動し、Kotlinにカーソルを合わせます。次に、[Kotlinプラグインの更新の構成]をクリックしてから、[もう一度確認]をクリックします。あなたは下のビジュアルを見ることができます。

その後、 [設定]メニューの[プラグイン]セクションでKMMプラグインを検索します。[ Kotlinマルチプラットフォームモバイルプラグインのインストール]をクリックしてインストールします。

言うまでもなく、JDKもインストールする必要があります。少し注意が必要ですので、注意してください。これを機能させるには、システムパスでJDKインストールパスを設定する必要がある場合があります。幸いなことに、StackOverflowにはあらゆる問題に対するリソースと回答があります。

SQLDelightを使用する必要がある場合は、このプラグインもインストールして、操作しやすくしてください。

KMMプロジェクトの作成

環境が設定されたので、KMMプロジェクトを作成しましょう。

KMMで拡張するAndroid/iOSプロジェクトがすでにある場合は、KMMモジュールをAndroidプロジェクトに追加し、コードを再構築して共通コードを共有モジュールに移動し、iOSプロジェクトを構成してKMMフレームワーク。ここでこのプロセスを参照できますが、最初はこの複雑なケースを無視しましょう。

まず、 [ファイル] ->[新規] ->[新しいプロジェクト]に移動して、AndroidStudioで新しいプロジェクトを作成しましょう。次に、KotlinMultiplatformAppを選択します。これは、環境のセットアップが正常に完了すると表示されます。

次に、パッケージ名とプロジェクトパスを入力します。

3番目のステップは新しく、KMMに固有です。ここで、Android、iOS、または共有モジュール名の名前を変更できます。KMMを初めて試すので、これらのオプションを同じに保ちましょう。

テストの実行を試して、iOSフレームワーク配布用の通常のフレームワークを選択する場合は、[共有モジュールのサンプルテストを追加する]チェックボックスを有効にします。次のようになります。

これで、プロジェクトはすぐに準備が整います!設定が完了したら、プロジェクトファイルビューAndroidからProjectに変更して、完全な構造を表示できます。舞台裏では多くのことが起こっていることを覚えておいてください。これについては次に説明します。

KMMプロジェクトの構造

KMMプロジェクトには、Android、共有、iOSの3つのモジュールがあります。

Androidモジュール

Androidモジュールは、Androidアプリケーションに組み込まれる通常のアプリモジュールです。この例では、androidAppという名前を付けました。

共有モジュール

これは、AndroidライブラリとiOSフレームワークにコンパイルされるKotlinマルチプラットフォームモジュールです。共有モジュールは、アプリのすべての一般的な再利用可能なコードを保持します。commonMainその中には、、、androidMainおよびの3つの部分がありますiosMain

commonMainプラットフォーム固有ではなく、両方のプラットフォームで直接実行できるすべてのコードを保持します。特定のユースケースでKMMと互換性のあるKotlinライブラリがあります。たとえば、データベースにはSQLDelightを使用し、ネットワーク呼び出しにはKtorを使用します。

ではandroidMain、共通モジュールの一部のコードで異なるAPIまたは動作が必要になる場合があります。このモジュールは、Android固有のコードを保持します。

iosMainandroidMain対応物です。iOS固有のコードを保持しますcommonMain。共有モジュールの内部にあることは直感に反するように思えるかもしれませんが、実際に作業を行うandroidMainiosMain明らかになります。

iOSモジュール

このモジュールは、iOSアプリケーションとしてビルドされ、共有KMMモジュールをフレームワークとして使用するXcodeプロジェクトです。思い出してください。前のセクションの3番目のステップで、iOS用の通常のフレームワークを選択しました。

このモジュールはルートプロジェクトディレクトリ内に作成されますが、プロジェクトの他の部分には接続されていないことに注意してください(共有モジュールを使用する場合を除く)。

Xcodeプロジェクトは共有フレームワークについてどのように知っていますか?

先ほど説明したように、共有モジュールはiOSフレームワークにコンパイルされ、ルートプロジェクト内のiOSアプリによって使用されます。しかし、それはどのようにしてそれを知るのでしょうか?それらを手動で接続する必要がありますか?いいえ!

Android Studioで新しいKMMプロジェクトを作成すると、フレームワークのビルドパスとその他の必要な設定が自動的に構成されます。

embedAndSignAppleFrameworkForXcode新しいフレームワークを生成するためにiOSアプリがビルドされるたびに実行されるというgradleタスクがあります。

Kotlinマルチプラットフォームモバイルを実際に体験する

上記の概念を固め、KMMの動作を確認するために、小さなアプリケーションを作成します。具体的には、デバイス上のデータベースを構築し、AndroidとiOSの両方に共通のコードを使用していくつかの操作を実行します。

特にAndroidの場合、デバイス上のデータベースから取得したメモのリストと、新しいメモを追加するためのCTAを含む起動画面が表示されます(以下を参照)。

ユーザーが[追加]をクリックすると、アプリはユーザーを新しい画面に移動して新しいメモを追加し、データベースに保存します。アプリは、次のように最初の画面を新しいデータで更新します。

ただし、iOSの場合、起動ごとにデータベースをクリアし、タイムスタンプ付きの新しい自動メモを挿入し、それらをフェッチして画面に表示する画面は1つだけです。

免責事項–私はiOS開発者ではありません。ここでの目標は、両方のプラットフォームで共通のビジネスロジックコードを実行する方法を示すことであるため、この記事の目的には、iOSで3つの操作すべてを実行するだけで十分です。

プロジェクトは私のGitHubリポジトリから複製することも、フォローして新しいプロジェクトを構築することもできます。この記事の「KMMプロジェクトの作成」セクションの手順に従って、新しいAndroidKMMアプリケーションを作成できます。

依存関係の追加

このプロジェクトには多くの外部依存関係がないため、依存関係の構造に集中できます。

プロジェクトレベルから始めてbuild.gradle、これにはSQLDelight関連の依存関係に加えて通常のgradle関連の依存関係が必要です。

classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21")
classpath("com.android.tools.build:gradle:7.1.3")
classpath("com.squareup.sqldelight:gradle-plugin:$sqlDelightVersion")

アプリレベルbuild.gradleにもすべての標準アイテムがありますが、追加の要件が1つあります。dependenciesブロック内の共有モジュールへの依存関係を追加する必要があります。

implementation(project(":shared"))

共有モジュールbuild.gradleには、すべての主要な変更があります。

plugins {
   kotlin("multiplatform")
   id("com.android.library")
   id("com.squareup.sqldelight")
}

kotlin {
   android()


   listOf(
       iosX64(),
       iosArm64(),
       iosSimulatorArm64()
   ).forEach {
       it.binaries.framework {
           baseName = "shared"
       }
   }

   val coroutinesVersion = "1.6.1"
   val ktorVersion = "1.6.1"
   val sqlDelightVersion: String by project

   sourceSets {
       val commonMain by getting {
           dependencies {
               implementation("com.squareup.sqldelight:runtime:$sqlDelightVersion")
           }
       }
       val commonTest by getting {
           dependencies {
               implementation(kotlin("test"))
           }
       }
       val androidMain by getting {
           dependencies {
               implementation("com.squareup.sqldelight:android-driver:$sqlDelightVersion")
           }
       }
       val androidTest by getting
       val iosX64Main by getting
       val iosArm64Main by getting
       val iosSimulatorArm64Main by getting
       val iosMain by creating {
           dependsOn(commonMain)
           iosX64Main.dependsOn(this)
           iosArm64Main.dependsOn(this)
           iosSimulatorArm64Main.dependsOn(this)
           dependencies {
               implementation("com.squareup.sqldelight:native-driver:$sqlDelightVersion")
           }
       }
       val iosX64Test by getting
       val iosArm64Test by getting
       val iosSimulatorArm64Test by getting
       val iosTest by creating {
           dependsOn(commonTest)
           iosX64Test.dependsOn(this)
           iosArm64Test.dependsOn(this)
           iosSimulatorArm64Test.dependsOn(this)
       }
   }
}

android {
   compileSdk = 31
   sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
   defaultConfig {
       minSdk = 21
       targetSdk = 31
   }
}

sqldelight {
   database("KmmDemoDB") {
       packageName = "com.outliers.kmmdemo.shared.cache"
   }
}

外部依存関係の宣言を除いて、他のすべては、新しいKMMプロジェクトの作成中に自動的に追加されます。ただし、依存関係を追加する場所を知るには、さまざまなブロックを理解する必要があります。

ブロックから始めて、自動的に追加されたプラグインと一緒にプラグインpluginsを適用したことに注意してください。プラグインはデータベースSDK用であり、他のプロジェクトでは必要ない場合があるため、手動で追加する必要があります。librarymultiplatformsqldelight

SQLDelightには、データベース名とクエリが定義されているパッケージ/パスを定義するための最後のブロックも必要です。androidMain共有モジュールので記述されたコードに必要な依存関係は、androidMain内のブロックで宣言されsourceSetsます。iosMain同じことがとにも当てはまりますcommonMain

それでは、ソースコードを見てみましょう。ここで重要なのは共有モジュールです。データベースSDK、SQLDelightをセットアップします。

まず、共有モジュールsqldelight/your-package-name/shared/cacheのフォルダー内にディレクトリパスを作成します。ここで、はプロジェクトの作成時に定義したパッケージ名です(この場合、パスはです)。commonMainyour-package-namesqldelight/com/outliers/kmmdemo/shared/cache

ここでは、または<your database name>.sqなどのすべてのSQLクエリを保持するファイルを作成する必要があります。このデモでは、ファイルに。という名前を付けました。insertselect allKmmDemoDB.sq

sqldelightデータベース名とパッケージ名は、のブロックで定義したものと一致する必要がありますshared/build.gradle。SQLDelightはこのファイルを使用して、で提供されたクエリに対応するコードを生成します~/shared/build/generated/sqldelight

これがわかりにくい場合は、時間をかけてプロジェクト内を移動し、話し合っているファイルを特定してください。これはSQLDelightライブラリにのみ固有であり、ドキュメントから完全に学ぶことができるという事実に安心できます。

Kotlin共通コードでSQLクエリを実際に実行するには、という名前のオブジェクトが必要SqlDriverです。ただし、これSqlDriverはAndroidプラットフォームとiOSプラットフォームで異なるAPIを使用して作成されています。

ここにとの役割がandroidMainありiosMainます。まず、と呼ばれるクラスを定義し、それを共通のソースセット()のDatabaseDriverFactoryように宣言します。expectcommonMain

expect class DatabaseDriverFactory {
   fun createDriver(): SqlDriver
}

このキーワードは、すべてのソースセットの同じパッケージ( )にあるプラットフォーム固有のソースセット(および)expectでこのクラスの実際の実装を探すようにコンパイラーに指示します。androidMainiosMaincommonMain/com/outliers/kmmdemo/shared/cache

これにより、プラットフォーム固有のAPIを使用して作成できSqlDriver、その後はすべて同じになります。の実際の実装は次のようにandroidMainなりiosMainます。

actual class DatabaseDriverFactory(private val context: Context) {
   actual fun createDriver(): SqlDriver {
       return AndroidSqliteDriver(KmmDemoDB.Schema, context, "notes.db")
   }
}

actual class DatabaseDriverFactory {
   actual fun createDriver(): SqlDriver {
       return NativeSqliteDriver(KmmDemoDB.Schema, "notes.db")
   }
}

のインスタンスを作成して返すためにさまざまなAPIを使用していることに注意してください。SqlDriverつまり、AndroidSqliteDriverNativeSqliteDriver。これらのAPIは、共有モジュールのファイル内のソースセットごとに宣言されたさまざまな依存関係androidMainで利用できます。iOSMainbuild.gradle

Database次に、同じパッケージで呼び出されるラッパークラスを作成します。このクラスは、データベース操作を実行する関数をSqlDriver使用してオブジェクトを内部的に作成しDatabaseDriverFactory、公開します。

class Database(databaseDriverFactory: DatabaseDriverFactory) {
   private val database = KmmDemoDB(databaseDriverFactory.createDriver())
   private val dbQuery = database.kmmDemoDBQueries

   internal fun getAllNotes(): List<Note> {
       return dbQuery.selectAllNotes().executeAsList()
   }

   internal fun getLastNote(): Note {
       return dbQuery.selectLastNote().executeAsOne()
   }

   internal fun insertNote(title: String, body: String?) {
       return dbQuery.insertNote(title, body)
   }

   internal fun deleteAll() {
       return dbQuery.deleteAll()
   }
}

次に、別のSDKルートクラスを作成しましょう。これにより、Databaseオブジェクトが作成され、すべての操作が公開されます。これが、Android/iOSアプリの共有ライブラリへのエントリポイントになります。

class KmmSDK(dbDriverFactory: DatabaseDriverFactory) {
   private val database: Database = Database(dbDriverFactory)

   fun getAllNotes(): List<Note> {
       return database.getAllNotes()
   }

   fun getLastNote(): Note {
       return database.getLastNote()
   }

   fun insertNote(title: String, body: String?) {
       database.insertNote(title, body)
   }

   fun deleteAll() {
       database.deleteAll()
   }
}

これは、AndroidおよびiOSアプリがインスタンス化してコア作業を実行するクラスです。このクラスはのインスタンスを取りますDatabseDriverFactory。したがって、iOSがこのクラスをインスタンス化すると、コンパイラはから実装を選択しますiosMain

KmmSDKこれで、上記のように、AndroidまたはsharediOSにインポートして、それらのメソッドを呼び出すだけです。残りはすべてUI開発であり、私たちがよく知っているものです。ノートリストをに渡し、RecyclerViewAdapter画面に表示します。同様のロジックがiOSにも実装されています。

結論

ふぅ!ここでは多くのことをカバーしました。しかし、まだ検討すべきことが1つあります。それは、すべてのコードをKotlinで記述した場合、iOSはそれをどのように理解するのでしょうか。

これは、Kotlinコンパイラが原因です。Kotlinコンパイラは、最初にKotlinコードを中間表現(Android JVMのバイトコード)に変換します。中間表現は、プラットフォームのネイティブコードに変換されます。

以上です!KotlinMultiplatformでの作業を開始するために必要なほとんどのことをカバーしました。

このストーリーは、もともとhttps://blog.logrocket.com/building-cross-platform-mobile-apps-kotlin-multiplatform/で公開されました

#mobile-apps #kotlin 

Kotlin Multiplatformを使用したクロスプラットフォームモバイルアプリの構築

Una Guía De Expresiones Regulares En Kotlin

En esta guía, hablaremos sobre RegEx y cómo se usa en Kotlin. RegEx significa "expresión regular". En términos sencillos, comprueba si una cadena coincide con un patrón. Las expresiones regulares son útiles en casos de validación de entrada.

¿Por qué es esto un gran problema? En lugar de tener numerosos operadores IF y ELSE, RegEx maneja todas estas validaciones a la vez.

Eso nos lleva al objetivo de esta guía. En este artículo, lo guiaremos sobre cómo usar la expresión regular como medio de validación en Kotlin mediante la creación de una aplicación simple para validar un número de teléfono móvil.

requisitos previos

Recomiendo a los lectores de esta guía tener lo siguiente:

  1. Conocimientos básicos de la sintaxis de Kotlin
  2. Un compilador de Kotlin en su dispositivo para ejecutar el código de Kotlin en la terminal

(Nota: también deberá tener su estudio de Android en funcionamiento en su sistema operativo)

Comprobación de patrones de entrada mediante RegEx

Trabajar con patrones de expresiones regulares a menudo puede ser confuso, por lo que aprenderemos a buscar patrones en una cadena usando RegEx. A continuación se muestran algunos de los patrones RegEx en Kotlin:

Para verificar un patrón de entrada con RegEx, cree una clase RegEx con el nombre de variable "patrón". Esta variable contendrá el patrón que queremos hacer coincidir.

De manera similar, crearemos otra variable con el nombre "resultado" para especificar la cadena RegEx. A continuación se muestra una implementación de esta explicación:

fun main ()
{
  var pattern = Regex("wa");
  var result = pattern.containsMatchIn("Muyiwa");

  println(result);
}

La salida es verdadera ya que "wa" está dentro de la cadena de expresión regular "Muyiwa".

(Nota: esto distingue entre mayúsculas y minúsculas, por lo que "WA" es diferente de "wa", pero podemos pasar por alto esta situación usando IGNORE_CASE)

RegexOption (IGNORE_CASE)

Esta sección le mostrará cómo comprobar patrones en una cadena independientemente de la distinción entre mayúsculas y minúsculas. Para ello, pasaremos RegexOption.IGNORE_CASEcomo parámetro al constructor RegEx. Por lo tanto, el código será el siguiente:

fun main ()
{
  var pattern = "WA".toRegex(RegexOption.IGNORE_CASE)
  var result = pattern.containsMatchIn("Muyiwa");

  println(result);
}

Hay procesos similares entre los dos (patrón anterior), con la única diferencia de que si hay un patrón, el código anterior lo reconocerá independientemente de su caso.

Validación de número de móvil

Este proyecto le mostrará cómo validar un número de teléfono móvil usando RegEx. Para facilitar el flujo, pondremos los procesos paso a paso. Por lo tanto, para comenzar, siga los pasos a continuación en consecuencia:

Primero, inicie su estudio de Android y seleccione "Nuevo proyecto".

Después de esto, seleccione "Actividad vacía" como plantilla de su proyecto y haga clic en "Siguiente".

En esta página, use cualquier nombre de su elección; para este proyecto, lo he llamado "PhoneNumberValidation". Seleccione el idioma de su elección (Kotlin) y haga clic en "Finalizar".

Hagamos un resumen rápido de lo que queremos hacer: queremos crear un botón simple y un área de "Editar texto". En este proceso, queremos que el botón esté habilitado una vez que el usuario ingresa un número de teléfono válido, y si es un número de teléfono no válido, el mensaje que se muestra debe ser "Número de teléfono no válido".

Ahora, podemos comenzar con el diseño.

Primero, en su nuevo proyecto, haga clic en activity_main.xml y seleccione "Mostrar interfaz de usuario del sistema" en la sección de diseño. Haga clic en el icono del ojo para encontrar esto.

A continuación, haga clic en "Dividir" y elimine la vista de texto predeterminada, ya que no la queremos en esa posición. Haga clic en la paleta, luego seleccione "Texto" y arrastre y suelte el texto del teléfono en la interfaz de usuario del sistema (haga clic en el icono de varita mágica para agregar la restricción a EditText).

Agregue una sugerencia a EditText con el texto "Ingresar número de teléfono". Por razones más sencillas, cambiaremos la ID para editar. Por lo tanto, nuestro oyente EditText se verá así:

(Nota: Depende de usted si desea cambiar la ID predeterminada en EditText)

A continuación, necesitaremos un botón. Para hacer esto, seguiremos un proceso similar a cómo agregamos el área de texto, con la diferencia de que estamos agregando un botón en su lugar. En este caso, necesitamos que el botón esté deshabilitado; por lo tanto, establecemos la propiedad habilitada como "falsa".

android:enabled="false"

Entonces, el código para el botón será el siguiente:

<Button
  android:id="@+id/button2"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_marginTop="6dp"
  android:text="Button"
  android:enabled="false"
  app:layout_constraintEnd_toEndOf="parent"
  app:layout_constraintStart_toStartOf="parent"
  app:layout_constraintTop_toBottomOf="@+id/edit" />

Vincular Kotlin a XML

Antes de continuar, necesitaremos vincular nuestra vista de Android con Kotlin. En el pasado, realizar esta operación era bastante fácil usando los sintéticos de Kotlin.

Todo lo que tiene que hacer es agregar el siguiente complemento a build.gradle(:app) y llamar a la vista por su ID en el archivo Kotlin:

id 'kotlin-android'
id 'kotlin-android-extensions'

Sin embargo, esto ha quedado obsoleto y no está incluido en los nuevos proyectos de Android Studio de forma predeterminada.

El problema con los sintéticos de Kotlin era que, aunque podíamos llamar a estas vistas por su ID, todo esto podía ser potencialmente nulo; lo que significa que podríamos acceder a vistas que no pertenecen al diseño.

Por otro lado, el enlace de vista rompe esa brecha porque garantiza que las vistas a las que se accede en una actividad o fragmento no sean nulas. Por lo tanto, no hay forma de que ocurra una excepción de puntero nulo.

(Nota: el enlace de datos es una alternativa al enlace de vista, pero el enlace de vista es más simple y el enlace de vista solo vinculará sus vistas)

Aquí hay un método paso a paso sobre cómo usar el enlace de vista en sus vistas:

Primero, dirígete a tu build.gradle(:app), porque tienes que habilitar explícitamente que deseas usar el enlace de vista en tu proyecto.

En el bloque de Android, agregue el nuevo bloque a continuación y sincronice su archivo Gradle:

buildFeatures {
  viewBinding true
}

Dirígete al archivo MainActivity.kt. Allí, queremos hacer referencia al EditText.

El enlace de vista genera una clase de enlace utilizada para acceder a nuestras vistas. Entonces, crearemos una private lateinit varllamada bindingcon el tipo ActivityMainBinding (este es el enlace de vista de clase generado para nosotros).

Básicamente, para cada archivo XML en nuestro proyecto, el enlace de vista hará eso, y no necesitamos reconstruir nuestro proyecto ya que no se requiere el procesamiento de anotaciones.

Ahora que tenemos el enlace, para inicializarlo, usaremos el siguiente código:

binding = ActivityMainBinding.inflate(layoutInflater)

layoutInflaterbásicamente agrega vistas de un grupo a otro. Finalmente, estableceremos la vista de contenido en binding.root usando el siguiente código:

setContentView(binding.root)

Ahora para tener acceso a la vista, use el código binding.id(ID de las vistas). Ahora estamos listos para irnos.

Queremos que el botón se habilite una vez que el usuario ingrese un número de móvil válido. Para hacer esto, dirígete al archivo MainActivity.kt. Tendremos que escribir el código de la tecla pulsada. A continuación se muestra el código Kotlin requerido para todo el proceso:

package com.example.phonenumbervalidation

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import com.example.phonenumbervalidation.databinding.ActivityMainBinding
import java.util.regex.Pattern

class MainActivity : AppCompatActivity() {

  private lateinit var binding: ActivityMainBinding

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)
// beginning
    binding.edit.addTextChangedListener(object:TextWatcher{
      override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
      }

      override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
        if (mobileValidate(binding.edit.text.toString()))
          binding.button2.isEnabled=true
        else{
          binding.button2.isEnabled=false
          binding.edit.setError("Invalid Phone number")
        }
      }

      override fun afterTextChanged(p0: Editable?) {
      }
    })
  }

  private fun mobileValidate(text: String?): Boolean {
    val p = Pattern.compile("\[0\][7-9]\[0-1\][0-9]{8}")
    val m = p.matcher(text)
    return m.matches()
  }
}

Explicando el fragmento de código

TextWatchersupervisa los cambios realizados en EditText utilizando cualquiera de sus tres métodos de devolución de llamada:

  • beforeTextChanged
  • onTextChanged
  • afterTextChanged

Aquí hay un enlace a un recurso si desea obtener información sobre estos métodos. En onTextChanged, creamos una instrucción if para verificar la función mobileValidate con el argumento binding.edit. texto .toString().

Inicialmente, hacer esto generará un error ya que no tenemos una función llamada mobileValidate, así que presione Alt+Enter para crear automáticamente una función en MainActivity. Cambie el argumento de Editablea String ya que desea que la entrada sea una cadena.

En la función mobileValidate, queremos que nuestro texto coincida con un cierto patrón: un número de teléfono estándar de Nigeria (generalmente 070, 080, 081, 090, 091 y un total de 11 dígitos).

Por lo tanto, compilamos un patrón para cuatro intervalos diferentes, con los tres primeros teniendo solo una combinación, y dividimos el último intervalo en ocho combinaciones de intervalos 0-9. Entonces, básicamente, cualquier dígito fuera de cada intervalo marcará el número como no válido y el botón permanecerá deshabilitado.

A continuación, declararemos nuestra instancia de comparación usando la función p.matcher(). Luego, analizaremos la entrada en nuestra función de comparación y devolveremos m.matches(). El resultado devuelve verdadero si nuestra entrada coincide con el patrón y el botón está habilitado, y falso si no hay ninguna coincidencia.

Volviendo a la declaración if, el botón debe estar habilitado si la condición del botón sigue siendo verdadera. También tenemos una sentencia else en caso de que la condición cambie a falsa. Configuramos un mensaje de error para lanzar una notificación de que "El número no es válido".

Después de esto, puede ejecutar la aplicación en su emulador deseado para probarla.

Conclusión

La versatilidad de RegEx lo convierte en una herramienta esencial en numerosos lenguajes de programación, y su capacidad para verificar patrones lo ha convertido en una herramienta eficiente para la validación.

Sus ventajas en términos de versatilidad también vienen con múltiples desventajas: su sintaxis compleja, por un lado, que hace que RegEx sea algo difícil incluso para los programadores veteranos.

No obstante, es absolutamente ventajoso tener el conocimiento de ReGex en su repertorio, porque un día, ¡será útil mientras esté en el espacio de desarrollo!

Esta historia se publicó originalmente en https://blog.logrocket.com/guide-regular-expression-kotlin/

#kotlin #express 

Una Guía De Expresiones Regulares En Kotlin
伊藤  直子

伊藤 直子

1654946340

Kotlinの正規表現のガイド

このガイドでは、RegExとKotlinでの使用方法について説明します。RegExは「正規表現」の略です。素人の言葉で言えば、文字列がパターンと一致するかどうかをチェックします。正規表現は、入力の検証の場合に役立ちます。

なぜこれが大したことなのですか?多数のIFおよびELSE演算子を使用する代わりに、RegExはこれらの検証を一度に処理します。

それがこのガイドの目的につながります。この記事では、携帯電話番号を検証するための簡単なアプリを作成することにより、Kotlinでの検証の手段として正規表現を使用する方法について説明します。

前提条件

このガイドの読者には、次のものをお勧めします。

  1. Kotlin構文の基本的な知識
  2. ターミナルでKotlinコードを実行するためのデバイス上のKotlinコンパイラ

(注:Android Studioをオペレーティングシステムで稼働させる必要もあります)

RegExを使用した入力パターンの確認

正規表現のパターンは扱いにくいことが多いため、RegExを使用して文字列内のパターンをチェックする方法を学習します。以下は、Kotlinの正規表現パターンの一部です。

RegExで入力パターンを確認するには、変数名「pattern」でRegExクラスを作成します。この変数には、一致させたいパターンが含まれます。

同様に、「result」という名前の別の変数を作成して、RegEx文字列を指定します。以下は、この説明の実装です。

fun main ()
{
  var pattern = Regex("wa");
  var result = pattern.containsMatchIn("Muyiwa");

  println(result);
}

「wa」は正規表現文字列「Muyiwa」内にあるため、出力はtrueです。

(注:これは大文字と小文字が区別されるため、「WA」は「wa」とは異なりますが、を使用してこの状況を見逃すことができますIGNORE_CASE

RegexOption(IGNORE_CASE)

このセクションでは、大文字と小文字の区別に関係なく、文字列のパターンをチェックする方法を示します。これを行うにはRegexOption.IGNORE_CASE、パラメーターとしてRegExコンストラクターに渡します。したがって、コードは次のようになります。

fun main ()
{
  var pattern = "WA".toRegex(RegexOption.IGNORE_CASE)
  var result = pattern.containsMatchIn("Muyiwa");

  println(result);
}

2つの間に同様のプロセスがあります(上記のパターン)。唯一の違いは、パターンがある場合、上記のコードはケースに関係なくそれを認識します。

携帯電話番号の検証

このプロジェクトでは、RegExを使用して携帯電話番号を検証する方法を紹介します。フローを簡単にするために、プロセスを段階的に説明します。したがって、開始するには、以下の手順に従ってください。

まず、Android Studioを起動し、「新しいプロジェクト」を選択します。

この後、プロジェクトテンプレートとして「空のアクティビティ」を選択し、「次へ」をクリックします。

このページでは、任意の名前を使用します—このプロジェクトでは、「PhoneNumberValidation」と呼んでいます。選択した言語(Kotlin)を選択し、[完了]をクリックします。

やりたいことを簡単に説明しましょう。簡単なボタンと「テキストの編集」領域を作成します。このプロセスでは、ユーザーが有効な電話番号を入力したらボタンを有効にします。無効な電話番号の場合、表示されるメッセージは「無効な電話番号」です。

これで、設計から始めることができます。

まず、新しいプロジェクトで、activity_main.xmlをクリックし、デザインセクションで[システムUIの表示]を選択します。これを見つけるには、目のアイコンをクリックします。

次に、「分割」をクリックし、デフォルトのテキストビューを削除します。その位置には配置したくないためです。パレットをクリックし、[テキスト]を選択して、電話のテキストをシステムUIにドラッグアンドドロップします(ワンドアイコンをクリックして、編集テキストに制約を追加します)。

「電話番号を入力してください」というテキストを使用して、EditTextにヒントを追加します。より簡単な理由で、編集するIDを変更します。したがって、EditTextリスナーは次のようになります。

(注:EditTextのデフォルトIDを変更するかどうかは、ユーザー次第です)

次に、ボタンが必要になります。これを行うには、テキスト領域を追加したのと同様のプロセスに従いますが、代わりにボタンを追加する点が異なります。この場合、ボタンを無効にする必要があります。したがって、enabledプロパティを「false」に設定します。

android:enabled="false"

したがって、ボタンのコードは次のようになります。

<Button
  android:id="@+id/button2"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_marginTop="6dp"
  android:text="Button"
  android:enabled="false"
  app:layout_constraintEnd_toEndOf="parent"
  app:layout_constraintStart_toStartOf="parent"
  app:layout_constraintTop_toBottomOf="@+id/edit" />

KotlinをXMLにバインドする

先に進む前に、AndroidビューをKotlinにバインドする必要があります。以前は、Kotlin合成を使用すると、この操作を実行するのはかなり簡単でした。

次のプラグインをbuild.gradle(:app)に追加し、KotlinファイルのIDでビューを呼び出すだけです。

id 'kotlin-android'
id 'kotlin-android-extensions'

ただし、これは非推奨になり、デフォルトでは新しいAndroidStudioプロジェクトに含まれていません。

Kotlin合成の問題は、これらのビューをIDで呼び出すことができたとしても、これらすべてがnullになる可能性があることでした。つまり、レイアウトに属していないビューにアクセスできます。

一方、ビューバインディングは、アクティビティまたはフラグメントでアクセスされるビューがnullにならないようにするため、そのギャップを解消します。したがって、nullポインタ例外が発生する可能性はありません。

(注:データバインディングはビューバインディングの代替手段ですが、ビューバインディングはたまたま単純であり、ビューバインディングはビューのみをバインドします)

ビューでビューバインディングを使用する方法のステップバイステップの方法は次のとおりです。

まず、build.gradle(:app)に進みます。これは、プロジェクトでビューバインディングを使用することを明示的に有効にする必要があるためです。

Androidブロックで、以下の新しいブロックを追加して、Gradleファイルを同期します。

buildFeatures {
  viewBinding true
}

MainActivity.ktファイルに移動します。そこでは、EditTextを参照します。

ビューバインディングは、ビューへのアクセスに使用されるバインディングクラスを生成します。したがって、ActivityMainBindingタイプのprivate lateinit var呼び出しを作成します(これは、生成されたクラスビューバインディングです)。binding

基本的に、プロジェクト内のすべてのXMLファイルに対して、ビューバインディングがそれを行います。注釈処理が必要ないため、プロジェクトを再構築する必要はありません。

バインディングができたので、それを初期化するために、以下のコードを使用します。

binding = ActivityMainBinding.inflate(layoutInflater)

layoutInflater基本的に、あるグループから別のグループにビューを追加します。最後に、以下のコードを使用して、コンテンツビューをbinding.rootに設定します。

setContentView(binding.root)

ビューにアクセスするには、コードbinding.id(ビューのID)を使用します。今、私たちは行ってもいいです。

ユーザーが有効な携帯電話番号を入力したら、ボタンを有効にします。これを行うには、MainActivity.ktファイルに移動します。押されたキーのコードを書く必要があります。以下は、プロセス全体に必要なKotlinコードです。

package com.example.phonenumbervalidation

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import com.example.phonenumbervalidation.databinding.ActivityMainBinding
import java.util.regex.Pattern

class MainActivity : AppCompatActivity() {

  private lateinit var binding: ActivityMainBinding

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)
// beginning
    binding.edit.addTextChangedListener(object:TextWatcher{
      override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
      }

      override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
        if (mobileValidate(binding.edit.text.toString()))
          binding.button2.isEnabled=true
        else{
          binding.button2.isEnabled=false
          binding.edit.setError("Invalid Phone number")
        }
      }

      override fun afterTextChanged(p0: Editable?) {
      }
    })
  }

  private fun mobileValidate(text: String?): Boolean {
    val p = Pattern.compile("\[0\][7-9]\[0-1\][0-9]{8}")
    val m = p.matcher(text)
    return m.matches()
  }
}

コードスニペットの説明

TextWatcher次の3つのコールバックメソッドのいずれかを使用して、EditTextに加えられた変更を監視します。

  • beforeTextChanged
  • onTextChanged
  • afterTextChanged

これらの方法について知りたい場合は、リソースへのリンクを次に示します。ではonTextChanged、引数binding.editを使用してmobileValidate関数をチェックするifステートメントを作成します。text .toString()。

mobileValidateという関数がないため、最初はこれを行うとエラーがスローされます。そのため、Alt + Enterキーを押して、MainActivityに関数を自動的に作成します。 入力を文字列にする必要があるため、引数をからEditableに変更します。String

mobileValidate関数では、テキストを特定のパターン(標準のナイジェリアの電話番号(通常は070、080、081、090、091、合計11桁))に一致させる必要があります。

したがって、最初の3つは1つの組み合わせのみで、最後の間隔を0〜9の間隔の8つの組み合わせに分割して、4つの異なる間隔のパターンをコンパイルします。したがって、基本的に、各間隔の外側の数字は番号に無効のフラグを立て、ボタンは無効のままになります。

次に、p.matcher()関数を使用してマッチャーインスタンスを宣言します。次に、マッチャー関数の入力を解析し、m.matches()を返します。入力がパターンに一致し、ボタンが有効になっている場合、結果はtrueを返し、一致しない場合はfalseを返します。

ifステートメントに戻ると、ボタンの条件がtrueのままの場合、ボタンを有効にする必要があります。条件がfalseに変更された場合のelseステートメントもあります。「番号が無効です」という通知をスローするエラーメッセージを設定しました。

この後、目的のエミュレーターでアプリを実行してテストできます。

結論

RegExの汎用性により、多くのプログラミング言語で不可欠なツールになり、パターンをチェックする機能により、検証のための効率的なツールになりました。

汎用性の点でのその利点には、複数の欠点もあります。たとえば、構文が複雑であるため、ベテランプログラマーでさえRegExがやや困難になります。

それでも、レパートリーにReGexの知識があることは絶対に有利です。いつの日か、開発スペースにいる限り、ReGexが役立つからです。

このストーリーは、もともとhttps://blog.logrocket.com/guide-regular-expression-kotlin/で公開されました

#kotlin #express 

Kotlinの正規表現のガイド
Best of Crypto

Best of Crypto

1654945800

Elrond Kotlin SDK for interacting with Elrond Network & Smart Contract

Elrond Kotlin SDK

This is the kotlin implementation of Elrond SDK

This project was primarily designed for Android but is also compatible with any Kotlin-friendly app since it doesn't use the Android SDK

Usage

This SDK is built with the clean architecture principles.
Interaction are done through usecases

Here is an example for sending a transaction.

// Create a wallet from mnemonics
val wallet = Wallet.createFromMnemonic(..., 0)

// Get information related to this address (ie: balance and nonce)
val account = ErdSdk.getAccountUsecase().execute(Address.fromHex(wallet.publicKeyHex))

// Get the network informations
val networkConfig = ErdSdk.getNetworkConfigUsecase().execute()

// Create the transaction object
val transaction = Transaction(
    sender = account.address,
    receiver = Address.fromHex(...),
    value = 1000000000000000000.toBigInteger(), // 1 xEGLD
    data = "Elrond rocks !",
    chainID = networkConfig.chainID,
    gasPrice = networkConfig.minGasPrice,
    gasLimit = networkConfig.minGasLimit,
    nonce = account.nonce
)

// Send transaction.
// Signature is handled internally
val sentTransaction = ErdSdk.sendTransactionUsecase().execute(transaction, wallet)
Log.d("Transaction", "tx:${sentTransaction.txHash}")

In a real world example, the usescases would be injected
The sample application showcase how to do it on Android with Hilt framework (see the Sample App).

Usecases list

API

UsecaseEndpoint
GetAccountUsecaseGET address/:bech32Address
GetAddressBalanceUsecaseGET address/:bech32Address/balance
GetAddressNonceUsecaseGET address/:bech32Address/nonce
GetAddressTransactionsUsecaseGET address/:bech32Address/transactions
GetTransactionInfoUsecaseGET transaction/:txHash
GetTransactionStatusUsecaseGET transaction/:txHash/status
SendTransactionUsecasePOST transaction/send
EstimateCostOfTransactionUsecasePOST transaction/cost
GetNetworkConfigUsecaseGET network/config
QueryContractUsecasePOST vm-values/query
QueryContractHexUsecasePOST vm-values/hex
QueryContractStringUsecasePOST vm-values/string
QueryContractIntUsecasePOST vm-values/int

Contract

UsecaseDescription
CallContractUsecaseInteract with a Smart Contract (execute function): equivalent to erdpy contract call

DNS

UsecaseDescription
RegisterDnsUsecaseSend a register transaction to the appropriate DNS contract from given user and with given name: equivalent to erdpy dns register
GetDnsRegistrationCostUsecaseGets the registration cost from a DNS smart contract: equivalent to erdpy dns registration-cost
CheckUsernameUsecaseCan be useful for validating a text field before calling RegisterDnsUsecase

Configuration

// default value is ProviderUrl.DevNet
ErdSdk.setNetwork(ProviderUrl.MainNet)

// configure the OkHttpClient
ErdSdk.elrondHttpClientBuilder.apply {
    addInterceptor(HttpLoggingInterceptor())
}

Build

The SDK is not yet uploaded to a maven repository
You can build the jar from the sources by running mvn package

Sample App

For a complete example you can checkout this sample application

Download Details:
Author: ElrondNetwork
Source Code: https://github.com/ElrondNetwork/elrond-sdk-erdkotlin
License: GPL-3.0 license

#elrond #kotlin  #blockchain  #smartcontract 

Elrond Kotlin SDK for interacting with Elrond Network & Smart Contract

Waves SDK for Android

WavesSDK is a collection of libraries used to integrate Waves blockchain features into your Android application

What is Waves?

Waves is an open source blockchain platform.

You can use it to build your own decentralised applications. Waves provides full blockchain ecosystem including smart contracts language called RIDE.

How does the blockchain network work?

There is a huge collection of nodes deloyed by miners that store all of the network information in the chain of blocks (aka blockchain), process requests and can add new transactions to the network after checking their compliance with the rules. The miners are rewarded with the network coins called MRT. 

The main advantage of this technology is that each node is a synchronized copy of the main blockchain: it means that the information is stored decentralized and won't be overwritten globally if one of the users changes it at one of the node storages. This can garantee that the user's information will stay fair and unchangable. 

The important addition is that the service built using Waves blockchain looks like a usual web application and doesn't make user experience more difficult. 

You can read the Waves node description and the definitions page for a better understanding of the blockchain functionality.

Easy start with WavesSDK

To build your first Waves platform integrated application and start using all of the blockchain features please go directly to the Waves Android SDK QuickStart tutorial and follow the instructions.

Waves SDK structure

There are three main SDK services that provide the blockchain interactions:

  • Waves Crypto handles interaction with crypto part of blockchain, allows to generate seed-phrases, convert public and private keys, obtain and verify addresses, translate bytes to string and back, sign the data with a private key, etc.
  • Waves Models contain models of transactions and other data transfer objects that are needed for building correct services.
  • Waves Node Service allows the application to cooperate directly with the blockchain: you can create transactions, broadcast them and load data from the node using these features. This is the main part of the SDK.
  • Waves Data Service suggests the easier way to access the data that is stored in the node. The methods presented in this service are the most efficient way to read blockchain data but do not help writing it.
  • Waves Matcher Service contains the methods that give ability to integrate Waves DEX (decentralized exchange platform) features into the iOS application. You can collect and add users' orders and work with exchange transactions using this service.
  • Mobile-Keeper is part of Waves client app, it designed to send or sign transactions created in third-party applications, but keep your seed-phrase in the safe place and do not show it to that applications

Testing

To test your app you can use Testnet. This is a Waves Mainnet duplicate where it's possible to repeat the real accounts structure without spending paid WAVES tokens. You can create multiple accounts, top up their balances using Faucet (just insert the account address to the input field and get 10 test tokens) and deploy RIDE scripts (as known as "smart contracts" or "dApps") using Waves RIDE IDE.

Useful links

Support

Keep up with the latest news and articles, and find out all about events happening on the Waves Platform.

Download Details:
Author: wavesplatform
Source Code: https://github.com/wavesplatform/WavesSDK-android
License: MIT license

#waves  #blockchain  #smartcontract #android #kotlin 

Waves SDK for Android

How to Download & Upload Blobs In React Native

react-native-blob-courier

Use this library to efficiently download and upload data in React Native. The library was inspired by rn-fetch-blob, and is focused strictly on http file transfers.

Installation

Install using yarn

yarn add react-native-blob-courier

Or install using npm

npm install react-native-blob-courier

Requirements

  • Android >= 21
  • Android Gradle Plugin >= 7
  • iOS >= 10
  • JDK >= 11
  • React Native >= 0.63.x

Note: you may have success with earlier versions of React Native but these are neither tested nor supported.

Usage

The library provides both a fluent and a more concise interface. In the examples the concise approach is applied; fluent interface is demonstrated later in this document.

Straightforward down- and upload

import BlobCourier from 'react-native-blob-courier';

// ...

// Download a file
const request0 = {
  filename: '5MB.zip',
  method: 'GET',
  mimeType: 'application/zip',
  url: 'http://ipv4.download.thinkbroadband.com/5MB.zip',
};

const fetchedResult = await BlobCourier.fetchBlob(request0);
console.log(fetchedResult);
// {
//   "data": {
//     "absoluteFilePath": "/path/to/app/cache/5MB.zip",
//     "response": {
//       "code":200,
//       "headers": {
//         "some_header": "some_value",
//         ...
//       }
//     },
//   },
//   "type":"Unmanaged"
// }

// ...

// Upload a file
const absoluteFilePath = fetchedResult.data.absoluteFilePath;

const request1 = {
  absoluteFilePath,
  method: 'POST',
  mimeType: 'application/zip',
  url: 'https://file.io',
};

const uploadResult = await BlobCourier.uploadBlob(request1);

console.log(uploadResult):
// {
//   "response": {
//     "code": {
//     "data": "<some response>",
//     "headers": {
//       "some_header": "some_value",
//        ...
//      }
//   }
// }

// Multipart file upload
const absoluteFilePath = fetchedResult.data.absoluteFilePath;

const request2 = {
  method: 'POST',
  parts: {
    body: {
      payload: 'some_value',
      type: 'string',
    },
    file: {
      payload: {
        absoluteFilePath,
        mimeType: 'application/zip',
      },
      type: 'file',
    },
  },
  url: 'https://file.io',
};

const multipartUploadResult = await BlobCourier.uploadBlob(request1);

console.log(multipartUploadResult):
// {
//   "response": {
//     "code": {
//     "data": "<some response>",
//     "headers": {
//       "some_header": "some_value",
//        ...
//      }
//   }
// }

Transfer progress reporting

import BlobCourier from 'react-native-blob-courier';

// ...

// Download a file
const request0 = {
  // ...
  onProgress: ((e: BlobProgressEvent) => {
    console.log(e)
    // {
    //  "written": <some_number_of_bytes_written>,
    //  "total": <some_total_number_of_bytes>
    // }
  })
};

const fetchedResult = await BlobCourier.fetchBlob(request0);

// ...

// Upload a file
const request1 = {
  // ...
  onProgress: ((e: BlobProgressEvent) => {
    console.log(e)
    // {
    //  "written": <some_number_of_bytes_written>,
    //  "total": <some_total_number_of_bytes>
    // }
  })
};

const uploadResult = await BlobCourier.uploadBlob(request1)

// ...

// Set progress updater interval
const request2 = ...

const someResult =
  await BlobCourier
    .fetchBlob({
      ...request2,
      progressIntervalMilliseconds: 1000,
    });

Managed download on Android (not available on iOS)

import BlobCourier from 'react-native-blob-courier';

// ...

const request = {
  android: {
    useDownloadManager: true // <--- set useDownloadManager to "true"
  },
  filename: '5MB.zip',
  method: 'GET',
  mimeType: 'application/zip',
  url: 'http://ipv4.download.thinkbroadband.com/5MB.zip',
};

const fetchResult = await BlobCourier.fetchBlob(request);

console.log(fetchedResult);
// {
//   "data": {
//     "result": "SUCCESS",
//     "absoluteFilePath": "/path/to/app/cache/5MB.zip"
//   },
//   "type":"Managed"
// }

Multipart upload

Sometimes order of multipart fields matters, and Blob Courier respects the order in which parts are provided. There is a catch though: due to how JavaScript works, when object keys are regular strings they are kept in the order they were added unless the keys are strings containing numbers, e.g.:

Object.keys({
  "b": "some_value1",
  "c": "some_value2",
  "a": "some_value3",
})

// ['b', 'c', 'a']

Object.keys({
  "b": "some_value1",
  "c": "some_value2",
  "a": "some_value3",
  "3": "some_value4",
  "2": "some_value5",
  "1": "some_value6",
});

// ['1', '2', '3', 'b', 'c', 'a']

The way to work around this, is to wrap all keys in a Symbol, by using Symbol.for. Do not use Symbol(<value>), this will not work, e.g.:

Object.getOwnPropertySymbols({
  [Symbol.for("b")]: "some_value1",
  [Symbol.for("c")]: "some_value2",
  [Symbol.for("a")]: "some_value3",
  [Symbol.for("3")]: "some_value4",
  [Symbol.for("2")]: "some_value5",
  [Symbol.for("1")]: "some_value6",
});

// [Symbol('b'), Symbol('c'), Symbol('a'), Symbol('3'), Symbol('2'), Symbol('1')]

Cancel request

import BlobCourier from 'react-native-blob-courier';

// ...

const abortController = new AbortController();

const { signal } = abortController;

const request0 = {
  // ...
  signal,
};

try {
  BlobCourier.fetchBlob(request0);

  abortController.abort();
} catch (e) {
  if (e.code === ERROR_CANCELED_EXCEPTION) {
    // ...
  }
}

// ...

Fluent interface

Blob Courier provides a fluent interface, that both protects you from using impossible setting combinations and arguably improves readability.

const req0 = ...

const someResult =
  await BlobCourier
    .settings({
      progressIntervalMilliseconds: 1000,
    })
    .onProgress((e: BlobProgressEvent) => {
      // ...
    })
    .useDownloadManagerOnAndroid({
      description: "Some file description",
      enableNotification: true,
      title: "Some title"
    })
    .fetchBlob(req0)

Available methods

fetchBlob(input: BlobFetchRequest)

Required

FieldTypeDescription
filenamestringThe name the file will have on disk after fetch.
mimeTypestringWhat is the mime type of the blob being transferred?
urlstringFrom which url will the blob be fetched?

Optional

FieldTypeDescriptionDefault
androidAndroidSettingsSettings to be used on Android{ downloadManager: {}, target: 'cache', useDownloadManager: false }
headers{ [key: string]: string }Map of headers to send with the request{}
iosIOSSettingsSettings to be used on iOS{ target: 'cache' }
headers{ [key: string]: string }Map of headers to send with the request{}
methodstringRepresenting the HTTP methodGET
onProgress(e: BlobProgressEvent) => voidFunction handling progress updates() => { }
signalAbortSignalRequest cancellation managernull

Response

FieldTypeDescription
type"Managed" | "Unmanaged"Was the blob downloaded through Android Download Manager, or without?
dataBlobManagedData | BlobUnmanagedDataEither managed or HTTP response data

uploadBlob(input: BlobUploadRequest)

Alias for:

const someResult =
  await BlobCourier
   // ...
   .uploadParts({
     headers,
     method,
     parts: {
       file:
         payload: {
           absoluteFilePath,
           filename,
           mimeType,
         },
         type: 'file',
       },
     },
     returnResponse,
     url,
   })

Required

FieldTypeDescription
absoluteFilePathstringPath to the file to be uploaded
mimeTypestringMime type of the blob being transferred
urlstringUrl to upload the blob to

Optional

FieldTypeDescriptionDefault
filenamestringMap of headers to send with the request<name part of 'absoluteFilePath'>
headers{ [key: string]: string }Map of headers to send with the request{}
methodstringThe HTTP method to be used in the request"POST"
multipartNamestringName for the file multipart"file"
onProgress(e: BlobProgressEvent) => voidFunction handling progress updates() => { }
returnResponsebooleanReturn the HTTP response body?false
signalAbortSignalRequest cancellation managernull

uploadParts(input: BlobMultipartUploadRequest)

Required

FieldTypeDescription
parts{ [key: string]: BlobMultipart }The parts to be sent
urlstringUrl to upload the blob to

Optional

FieldTypeDescriptionDefault
headers{ [key: string]: string }Map of headers to send with the request{}
methodstringThe HTTP method to be used in the request"POST"
onProgress(e: BlobProgressEvent) => voidFunction handling progress updates() => { }
returnResponsebooleanReturn the HTTP response body?false
signalAbortSignalRequest cancellation managernull

Response

FieldTypeDescription
responseBlobUnmanagedHttpResponseThe HTTP response

AndroidDownloadManagerSettings

FieldTypeDescription
description?stringDescription of the downloaded file
enableNotification?booleanDisplay notification when download completes
title?stringTitle to be displayed with the download

AndroidSettings

FieldTypeDescription
downloadManagerAndroidDownloadManagerSettingsSettings to be used on download manager
target"cache" | "data"Where will the file be stored?
useDownloadManagerbooleanEnable download manager on Android?

BlobManagedData

FieldTypeDescription
absoluteFilePathstringThe absolute file path to where the file was stored
result"SUCCESS" | "FAILURE"Was the request successful or did it fail?

BlobMultipart

Required

FieldTypeDescription
payloadBlobMultipartFormData | BlobMultipartFormDataFileContains the payload of the part
type"string" | "file"What is the type of the payload?

BlobMultipartFormData

Type of string | { [key:string] : any }

BlobMultipartFormDataFile

Required

FieldTypeDescription
absoluteFilePathstringPath to the file to be uploaded
mimeTypestringMime type of the blob being transferred

Optional

FieldTypeDescriptionDefault
filenamestringMap of headers to send with the request<name part of 'absoluteFilePath'>

BlobProgressEvent

FieldTypeDescription
writtennumberNumber of bytes processed
totalnumberTotal number of bytes to be processed

BlobUnmanagedData

FieldTypeDescription
absoluteFilePathstringThe absolute file path to where the file was stored
responseBlobUnmanagedHttpResponseHTTP response, including headers and status code

BlobUnmanagedHttpResponse

FieldTypeDescription
codenumberHTTP status code
headers{ [key: string]: string }HTTP response headers

IOSSettings

FieldTypeDescription
target"cache" | "data"Where will the file be stored?

Example app

You can find an example of how to use the library in the example directory.

Android

Permissions

Android 5.1 and below (API level < 23)

Add the following line to AndroidManifest.xml.

<manifest xmlns:android="http://schemas.android.com/apk/res/android" (...)>

+   <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+   <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
    (...)
    <application (...)>
      <activity (...)>
        <intent-filter>
          (...)
+          <action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
        </intent-filter>
    (...)

Android 6.0+ (API level 23+)

Grant permissions using the PermissionAndroid API, like so:

const function App = () => {

  // ...

  React.useEffect(() => {
    const requestPermissionAsync = async () => {
      try {
        await PermissionsAndroid.request(
          PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE
        );

        // ...
      } catch (err) {
        console.error(err);
      }

      // ...
    };

    requestPermissionAsync();
  }, []);

  // ...

iOS

Add to Info.plist of your app:

<key>NSAllowsArbitraryLoads</key>
<true/>

Using the integrated download manager for Android

This library allows you to use the integrated download manager on Android, this option is not available for iOS.

To enable the download manager, simply set the request's useDownloadManager property of field android to true when passing it to fetchBlob, or call the useDownloadManagerOnAndroid method when using the fluent interface.

Shared directories

As this library is focussed on transferring files, it only supports storage to the app's cache and data directories. To move files from these app specific directories to other locations on the filesystem, use another library like @react-native-community/cameraroll, e.g.:

import BlobCourier from 'react-native-blob-courier';
import CameraRoll from '@react-native-community/cameraroll';

// ...

const request = {
  filename: 'teh_cage640x360.png',
  method: 'GET',
  mimeType: 'image/png',
  url: 'https://www.placecage.com/640/360',
};

const cageResult = await BlobCourier.fetchBlob(request)

const cageLocalPath = cageResult.data.absoluteFilePath

CameraRoll.save(cageLocalPath);

Contributing

See the contributing guide to learn how to contribute to the repository and the development workflow.

Download Details:
Author: edeckers
Source Code: https://github.com/edeckers/react-native-blob-courier
License: MPL-2.0 license

#react  #reactnative  #mobileapp  #javascript  #backend #kotlin #typescript 

How to Download & Upload Blobs In React Native