├── .gitignore
├── .idea
└── vcs.xml
├── LICENSE
├── README.md
├── app
├── build.gradle
└── src
│ ├── commonMain
│ └── kotlin
│ │ └── sample
│ │ ├── AllData.kt
│ │ ├── Const.kt
│ │ ├── IosUtilities.kt
│ │ ├── Utils.kt
│ │ ├── api
│ │ ├── Errors.kt
│ │ └── NetworkApi.kt
│ │ ├── launchAndCatch.kt
│ │ ├── model
│ │ └── DataRepositoryImpl.kt
│ │ └── presentation
│ │ ├── BaseView.kt
│ │ ├── DataRepository.kt
│ │ ├── MainPresenter.kt
│ │ └── MainView.kt
│ ├── commonTest
│ └── kotlin
│ │ └── sample
│ │ ├── TestHelper.kt
│ │ ├── model
│ │ └── DataRepositoryTest.kt
│ │ └── presentation
│ │ └── MainPresenterTest.kt
│ ├── iosMain
│ └── kotlin
│ │ └── sample
│ │ └── UtilsIos.kt
│ ├── iosTest
│ └── kotlin
│ │ └── sample
│ │ └── SampleTestsIOS.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── sample
│ │ │ ├── Extensions.kt
│ │ │ ├── KmpApp.kt
│ │ │ ├── MainActivity.kt
│ │ │ └── UtilsAndroid.kt
│ └── res
│ │ ├── drawable
│ │ └── ic_arrow_forward_black_24dp.xml
│ │ ├── font
│ │ ├── roboto_mono.xml
│ │ └── roboto_mono_light.xml
│ │ ├── layout
│ │ └── activity_main.xml
│ │ └── values
│ │ ├── font_certs.xml
│ │ ├── preloaded_fonts.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── sample
│ └── MainPresenterTestHelper.kt
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── iosApp
├── .gitignore
├── Podfile
├── Podfile.lock
├── iosApp.xcodeproj
│ └── project.pbxproj
├── iosApp
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ └── right-arrow-forward.imageset
│ │ │ ├── Contents.json
│ │ │ ├── right-arrow-forward@2x.png
│ │ │ └── right-arrow-forward@3x.png
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── BaseView.swift
│ ├── Extensions
│ │ ├── UILayer
│ │ │ └── UILayer+Color.swift
│ │ └── UIViewController
│ │ │ └── UIViewController+TextField.swift
│ ├── Fonts
│ │ ├── RobotoMono-Light.ttf
│ │ └── RobotoMono-Regular.ttf
│ ├── Info.plist
│ ├── UI.swift
│ └── ViewController.swift
└── iosAppTests
│ ├── Info.plist
│ └── iosAppTests.swift
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | .gradle
3 | .idea/*
4 | !.idea/runConfigurations
5 | !.idea/runConfigurations/*
6 | !.idea/vcs.xml
7 | !.idea/dictionaries
8 | !.idea/dictionaries/*
9 | out
10 | *.iml
11 | .directory
12 | node_modules
13 | local.properties
14 |
15 | .DS_Store
16 | .externalNativeBuild
17 | .database
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Bedanta Bikash Borah
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Not in active development. Please follow this repository https://github.com/iamBedant/Multiplatform-Sample
3 |
4 | ## Kotlin Multiplatform Project (Android/ iOS)
5 |
6 | Android | iOS
7 | :-------------------------:|:-------------------------:
8 |  | 
9 |
10 |
11 |
12 | ### Goals
13 |
14 | * Share business logic between all platforms.
15 |
16 |
17 | ### Non-Goals
18 |
19 | * Architecture / UI
20 |
21 | ### Libraries Used
22 |
23 | * Ktor
24 | * coroutines
25 | * kotlinx-serialization
26 |
27 |
28 | ## Running the App
29 |
30 | 1. Install Android Studio
31 | 2. Create a file called `local.properties` in the root directory with the following content:
32 |
33 | ```
34 | ## This file does *NOT* get checked into your VCS, as it
35 | ## contains information specific to your local configuration.
36 |
37 | # Location of the SDK. This is only used by Gradle.
38 | sdk.dir=/Users/{your-username}/Library/Android/sdk
39 | ```
40 | Replace `{your-username}` in `local.properties` with your actual username.
41 |
42 | ### Android
43 | * Open the project on **Android Studio** or **Intellij IDEA**.
44 | * Select `app` configuration and hit `Run`.
45 |
46 | ### iOS
47 | * Run `./gradlew build -x test` on root directory.
48 | * Open `iosApp` project in **Xcode**
49 | * Run the project.
50 |
51 | ## TODO
52 | * Add `HttpClient` logging. [https://github.com/ktorio/ktor/issues/722]()
53 | * Add common db Support.
54 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'kotlin-multiplatform' version '1.3.10'
3 | id 'kotlinx-serialization' version '1.3.0'
4 | }
5 | repositories {
6 | maven { url "https://kotlin.bintray.com/ktor" }
7 | maven { url "https://kotlin.bintray.com/kotlinx" }
8 | google()
9 | jcenter()
10 | mavenCentral()
11 | }
12 | apply plugin: 'com.android.application'
13 | apply plugin: 'kotlin-android-extensions'
14 |
15 | android {
16 | compileSdkVersion 28
17 | defaultConfig {
18 | applicationId "org.jetbrains.kotlin.mpp_app_android"
19 | minSdkVersion 15
20 | targetSdkVersion 28
21 | versionCode 1
22 | versionName "1.0"
23 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
24 | vectorDrawables.useSupportLibrary = true
25 | }
26 | buildTypes {
27 | release {
28 | minifyEnabled false
29 | }
30 | }
31 | }
32 |
33 | dependencies {
34 | implementation fileTree(dir: 'libs', include: ['*.jar'])
35 | implementation 'com.android.support:appcompat-v7:28.0.0'
36 | implementation 'com.android.support.constraint:constraint-layout:1.1.3'
37 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
38 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.1'
39 | implementation "io.ktor:ktor-client-android:1.0.0-beta-4"
40 | implementation 'com.jakewharton.timber:timber:4.7.1'
41 | implementation 'com.android.support:design:28.0.0'
42 | implementation 'de.hdodenhof:circleimageview:2.2.0'
43 | implementation 'com.github.bumptech.glide:glide:4.8.0'
44 | annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'
45 | }
46 |
47 | kotlin {
48 | targets {
49 | fromPreset(presets.android, 'android')
50 | // This preset is for iPhone emulator
51 | // Switch here to presets.iosArm64 (or iosArm32) to build library for iPhone device
52 | fromPreset(presets.iosX64, 'ios') {
53 | compilations.main.outputKinds('FRAMEWORK')
54 | }
55 | }
56 | sourceSets {
57 | commonMain {
58 | dependencies {
59 | implementation 'org.jetbrains.kotlin:kotlin-stdlib-common'
60 | implementation "io.ktor:ktor-client:1.0.0-beta-4"
61 | implementation "io.ktor:ktor-client-json:1.0.0-beta-4"
62 | implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:0.9.0"
63 | }
64 | }
65 | commonTest {
66 | dependencies {
67 | implementation 'org.jetbrains.kotlin:kotlin-test-common'
68 | implementation 'org.jetbrains.kotlin:kotlin-test-annotations-common'
69 | implementation "io.mockk:mockk-common:1.8.13.kotlin13"
70 | }
71 | }
72 | androidMain {
73 | dependencies {
74 | implementation 'org.jetbrains.kotlin:kotlin-stdlib'
75 | implementation "io.ktor:ktor-client-core-jvm:1.0.0-beta-4"
76 | implementation "io.ktor:ktor-client-json-jvm:1.0.0-beta-4"
77 | implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.9.0"
78 | }
79 | }
80 | androidTest {
81 | dependencies {
82 | implementation 'org.jetbrains.kotlin:kotlin-test'
83 | implementation 'org.jetbrains.kotlin:kotlin-test-junit'
84 | implementation "io.mockk:mockk:1.8.13.kotlin13"
85 | }
86 | }
87 | iosMain {
88 | dependencies {
89 | implementation "io.ktor:ktor-client-ios:1.0.0-beta-4"
90 | implementation "io.ktor:ktor-client-core-ios:1.0.0-beta-4"
91 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:1.0.1"
92 | implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:0.9.0"
93 | implementation "io.ktor:ktor-client-json-ios:1.0.0-beta-4"
94 | }
95 | }
96 | iosTest {
97 | }
98 | }
99 | }
100 |
101 | // This task attaches native framework built from ios module to Xcode project
102 | // (see iosApp directory). Don't run this task directly,
103 | // Xcode runs this task itself during its build process.
104 | // Before opening the project from iosApp directory in Xcode,
105 | // make sure all Gradle infrastructure exists (gradle.wrapper, gradlew).
106 | task copyFramework {
107 | def buildType = project.findProperty("kotlin.build.type") ?: "DEBUG"
108 | def target = project.findProperty("kotlin.target") ?: "ios"
109 | dependsOn "link${buildType.toLowerCase().capitalize()}Framework${target.capitalize()}"
110 |
111 | doLast {
112 | def srcFile = kotlin.targets."$target".compilations.main.getBinary("FRAMEWORK", buildType)
113 | def targetDir = getProperty("configuration.build.dir")
114 | copy {
115 | from srcFile.parent
116 | into targetDir
117 | include 'app.framework/**'
118 | include 'app.framework.dSYM'
119 | }
120 | }
121 | }
--------------------------------------------------------------------------------
/app/src/commonMain/kotlin/sample/AllData.kt:
--------------------------------------------------------------------------------
1 | package sample
2 |
3 | import kotlinx.serialization.*
4 |
5 | /**
6 | * Created by @iamBedant on 13/11/18.
7 | */
8 |
9 |
10 | @Serializable
11 | data class AllData(
12 | val avatar_url: String?,
13 | val bio: String?,
14 | val blog: String,
15 | val company: String?,
16 | val created_at: String,
17 | val email:String?,
18 | val events_url: String,
19 | val followers: Int,
20 | val followers_url: String,
21 | val following: Int,
22 | val following_url: String,
23 | val gists_url: String,
24 | val gravatar_id: String,
25 | val hireable: Boolean?,
26 | val html_url: String,
27 | val id: Int,
28 | val location: String?,
29 | val login: String,
30 | val name: String?,
31 | val node_id: String,
32 | val organizations_url: String,
33 | val public_gists: Int,
34 | val public_repos: Int,
35 | val received_events_url: String,
36 | val repos_url: String,
37 | val site_admin: Boolean,
38 | val starred_url: String,
39 | val subscriptions_url: String,
40 | val type: String,
41 | val updated_at: String,
42 | val url: String?
43 | )
44 |
45 | data class DisplayData(
46 | val name:String,
47 | val publicRepos:String,
48 | val publicGists:String,
49 | val bio: String,
50 | val avatarUrl :String
51 | )
--------------------------------------------------------------------------------
/app/src/commonMain/kotlin/sample/Const.kt:
--------------------------------------------------------------------------------
1 | package sample
2 |
3 | /**
4 | * Created by @iamBedant on 03/12/18.
5 | */
6 |
7 | val USER_NAME_NOT_VALID = "Please enter a valid user name"
8 | val GENERIC_ERROR_MESSAGE ="Something went wrong"
9 | val NO_BIO_AVAILABLE = "No Bio Available"
10 | val DEFAULT_AVATAR = "https://api.adorable.io/avatars/285/abott@adorable.png"
11 | val PUBLIC_REPOS = "Public Repos"
12 | val PUBLIC_GISTS ="Public Gists"
--------------------------------------------------------------------------------
/app/src/commonMain/kotlin/sample/IosUtilities.kt:
--------------------------------------------------------------------------------
1 | package sample
2 |
3 | /**
4 | * Created by @iamBedant on 04/12/18.
5 | */
6 |
7 | class IosUtilities {
8 |
9 | /**
10 | * A hack to provide dispetcher to iOS
11 | */
12 |
13 | fun getDispetcher() = getMainDispetcher()
14 | }
--------------------------------------------------------------------------------
/app/src/commonMain/kotlin/sample/Utils.kt:
--------------------------------------------------------------------------------
1 | package sample
2 |
3 | import kotlinx.coroutines.CoroutineDispatcher
4 |
5 | /**
6 | * Created by @iamBedant on 13/11/18.
7 | */
8 |
9 | expect object Log{
10 | fun d(message: String)
11 | fun e(error:Throwable)
12 | fun e(message: String)
13 | fun i(message: String)
14 | }
15 |
16 | expect fun getMainDispetcher(): CoroutineDispatcher
17 |
18 | expect fun runTest(block: suspend () -> T)
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/commonMain/kotlin/sample/api/Errors.kt:
--------------------------------------------------------------------------------
1 | package sample.api
2 |
3 | /**
4 | * Created by @iamBedant on 13/11/18.
5 | */
6 | class UpdateProblem : Throwable()
7 | class Unauthorized : Throwable()
8 | class CannotFavorite : Throwable()
9 |
--------------------------------------------------------------------------------
/app/src/commonMain/kotlin/sample/api/NetworkApi.kt:
--------------------------------------------------------------------------------
1 | package sample.api
2 |
3 | import io.ktor.client.HttpClient
4 | import io.ktor.client.features.ExpectSuccess
5 | import io.ktor.client.features.json.JsonFeature
6 | import io.ktor.client.features.json.serializer.KotlinxSerializer
7 | import io.ktor.client.request.HttpRequestBuilder
8 | import io.ktor.client.request.get
9 | import io.ktor.http.ContentType
10 | import io.ktor.http.URLProtocol
11 | import io.ktor.http.contentType
12 | import io.ktor.http.takeFrom
13 | import sample.AllData
14 |
15 | /**
16 | * Created by @iamBedant on 13/11/18.
17 | */
18 | class NetworkApi(private val endPoint: String) {
19 |
20 | private val httpClient = HttpClient {
21 | install(JsonFeature){
22 | serializer = KotlinxSerializer().apply {
23 | setMapper(AllData::class, AllData.serializer())
24 | }
25 | }
26 | install(ExpectSuccess)
27 | }
28 |
29 | suspend fun getAll(userId: String): AllData = httpClient.get {
30 | url {
31 | protocol = URLProtocol.HTTPS
32 | host = "api.github.com"
33 | encodedPath = "users/$userId"
34 | }
35 | }
36 |
37 |
38 | private fun HttpRequestBuilder.json() {
39 | contentType(ContentType.Application.Json)
40 | }
41 |
42 |
43 | private fun HttpRequestBuilder.apiUrl(path: String) {
44 | url {
45 | takeFrom(endPoint)
46 | encodedPath = path
47 | }
48 | }
49 | }
--------------------------------------------------------------------------------
/app/src/commonMain/kotlin/sample/launchAndCatch.kt:
--------------------------------------------------------------------------------
1 | package sample
2 |
3 | import kotlinx.coroutines.GlobalScope
4 | import kotlinx.coroutines.launch
5 | import kotlin.coroutines.CoroutineContext
6 |
7 | /**
8 | * Created by @iamBedant on 13/11/18.
9 | */
10 | fun launchAndCatch(
11 | context: CoroutineContext,
12 | onError: (String) -> Unit,
13 | function: suspend () -> Unit
14 | ): Finalizable {
15 | val ret = Finalizable()
16 | GlobalScope.launch(context) {
17 | try {
18 | function()
19 | } catch (e: Throwable) {
20 | onError(e.message?: GENERIC_ERROR_MESSAGE)
21 | } finally {
22 | ret.onFinally?.invoke()
23 | }
24 | }
25 |
26 | return ret
27 | }
28 |
29 | class Finalizable {
30 | var onFinally: (() -> Unit)? = null
31 |
32 | infix fun finally(f: () -> Unit) {
33 | onFinally = f
34 | }
35 | }
--------------------------------------------------------------------------------
/app/src/commonMain/kotlin/sample/model/DataRepositoryImpl.kt:
--------------------------------------------------------------------------------
1 | package sample.model
2 |
3 | import sample.AllData
4 | import sample.Log
5 | import sample.api.NetworkApi
6 | import sample.api.UpdateProblem
7 | import sample.presentation.DataRepository
8 |
9 | /**
10 | * Created by @iamBedant on 13/11/18.
11 | */
12 | class DataRepositoryImpl : DataRepository {
13 |
14 | private val api = NetworkApi("https://github.com")
15 | override var data: AllData? = null
16 |
17 | override suspend fun getData(username: String) {
18 |
19 | val data = try {
20 | api.getAll(username)
21 | } catch (cause: Throwable) {
22 | Log.e(cause)
23 |
24 | throw UpdateProblem()
25 | }
26 |
27 | this.data = data
28 |
29 | }
30 |
31 | override suspend fun update() {
32 | }
33 |
34 |
35 | }
--------------------------------------------------------------------------------
/app/src/commonMain/kotlin/sample/presentation/BaseView.kt:
--------------------------------------------------------------------------------
1 | package sample.presentation
2 |
3 | /**
4 | * Created by @iamBedant on 13/11/18.
5 | */
6 | interface BaseView {
7 | fun showError(error: String)
8 | }
--------------------------------------------------------------------------------
/app/src/commonMain/kotlin/sample/presentation/DataRepository.kt:
--------------------------------------------------------------------------------
1 | package sample.presentation
2 |
3 | import sample.AllData
4 |
5 | /**
6 | * Created by @iamBedant on 13/11/18.
7 | */
8 | interface DataRepository{
9 | val data : AllData?
10 | suspend fun getData(username:String)
11 | suspend fun update()
12 | }
--------------------------------------------------------------------------------
/app/src/commonMain/kotlin/sample/presentation/MainPresenter.kt:
--------------------------------------------------------------------------------
1 | package sample.presentation
2 |
3 | import sample.*
4 | import kotlin.coroutines.CoroutineContext
5 |
6 | /**
7 | * Created by @iamBedant on 13/11/18.
8 | */
9 | class MainPresenter(private val view: MainView,
10 | private val repository: DataRepository,
11 | private val uiContext: CoroutineContext = getMainDispetcher()) {
12 |
13 | fun loadData(userName: String) {
14 | if (userName.isNullOrEmpty()) {
15 | view.showError(USER_NAME_NOT_VALID)
16 | } else {
17 | view.showLoader()
18 | launchAndCatch(uiContext, view::showError) {
19 | repository.getData(userName)
20 | showData()
21 | } finally {
22 | view.hideLoader()
23 | }
24 | }
25 | }
26 |
27 | private fun showData() {
28 | repository.data?.let {
29 | view.displayData(getDisplayData(it))
30 | }
31 | }
32 |
33 | /**
34 | * Made this function internal to make it accessible while writing tests.
35 | */
36 |
37 | internal fun getDisplayData(allData : AllData) = DisplayData(
38 | name = allData.name ?: allData.login,
39 | publicGists = "${allData.public_gists} $PUBLIC_GISTS",
40 | publicRepos = "${allData.public_repos} $PUBLIC_REPOS",
41 | avatarUrl = allData.avatar_url ?: DEFAULT_AVATAR,
42 | bio = allData.bio ?: NO_BIO_AVAILABLE
43 | )
44 | }
--------------------------------------------------------------------------------
/app/src/commonMain/kotlin/sample/presentation/MainView.kt:
--------------------------------------------------------------------------------
1 | package sample.presentation
2 | import sample.DisplayData
3 |
4 | /**
5 | * Created by @iamBedant on 13/11/18.
6 | */
7 | interface MainView : BaseView{
8 | fun displayData(data: DisplayData)
9 | fun showLoader()
10 | fun hideLoader()
11 | }
--------------------------------------------------------------------------------
/app/src/commonTest/kotlin/sample/TestHelper.kt:
--------------------------------------------------------------------------------
1 | package sample
2 |
3 | /**
4 | * Created by @iamBedant on 04/12/18.
5 | */
6 |
7 | val allData = AllData(
8 | name = "TestName",
9 | avatar_url = "",
10 | bio = "this is a test bio",
11 | blog = "",
12 | company = "",
13 | created_at = "",
14 | email = "",
15 | events_url = "",
16 | followers = 4,
17 | followers_url = "",
18 | following = 5,
19 | following_url = "",
20 | gists_url = "",
21 | gravatar_id = "",
22 | hireable = true,
23 | html_url = "",
24 | id = 8,
25 | location = "",
26 | login = "",
27 | node_id = "",
28 | organizations_url = "",
29 | public_gists = 5,
30 | public_repos = 6,
31 | received_events_url = "",
32 | repos_url = "",
33 | site_admin = false,
34 | starred_url = "",
35 | subscriptions_url = "",
36 | type = "",
37 | updated_at = "",
38 | url = ""
39 | )
--------------------------------------------------------------------------------
/app/src/commonTest/kotlin/sample/model/DataRepositoryTest.kt:
--------------------------------------------------------------------------------
1 | package sample.model
2 |
3 | /**
4 | * Created by @iamBedant on 04/12/18.
5 | */
6 | class DataRepositoryTest {
7 | }
--------------------------------------------------------------------------------
/app/src/commonTest/kotlin/sample/presentation/MainPresenterTest.kt:
--------------------------------------------------------------------------------
1 | package sample.presentation
2 |
3 | import io.mockk.*
4 | import kotlinx.coroutines.Dispatchers
5 | import sample.*
6 | import kotlin.test.*
7 |
8 | /**
9 | * Created by @iamBedant on 03/12/18.
10 | */
11 | @Ignore
12 | open class MainPresenterTest {
13 |
14 | lateinit var view: MainView
15 | lateinit var repository: DataRepository
16 | lateinit var presenter: MainPresenter
17 |
18 |
19 | @BeforeTest
20 | fun initDeps() {
21 |
22 | view = mockk(relaxed = true)
23 | repository = mockk(relaxed = true)
24 |
25 | presenter = MainPresenter(
26 | view = view,
27 | repository = repository,
28 | uiContext = Dispatchers.Default
29 | )
30 | }
31 |
32 | @Test
33 | fun `when load data is empty, it shows an error message`() {
34 |
35 | presenter.loadData("")
36 | verify {
37 | view.showError(USER_NAME_NOT_VALID)
38 |
39 | }
40 | }
41 |
42 | @Test
43 | fun `shows loader while calling an API`() {
44 |
45 | presenter.loadData("iamBedant")
46 | verify {
47 | view.showLoader()
48 | }
49 | }
50 |
51 | @Test
52 | fun `calls getData and displays result if call is successfull`() = runTest {
53 |
54 | every { repository.data } returns allData
55 | presenter.loadData("iamBedant")
56 | coVerify {
57 | repository.getData("iamBedant")
58 | }
59 | verify(ordering = Ordering.ORDERED) {
60 | view.showLoader()
61 | view.hideLoader()
62 | }
63 | }
64 |
65 |
66 | @Test
67 | fun `if user name is valid show actual name`() {
68 | val displayData = presenter.getDisplayData(allData.copy("Test Name"))
69 | assertEquals(displayData.name, allData.name)
70 | }
71 |
72 | @Test
73 | fun `if user name is empty or null show login id as name`() {
74 | val displayData = presenter.getDisplayData(allData.copy(name = null))
75 | assertEquals(displayData.name, allData.login)
76 | }
77 |
78 | @Test
79 | fun `if user bio is valid show bio`() {
80 | val displayData = presenter.getDisplayData(allData.copy(bio = "This is test Bio"))
81 | assertEquals(displayData.bio, "This is test Bio")
82 | }
83 |
84 | @Test
85 | fun `if user bio is empty or null show no bio available`() {
86 | val displayData = presenter.getDisplayData(allData.copy(bio = null))
87 | assertEquals(displayData.bio, NO_BIO_AVAILABLE)
88 | }
89 |
90 | @Test
91 | fun `if user avatar is valid show avatar`() {
92 | val displayData = presenter.getDisplayData(allData.copy(avatar_url = "https://abc.def"))
93 | assertEquals(displayData.avatarUrl, "https://abc.def")
94 | }
95 |
96 |
97 | @Test
98 | fun `if user avatar is empty or null show default avatar`() {
99 | val displayData = presenter.getDisplayData(allData.copy(avatar_url = null))
100 | assertEquals(displayData.avatarUrl, DEFAULT_AVATAR)
101 | }
102 |
103 | @Test
104 | fun `shows gists and repos in proper format`() {
105 | val displayData = presenter.getDisplayData(allData.copy(public_gists = 4, public_repos = 5))
106 | assertEquals(displayData.publicGists, "4 $PUBLIC_GISTS")
107 | assertEquals(displayData.publicRepos, "5 $PUBLIC_REPOS")
108 | }
109 |
110 | @AfterTest
111 | fun tearDown() {
112 |
113 | }
114 | }
--------------------------------------------------------------------------------
/app/src/iosMain/kotlin/sample/UtilsIos.kt:
--------------------------------------------------------------------------------
1 | package sample
2 |
3 | import kotlinx.coroutines.CoroutineDispatcher
4 | import kotlinx.coroutines.Runnable
5 | import kotlinx.coroutines.runBlocking
6 | import platform.Foundation.NSRunLoop
7 | import platform.Foundation.performBlock
8 | import kotlin.coroutines.CoroutineContext
9 |
10 | /**
11 | * Created by @iamBedant on 13/11/18.
12 | */
13 | actual object Log {
14 | actual fun d(message: String) {
15 | print(message)
16 | }
17 | actual fun e(message: String) {
18 | print(message)
19 | }
20 |
21 | actual fun i(message: String) {
22 | print(message)
23 | }
24 |
25 | actual fun e(error: Throwable) {
26 | print(error)
27 | }
28 | }
29 |
30 | object MainLoopDispatcher: CoroutineDispatcher() {
31 | override fun dispatch(context: CoroutineContext, block: Runnable) {
32 | NSRunLoop.mainRunLoop().performBlock {
33 | block.run()
34 | }
35 | }
36 | }
37 |
38 | actual fun getMainDispetcher(): CoroutineDispatcher {
39 | return MainLoopDispatcher
40 | }
41 |
42 | actual fun runTest(block: suspend () -> T) {
43 | runBlocking { block() }
44 | }
--------------------------------------------------------------------------------
/app/src/iosTest/kotlin/sample/SampleTestsIOS.kt:
--------------------------------------------------------------------------------
1 | package sample
2 |
3 | import kotlin.test.Test
4 | import kotlin.test.assertTrue
5 |
6 | class SampleTestsIOS {
7 | @Test
8 | fun testHello() {
9 |
10 | }
11 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/java/sample/Extensions.kt:
--------------------------------------------------------------------------------
1 | package sample
2 |
3 | import android.view.View
4 |
5 | /**
6 | * Created by @iamBedant on 20/11/18.
7 | */
8 |
9 | fun View.show(){
10 | this.visibility = View.VISIBLE
11 | }
12 |
13 | fun View.hide(){
14 | this.visibility = View.GONE
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/sample/KmpApp.kt:
--------------------------------------------------------------------------------
1 | package sample
2 |
3 | import android.app.Application
4 | import timber.log.Timber
5 | import timber.log.Timber.plant
6 |
7 | /**
8 | * Created by @iamBedant on 13/11/18.
9 | */
10 |
11 | class KmpApp : Application() {
12 |
13 | override fun onCreate() {
14 | super.onCreate()
15 | if (BuildConfig.DEBUG) {
16 | Timber.plant(Timber.DebugTree())
17 | } else {
18 | // Timber.plant(CrashReportingTree())
19 |
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/sample/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package sample
2 |
3 | import android.support.v7.app.AppCompatActivity
4 | import android.os.Bundle
5 | import android.widget.TextView
6 | import android.widget.Toast
7 | import com.bumptech.glide.Glide
8 | import kotlinx.android.synthetic.main.activity_main.*
9 | import kotlinx.coroutines.Dispatchers
10 | import sample.R.id.*
11 | import sample.model.DataRepositoryImpl
12 | import sample.presentation.MainPresenter
13 | import sample.presentation.MainView
14 | import timber.log.Timber
15 | import kotlin.properties.Delegates
16 |
17 | class MainActivity : AppCompatActivity(), MainView {
18 |
19 | override fun showLoader() {
20 | pb.show()
21 | tvBio.hide()
22 | tvName.hide()
23 | tvGists.hide()
24 | tvRepos.hide()
25 | ivAvatar.hide()
26 | }
27 |
28 | override fun hideLoader() {
29 | pb.hide()
30 | tvBio.show()
31 | tvName.show()
32 | tvGists.show()
33 | tvRepos.show()
34 | ivAvatar.show()
35 | }
36 |
37 | override fun displayData(data: DisplayData) {
38 | with(data) {
39 | tvName.text = name
40 | Glide.with(this@MainActivity).load(avatarUrl).into(ivAvatar)
41 | tvRepos.text = publicRepos
42 | tvGists.text = publicGists
43 | tvBio.text = bio
44 | }
45 | }
46 |
47 | override fun showError(error: String) {
48 | error.let {
49 | Toast.makeText(this, error, Toast.LENGTH_LONG).show()
50 | }
51 | }
52 |
53 | private val presenter by lazy { MainPresenter(this, DataRepositoryImpl()) }
54 |
55 | override fun onCreate(savedInstanceState: Bundle?) {
56 | super.onCreate(savedInstanceState)
57 | setContentView(R.layout.activity_main)
58 | fabGo.setOnClickListener {
59 | presenter.loadData(etUserName.text.toString())
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/app/src/main/java/sample/UtilsAndroid.kt:
--------------------------------------------------------------------------------
1 | package sample
2 |
3 | import kotlinx.coroutines.CoroutineDispatcher
4 | import kotlinx.coroutines.Dispatchers
5 | import kotlinx.coroutines.runBlocking
6 | import timber.log.Timber
7 |
8 | /**
9 | * Created by @iamBedant on 13/11/18.
10 | */
11 | actual object Log{
12 | actual fun d(message: String) {
13 | Timber.d(message)
14 | }
15 |
16 | actual fun e(message: String) {
17 | Timber.e(message)
18 | }
19 |
20 | actual fun i(message: String) {
21 | Timber.i(message)
22 | }
23 |
24 | actual fun e(error: Throwable) {
25 | Timber.e(error)
26 | }
27 | }
28 |
29 | actual fun getMainDispetcher(): CoroutineDispatcher {
30 | return Dispatchers.Main
31 | }
32 |
33 | actual fun runTest(block: suspend () -> T) {
34 | runBlocking { block() }
35 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_arrow_forward_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/font/roboto_mono.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/font/roboto_mono_light.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
33 |
44 |
45 |
56 |
64 |
75 |
87 |
96 |
--------------------------------------------------------------------------------
/app/src/main/res/values/font_certs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - @array/com_google_android_gms_fonts_certs_dev
5 | - @array/com_google_android_gms_fonts_certs_prod
6 |
7 |
8 | -
9 | MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
10 |
11 |
12 |
13 | -
14 | MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/values/preloaded_fonts.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - @font/roboto_mono
5 | - @font/roboto_mono_light
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | android-app
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
--------------------------------------------------------------------------------
/app/src/test/java/sample/MainPresenterTestHelper.kt:
--------------------------------------------------------------------------------
1 | package sample
2 |
3 | import org.junit.runner.RunWith
4 | import org.junit.runners.JUnit4
5 | import sample.presentation.MainPresenterTest
6 |
7 | /**
8 | * Created by @iamBedant on 03/12/18.
9 | */
10 |
11 |
12 | @RunWith(JUnit4::class)
13 | class MainPresenterTestHelper : MainPresenterTest()
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | jcenter()
5 | maven { url "https://kotlin.bintray.com/kotlinx" }
6 | maven { url 'https://dl.bintray.com/jetbrains/kotlin-native-dependencies' }
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.2.1'
10 |
11 |
12 | }
13 | }
14 | repositories {
15 | google()
16 | jcenter()
17 | }
18 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | kotlin.code.style=official
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iamBedant/Multiplatform/9e0f3f80cd6cdb1c22e53bd15188bb5836a4c112/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Nov 13 15:19:23 IST 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/iosApp/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | .DS_Store
6 |
7 | ## Build generated
8 | build/
9 | DerivedData/
10 |
11 | ## Various settings
12 | *.pbxuser
13 | !default.pbxuser
14 | *.mode1v3
15 | !default.mode1v3
16 | *.mode2v3
17 | !default.mode2v3
18 | *.perspectivev3
19 | !default.perspectivev3
20 | xcuserdata
21 | xcuserdata/
22 | xcshareddata
23 |
24 | ## Workspace file
25 |
26 | *.xcworkspace/
27 |
28 | ## Other
29 | *.moved-aside
30 | *.xccheckout
31 | *.xcuserstate
32 | *.xcscmblueprint
33 |
34 | ## Obj-C/Swift specific
35 | *.hmap
36 | *.ipa
37 | *.dSYM.zip
38 | *.dSYM
39 | *.app.dSYM.zip
40 |
41 | ## Playgrounds
42 | timeline.xctimeline
43 | playground.xcworkspace
44 |
45 | # Swift Package Manager
46 | #
47 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
48 | # Packages/
49 | # Package.pins
50 | # Package.resolved
51 | .build/
52 |
53 | # CocoaPods
54 | #
55 | # We recommend against adding the Pods directory to your .gitignore. However
56 | # you should judge for yourself, the pros and cons are mentioned at:
57 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
58 | #
59 | Pods/
60 |
61 | # Carthage
62 | #
63 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
64 | # Carthage/Checkouts
65 |
66 | Carthage/Build
67 |
68 | # fastlane
69 | #
70 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
71 | # screenshots whenever they are needed.
72 | # For more information about the recommended setup visit:
73 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
74 |
75 | fastlane/report.xml
76 | fastlane/Preview.html
77 | fastlane/screenshots
78 | fastlane/test_output
79 |
80 |
81 | # Ignore config files
82 | */Configs/*.config
83 |
84 | # FBSnapshot failures
85 | Screenshots/FailureDiffs/*
86 |
87 | Frameworks/OpenTok/*
88 | Frameworks/native-secrets
89 |
90 | fastlane/README\.md
91 |
--------------------------------------------------------------------------------
/iosApp/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment the next line to define a global platform for your project
2 | # platform :ios, '9.0'
3 |
4 | target 'app' do
5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
6 | use_frameworks!
7 |
8 | # Pods for app
9 |
10 | end
11 |
12 | target 'iosApp' do
13 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
14 | use_frameworks!
15 |
16 | # Pods for iosApp
17 | pod 'Kingfisher', '~> 4.0'
18 |
19 | target 'iosAppTests' do
20 | inherit! :search_paths
21 | # Pods for testing
22 | end
23 |
24 | end
25 |
--------------------------------------------------------------------------------
/iosApp/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Kingfisher (4.10.1)
3 |
4 | DEPENDENCIES:
5 | - Kingfisher (~> 4.0)
6 |
7 | SPEC REPOS:
8 | https://github.com/cocoapods/specs.git:
9 | - Kingfisher
10 |
11 | SPEC CHECKSUMS:
12 | Kingfisher: c148cd7b47ebde9989f6bc7c27dcaa79d81279a0
13 |
14 | PODFILE CHECKSUM: 937c671bb9fb879bb29f3ea267838fbeea7e97fa
15 |
16 | COCOAPODS: 1.5.3
17 |
--------------------------------------------------------------------------------
/iosApp/iosApp.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 056D4C7A0EA112486654F989 /* Pods_iosAppTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CF48D3164D7A87010C4407DD /* Pods_iosAppTests.framework */; };
11 | 31F5D6EE219C7A9500DAB828 /* BaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31F5D6ED219C7A9500DAB828 /* BaseView.swift */; };
12 | 31F5D6F2219C7DCB00DAB828 /* UI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31F5D6F1219C7DCB00DAB828 /* UI.swift */; };
13 | 57F2888208B470E36A85AE20 /* Pods_app.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E6F9EA427F3F2FCCCB6841EC /* Pods_app.framework */; };
14 | A4E623C1BE8D50B4CB7BE815 /* Pods_iosApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E44AF6F7829FBE3862CAAAB /* Pods_iosApp.framework */; };
15 | CA5CDE8C21B4699400A21C24 /* UIViewController+TextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA5CDE8B21B4699400A21C24 /* UIViewController+TextField.swift */; };
16 | CA5CDE8F21B483DD00A21C24 /* UILayer+Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA5CDE8E21B483DD00A21C24 /* UILayer+Color.swift */; };
17 | CA5CDE9121B4856600A21C24 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CA5CDE9021B4856600A21C24 /* Assets.xcassets */; };
18 | CA5CDE9421B48AEC00A21C24 /* RobotoMono-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = CA5CDE9321B48AEC00A21C24 /* RobotoMono-Light.ttf */; };
19 | CA5CDE9621B48B7100A21C24 /* RobotoMono-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = CA5CDE9521B48B7100A21C24 /* RobotoMono-Regular.ttf */; };
20 | F861D7E1207FA40F0085E80D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F861D7E0207FA40F0085E80D /* AppDelegate.swift */; };
21 | F861D7E3207FA40F0085E80D /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F861D7E2207FA40F0085E80D /* ViewController.swift */; };
22 | F861D7E6207FA40F0085E80D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F861D7E4207FA40F0085E80D /* Main.storyboard */; };
23 | F861D7EB207FA4100085E80D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F861D7E9207FA4100085E80D /* LaunchScreen.storyboard */; };
24 | F861D7F6207FA4100085E80D /* iosAppTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F861D7F5207FA4100085E80D /* iosAppTests.swift */; };
25 | F861D80C207FA4200085E80D /* app.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F861D805207FA4200085E80D /* app.framework */; };
26 | F861D80D207FA4200085E80D /* app.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F861D805207FA4200085E80D /* app.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
27 | /* End PBXBuildFile section */
28 |
29 | /* Begin PBXContainerItemProxy section */
30 | F861D7F2207FA4100085E80D /* PBXContainerItemProxy */ = {
31 | isa = PBXContainerItemProxy;
32 | containerPortal = F861D7D5207FA40F0085E80D /* Project object */;
33 | proxyType = 1;
34 | remoteGlobalIDString = F861D7DC207FA40F0085E80D;
35 | remoteInfo = iosApp;
36 | };
37 | F861D80A207FA4200085E80D /* PBXContainerItemProxy */ = {
38 | isa = PBXContainerItemProxy;
39 | containerPortal = F861D7D5207FA40F0085E80D /* Project object */;
40 | proxyType = 1;
41 | remoteGlobalIDString = F861D804207FA4200085E80D;
42 | remoteInfo = app;
43 | };
44 | /* End PBXContainerItemProxy section */
45 |
46 | /* Begin PBXCopyFilesBuildPhase section */
47 | F861D811207FA4200085E80D /* Embed Frameworks */ = {
48 | isa = PBXCopyFilesBuildPhase;
49 | buildActionMask = 2147483647;
50 | dstPath = "";
51 | dstSubfolderSpec = 10;
52 | files = (
53 | F861D80D207FA4200085E80D /* app.framework in Embed Frameworks */,
54 | );
55 | name = "Embed Frameworks";
56 | runOnlyForDeploymentPostprocessing = 0;
57 | };
58 | /* End PBXCopyFilesBuildPhase section */
59 |
60 | /* Begin PBXFileReference section */
61 | 31F5D6ED219C7A9500DAB828 /* BaseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseView.swift; sourceTree = ""; };
62 | 31F5D6F1219C7DCB00DAB828 /* UI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UI.swift; sourceTree = ""; };
63 | 4E44AF6F7829FBE3862CAAAB /* Pods_iosApp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_iosApp.framework; sourceTree = BUILT_PRODUCTS_DIR; };
64 | 5E569EE4FA3592292991A393 /* Pods-iosApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iosApp.debug.xcconfig"; path = "Pods/Target Support Files/Pods-iosApp/Pods-iosApp.debug.xcconfig"; sourceTree = ""; };
65 | 76E6BD9963CC1676192496A6 /* Pods-iosAppTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iosAppTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-iosAppTests/Pods-iosAppTests.release.xcconfig"; sourceTree = ""; };
66 | 76F8EAB9A3C1708123D5A635 /* Pods-iosAppTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iosAppTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-iosAppTests/Pods-iosAppTests.debug.xcconfig"; sourceTree = ""; };
67 | A777AD0801F2C6E78C4A00B7 /* Pods-app.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app.debug.xcconfig"; path = "Pods/Target Support Files/Pods-app/Pods-app.debug.xcconfig"; sourceTree = ""; };
68 | AB20C31AD1A8F977AD01F195 /* Pods-app.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-app.release.xcconfig"; path = "Pods/Target Support Files/Pods-app/Pods-app.release.xcconfig"; sourceTree = ""; };
69 | BA6CC8EC1727D396EAEB9793 /* Pods-iosApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iosApp.release.xcconfig"; path = "Pods/Target Support Files/Pods-iosApp/Pods-iosApp.release.xcconfig"; sourceTree = ""; };
70 | CA5CDE8B21B4699400A21C24 /* UIViewController+TextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+TextField.swift"; sourceTree = ""; };
71 | CA5CDE8E21B483DD00A21C24 /* UILayer+Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILayer+Color.swift"; sourceTree = ""; };
72 | CA5CDE9021B4856600A21C24 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
73 | CA5CDE9321B48AEC00A21C24 /* RobotoMono-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "RobotoMono-Light.ttf"; sourceTree = ""; };
74 | CA5CDE9521B48B7100A21C24 /* RobotoMono-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "RobotoMono-Regular.ttf"; sourceTree = ""; };
75 | CF48D3164D7A87010C4407DD /* Pods_iosAppTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_iosAppTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
76 | E6F9EA427F3F2FCCCB6841EC /* Pods_app.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_app.framework; sourceTree = BUILT_PRODUCTS_DIR; };
77 | F861D7DD207FA40F0085E80D /* iosApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iosApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
78 | F861D7E0207FA40F0085E80D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
79 | F861D7E2207FA40F0085E80D /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
80 | F861D7E5207FA40F0085E80D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
81 | F861D7EA207FA4100085E80D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
82 | F861D7EC207FA4100085E80D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
83 | F861D7F1207FA4100085E80D /* iosAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = iosAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
84 | F861D7F5207FA4100085E80D /* iosAppTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iosAppTests.swift; sourceTree = ""; };
85 | F861D7F7207FA4100085E80D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
86 | F861D805207FA4200085E80D /* app.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = app.framework; sourceTree = BUILT_PRODUCTS_DIR; };
87 | F861D813207FA4520085E80D /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; name = app; path = ../app; sourceTree = ""; };
88 | /* End PBXFileReference section */
89 |
90 | /* Begin PBXFrameworksBuildPhase section */
91 | 5F6E3A7C66A5CE2EA6ECB580 /* Frameworks */ = {
92 | isa = PBXFrameworksBuildPhase;
93 | buildActionMask = 2147483647;
94 | files = (
95 | 57F2888208B470E36A85AE20 /* Pods_app.framework in Frameworks */,
96 | );
97 | runOnlyForDeploymentPostprocessing = 0;
98 | };
99 | F861D7DA207FA40F0085E80D /* Frameworks */ = {
100 | isa = PBXFrameworksBuildPhase;
101 | buildActionMask = 2147483647;
102 | files = (
103 | F861D80C207FA4200085E80D /* app.framework in Frameworks */,
104 | A4E623C1BE8D50B4CB7BE815 /* Pods_iosApp.framework in Frameworks */,
105 | );
106 | runOnlyForDeploymentPostprocessing = 0;
107 | };
108 | F861D7EE207FA4100085E80D /* Frameworks */ = {
109 | isa = PBXFrameworksBuildPhase;
110 | buildActionMask = 2147483647;
111 | files = (
112 | 056D4C7A0EA112486654F989 /* Pods_iosAppTests.framework in Frameworks */,
113 | );
114 | runOnlyForDeploymentPostprocessing = 0;
115 | };
116 | /* End PBXFrameworksBuildPhase section */
117 |
118 | /* Begin PBXGroup section */
119 | 04AB7158CC5FA048FBC61189 /* Pods */ = {
120 | isa = PBXGroup;
121 | children = (
122 | A777AD0801F2C6E78C4A00B7 /* Pods-app.debug.xcconfig */,
123 | AB20C31AD1A8F977AD01F195 /* Pods-app.release.xcconfig */,
124 | 5E569EE4FA3592292991A393 /* Pods-iosApp.debug.xcconfig */,
125 | BA6CC8EC1727D396EAEB9793 /* Pods-iosApp.release.xcconfig */,
126 | 76F8EAB9A3C1708123D5A635 /* Pods-iosAppTests.debug.xcconfig */,
127 | 76E6BD9963CC1676192496A6 /* Pods-iosAppTests.release.xcconfig */,
128 | );
129 | name = Pods;
130 | sourceTree = "";
131 | };
132 | CA5CDE8921B4692500A21C24 /* Extensions */ = {
133 | isa = PBXGroup;
134 | children = (
135 | CA5CDE8D21B4838D00A21C24 /* UILayer */,
136 | CA5CDE8A21B4693B00A21C24 /* UIViewController */,
137 | );
138 | path = Extensions;
139 | sourceTree = "";
140 | };
141 | CA5CDE8A21B4693B00A21C24 /* UIViewController */ = {
142 | isa = PBXGroup;
143 | children = (
144 | CA5CDE8B21B4699400A21C24 /* UIViewController+TextField.swift */,
145 | );
146 | path = UIViewController;
147 | sourceTree = "";
148 | };
149 | CA5CDE8D21B4838D00A21C24 /* UILayer */ = {
150 | isa = PBXGroup;
151 | children = (
152 | CA5CDE8E21B483DD00A21C24 /* UILayer+Color.swift */,
153 | );
154 | path = UILayer;
155 | sourceTree = "";
156 | };
157 | CA5CDE9221B48AC700A21C24 /* Fonts */ = {
158 | isa = PBXGroup;
159 | children = (
160 | CA5CDE9321B48AEC00A21C24 /* RobotoMono-Light.ttf */,
161 | CA5CDE9521B48B7100A21C24 /* RobotoMono-Regular.ttf */,
162 | );
163 | path = Fonts;
164 | sourceTree = "";
165 | };
166 | F861D7D4207FA40F0085E80D = {
167 | isa = PBXGroup;
168 | children = (
169 | F861D813207FA4520085E80D /* app */,
170 | F861D7DF207FA40F0085E80D /* iosApp */,
171 | F861D7F4207FA4100085E80D /* iosAppTests */,
172 | F861D7DE207FA40F0085E80D /* Products */,
173 | 04AB7158CC5FA048FBC61189 /* Pods */,
174 | FAD6F5D46EEAB3109315D5E4 /* Frameworks */,
175 | );
176 | sourceTree = "";
177 | };
178 | F861D7DE207FA40F0085E80D /* Products */ = {
179 | isa = PBXGroup;
180 | children = (
181 | F861D7DD207FA40F0085E80D /* iosApp.app */,
182 | F861D7F1207FA4100085E80D /* iosAppTests.xctest */,
183 | F861D805207FA4200085E80D /* app.framework */,
184 | );
185 | name = Products;
186 | sourceTree = "";
187 | };
188 | F861D7DF207FA40F0085E80D /* iosApp */ = {
189 | isa = PBXGroup;
190 | children = (
191 | CA5CDE9221B48AC700A21C24 /* Fonts */,
192 | CA5CDE8921B4692500A21C24 /* Extensions */,
193 | F861D7E0207FA40F0085E80D /* AppDelegate.swift */,
194 | F861D7E2207FA40F0085E80D /* ViewController.swift */,
195 | F861D7E4207FA40F0085E80D /* Main.storyboard */,
196 | F861D7E9207FA4100085E80D /* LaunchScreen.storyboard */,
197 | F861D7EC207FA4100085E80D /* Info.plist */,
198 | 31F5D6ED219C7A9500DAB828 /* BaseView.swift */,
199 | 31F5D6F1219C7DCB00DAB828 /* UI.swift */,
200 | CA5CDE9021B4856600A21C24 /* Assets.xcassets */,
201 | );
202 | path = iosApp;
203 | sourceTree = "";
204 | };
205 | F861D7F4207FA4100085E80D /* iosAppTests */ = {
206 | isa = PBXGroup;
207 | children = (
208 | F861D7F5207FA4100085E80D /* iosAppTests.swift */,
209 | F861D7F7207FA4100085E80D /* Info.plist */,
210 | );
211 | path = iosAppTests;
212 | sourceTree = "";
213 | };
214 | FAD6F5D46EEAB3109315D5E4 /* Frameworks */ = {
215 | isa = PBXGroup;
216 | children = (
217 | E6F9EA427F3F2FCCCB6841EC /* Pods_app.framework */,
218 | 4E44AF6F7829FBE3862CAAAB /* Pods_iosApp.framework */,
219 | CF48D3164D7A87010C4407DD /* Pods_iosAppTests.framework */,
220 | );
221 | name = Frameworks;
222 | sourceTree = "";
223 | };
224 | /* End PBXGroup section */
225 |
226 | /* Begin PBXNativeTarget section */
227 | F861D7DC207FA40F0085E80D /* iosApp */ = {
228 | isa = PBXNativeTarget;
229 | buildConfigurationList = F861D7FA207FA4100085E80D /* Build configuration list for PBXNativeTarget "iosApp" */;
230 | buildPhases = (
231 | 7AA7AED70A02F86B0B96E3BC /* [CP] Check Pods Manifest.lock */,
232 | F861D7D9207FA40F0085E80D /* Sources */,
233 | F861D7DA207FA40F0085E80D /* Frameworks */,
234 | F861D7DB207FA40F0085E80D /* Resources */,
235 | F861D811207FA4200085E80D /* Embed Frameworks */,
236 | EE58EF33EB79FE2752690D85 /* [CP] Embed Pods Frameworks */,
237 | );
238 | buildRules = (
239 | );
240 | dependencies = (
241 | F861D80B207FA4200085E80D /* PBXTargetDependency */,
242 | );
243 | name = iosApp;
244 | productName = iosApp;
245 | productReference = F861D7DD207FA40F0085E80D /* iosApp.app */;
246 | productType = "com.apple.product-type.application";
247 | };
248 | F861D7F0207FA4100085E80D /* iosAppTests */ = {
249 | isa = PBXNativeTarget;
250 | buildConfigurationList = F861D7FD207FA4100085E80D /* Build configuration list for PBXNativeTarget "iosAppTests" */;
251 | buildPhases = (
252 | 07FA7A6C9347896C05367E4E /* [CP] Check Pods Manifest.lock */,
253 | F861D7ED207FA4100085E80D /* Sources */,
254 | F861D7EE207FA4100085E80D /* Frameworks */,
255 | F861D7EF207FA4100085E80D /* Resources */,
256 | );
257 | buildRules = (
258 | );
259 | dependencies = (
260 | F861D7F3207FA4100085E80D /* PBXTargetDependency */,
261 | );
262 | name = iosAppTests;
263 | productName = iosAppTests;
264 | productReference = F861D7F1207FA4100085E80D /* iosAppTests.xctest */;
265 | productType = "com.apple.product-type.bundle.unit-test";
266 | };
267 | F861D804207FA4200085E80D /* app */ = {
268 | isa = PBXNativeTarget;
269 | buildConfigurationList = F861D80E207FA4200085E80D /* Build configuration list for PBXNativeTarget "app" */;
270 | buildPhases = (
271 | 2FB6616C86CFCED6EAEAB152 /* [CP] Check Pods Manifest.lock */,
272 | F861D812207FA4320085E80D /* ShellScript */,
273 | 5F6E3A7C66A5CE2EA6ECB580 /* Frameworks */,
274 | );
275 | buildRules = (
276 | );
277 | dependencies = (
278 | );
279 | name = app;
280 | productName = app;
281 | productReference = F861D805207FA4200085E80D /* app.framework */;
282 | productType = "com.apple.product-type.framework";
283 | };
284 | /* End PBXNativeTarget section */
285 |
286 | /* Begin PBXProject section */
287 | F861D7D5207FA40F0085E80D /* Project object */ = {
288 | isa = PBXProject;
289 | attributes = {
290 | LastSwiftUpdateCheck = 0930;
291 | LastUpgradeCheck = 0930;
292 | TargetAttributes = {
293 | F861D7DC207FA40F0085E80D = {
294 | CreatedOnToolsVersion = 9.3;
295 | };
296 | F861D7F0207FA4100085E80D = {
297 | CreatedOnToolsVersion = 9.3;
298 | TestTargetID = F861D7DC207FA40F0085E80D;
299 | };
300 | F861D804207FA4200085E80D = {
301 | CreatedOnToolsVersion = 9.3;
302 | };
303 | };
304 | };
305 | buildConfigurationList = F861D7D8207FA40F0085E80D /* Build configuration list for PBXProject "iosApp" */;
306 | compatibilityVersion = "Xcode 9.3";
307 | developmentRegion = en;
308 | hasScannedForEncodings = 0;
309 | knownRegions = (
310 | en,
311 | Base,
312 | );
313 | mainGroup = F861D7D4207FA40F0085E80D;
314 | productRefGroup = F861D7DE207FA40F0085E80D /* Products */;
315 | projectDirPath = "";
316 | projectRoot = "";
317 | targets = (
318 | F861D7DC207FA40F0085E80D /* iosApp */,
319 | F861D7F0207FA4100085E80D /* iosAppTests */,
320 | F861D804207FA4200085E80D /* app */,
321 | );
322 | };
323 | /* End PBXProject section */
324 |
325 | /* Begin PBXResourcesBuildPhase section */
326 | F861D7DB207FA40F0085E80D /* Resources */ = {
327 | isa = PBXResourcesBuildPhase;
328 | buildActionMask = 2147483647;
329 | files = (
330 | F861D7EB207FA4100085E80D /* LaunchScreen.storyboard in Resources */,
331 | CA5CDE9621B48B7100A21C24 /* RobotoMono-Regular.ttf in Resources */,
332 | CA5CDE9121B4856600A21C24 /* Assets.xcassets in Resources */,
333 | F861D7E6207FA40F0085E80D /* Main.storyboard in Resources */,
334 | CA5CDE9421B48AEC00A21C24 /* RobotoMono-Light.ttf in Resources */,
335 | );
336 | runOnlyForDeploymentPostprocessing = 0;
337 | };
338 | F861D7EF207FA4100085E80D /* Resources */ = {
339 | isa = PBXResourcesBuildPhase;
340 | buildActionMask = 2147483647;
341 | files = (
342 | );
343 | runOnlyForDeploymentPostprocessing = 0;
344 | };
345 | /* End PBXResourcesBuildPhase section */
346 |
347 | /* Begin PBXShellScriptBuildPhase section */
348 | 07FA7A6C9347896C05367E4E /* [CP] Check Pods Manifest.lock */ = {
349 | isa = PBXShellScriptBuildPhase;
350 | buildActionMask = 2147483647;
351 | files = (
352 | );
353 | inputPaths = (
354 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
355 | "${PODS_ROOT}/Manifest.lock",
356 | );
357 | name = "[CP] Check Pods Manifest.lock";
358 | outputPaths = (
359 | "$(DERIVED_FILE_DIR)/Pods-iosAppTests-checkManifestLockResult.txt",
360 | );
361 | runOnlyForDeploymentPostprocessing = 0;
362 | shellPath = /bin/sh;
363 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
364 | showEnvVarsInLog = 0;
365 | };
366 | 2FB6616C86CFCED6EAEAB152 /* [CP] Check Pods Manifest.lock */ = {
367 | isa = PBXShellScriptBuildPhase;
368 | buildActionMask = 2147483647;
369 | files = (
370 | );
371 | inputPaths = (
372 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
373 | "${PODS_ROOT}/Manifest.lock",
374 | );
375 | name = "[CP] Check Pods Manifest.lock";
376 | outputPaths = (
377 | "$(DERIVED_FILE_DIR)/Pods-app-checkManifestLockResult.txt",
378 | );
379 | runOnlyForDeploymentPostprocessing = 0;
380 | shellPath = /bin/sh;
381 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
382 | showEnvVarsInLog = 0;
383 | };
384 | 7AA7AED70A02F86B0B96E3BC /* [CP] Check Pods Manifest.lock */ = {
385 | isa = PBXShellScriptBuildPhase;
386 | buildActionMask = 2147483647;
387 | files = (
388 | );
389 | inputPaths = (
390 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
391 | "${PODS_ROOT}/Manifest.lock",
392 | );
393 | name = "[CP] Check Pods Manifest.lock";
394 | outputPaths = (
395 | "$(DERIVED_FILE_DIR)/Pods-iosApp-checkManifestLockResult.txt",
396 | );
397 | runOnlyForDeploymentPostprocessing = 0;
398 | shellPath = /bin/sh;
399 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
400 | showEnvVarsInLog = 0;
401 | };
402 | EE58EF33EB79FE2752690D85 /* [CP] Embed Pods Frameworks */ = {
403 | isa = PBXShellScriptBuildPhase;
404 | buildActionMask = 2147483647;
405 | files = (
406 | );
407 | inputPaths = (
408 | "${SRCROOT}/Pods/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks.sh",
409 | "${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework",
410 | );
411 | name = "[CP] Embed Pods Frameworks";
412 | outputPaths = (
413 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Kingfisher.framework",
414 | );
415 | runOnlyForDeploymentPostprocessing = 0;
416 | shellPath = /bin/sh;
417 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-iosApp/Pods-iosApp-frameworks.sh\"\n";
418 | showEnvVarsInLog = 0;
419 | };
420 | F861D812207FA4320085E80D /* ShellScript */ = {
421 | isa = PBXShellScriptBuildPhase;
422 | buildActionMask = 2147483647;
423 | files = (
424 | );
425 | inputPaths = (
426 | );
427 | outputPaths = (
428 | );
429 | runOnlyForDeploymentPostprocessing = 0;
430 | shellPath = /bin/sh;
431 | shellScript = "\"$SRCROOT/../gradlew\" -p \"$SRCROOT/../app\" copyFramework \\\n-Pconfiguration.build.dir=\"$CONFIGURATION_BUILD_DIR\" \\\n-Pkotlin.build.type=\"$KOTLIN_BUILD_TYPE\" \\\n-Pkotlin.target=\"$KOTLIN_TARGET\"\n";
432 | };
433 | /* End PBXShellScriptBuildPhase section */
434 |
435 | /* Begin PBXSourcesBuildPhase section */
436 | F861D7D9207FA40F0085E80D /* Sources */ = {
437 | isa = PBXSourcesBuildPhase;
438 | buildActionMask = 2147483647;
439 | files = (
440 | 31F5D6EE219C7A9500DAB828 /* BaseView.swift in Sources */,
441 | 31F5D6F2219C7DCB00DAB828 /* UI.swift in Sources */,
442 | F861D7E3207FA40F0085E80D /* ViewController.swift in Sources */,
443 | CA5CDE8F21B483DD00A21C24 /* UILayer+Color.swift in Sources */,
444 | F861D7E1207FA40F0085E80D /* AppDelegate.swift in Sources */,
445 | CA5CDE8C21B4699400A21C24 /* UIViewController+TextField.swift in Sources */,
446 | );
447 | runOnlyForDeploymentPostprocessing = 0;
448 | };
449 | F861D7ED207FA4100085E80D /* Sources */ = {
450 | isa = PBXSourcesBuildPhase;
451 | buildActionMask = 2147483647;
452 | files = (
453 | F861D7F6207FA4100085E80D /* iosAppTests.swift in Sources */,
454 | );
455 | runOnlyForDeploymentPostprocessing = 0;
456 | };
457 | /* End PBXSourcesBuildPhase section */
458 |
459 | /* Begin PBXTargetDependency section */
460 | F861D7F3207FA4100085E80D /* PBXTargetDependency */ = {
461 | isa = PBXTargetDependency;
462 | target = F861D7DC207FA40F0085E80D /* iosApp */;
463 | targetProxy = F861D7F2207FA4100085E80D /* PBXContainerItemProxy */;
464 | };
465 | F861D80B207FA4200085E80D /* PBXTargetDependency */ = {
466 | isa = PBXTargetDependency;
467 | target = F861D804207FA4200085E80D /* app */;
468 | targetProxy = F861D80A207FA4200085E80D /* PBXContainerItemProxy */;
469 | };
470 | /* End PBXTargetDependency section */
471 |
472 | /* Begin PBXVariantGroup section */
473 | F861D7E4207FA40F0085E80D /* Main.storyboard */ = {
474 | isa = PBXVariantGroup;
475 | children = (
476 | F861D7E5207FA40F0085E80D /* Base */,
477 | );
478 | name = Main.storyboard;
479 | sourceTree = "";
480 | };
481 | F861D7E9207FA4100085E80D /* LaunchScreen.storyboard */ = {
482 | isa = PBXVariantGroup;
483 | children = (
484 | F861D7EA207FA4100085E80D /* Base */,
485 | );
486 | name = LaunchScreen.storyboard;
487 | sourceTree = "";
488 | };
489 | /* End PBXVariantGroup section */
490 |
491 | /* Begin XCBuildConfiguration section */
492 | F861D7F8207FA4100085E80D /* Debug */ = {
493 | isa = XCBuildConfiguration;
494 | buildSettings = {
495 | ALWAYS_SEARCH_USER_PATHS = NO;
496 | CLANG_ANALYZER_NONNULL = YES;
497 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
498 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
499 | CLANG_CXX_LIBRARY = "libc++";
500 | CLANG_ENABLE_MODULES = YES;
501 | CLANG_ENABLE_OBJC_ARC = YES;
502 | CLANG_ENABLE_OBJC_WEAK = YES;
503 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
504 | CLANG_WARN_BOOL_CONVERSION = YES;
505 | CLANG_WARN_COMMA = YES;
506 | CLANG_WARN_CONSTANT_CONVERSION = YES;
507 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
508 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
509 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
510 | CLANG_WARN_EMPTY_BODY = YES;
511 | CLANG_WARN_ENUM_CONVERSION = YES;
512 | CLANG_WARN_INFINITE_RECURSION = YES;
513 | CLANG_WARN_INT_CONVERSION = YES;
514 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
515 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
516 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
517 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
518 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
519 | CLANG_WARN_STRICT_PROTOTYPES = YES;
520 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
521 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
522 | CLANG_WARN_UNREACHABLE_CODE = YES;
523 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
524 | CODE_SIGN_IDENTITY = "iPhone Developer";
525 | COPY_PHASE_STRIP = NO;
526 | DEBUG_INFORMATION_FORMAT = dwarf;
527 | ENABLE_BITCODE = NO;
528 | ENABLE_STRICT_OBJC_MSGSEND = YES;
529 | ENABLE_TESTABILITY = YES;
530 | GCC_C_LANGUAGE_STANDARD = gnu11;
531 | GCC_DYNAMIC_NO_PIC = NO;
532 | GCC_NO_COMMON_BLOCKS = YES;
533 | GCC_OPTIMIZATION_LEVEL = 0;
534 | GCC_PREPROCESSOR_DEFINITIONS = (
535 | "DEBUG=1",
536 | "$(inherited)",
537 | );
538 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
539 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
540 | GCC_WARN_UNDECLARED_SELECTOR = YES;
541 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
542 | GCC_WARN_UNUSED_FUNCTION = YES;
543 | GCC_WARN_UNUSED_VARIABLE = YES;
544 | IPHONEOS_DEPLOYMENT_TARGET = 11.3;
545 | MTL_ENABLE_DEBUG_INFO = YES;
546 | ONLY_ACTIVE_ARCH = YES;
547 | OTHER_LDFLAGS = "-v";
548 | SDKROOT = iphoneos;
549 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
550 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
551 | };
552 | name = Debug;
553 | };
554 | F861D7F9207FA4100085E80D /* Release */ = {
555 | isa = XCBuildConfiguration;
556 | buildSettings = {
557 | ALWAYS_SEARCH_USER_PATHS = NO;
558 | CLANG_ANALYZER_NONNULL = YES;
559 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
560 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
561 | CLANG_CXX_LIBRARY = "libc++";
562 | CLANG_ENABLE_MODULES = YES;
563 | CLANG_ENABLE_OBJC_ARC = YES;
564 | CLANG_ENABLE_OBJC_WEAK = YES;
565 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
566 | CLANG_WARN_BOOL_CONVERSION = YES;
567 | CLANG_WARN_COMMA = YES;
568 | CLANG_WARN_CONSTANT_CONVERSION = YES;
569 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
570 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
571 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
572 | CLANG_WARN_EMPTY_BODY = YES;
573 | CLANG_WARN_ENUM_CONVERSION = YES;
574 | CLANG_WARN_INFINITE_RECURSION = YES;
575 | CLANG_WARN_INT_CONVERSION = YES;
576 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
577 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
578 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
579 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
580 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
581 | CLANG_WARN_STRICT_PROTOTYPES = YES;
582 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
583 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
584 | CLANG_WARN_UNREACHABLE_CODE = YES;
585 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
586 | CODE_SIGN_IDENTITY = "iPhone Developer";
587 | COPY_PHASE_STRIP = NO;
588 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
589 | ENABLE_BITCODE = NO;
590 | ENABLE_NS_ASSERTIONS = NO;
591 | ENABLE_STRICT_OBJC_MSGSEND = YES;
592 | GCC_C_LANGUAGE_STANDARD = gnu11;
593 | GCC_NO_COMMON_BLOCKS = YES;
594 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
595 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
596 | GCC_WARN_UNDECLARED_SELECTOR = YES;
597 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
598 | GCC_WARN_UNUSED_FUNCTION = YES;
599 | GCC_WARN_UNUSED_VARIABLE = YES;
600 | IPHONEOS_DEPLOYMENT_TARGET = 11.3;
601 | MTL_ENABLE_DEBUG_INFO = NO;
602 | OTHER_LDFLAGS = "-v";
603 | SDKROOT = iphoneos;
604 | SWIFT_COMPILATION_MODE = wholemodule;
605 | SWIFT_OPTIMIZATION_LEVEL = "-O";
606 | VALIDATE_PRODUCT = YES;
607 | };
608 | name = Release;
609 | };
610 | F861D7FB207FA4100085E80D /* Debug */ = {
611 | isa = XCBuildConfiguration;
612 | baseConfigurationReference = 5E569EE4FA3592292991A393 /* Pods-iosApp.debug.xcconfig */;
613 | buildSettings = {
614 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
615 | CODE_SIGN_STYLE = Automatic;
616 | DEVELOPMENT_TEAM = NS5ZYWT25D;
617 | ENABLE_BITCODE = NO;
618 | INFOPLIST_FILE = iosApp/Info.plist;
619 | LD_RUNPATH_SEARCH_PATHS = (
620 | "$(inherited)",
621 | "@executable_path/Frameworks",
622 | );
623 | OTHER_LDFLAGS = "-v";
624 | PRODUCT_BUNDLE_IDENTIFIER = com.example.iosApp;
625 | PRODUCT_NAME = "$(TARGET_NAME)";
626 | SWIFT_VERSION = 4.0;
627 | TARGETED_DEVICE_FAMILY = "1,2";
628 | VALID_ARCHS = "arm64 armv7";
629 | };
630 | name = Debug;
631 | };
632 | F861D7FC207FA4100085E80D /* Release */ = {
633 | isa = XCBuildConfiguration;
634 | baseConfigurationReference = BA6CC8EC1727D396EAEB9793 /* Pods-iosApp.release.xcconfig */;
635 | buildSettings = {
636 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
637 | CODE_SIGN_STYLE = Automatic;
638 | DEVELOPMENT_TEAM = NS5ZYWT25D;
639 | ENABLE_BITCODE = NO;
640 | INFOPLIST_FILE = iosApp/Info.plist;
641 | LD_RUNPATH_SEARCH_PATHS = (
642 | "$(inherited)",
643 | "@executable_path/Frameworks",
644 | );
645 | OTHER_LDFLAGS = "-v";
646 | PRODUCT_BUNDLE_IDENTIFIER = com.example.iosApp;
647 | PRODUCT_NAME = "$(TARGET_NAME)";
648 | SWIFT_VERSION = 4.0;
649 | TARGETED_DEVICE_FAMILY = "1,2";
650 | VALID_ARCHS = "arm64 armv7";
651 | };
652 | name = Release;
653 | };
654 | F861D7FE207FA4100085E80D /* Debug */ = {
655 | isa = XCBuildConfiguration;
656 | baseConfigurationReference = 76F8EAB9A3C1708123D5A635 /* Pods-iosAppTests.debug.xcconfig */;
657 | buildSettings = {
658 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
659 | BUNDLE_LOADER = "$(TEST_HOST)";
660 | CODE_SIGN_STYLE = Automatic;
661 | INFOPLIST_FILE = iosAppTests/Info.plist;
662 | LD_RUNPATH_SEARCH_PATHS = (
663 | "$(inherited)",
664 | "@executable_path/Frameworks",
665 | "@loader_path/Frameworks",
666 | );
667 | PRODUCT_BUNDLE_IDENTIFIER = com.example.iosAppTests;
668 | PRODUCT_NAME = "$(TARGET_NAME)";
669 | SWIFT_VERSION = 4.0;
670 | TARGETED_DEVICE_FAMILY = "1,2";
671 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/iosApp.app/iosApp";
672 | };
673 | name = Debug;
674 | };
675 | F861D7FF207FA4100085E80D /* Release */ = {
676 | isa = XCBuildConfiguration;
677 | baseConfigurationReference = 76E6BD9963CC1676192496A6 /* Pods-iosAppTests.release.xcconfig */;
678 | buildSettings = {
679 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
680 | BUNDLE_LOADER = "$(TEST_HOST)";
681 | CODE_SIGN_STYLE = Automatic;
682 | INFOPLIST_FILE = iosAppTests/Info.plist;
683 | LD_RUNPATH_SEARCH_PATHS = (
684 | "$(inherited)",
685 | "@executable_path/Frameworks",
686 | "@loader_path/Frameworks",
687 | );
688 | PRODUCT_BUNDLE_IDENTIFIER = com.example.iosAppTests;
689 | PRODUCT_NAME = "$(TARGET_NAME)";
690 | SWIFT_VERSION = 4.0;
691 | TARGETED_DEVICE_FAMILY = "1,2";
692 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/iosApp.app/iosApp";
693 | };
694 | name = Release;
695 | };
696 | F861D80F207FA4200085E80D /* Debug */ = {
697 | isa = XCBuildConfiguration;
698 | baseConfigurationReference = A777AD0801F2C6E78C4A00B7 /* Pods-app.debug.xcconfig */;
699 | buildSettings = {
700 | CODE_SIGN_IDENTITY = "";
701 | CODE_SIGN_STYLE = Automatic;
702 | CURRENT_PROJECT_VERSION = 1;
703 | DEFINES_MODULE = YES;
704 | DYLIB_COMPATIBILITY_VERSION = 1;
705 | DYLIB_CURRENT_VERSION = 1;
706 | DYLIB_INSTALL_NAME_BASE = "@rpath";
707 | ENABLE_BITCODE = NO;
708 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
709 | KOTLIN_BUILD_TYPE = DEBUG;
710 | KOTLIN_TARGET = "";
711 | "KOTLIN_TARGET[sdk=iphoneos*]" = ios;
712 | "KOTLIN_TARGET[sdk=iphonesimulator*]" = ios;
713 | LD_RUNPATH_SEARCH_PATHS = (
714 | "$(inherited)",
715 | "@executable_path/Frameworks",
716 | "@loader_path/Frameworks",
717 | );
718 | PRODUCT_BUNDLE_IDENTIFIER = com.example.app;
719 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
720 | SKIP_INSTALL = YES;
721 | SWIFT_VERSION = 4.0;
722 | TARGETED_DEVICE_FAMILY = "1,2";
723 | VERSIONING_SYSTEM = "apple-generic";
724 | VERSION_INFO_PREFIX = "";
725 | };
726 | name = Debug;
727 | };
728 | F861D810207FA4200085E80D /* Release */ = {
729 | isa = XCBuildConfiguration;
730 | baseConfigurationReference = AB20C31AD1A8F977AD01F195 /* Pods-app.release.xcconfig */;
731 | buildSettings = {
732 | CODE_SIGN_IDENTITY = "";
733 | CODE_SIGN_STYLE = Automatic;
734 | CURRENT_PROJECT_VERSION = 1;
735 | DEFINES_MODULE = YES;
736 | DYLIB_COMPATIBILITY_VERSION = 1;
737 | DYLIB_CURRENT_VERSION = 1;
738 | DYLIB_INSTALL_NAME_BASE = "@rpath";
739 | ENABLE_BITCODE = NO;
740 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
741 | KOTLIN_BUILD_TYPE = RELEASE;
742 | KOTLIN_TARGET = "";
743 | "KOTLIN_TARGET[sdk=iphoneos*]" = ios;
744 | "KOTLIN_TARGET[sdk=iphonesimulator*]" = ios;
745 | LD_RUNPATH_SEARCH_PATHS = (
746 | "$(inherited)",
747 | "@executable_path/Frameworks",
748 | "@loader_path/Frameworks",
749 | );
750 | PRODUCT_BUNDLE_IDENTIFIER = com.example.app;
751 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
752 | SKIP_INSTALL = YES;
753 | SWIFT_VERSION = 4.0;
754 | TARGETED_DEVICE_FAMILY = 1;
755 | VERSIONING_SYSTEM = "apple-generic";
756 | VERSION_INFO_PREFIX = "";
757 | };
758 | name = Release;
759 | };
760 | /* End XCBuildConfiguration section */
761 |
762 | /* Begin XCConfigurationList section */
763 | F861D7D8207FA40F0085E80D /* Build configuration list for PBXProject "iosApp" */ = {
764 | isa = XCConfigurationList;
765 | buildConfigurations = (
766 | F861D7F8207FA4100085E80D /* Debug */,
767 | F861D7F9207FA4100085E80D /* Release */,
768 | );
769 | defaultConfigurationIsVisible = 0;
770 | defaultConfigurationName = Release;
771 | };
772 | F861D7FA207FA4100085E80D /* Build configuration list for PBXNativeTarget "iosApp" */ = {
773 | isa = XCConfigurationList;
774 | buildConfigurations = (
775 | F861D7FB207FA4100085E80D /* Debug */,
776 | F861D7FC207FA4100085E80D /* Release */,
777 | );
778 | defaultConfigurationIsVisible = 0;
779 | defaultConfigurationName = Release;
780 | };
781 | F861D7FD207FA4100085E80D /* Build configuration list for PBXNativeTarget "iosAppTests" */ = {
782 | isa = XCConfigurationList;
783 | buildConfigurations = (
784 | F861D7FE207FA4100085E80D /* Debug */,
785 | F861D7FF207FA4100085E80D /* Release */,
786 | );
787 | defaultConfigurationIsVisible = 0;
788 | defaultConfigurationName = Release;
789 | };
790 | F861D80E207FA4200085E80D /* Build configuration list for PBXNativeTarget "app" */ = {
791 | isa = XCConfigurationList;
792 | buildConfigurations = (
793 | F861D80F207FA4200085E80D /* Debug */,
794 | F861D810207FA4200085E80D /* Release */,
795 | );
796 | defaultConfigurationIsVisible = 0;
797 | defaultConfigurationName = Release;
798 | };
799 | /* End XCConfigurationList section */
800 | };
801 | rootObject = F861D7D5207FA40F0085E80D /* Project object */;
802 | }
803 |
--------------------------------------------------------------------------------
/iosApp/iosApp/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 |
3 | @UIApplicationMain
4 | class AppDelegate: UIResponder, UIApplicationDelegate {
5 | var window: UIWindow?
6 |
7 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
8 | return true
9 | }
10 |
11 | func applicationWillResignActive(_ application: UIApplication) {}
12 |
13 | func applicationDidEnterBackground(_ application: UIApplication) {}
14 |
15 | func applicationWillEnterForeground(_ application: UIApplication) {}
16 |
17 | func applicationDidBecomeActive(_ application: UIApplication) {}
18 |
19 | func applicationWillTerminate(_ application: UIApplication) {}
20 | }
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/right-arrow-forward.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "right-arrow-forward@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "right-arrow-forward@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/right-arrow-forward.imageset/right-arrow-forward@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iamBedant/Multiplatform/9e0f3f80cd6cdb1c22e53bd15188bb5836a4c112/iosApp/iosApp/Assets.xcassets/right-arrow-forward.imageset/right-arrow-forward@2x.png
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/right-arrow-forward.imageset/right-arrow-forward@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iamBedant/Multiplatform/9e0f3f80cd6cdb1c22e53bd15188bb5836a4c112/iosApp/iosApp/Assets.xcassets/right-arrow-forward.imageset/right-arrow-forward@3x.png
--------------------------------------------------------------------------------
/iosApp/iosApp/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | RobotoMono-Light
15 |
16 |
17 | RobotoMono-Regular
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
90 |
91 |
92 |
93 |
99 |
105 |
106 |
107 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
--------------------------------------------------------------------------------
/iosApp/iosApp/BaseView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseView.swift
3 | // iosApp
4 | //
5 | // Created by @iamBedant on 14/11/18.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 | import app
11 |
12 | var firstUpdateError = true
13 |
14 | extension UIViewController: BaseView {
15 |
16 | public func showError(error: String) {
17 | self.showError(title: "Error", message: error)
18 | }
19 |
20 | func showError(title: String, message: String) {
21 | let alertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
22 | alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.default,handler: nil))
23 | self.present(alertController, animated: true, completion: nil)
24 | }
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Extensions/UILayer/UILayer+Color.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UILayer+Color.swift
3 | // iosApp
4 | //
5 | // Created by Mohit Anand on 03/12/18.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | extension CALayer {
12 |
13 | var borderUIColor: UIColor {
14 | set {
15 | self.borderColor = newValue.cgColor
16 | }
17 | get {
18 | return UIColor(cgColor: self.borderColor!)
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Extensions/UIViewController/UIViewController+TextField.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+TextField.swift
3 | // iosApp
4 | //
5 | // Created by Mohit Anand on 03/12/18.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | extension UIViewController {
12 |
13 | func hideKeyboardWhenTappedAround() {
14 | let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
15 | tap.cancelsTouchesInView = false
16 | view.addGestureRecognizer(tap)
17 | }
18 |
19 | @objc func dismissKeyboard() {
20 | view.endEditing(true)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Fonts/RobotoMono-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iamBedant/Multiplatform/9e0f3f80cd6cdb1c22e53bd15188bb5836a4c112/iosApp/iosApp/Fonts/RobotoMono-Light.ttf
--------------------------------------------------------------------------------
/iosApp/iosApp/Fonts/RobotoMono-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iamBedant/Multiplatform/9e0f3f80cd6cdb1c22e53bd15188bb5836a4c112/iosApp/iosApp/Fonts/RobotoMono-Regular.ttf
--------------------------------------------------------------------------------
/iosApp/iosApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 | UIAppFonts
45 |
46 | RobotoMono-Light.ttf
47 | RobotoMono-Regular.ttf
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/iosApp/iosApp/UI.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UI.swift
3 | // iosApp
4 | //
5 | // Created by kuliza on 14/11/18.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 | import app
11 |
12 | //public class UI: KotlinCorou {
13 | // override public func dispatch(context: KotlinCoroutineContext, block: Kotlinx_coroutines_core_nativeRunnable) {
14 | // DispatchQueue.main.async {
15 | // block.run()
16 | // }
17 | // }
18 | //}
19 |
--------------------------------------------------------------------------------
/iosApp/iosApp/ViewController.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import app
3 | import Kingfisher
4 |
5 | class ViewController: UIViewController {
6 |
7 |
8 | @IBOutlet weak var userNameTextField: UITextField! {
9 | didSet {
10 | userNameTextField.delegate = self
11 | }
12 | }
13 | @IBOutlet weak var searchButton: UIButton!
14 | @IBOutlet weak var activityIndicator: UIActivityIndicatorView!
15 | @IBOutlet weak var userPic: UIImageView!
16 | @IBOutlet weak var userNameLabel: UILabel!
17 | @IBOutlet weak var reposLabel: UILabel!
18 | @IBOutlet weak var gistsLabel: UILabel!
19 | @IBOutlet weak var bioLabel: UILabel!
20 | @IBOutlet weak var userDetailsView: UIView!
21 |
22 | lazy var presenter : MainPresenter = {
23 | MainPresenter(view: self,
24 | repository: DataRepositoryImpl(),
25 | uiContext:IosUtilities().getDispetcher()
26 | )
27 | }()
28 |
29 | override func viewDidLoad() {
30 | super.viewDidLoad()
31 | setupUI()
32 | }
33 |
34 | @IBAction func goButtonTapped(_ sender: Any) {
35 |
36 | let userNameText = userNameTextField.text ?? ""
37 | hideUserDetails()
38 | presenter.loadData(userName: userNameText)
39 | }
40 | }
41 |
42 | // MARK: UI Updates
43 | private extension ViewController {
44 |
45 | func setupUI() {
46 | hideUserDetails()
47 | hideKeyboardWhenTappedAround()
48 | }
49 |
50 | func showUserDetails() {
51 | userDetailsView.isHidden = false
52 | }
53 |
54 | func hideUserDetails() {
55 | userDetailsView.isHidden = true
56 | }
57 | }
58 |
59 | // MARK: UITextField Delegate
60 | extension ViewController: UITextFieldDelegate {
61 |
62 | func textFieldShouldReturn(_ textField: UITextField) -> Bool {
63 | textField.resignFirstResponder()
64 | return true
65 | }
66 | }
67 |
68 | // MARK: Presenter Delegate
69 | extension ViewController: MainView {
70 |
71 | func showLoader() {
72 | activityIndicator.startAnimating()
73 | }
74 |
75 | func hideLoader() {
76 | activityIndicator.stopAnimating()
77 | }
78 |
79 | func displayData(data: DisplayData) {
80 |
81 | userNameLabel.text = data.name
82 | userPic.kf.setImage(with: URL.init(string: data.avatarUrl))
83 | reposLabel.text = data.publicRepos
84 | gistsLabel.text = data.publicGists
85 | bioLabel.text = data.bio
86 |
87 | showUserDetails()
88 | }
89 |
90 | }
91 |
92 |
--------------------------------------------------------------------------------
/iosApp/iosAppTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
--------------------------------------------------------------------------------
/iosApp/iosAppTests/iosAppTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | import app
3 |
4 | class iosAppTests: XCTestCase {
5 | func testExample() {
6 | assert(Sample().checkMe() == 7)
7 | }
8 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | resolutionStrategy {
3 | eachPlugin {
4 | if (requested.id.id == "kotlin-multiplatform") {
5 | useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}")
6 | }
7 | if (requested.id.id == "kotlinx-serialization") {
8 | useModule("org.jetbrains.kotlin:kotlin-serialization:${requested.version}")
9 | }
10 | }
11 | }
12 | }
13 | rootProject.name = 'KMP'
14 | enableFeaturePreview('GRADLE_METADATA')
15 | include ':app'
16 |
--------------------------------------------------------------------------------