Header image

Xử Lý Lỗi Theo Phong Cách Lập Trình Hàm Trong Kotlin Với Arrow-kt

07/04/2022

589

Xử Lý Lỗi Theo Phong Cách Lập Trình Hàm Trong Kotlin Với Arrow-kt

Bài viết này sẽ giới thiệu một số cách để xử lý lỗi trong Kotlin theo phong cách lập trình hàm sử dụng thư viện Arrow-kt. Những ví dụ được đưa ra sẽ theo thứ tự từ đơn giản đến phức tạp nhưng mạnh mẽ hơn.

Kotlin là gì?

Kotlin là một ngôn ngữ lập trình kiểu tĩnh, ban đầu được thiết kế để chạy trên máy ảo Java (JVM), sau này có thể biên dịch sang JavaScript và Native binaries sử dụng công nghệ LLVM. Kotlin có cú pháp hiện đại, ngắn gọn, an toàn và hỗ trợ cả lập trình hướng đối tượng (OOP)lập trình hàm (FP).

Arrow-kt là gì?

Arrow-kt (https://arrow-kt.io/) là một thư viện Typed Functional Programming trong Kotlin. Arrow cung cấp một ngôn ngữ chung của các interface và sự trừu tượng hóa trên các thư viện Kotlin. Nó bao gồm các kiểu dữ liệu phổ biến nhất như Option, Either, Validated, v.v … và các functional operator như traversecomputation blocks giúp cho người dùng viết các ứng dụng và thư viện pure FP dễ dàng hơn.

Setup

Trong file build.gradle.kts của root project, thêm mavenCentral vào danh sách:

allprojects {
    repositories {
        mavenCentral()
    }
}

Thêm dependency vào file build.gradle.kts của project:

val arrow_version = "1.0.1"
dependencies {
    implementation("io.arrow-kt:arrow-core:$arrow_version")
}

Pure function và exceptions

Pure function

Trong lập trình hàm, pure function là những function có hai tính chất:

  • Giá trị trả về chỉ phụ thuộc vào tham số truyền vào nó (tức là nếu cùng input thì cùng output).
  • Không tạo ra các side effect.

Side effect là những tác dụng xảy ra khi thực hiện một function mà không phải công dụng chính của nó. Ví dụ: ngoài việc trả về các giá trị, nó gây ra những tương tác thay đổi môi trường, thay đổi các biến toàn cục, thực hiện các hoạt động I/O như HTTP request, in dữ liệu ra console, đọc và ghi files Trong Kotlin, tất cả các function trả về Unit rất có thể tạo ra side effect. Đó là bởi vì giá trị trả về là Unit biểu thị “không có giá trị hữu ích được trả về”, điều này ngụ ý rằng function không làm gì khác ngoài việc thực hiện các side effect.

Một số ví dụ pure function trong Kotlin là những hàm toán học như sin, cos, max, …

Lợi ích của pure functions: dễ dàng combine, dễ dàng test, dễ dàng debug, dễ dàng parallelize, … Vì thế trong lập trình hàm, chúng ta sẽ cố gắng sử dụng nhiều pure functions nhất có thể, và tách biệt các phần pure và impure.

Exceptions

Kotlin có thể throwcatch các Exception tương tự như ngôn ngữ Java, JavaScript, C++,… Sử dụngtry { } catch(e) { } finally { } là cách xử lý lỗi phổ biến trong các ngôn ngữ lập trình mệnh lệnh.

Tuy nhiên việc throw và catch các Exception, chúng ta có thể thay đổi hành vi của function, khiến cho các function không còn pure nữa (catch Exception là một side effect). Ví dụ, một function catch hai Exception là ex1ex2 từ một function khác và tính toán kết quả, lúc đó kết quả đó sẽ phụ thuộc vào thứ tự thực thi của các câu lệnh, thậm chí có thể thay đổi giữa hai lần thực thi khác nhau của cùng một hệ thống.

Partial function

Ngoài ra, việc throw các Exception khiến cho các function trở thành một Partial function, tức là một function không hoàn toàn – không được xác định cho tất cả các giá trị input có thể có, bởi vì trong một số trường hợp, nó có thể không bao giờ trả về bất cứ thứ gì. Ví dụ, trong trường hợp một vòng lặp vô hạn hoặc nếu một Exception được throw.

Ví dụ: findUserById ở ví dụ dưới là một partial function.

@JvmInline
value class UserId(val value: String)

@JvmInline
value class Username(val value: String)

@JvmInline
value class PostId(val value: String)

data class User(
    val id: UserId,
    val username: Username,
    val postIds: List<PostId>
)

class UserException(message: String?, cause: Throwable?) : Exception(message, cause)

/**
 * @return an [User] if found or `null` otherwise.
 * @throws UserException if there is any error (eg. database error, connection error, ...)
 */
suspend fun findUserById(id: UserId): User? = TODO()

Đề làm cho findUserById trở thành một total function, chúng ta thay vì throw UserException, chúng ta có thể return nó như một giá trị, thay return type của findUserById thành UserResult.

sealed interface UserResult {
    data class Success(val user: User?) : UserResult
    data class Failure(val error: UserException) : UserResult
}

suspend fun findUserById(id: UserId): UserResult = TODO()

Các vấn đề với Exceptions

Exception có thể được xem như là những câu lệnh GOTO như trong C/C++, vì chúng làm gián đoạn luồng chương trình bằng cách quay lại nơi gọi nó. Các Exception không nhất quán, đặc biệt là khi trong lập trình Multithread, chúngta try...catch một function nhưng Exception được throw ở một Thread khác mà không thể catch nó được.

Một vấn đề khác là việc lạm dụng catch Exception: catch nhiều hơn những gì cần thiết và cả những Exception từ hệ thống như VirtualMachineError, OutOfMemoryError,…

try {
    doExceptionalStuff() //throws IllegalArgumentException
} catch (e: Throwable) {
    // too broad, `Throwable` matches a set of fatal exceptions and errors
    // a user may be unable to recover from:
    /*
    VirtualMachineError
    OutOfMemoryError
    ThreadDeath
    LinkageError
    InterruptedException
    ControlThrowable
    NotImplementedError
    */
}

Và cuối cùng, nhìn vào một signature của một function, chúng ta không thể biết được, nó sẽ throw ra Exception nào, ngoài việc đọc docs hay là đọc source code của nó, thay vào đó, chúng ta hay để signature của function đó biểu hiện rõ ràng những lỗi nào có thể xảy ra khi gọi function đó.

Vì vậy, để xử lý lỗi, chúng ta cần một type có thể được compose với nhau, và biểu thị một kết quả hợp lệ hoặc một lỗi. Những type đó là Discriminated union/ tagged union, trong Kotlin đó được triển khai thông qua sealed class/sealed interface/enum class. Chúng ta sẽ cùng tìm hiểu kotlin.Result được cung cấp bởi Kotlin Sdtlib từ version 1.3 , và sau đó là arrow.core.Either đến từ thư viện Arrow-kt.

Sử dụng kotlin.Result để xử lý lỗi

Chúng ta có thể sử dụng Result<T> như là một type để biểu thị: hoặc là giá trị thành công với type là T, hoặc là là một lỗi xảy ra với một một Throwable. Nếu theo cách hiểu đơn giản, Result<T> = T | Throwable.

Để tạo ra giá trị Result, ta có thể dụng các function có sẵn như

  • Result.success
  • Result.failure
  • runCatching (tương tự như try { } catch { }
    nhưng trả về Result).
suspend fun findUserByIdFromDb(id: String): UserDb? = TODO()

fun UserDb.toUser(): User = TODO()

suspend fun findUserById(id: UserId): Result<User?> = runCatching { findUserByIdFromDb(id.value)?.toUser() }

Chúng ta có thể kiểm tra Result là giá trị thành công hay không, thông qua hai property là isSuccessisFailure. Để thực hiện các hành động ứng với mỗi trường hợp của Result thông qua function onSuccessonFailure.

val userResult: Result<User?> = findUserById(UserId("#id"))
userResult.isSuccess
userResult.isFailure
userResult.onSuccess { u: User? -> println(u) }
userResult.onFailure { e: Throwable -> println(e) }

Để có thể lấy giá trị bên trong Result, chúng ta sử dụng các function getOr__. Sử dụng exceptionOrNull để truy cập giá trị Throwable bên trong nếu Result đại diện cho giá trị thất bại. Ngoài ra, còn có function fold có thể handle một trong hai case dễ dàng.

val userResult: Result<User?> = findUserById(UserId("#id"))

// Access value
userResult.getOrNull()
userResult.getOrThrow()
userResult.getOrDefault(defaultValue)
userResult.getOrElse { e: Throwable -> defaultValue(e) }

// Access Throwable
userResult.exceptionOrNull()

fun handleUser(u: User?) {}
fun handleError(e: Throwable) {
    when (e) {
        is UserException -> {
            // handle UserException
        }
        else -> {
            // handle other cases
        }
    }
}

userResult.fold(
    onSuccess = { handleUser(it) },
    onFailure = { handleError(it) }
)

Tuy nhiên, sức mạnh thực sự của Result nằm ở việc chain các hoạt động trên nó. Ví dụ: nếu bạn muốn truy cập một property của User:

val userResult: Result<User?> = findUserById(UserId("#id"))
val usernameNullableResult: Result<Username?> = userResult.map { it?.username }

Chú ý rằng, nếu việc gọi lambda truyền vào function map throw Exception, thì Exception đó sẽ bị throw ra ngoài. Nếu muốn Exception đó được catch và chuyển thành giá trị Result, sử dụng mapCatching để vừa map vừa catching.

val usernameResult: Result<Username> = userResult.map {
    checkNotNull(it?.username) { "user is null!" }
}

Một vấn đề đặt ra là làm sao để chain các Result mà phụ thuộc lẫn nhau

// (UserId) -> Result<User?>
suspend fun findUserById(id: UserId): Result<User?> = TODO()

// User -> List<Post>
suspend fun getPostsByUser(user: User): Result<List<Post>> = TODO()

// List<Post> -> Result<Unit>
suspend fun doSomethingWithPosts(posts: List<Post>): Result<Unit> = TODO()

Chúng ta tạo một function flatMap (mapflatten).

// Map and flatten
inline fun <T, R> Result<T>.flatMap(transform: (T) -> Result<R>): Result<R> = mapCatching { transform(it).getOrThrow() }

// or
inline fun <T, R> Result<T>.flatMap(transform: (T) -> Result<R>): Result<R> = map(transform).flatten()
inline fun <T> Result<Result<T>>.flatten(): Result<T> = getOrElse { Result.failure(it) }

Bằng việc sử dụng flatMap, chúng ta có thể chain các Result với nhau

val unitResult: Result<Unit> = findUserById(UserId("#id"))
    .flatMap { user: User? -> runCatching { checkNotNull(user) { "user is null!" } } }
    .flatMap { getPostsByUser(it) }
    .flatMap { doSomethingWithPosts(it) }

Thư viện Arrow-kt cũng cung cấp block result { ... } để có thể handle việc chain các Result với nhau, tránh một số trường hợp sử dụng quá nhiều các nested flatMap. Trong block result { ... }, sử dụng function bind() để lấy giá trị T từ Result<T>. Nếu bind được gọi trên một Result đại diện một lỗi, thì phần code ở phía dưới nó trong block result { ... } sẽ bị bỏ qua (cơ chế short-circuits).

import arrow.core.*

val unitResult: Result<Unit> = result { /*this: ResultEffect*/
    val userNullable: User? = findUserById(UserId("#id")).bind()
    val user: User = checkNotNull(userNullable) { "user is null!" }
    val posts: List<Post> = getPostsByUser(user).bind()
    doSomethingWithPosts(posts).bind()
}

Một số tình huống khác có thể yêu cầu các chiến lược xử lý lỗi phức tạp có thể bao gồm khôi phục hoặc báo cáo lỗi. Ví dụ, chúng ta fetch data từ remote server, nếu bị lỗi thì sẽ lấy data từ cache. Chúng ta có thể sử dụng 2 function recoverrecoverCatching,

class MyData(...)

fun getFromRemote(): MyData = TODO()
fun getFromCache(): MyData = TODO()

val result: Result<MyData> = runCatching { getFromRemote() }
    .recoverCatching { e: Throwable ->
        logger.error(e, "getFromRemote")
        getFromCache()
    }

Sử dụng Result là một cách tiếp cận này tốt hơn, tuy nhiên vấn đề là lỗi luôn luôn là một Throwable, ta phải đọc docs hoặc đọc source code của nó. Một vấn đề nữa là runCatching khi kết hợp với suspend function, nó sẽ catch mọi Throwable, kể cả kotlinx.coroutines.CancellationException, CancellationException là một Exception đặc biệt, được coroutines sử dụng để đảm bảo cơ chế cooperative cancellation (xem issues 1814 Kotlin/kotlinx.coroutines).

Một cách tiếp cận tốt hơn là sử dụng arrow.core.Either, khắc phục các nhược điểm của Result.

Sử dụng arrow.core.Either để xử lý lỗi

Chúng ta có thể sử dụng Either<L, R> như là một type để biểu thị: hoặc là giá trị Left(value: L) , hoặc là giá trị Right(value: R). Nếu theo cách hiểu đơn giản, Either<L, R> = L | R.

public sealed class Either<out A, out B> {
    public data class Left<out A> constructor(val value: A) : Either<A, Nothing>()
    public data class Right<out B> constructor(val value: B) : Either<Nothing, B>()
}

Trong đó, Left thường đại diện cho các giá trị lỗi, giá trị không mong muốn, và Right thường đại diện cho các giá trị thành công, giá trị mong muốn. Nhìn chung Either<L, R> tương tự với Result<T>, Result<T> chỉ tập trung vào type của giá trị thành công mà không quan tâm đến type của giá trị lỗi, và chúng ta có thể xem Result<R> ~= Either<Throwable, R>. Eitherright-biased, tức là các function như map, filter, flatMap, … sẽ theo nhánh Right, nhánh Left bị bỏ qua (được return trực tiếp mà không có hành động nào trên nó cả).

Để tạo ra giá trị Either, ta có thể dụng các function có sẵn như:

  • Left constructor, ví dụ: val e: Either<Int, Nothing> = Left(1)
  • Right constructor, ví dụ: val e: Either<Nothing, Int> = Right(1)
  • left extension function, ví dụ val e: Either<Int, Nothing> = 1.left().
  • right extension function, ví dụ val e: Either<Nothing, Int> = 1.right().
  • Either.catch functions, catch các Exceptions nhưng nó sẽ bỏ qua các fatal Exception
    như kotlinx.coroutines.CancellationException, VirtualMachineError, OutOfMemoryError,…
  • Và nhiều cách được cung cấp bởi arrow.core.Either.Companion.
suspend fun findUserByIdFromDb(id: String): UserDb? = TODO()

fun UserDb.toUser(): User = TODO()

fun Throwable.toUserException(): UserException = TODO()

suspend fun findUserById(id: UserId): Either<UserException, User?> =
    Either
        .catch { findUserByIdFromDb(id.value)?.toUser() } // Either<Throwable, User?>
        .mapLeft { it.toUserException() }                 // Either<UserException, User?>

Chúng ta có thể kiểm tra Either là giá trị Left hay Right, thông qua hai function là isLeft()isLeft(). Either cũng cung cấp hai function tap (tương tự onSucesscủa Result) và tapLeft (tương tự onFailure của Result).

val result: Either<UserException, User?> = findUserById(UserId("#id"))
result.isLeft()
result.isRight()
result.tap { u: User? -> println(u) }
result.tapLeft { e: UserException -> println(e) }

Tương tự như Result, chúng ta sử dụng các function getOrElse, orNull, getOrHandle để lấy giá trị mà Right chứa nếu nó là Right. Một số function hữu ích nữa là fold, bimap, mapError, filter,…

val result: Either<UserException, User?> = findUserById(UserId("#id"))

// Access value
result.getOrElse { defaultValue }
result.orNull()
result.getOrHandle { e: UserException -> defaultValue(e) }

fun handleUser(u: User?) {}
fun handleError(e: UserException) {
    // handle UserException
}

result.fold(
    ifRight = { handleUser(it) },
    ifLeft = { handleError(it) }
)

Tương tự ví dụ khi sử dụng Result, chúng ta cũng muốn chain nhiều giá trị Either với nhau

// (UserId) -> Either<UserException, User?>
suspend fun findUserById(id: UserId): Either<UserException, User?> = TODO()

// User -> Either<UserException, List<Post>>
suspend fun getPostsByUser(user: User): Either<UserException, List<Post>> = TODO()

// List<Post> -> Either<UserException, Unit>
suspend fun doSomethingWithPosts(posts: List<Post>): Either<UserException, Unit> = TODO()

Thư viện Arrow-kt đã cung cấp sẵn function flatMap và block either { ... } để có thể chain các Either với nhau dễ dàng. Trong either { ... } block, sử dụng function bind() để lấy giá trị R từ Either<L, R>. Nếu bind được gọi trên một Either chứa giá trị Left, thì phần code ở phía dưới nó trong block either { ... } sẽ bị bỏ qua (cơ chế short-circuits).

import arrow.core.*

class UserNotFoundException() : UserException("User is null", null)

val result: Either<UserException, Unit> = findUserById(UserId("#id"))
    .flatMap { user: User? ->
        if (user == null) UserNotFoundException().left()
        else user.right()
    }
    .flatMap { getPostsByUser(it) }
    .flatMap { doSomethingWithPosts(it) }

// or either block

val result: Either<UserException, Unit> = either { /*this: EitherEffect*/
    val userNullable: User? = findUserById(UserId("#id")).bind()
    val user: User = ensureNotNull(userNullable) { UserNotFoundException() }
    val posts: List<Post> = getPostsByUser(user).bind()
    doSomethingWithPosts(posts).bind()
}

Cuối cùng là cách khôi phục lỗi. Tương tự như recoverrecoverCatching của Result, chúng ta có thể sử dụng hai function handleErrorhandleErrorWith (giống như flatMap nhưng theo nhánh Left).

class MyData(...)

suspend fun getFromRemote(): MyData = TODO()
suspend fun getFromCache(): MyData = TODO()

val result: Either<Throwable, MyData> =
    Either
        .catch { getFromRemote() }
        .handleErrorWith { e: Throwable ->
            Either.catch {
                logger.error(e, "getFromRemote")
                getFromCache()
            }
        }

Kết luận

Chúng ta đã cùng tìm hiểu Result và sau đó là Either, cả hai type giúp xử lý lỗi và làm giảm side effect. Either còn chỉ rõ về những lỗi có thể xảy ra mà chỉ cần nhìn vào signature của một function. Ngoài ra, Either hỗ trợ cho suspend function mà không làm mất đi cơ chế cancellation, và Arrow-kt cũng có module Fx (https://arrow-kt.io/docs/fx/) giúp cho việc sử dụng Kotlin Coroutines dễ dàng hơn, khi viết các chương trình asyncconcurrent.

Hy vọng bạn thích bài viết này và hôm nay bạn đã học được điều gì đó hữu ích!

Tài liệu tham khảo

  • Arrow-kt – Either docs
  • Arrow-kt – Error handlding
  • Arrow-kt – Monad comprehension
  • Ciocîrlan, D. (2021) Idiomatic error handling in scala, Rock the JVM Blog. Available at: https://blog.rockthejvm.com/idiomatic-error-handling-in-scala/ (Accessed: 04 October 2024).

Author: st-hocnguyen

Related Blog

How-to

Knowledge

Software Development

+0

    How to Undo Commits Safely in Git: Git Reset and Git Revert Explained

    Introduction In software development, mistakes in commits happen more frequently than we would prefer. Imagine you are working on a feature branch and accidentally commit sensitive information, like an API key, or commit in the wrong branch. You quickly realize the need to undo these changes, but as you search for solutions, you come across two common commands: git reset and git revert. Each offers a way to return, but which is right for your situation? In this article, SupremeTech will explore both commands, how they work, when to use them, and how to decide which approach best addresses your specific needs. Three trees in Git Before getting started, it’s important to understand Git's internal state management systems, called “Git’s three-tree”: Working Directory: This is the workspace on your local machine, it reflects the current state of your files and any changes made that have not yet been staged or committed. You can see changes in the Working Directory with git status.Staging Index: This space holds a snapshot of changes ready to be committed. After you’ve made changes in the Working Directory, you can add them to the Staging Index with git add.Commit History: This is the timeline of saved changes in your project. When you use the git commit command, it takes the changes from the Staging Index and adds them to this history as a new commit. Figure 1. The Git’s three-tree The animation above demonstrates Git's three-tree structure by showing the creation of file1.js and committing it as C1. We add two more examples: file2.js as a C2 commit and file3.js as a C3 commit. These three commits will be used throughout the article as we explore git reset and git revert commands. Figure 2. Visualizing Git's three-tree with three commits Undoing commits with git reset The git reset command allows you to undo changes in your working directory by moving the branch tip back to a specific commit and discarding all commits made after that point. Figure 3. Visualizing the git reset command After running the command git reset HEAD~1, you’ll notice two changes: The branch tip has moved to the commit C2.The latest commit (C3) has been discarded from the commit history. The HEAD~1 is a way to reference the commit before the current HEAD. You can use similar syntax to go back further, like HEAD~2 to go back two commits from HEAD. Alternatively, you can specify a particular commit using its hash ID. The next question is where did the changes from C3 commit go? (the file3.js in this example). Did it delete permanently, or is it saved somewhere? This is where the git reset flags come into play. Bypassing one of the following flags, you can control the changes: --soft: It undoes the commits in the history and places the changes back in the Staging Index, ready to be committed again if needed. Figure 4. Visualizing git reset command with --soft flag -—mixed (this is the default option): It is similar to—-soft but also clears the Staging Index. This means any changes from the discarded commits are left unstaged in the Working Directory, requiring you to re-add them before re-committing. Figure 5. Visualizing git reset command with --mixed flag --hard: This option clears all changes from both the Staging Index and Working Directory and resets the codebase to match the specified commit without making any modifications. Figure 6. Visualizing git reset command with --hard flag By using git reset, you've successfully undone a specific commit. However, try to push these changes to the remote repository with a regular git push. You’ll get an error because the local commit history no longer matches the remote. To push these changes, you need to use a force push (git push --force). While this command will update the remote branch, it comes with risks - it can overwrite the remote history, creating potential issues for other developers. To avoid these problems, let’s explore a safer alternative: Undoing public commits with git revert The git revert command is an undo command, but it doesn’t work like the git reset. Instead of removing a commit from the project history, it creates a new one containing the inverse of the original changes. Figure 7. Visualizing the git revert command The result of running the command git revert HEAD is a new commit that undoes the changes made in the C3 commit. Since the C3 commit added file3.js, the revert will effectively delete this file. In short, running git revert HEAD will bring your code back to its state at the C2 commit. You can prevent git revert from automatically creating a new commit by using the -n or --no-commit flag. With this option, the inverse changes are placed in the Staging Index and Working Directory, allowing you to review or modify them before committing. Figure 8. Visualizing git revert command with --no-commit flag The git revert command allows you to go back to previous commits without removing any mistake commits. It doesn’t re-write the project history. Because of this, this command should be used to undo changes on a public branch. What is the difference between Git Reset vs. Git Revert? The difference between git reset and git revert is that git reset should be used to undo changes in your local history, while git revert should be recommended for undoing changes on a shared or public branch. Both git reset and git revert are commands for undoing changes, but they work differently in key ways: git resetgit revertHow it worksReverts to a previous state by removing the specified commit.Reverts to a previous state by creating a new commit with inverse changes.OptionsOffers --mixed, --soft, and --hard flags to control how changes are handled.Offers --no-commit to add inverse changes without automatically committing them.UsageRecommended for undoing changes in your local history.Recommended for undoing changes on a shared or public branch. Conclusion By now, you should clearly understand how to undo changes in a Git repository using git reset and git revert. In short, use git reset for local-only history changes, and use git revert to undo changes on a shared branch safely. Choosing the right command for your situation lets you keep your project history clean and ensures smoother collaboration with your teammates.

    25/11/2024

    75

    Huy Nguyen K.

    How-to

    +2

    • Knowledge
    • Software Development

    How to Undo Commits Safely in Git: Git Reset and Git Revert Explained

    25/11/2024

    75

    Huy Nguyen K.

    ionic vs react native

    Software Development

    +0

      Ionic vs. React Native: A Comprehensive Comparison

      Ionic vs. React Native is a common debate when choosing a framework for cross-platform app development. Both frameworks allow developers to create apps for multiple platforms from a single codebase. However, they take different approaches and excel in different scenarios. Here’s a detailed comparison. Check out for more comparisons like this with React Native React Native vs. Kotlin Platform Native Script vs. React Native The origin of Ionic Framework Ionic Framework was first released in 2013 by Max Lynch, Ben Sperry, and Adam Bradley, founders of the software company Drifty Co., based in Madison, Wisconsin, USA. What's the idea behind Ionic? The creators of Ionic saw a need for a tool that could simplify the development of hybrid mobile apps. At the time, building apps for multiple platforms like iOS and Android required separate codebases, which was time-consuming and resource-intensive. Therefore, the goal was to create a framework that allowed developers to use web technologies—HTML, CSS, and JavaScript—to build apps that could run on multiple platforms with a single codebase. Its release and evolution over time The first version of Ionic was released in 2013 and was built on top of AngularJS. It leveraged Apache Cordova (formerly PhoneGap) to package web apps into native containers, allowing access to device features like cameras and GPS. 2016: With the rise of Angular 2, the team rebuilt Ionic to work with modern Angular. The renovation improved performance and functionality. 2018: Ionic introduced Ionic 4, which decoupled the framework from Angular, making it compatible with other frameworks like React, Vue, or even plain JavaScript. 2020: The company developed Capacitor, a modern alternative to Cordova. It provides better native integrations and supports Progressive Web Apps (PWAs) seamlessly. Key innovations of Ionic First of all, Ionic popularized the use of web components for building mobile apps. In addition, it focused on design consistency, offering pre-built UI components that mimic native app designs on iOS and Android. Thirdly, its integration with modern frameworks (React, Vue) made it appealing to a broader developer audience. Today, Ionic remains a significant player in the hybrid app development space. It's an optimal choice for projects prioritizing simplicity, web compatibility, and fast development cycles. It has a robust ecosystem with tools like Ionic Studio. Ionic Studio is a development environment for building Ionic apps. The origin of React Native React Native originated at Facebook in 2013 as an internal project to solve challenges in mobile app development. Its public release followed in March 2015 at Facebook’s developer conference, F8. Starting from the problem of scaling mobile development In the early 2010s, Facebook faced a significant challenge in scaling its mobile app development. They were maintaining separate native apps for iOS and Android. It made up duplicate effort and slowed down development cycles. Additionally, their initial solution—a hybrid app built with HTML5—failed to deliver the performance and user experience of native apps. This failure prompted Facebook to seek a new approach. The introduction of React for Mobile React Native was inspired by the success of React, Facebook’s JavaScript library for building user interfaces, introduced in 2013. React allowed developers to create fast, interactive UIs for the web using a declarative programming model. The key innovation was enabling JavaScript to control native UI components instead of relying on WebView rendering. Its adoption and growth React Native quickly gained popularity due to its: Single codebase for iOS and Android.Performance comparable to native apps.Familiarity for web developers already using React.Active community and support from Facebook. Prominent companies like Instagram, Airbnb, and Walmart adopted React Native early on for their apps. Today, React Native remains a leading framework for cross-platform app development. While it has faced competition from newer frameworks like Flutter, it continues to evolve with strong community support and regular updates from Meta (formerly Facebook). Ionic vs. React Native: What's the key differences? Core Technology and Approach React Native Uses JavaScript and React to build mobile apps.Renders components using native APIs, resulting in apps that feel closer to native experiences.Follows a “native-first” approach, meaning the UI and performance mimic native apps. Ionic Uses HTML, CSS, and JavaScript with frameworks like Angular, React, or Vue.Builds apps as Progressive Web Apps (PWAs) or hybrid mobile apps.Renders UI components in a WebView instead of native APIs. Performance React Native: Better performance for apps that require complex animations or heavy computations.Direct communication with native modules reduces lag, making it suitable for performance-intensive apps. Ionic: Performance depends on the capabilities of the WebView.Works well for apps with simpler UI and functionality, but may struggle with intensive tasks or animations. User Interface (UI) React Native: Leverages native components, resulting in a UI that feels consistent with the platform (e.g., iOS or Android).Offers flexibility to customize designs to match platform guidelines. Ionic: Uses a single, web-based design system that runs consistently across all platforms.While flexible, it may not perfectly match the native look and feel of iOS or Android apps. Development Experience React Native: Ideal for teams familiar with React and JavaScript.Offers tools like Hot Reloading, making development faster.Requires setting up native environments (Xcode, Android Studio), which can be complex for beginners. Ionic: Easier to get started for web developers, as it uses familiar web technologies (HTML, CSS, JavaScript).Faster setup without needing native development environments initially. Ecosystem and Plugins React Native: Extensive library of third-party packages and community-driven plugins.Can access native features directly but may require writing custom native modules for some functionalities. Ionic: Has a wide range of plugins via Capacitor or Cordova for accessing native features.Some plugins may have limitations in terms of performance or compatibility compared to native implementations. Conclusion: Which One to Choose? Choose React Native if:You want high performance and a native-like user experience.Your app involves complex interactions, animations, or heavy processing.You’re building an app specifically for mobile platforms.Choose Ionic if:You need a simple app that works across mobile, web, and desktop.You have a team of web developers familiar with HTML, CSS, and JavaScript.You’re on a tight budget and want to maximize code reusability. Both frameworks are excellent in their own right. Your choice depends on your project’s specific needs, the skill set of your development team, and your long-term goals.

      19/11/2024

      71

      Linh Le

      Software Development

      +0

        Ionic vs. React Native: A Comprehensive Comparison

        19/11/2024

        71

        Linh Le

        inter bee 2024

        OTT Streaming

        Our success stories

        +0

          SupremeTech & Enlyt Showcase CloudTV Streaming Solution at Inter BEE 2024

          SupremeTech, along with our partner Enlyt, is excited to present CloudTV, our advanced streaming platform, at Inter BEE 2024. This event is a great chance for us to connect with other professionals in media and technology. We aim to show how CloudTV can help businesses deliver smooth and high-quality streaming to their audiences. Through CloudTV, we’re ready to make streaming easier and more accessible. Inter BEE 2024 is held between November 13 and 15, 2024. Let's join us! About Inter BEE 2024 Inter BEE, or International Broadcast Equipment Exhibition, is one of Japan’s biggest media and tech events. It takes place at Makuhari Messe and marks its significant 60th year in 2024. This exhibition gathers thousands of visitors and exhibitors from around the world. They include professionals from video, audio, and digital media industries. Over 1,000 exhibitors are part of this year’s event, and they bring their latest tools and ideas in content creation, delivery, and audience engagement. Inter BEE also includes unique interactive spaces, like INTER BEE CINEMA, which focuses on new film production tools, and the INTER BEE AWARD, which celebrates top media innovations. This setup makes Inter BEE a place where the latest trends and future ideas come together. Introducing CloudTV, our custom OTT streaming solution CloudTV is a flexible streaming solution that SupremeTech has created for businesses. Built on strong cloud technology, CloudTV lets businesses create their own streaming platforms quickly and easily. It allows for high-quality video streaming that can be viewed on any device—smartphones, computers, or smart TVs. CloudTV is also secure and easy to customize, so each business can make the platform look and feel their own. CloudTV solves problems around scaling and technical complexity. Whether a business is large or small, CloudTV’s setup makes streaming simple and reliable. By supporting the latest streaming standards, CloudTV ensures great video quality across different screens and devices. This makes CloudTV a smart choice for businesses that want to reach viewers with a smooth and simple streaming experience. Get in Touch with SupremeTech If you’re interested in a streaming solution that is simple, reliable, and flexible, come visit our booth at Inter BEE 2024. We’re ready to discuss how CloudTV can help meet your unique streaming needs. You can also reach out to SupremeTech directly to explore how we can create a custom streaming platform for your business. Let’s work together to make streaming easy and engaging with CloudTV.

          14/11/2024

          325

          Linh Le

          OTT Streaming

          +1

          • Our success stories

          SupremeTech & Enlyt Showcase CloudTV Streaming Solution at Inter BEE 2024

          14/11/2024

          325

          Linh Le

          Our success stories

          +0

            SupremeTech welcomes GITS Group to the first GITS CEO Summit in Da Nang

            SupremeTech is excited to announce that we are co-hosting the GITS CEO Summit 2024 in Da Nang. As a proud member of the GITS Group, we look forward to welcoming leaders from around the world to this annual gathering. The summit is our largest internal event, where top executives will discuss technology trends, opportunities, and challenges for the upcoming year. We will focus on innovation, global strategies, and building a stronger GITS community. Why Da Nang? The summit will take place in Da Nang, which is not only known for its beautiful beaches and modern facilities but also for the thriving hub of technology in Vietnam. This vibrant city provides an ideal setting for leaders to connect, explore new business ideas, and share valuable insights. Da Nang is also home to many tech companies and startups advancing Vietnam’s position on the global technology map. By hosting the GITS CEO Summit here, SupremeTech aims to highlight Da Nang’s strategic importance and underscore its potential to be an international center for digital innovation. The Significance of the GITS CEO Summit 2024 The GITS CEO Summit 2024 will take place on December 06, bringing together over 70 CEOs from GITS member companies across Vietnam, Australia, Germany, South Korea, and Singapore. We will exchange ideas on technology trends, opportunities, and challenges for the coming year to drive business growth. We will also review the past year’s activities, share plans for 2025, and honor members who have made outstanding contributions to GITS. This year, the summit will be held at the beautiful Naman Retreat Resort, marking a fresh chapter in their journey toward tech excellence. This summit will be a crucial gathering for industry leaders and tech experts, offering meaningful connections through focused conference sessions, a poolside dinner party under the stars, expert talks on manufacturing and finance, and plenty of networking opportunities for everyone involved. This summit goes beyond the normal conference event. It’s a spontaneous gathering hub that puts technology discussion in a practical context and accelerates union among tech leaders. As collaboration becomes ever more important, this summit will help address tech development challenges and inspire fresh ideas for the future. Key Themes “Global Connections, Technology Adoptions” Under the theme "Global Connections. Technology Adoption," the summit aims to foster meaningful conversations about new technologies and how they affect Vietnam's economy. The agenda includes: 1. Keynote presentation: Vietnam IT Report by Dragon Capital2. Panel discussion: Digital Transformation in Manufacturing3. Panel discussion: ESG Compliance & Opportunities for Growth4. Panel discussion: Vietnam IT Goes Global: Challenges and Lessons Learned At the end of the gathering, our CEO members will engage in a joyful gala dinner to celebrate the past year milestones. The event is expected to inspire technology leaders by both insightful talks and vibrant connections. The GITS Summit 2023 successfully took place in Hanoi Look back on the success of SupremeTech and GITS in Vietnam IT Day 2024 SupremeTech and GITS previously teamed up to host Vietnam IT Day 2024, an event that left a strong impression on Vietnam’s IT community and beyond. This event, held in Sydney, Australia, brought together IT leaders to discuss the future of Vietnam’s tech industry. The focus was on strengthening Vietnam’s reputation as a global tech player, and it was highly successful in facilitating meaningful connections between Vietnamese IT companies and international partners. Learn more about Vietnam IT Day 2024 here. Building on the success of Vietnam IT Day, SupremeTech and GITS are excited to expand our vision for the GITS CEO Summit 2024. Want to stay updated about the event, let's follow us for more. See you in Da Nang!

            13/11/2024

            103

            Ngan Phan

            Our success stories

            +0

              SupremeTech welcomes GITS Group to the first GITS CEO Summit in Da Nang

              13/11/2024

              103

              Ngan Phan

              Customize software background

              Want to customize a software for your business?

              Meet with us! Schedule a meeting with us!