Coder Social home page Coder Social logo

incio's Introduction

Incio logo

Introduction

The goal of this Template is to be our starting point for new projects, following the best development practices. It's our interpretation and adaptation of years in mobile development that we have implemented in our internal codebases for all kind of Mobile Projects.

Module

  • shared: data and domain layer
  • iosApp: ios presentation layer
  • androidApp: android presentation layer

Table of Contents

Architecture

Screen Shot 2023-02-04 at 12 19 59 PM

Features

This template includes:

  • Expect actual implementation

    • Cryptography SHA 256
  • Networking :

    • HTTP GET
    • HTTP POST
    • HTTP POST Multipart/Form-Data
    • Authenticator
    • HTTP Error Handler
  • Database

    • Create
    • Read
    • Delete
  • Preference

    • Read
    • Update

Installation

  • Follow the KMM Guide by Jetbrains for getting started building a project with KMM.
  • Install Kotlin Multiplatform Mobile plugin in Android Studio
  • Clone or Download the repo
  • Rebuild Project
  • To run in iOS, Open Xcode, select .xcworkspace, then pod install inside iosApp folder to install shared module and ios dependencies

Libraries

Screen Shot 2023-02-03 at 7 08 19 PM

Domain to Presentation

In Android, Because both shared and androidApp written in Kotlin, we can simply collect flow :

fun getRocketLaunches() = viewModelScope.launch {
   _rocketLaunchResults.value = Resource.loading()
   proceed(_rocketLaunchResults) {
      rocketLaunchUseCase.getRocketLaunches()
   }
}

But in iOS, we have to deal with swift, here i'm using createPublisher() from KMPNativeCoroutines to collect flow as Publisher in Combine :

func getRocketLaunches() {
   rocketLaunch = .loading
   viewStatePublisher(
     for: rocketLaunchUseCase.getRocketLaunchesNative(),
     in: &cancellables
   ) { self.rocketLaunch = $0 }
}

both proceed() and viewStatePublisher(for: , in:) are the same logic under the hood, to handle general error, reactively retrying the function, etc.

learn more: https://github.com/rickclephas/KMP-NativeCoroutines

Expect and Actual

in KMM, there is a negative case when there's no support to share code for some feature in both ios and android, and it's expensive to write separately in each module

so the solution is ✨expect and actual✨, we can write expect inside commonMain and write " actual" implementation with actual inside androidMain and iosMain and then each module will use expect

example:

commonMain/Platform.kt

expect fun getRequestHash(): String

androidMain/Platform.kt

actual fun getRequestHash(): String {
  val key: String = "NBS KMM Sample"
  val timestamp = (System.currentTimeMillis() / 1000).toString()
  val algorithm: String = "HmacSHA256"
  val charset: Charset = Charset.forName("UTF-8")

  val sha256Hmac: Mac = Mac.getInstance(algorithm)
  val secretKeySpec = SecretKeySpec(key.toByteArray(charset), algorithm)
  sha256Hmac.init(secretKeySpec)
  val hash: String = bytesToHex(sha256Hmac.doFinal(timestamp.toByteArray(charset))).orEmpty()
  logging { "HASH ANDROID $hash" }
  return hash
}


iosMain/Platform.kt

actual fun getRequestHash(): String {
  val key = "NBS KMM Sample"
  val timestamp = NSDate().timeIntervalSince1970.toLong().toString()
  val hash = (timestamp as NSString).sha256Hmac(key = key)
  logging { "HASH IOS $hash" }
  return hash
}

@OptIn(ExperimentalUnsignedTypes::class)
fun NSString.sha256Hmac(algorithm: CCHmacAlgorithm = kCCHmacAlgSHA256, key: String): String {
  val string = this.cStringUsingEncoding(encoding = NSUTF8StringEncoding)
  val stringLength = this.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
  val digestLength = CC_SHA256_DIGEST_LENGTH
  var result = UByteArray(size = digestLength)
  val keyString = (key as NSString).cStringUsingEncoding(encoding = NSUTF8StringEncoding)
  val keyLength = key.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)

  CCHmac(algorithm, keyString, keyLength, string, stringLength, result.refTo(0))

  return stringFromResult(result, digestLength)
}

yes, we can use Foundation, CoreCrypto, CoreFoundation same as what we use in Xcode

Project Structure

shared:

  • base
  • cache
  • data
    • sample
      • model
        • response
        • request
      • remote
        • SampleApi
        • SampleApiClient
  • di
    • ios
    • feature
  • domain
    • sample
      • model
      • mapper
      • SampleInteractor
      • SampleUseCase
  • utils
  • enum
  • eventbus
  • ext

androidApp:

  • base
  • di
  • sample
  • theme
  • utils

iosApp:

  • Dependency
  • App
  • Main
  • Resources
  • ReusableView
  • Extensions
  • Utils
  • Features
    • Sample
      • Navigator
      • Views
      • ViewModel

Build Config

You can setup Build Config for multiple Environment, Just add your build configuration at build.gradle on shared in the buildkonfig section like this:

buildkonfig {
    packageName = "com.nbs.kmm.sample"
    objectName = "NbsKmmSharedConfig"
    exposeObjectWithName = "NbsKmmSharedPublicConfig"

    // default config is required
    defaultConfigs {
        buildConfigField(STRING, "BASE_URL", "story-api.dicoding.dev")
        buildConfigField(STRING, "BASE_URL_SPACEX", "api.spacexdata.com")
    }

    // config for staging
    defaultConfigs("staging") {
        buildConfigField(STRING, "BASE_URL", "story-api.dicoding.dev")
        buildConfigField(STRING, "BASE_URL_SPACEX", "api.spacexdata.com")
    }

    // config for release
    defaultConfigs("release") {
        buildConfigField(STRING, "BASE_URL", "story-api.dicoding.dev")
        buildConfigField(STRING, "BASE_URL_SPACEX", "api.spacexdata.com")
    }
}

And for changing the Environment just set it on gradle.properties with buildkonfig.flavor and assign the value with the Environment name that you want to use, for default config just let the buildkonfig.flavor value to be empty

incio's People

Contributors

uwaisalqadri avatar phyqi avatar abdhi-nbs avatar sidiqpermana avatar nbsmobile avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.