Kotlin Multiplatform

Bildiğiniz gibi, Kotlin programlama dilinin büyük destekçileriyiz. Google, resmi olarak destekleneceğini açıkladığı andan itibaren biraz zaman harcayarak öğrenmeye ve ustalaşmaya karar verdik. En başından beri, kendi tip sisteminin etkileyiciliğinden, üst düzey işlevler için desteğe kadar, aşık olduk, bu kadar güçlü bir araçla Android gelişimini yeniden keşfetmek harikaydı.

Bunun sadece bir başlangıç ​​olduğunu düşünmekten hoşlanıyoruz ve JetBrains ekibinden gelen en son güncellemeleri yakından izliyoruz. En heyecan verici haberlerden biri, Kotlin’in çok platformlu çalışmasını sağlamak için sabırsızlayan Kotlin ekibiydi. Bu, Android uygulamalarında Kotlin’i çalıştırabilmemizin yanı sıra, artık iOS uygulamaları yazabiliyoruz, arka uç ve web’i favori dilimizle yazabiliyoruz!

Kotlin çoklu platformunu kullanarak, önde gelen iki platform olan iOS ve Android için daha fazla araştırma yapmaya ve bir uygulama oluşturmaya karar verdik. Bu hala devam eden bir çaba ve denemek ve uygulamak istediğimiz birçok şey var, ancak bu arada size yolunda bulduğumuz şeyleri anlatmak istiyoruz.

Model

Kotlin çok platformlu projeler genellikle birkaç modüle ayrılır. Yazılımımızı oluşturmak istediğimiz her hedef için bir tane olacak, bizim durumumuzda, biri iOS için, diğeri Android için. Her iki platformda da paylaşılan veya ortak modül olan kodu tekrar kullanmak için başka bir modül oluşturuyoruz. Multiplatform inşa etmenin yolu, daha sonra farklı hedeflerden tüketmek için her hedef için paylaşılan modülün bir eseri yaratmaktır.

Bu, Kotlin’in hem son bir projenin hem de tüketmesi için aynı koddan bir kavanoz dosyası ve bir çerçeve dosyası oluşturacağı anlamına gelir. Yukarıdaki grafiğin kesikli bölümü, neye ulaşmak istediğimizi gösterir. Kotlin çoklu platformunun Backend ve Web’de de kullanılması amaçlansa da, bunları küçük denememizin kapsamından uzak tutacağız.

Çok platformlu bir projeyi ve tüm bağımlılıklarını yapılandırmak için önce paylaşılan modülü oluşturmak üzere yeni bir Gradle modülü oluşturmamız gerekir.

kotlin {
    targets {
        fromPreset(presets.jvm, "jvm")

        fromPreset(presets.iosX64, "ios_x86_64")
        fromPreset(presets.iosArm64, "ios_arm64")
        configure([ios_x86_64, ios_arm64]) {
            compilations.main.outputKinds("FRAMEWORK")
        }
    }
    
    sourceSets {
        commonMain {
            dependencies {
                implementation "org.jetbrains.kotlin:kotlin-stdlib"
            }
        }
        jvmMain {
            dependencies {
                implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
            }
        }
        iosMain {
            dependencies {
            }
        }
        configure([ios_x86_64Main, ios_arm64Main]) {
            dependsOn iosMain
        }
    }
}

configurations {
    compileClasspath
}

Daha sonra inceleyeceğimiz bir çok iOS yapılandırması görebiliriz. Daha sonra Android proje yapılandırmasını tanımlayabilir ve Gradle’a paylaşılan modüle bir bağımlılık eklemesini söyleyebiliriz.

android {
    buildToolsVersion = "28.0.3"
    compileSdkVersion(28)
    defaultConfig {
        minSdkVersion(21)
        targetSdkVersion(28)
        multiDexEnabled = true
        versionCode = 1
        testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = false
            proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
        }
    }

    sourceSets {
        getByName("main").java.srcDirs("src/main/kotlin")
    }
}

dependencies {
    implementation("com.android.support.constraint:constraint-layout:1.1.3")
    implementation("com.android.support.constraint:constraint-layout:1.1.3")
    implementation("com.android.support:appcompat-v7:28.0.0")
    implementation("com.android.support:recyclerview-v7:28.0.0")
    implementation(kotlin("stdlib-jdk7", KotlinCompilerVersion.VERSION))
    implementation(project(":shared"))
}

Son olarak, iOS projesini yapılandırmalıyız. Bu, süreçteki en zor adımdır çünkü iOS kendi inşa araç takımı ile çalışır . Buradaki hile, bir çerçeve eseri üretmek için yeni bir Gradle görevi oluşturmaktır. Akılda tutulması gereken önemli bir şey, farklı mimariler, x86_64simülatör ve arm64gerçek cihazlar için yapıyı yaratmamız gerektiğidir .

task packForXCode(type: Sync) {
    final File frameworkDir = new File(buildDir, "xcode-frameworks")

    final String configuration = project.findProperty("CONFIGURATION")?.toUpperCase() ?: "DEBUG"
    final String arch = project.findProperty("ARCHS") ?: "x86_64"

    dependsOn kotlin.targets."ios_${arch}".compilations.main.linkTaskName("FRAMEWORK", configuration)

    from { kotlin.targets."ios_${arch}".compilations.main.getBinary("FRAMEWORK", configuration).parentFile }
    into frameworkDir
}

tasks.build.dependsOn packForXCode

Şimdi yeni Gradle görevini çağırarak paylaşılan modülü derlemek ve oluşturulan çerçeveyi projeye dahil etmek için bir XCode derleme aşamasını yapılandırabiliriz.

En temel kontrolleri yapmak

Her iki platformda da proje başladı ve devam ettikten sonra kaliteye yatırım yapmak istedik. İOS projesinde SwiftLint’i, bağımlılığı olan bir Podfile ekleyerek yapılandırmaya başladık:

platform :ios, "11.4"

target "Multiplatform app" do
  use_frameworks!
  pod "SwiftLint"
end

shared/build.gradle


apply plugin: "io.gitlab.arturbosch.detekt"
apply plugin: "org.jlleitschuh.gradle.ktlint"

ktlint {
    version = "0.30.0"
    verbose = true
    android = false
    outputToConsole = true
    reporters = [ReporterType.PLAIN, ReporterType.CHECKSTYLE]
    filter {
        exclude("**/generated/**")
        include("**/kotlin/**")
    }
}

detekt {
    toolVersion = "1.0.0-RC14"
    input = files("src/main/kotlin")
    filters = ".*/resources/.*,.*/build/.*"
}

check.dependsOn ktlintCheck
check.dependsOn "detekt"

CI

Projemizdeki bazı otomatik kontrolleri ayarladıktan sonra en önemli şey, zaman zaman bunları çalıştırmaktır . Bitrise kullanıyoruz ve en başından beri, her platform için bir tane olmak üzere iki farklı proje kullanmayı seçtik. Bu şekilde, yığını her biri için bağımsız olarak yapılandırabilir ve her ikisi için de paralel olarak testler yapabiliriz!
Android için tüm birim testlerini ve tüy bırakmayan kontrolleri yapıyoruz. İOS için, aynı şeyi yapmak için elimizden gelenin en iyisini yaptık, ancak Gradle ve XCode’un modern versiyonlarını destekleyen bir yığın bulmaya çalışırken hala bazı sorunlar yaşıyoruz.


Kotlin Multiplatform’da Paylaşılan Kütüphane

Bu yazının amacı, ortak modülünüzü iki farklı mobil platforma nasıl entegre edeceğinizi göstermektir. Bu gelişme yaklaşımını kullanırken bulduğumuz sorunları ve çözümleri de gözden geçireceğiz. UI katmanından otomatik teste veya kodlamada bulduğumuz bazı sorunlara.

Bu Mimari hakkında daha fazla bilgi edinmek istiyorsanız, GUI Mimarisini Martin Fowler tarafından okuyabilirsiniz.

Paylaşılan Kütüphane hedefleri

Aynı kütüphaneyi kullanarak platformlar arasında kod paylaşımı zor olabilir ama önce hedeflerimizi netleştirmemiz gerekiyor:

  • Mümkün olduğunca kod paylaşmak, yani, desteklemek istediğimiz her platform için aynı şeyleri yeniden uygulamaktan kaçınmak.
  • Bağımlılıkları iki kez inşa etmeye gerek kalmadan her platform için bağımlılıkları bir kez sağlama yeteneğine sahip olmak.
  • Her platform için farklı bağımlılık enjektörleri veya sağlayıcıları kullanarak bağımlılık inversiyonunu çözmek istemiyoruz. İdeal olarak, sadece bir bağımlılık enjektörü istiyoruz.
  • Testlerde bağımlılıkları değiştirebilme.
  • Alıştığımız gibi testleri yazın: UI testi, entegrasyon testi, vb …

UI katmanını uygulama

Şimdi, görevimiz için hangi mimariyi kullanmamız gerektiğini düşündüğümüzde, açıkça ortaya koyduk: ilk denememiz için Model View Presenter kullanıyorduk.

MVP Kotlin Multiplatform yayınındaki Jt Liew’dan görüntü

MVP, UI testlerinin yazılma şeklini geliştirmek için oluşturuldu. Desenin yaratıcıları, test edilen konuya dahil olan herhangi bir gerçek GUI olmadan UI katmanı için otomatik testler yazmak zorunda kaldılar. Bu yüzden tüm sunum mantığını “sunum yapanlar” adlı sınıflara taşımaya karar verdiler .

Bu modeli uygulamak ve UI davranışını paylaşılan kütüphanemize taşımak, hedefimize ulaşmak için bize çok yardımcı olacaktır. UI oluşturma ile ilgili tüm kodlar (ancak yerel platformlara bağlanan gerçek çizim kısmı) paylaşılan kütüphanemize taşınacaktır. Bu, her platformun uygulayacağı halka açık görüş sözleşmesini tanımlamamıza yardımcı olacaktır. Bu nedenle, her platform uygulamasını basitleştireceğiz çünkü yalnızca UI’nin önceden tanımlanmış davranışı nasıl göstereceğini düşünmemiz gerekecek.

Platforma özel altyapı

Karşılaşacağınız ilk sorun altyapı koduyla ilgili olacaktır. Ağ veya yerel bir veritabanı gibi herhangi bir veri kaynağından bilgi istemek isteyeceksiniz. Belki, hem Android’de hem de iOS’da görmeniz gereken bazı günlükleri yazdırmak istersiniz. Bu tür sorunlar için, expectedher platformda uygulamak istediğimiz özellikleri bildirmemiz gerekiyor . Aynı zamanda, platforma özel kod, actualbeklenen özelliklerin uygulandığını ilan edecektir . Bu anahtar kelimeler resmi belgelerde çok iyi açıklanmıştır .

Neyse ki, bu kodu iki kez yazmak zorunda kalmayacağız. İhtiyacımız olacak şeylerin çoğu, çözümü Kotlin Native’in desteklediği çoklu platformlarda uygulayan kütüphanelerde zaten yapıldı.

İşte kotlin multiplatform için desteği olan kütüphaneler kümesi:

Gerçek hayat

Çok platformlu projeler Kotlin 1.2 ve 1.3’teki deneysel bir özelliktir. Bu belgede açıklanan tüm dil ve takım özellikleri gelecekteki Kotlin versiyonlarında değişebilir.

1.3.30 Kotlin versiyonlarında sorun yaşadığımızı bilmenizi istiyoruz. Bunlar, Kotlin Multiplatform ile paylaşılan bir kütüphane oluştururken bulduğumuz sorunlardan bazıları.

  • Yerel testler işe yaradı, ancak rastgele bazı testleri atladı.
  • Paylaşılan kütüphanemiz kodunu güncelledikten sonra bazen yenilenmedi. Bu bize koştuğumuz iOS koduna güvenemeyeceğimizi düşündürdü.
  • Derleme zamanı bir problemdir. Ortalama derleme zamanı şimdi yaklaşık 5 dakikadır.

Bulduğumuz sorunların bir başka örneği de, Kotlin sürüm yükseltme ile ilgili. Biz bir kez ortaya kondu KOTLIN 1.3.40 yayınlandı ve kullanmak için projemizi güncelledikten sonra, uygulama derleme durdu. Bununla ilgili temel sorunlardan biri, bizim gibi birçok bağımlılığınız varsa, Kotlin’in son sürümüyle güncellenmemiş olmaları olabilir ve bu nedenle, yaptığınız zaman, onu desteklemeye karar verene kadar tutarsızlıklar olabilir. Ayrıca, her zaman kepçe sarmalayıcısını yükseltmeyi unutmayın .

İOS ve XCode ile ilgilenmek

İOS ile çalışırken büyük olasılıkla birçok sorun bulacaksınız. Android uygulamanızı kendinizin yapabildiğini ancak sizin iOS’unuz olmadığına karar verirseniz, işte ilk elden edindiğimiz tecrübelerden bazıları.

Statik nesneler veya alanlar kullanmak frozeniOS’ta değiştiriciyi kullanır . Bu, değişmez olacakları anlamına gelir. Statik bağlamda değişken bir değişkeniniz varsa, çalışma zamanında değiştiremezsiniz, aksi takdirde bir “InvalidMutabilityException” istisnası elde edersiniz. Sen mutability Kotlin Native nasıl çalıştığını okuyabilir belgede ve biz de zamanında bağımlılık uygulamalarını değiştirmek için kullanabilirsiniz nasıl bir örneğini görmek Karumi KotlinMultiplatformApp Github depo.

Ktor istemcisini HTTP istekleri için kullanmadan önce bahsettiğimiz gibi, # 1165 : Ktor istemcisi çevrimdışı istisnalar yakalayamıyor ve hala # 887: HttpClient nesnenizdeyken HttpRequestPipeline üzerinde # 887: InvalidMutabilityExceptionsorunu var. statik bağlam ve POST / PUT isteği yapmaya çalışın. Android uygulamanızın mükemmel çalışmasını sağlarken kötü kazalar göreceksiniz.

Otomatik test

Son fakat en az değil, mevcut test yığınının başka bir Android veya iOS projesinde olduğu gibi kullanılıp kullanılamayacağını bilmek istedik. İşte herhangi bir Mobil Multiplatform uygulaması için test stratejisi hakkında bazı düşünceler.

Bildiğimiz gibi, iOS kodumuzun Android’de olmasına rağmen çalışmasını sağlayamıyoruz. Bu yüzden her platformda entegrasyon testi yapmak istedik ve bir şeyleri bozmadığımızdan emin olduk. Bunun için yığında mümkün olduğunca derin gitmek zorunda kaldı.

Bize bağımlılıklar sağlamak ve daha da önemlisi testlerdeki örnekleri değiştirmek için bir servis bulucuya ihtiyacımız vardı. Henüz bu gereksinimleri karşılayan bir Multiplatform projesi bulamadık. Herhangi bir şey biliyorsanız, lütfen bir yorum bırakın.

Aşağıdaki kod parçası, servis bulucumuzdaki Mutabilitenin nasıl kullanıldığını ele almanın bir örneğidir AtomicReferenceInjectionModulebağımlılıklarımızı Swift ve Kotlin kullanarak değiştirilmek üzere sakladığımız yer. Kodun tamamını github depomuzda görebilirsiniz.

object GalleryInjector {
  private val galleryInjector = AtomicReference<InjectionModule?>(null)
  private val defaultInjector = InjectionModule()

  operator fun invoke(): InjectionModule =
    galleryInjector.value ?: defaultInjector

  fun config(injector: InjectionModule) {
    galleryInjector.value = injector.freeze()
  }
}

Buradaki sorun mockito veya mockk kullanmamız gerektiği ve Swift’de kullanamayacağımız. Bu yüzden, onları iOS testlerinde kullanmak için Kotlin’de manuel olarak taslaklar oluşturmalıyız.

Onları Android Testlerinde değiştiriyoruz.

val apiClient = mock<PhotosApiClient> {
      onBlocking { getPhotos() } doThrow NullPointerException()
    }

GalleryInjector.config(PhotoListStub(apiClient))

Ve iOS’ta bunların yerini nasıl alıyoruz onu da gösterelim

let injectionModule = TestModule(apiClient: PhotoApiClientStub(photos: photos, withErrors: false))
        GalleryInjector().config(injector: injectionModule)       

Sunucular için yerel testler yazmamaya karar verdik ve bunun nedeni yalnızca ünite testleri oluşturabileceğimizi, onları Swift’e çevirip yerel bir ortamda çalıştıracağımızı bilmiyorduk. Bu bizi birçok testten kurtarır.

Comments

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

GIPHY App Key not set. Please check settings

Yükleniyor…

0

Ne düşünüyorsun?

Yukarıdaki hata düzeltme testleri son derece faydalı olsa da, daha önce neden olmuş bir hataya tepki olarak yazılırlar ve bizi aynı gerilemeye karşı korurlar.

Swift | Dark Mode | 2020

Google Play Store’da bir “Web Uygulama” yayınlayın