├── iosApp
├── Configuration
│ └── Config.xcconfig
├── iosApp
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ ├── app-icon-1024.png
│ │ │ └── Contents.json
│ │ └── AccentColor.colorset
│ │ │ └── Contents.json
│ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ ├── iOSApp.swift
│ ├── ContentView.swift
│ └── Info.plist
├── iosApp.xcodeproj
│ └── project.xcworkspace
│ │ └── contents.xcworkspacedata
└── Learning App.xcodeproj
│ ├── project.xcworkspace
│ ├── xcshareddata
│ │ ├── WorkspaceSettings.xcsettings
│ │ └── IDEWorkspaceChecks.plist
│ └── contents.xcworkspacedata
│ ├── xcshareddata
│ └── xcschemes
│ │ └── iosApp.xcscheme
│ └── project.pbxproj
├── shared
├── src
│ ├── iosMain
│ │ └── kotlin
│ │ │ ├── Koin.ios.kt
│ │ │ └── main.ios.kt
│ ├── androidMain
│ │ └── kotlin
│ │ │ └── main.android.kt
│ └── commonMain
│ │ ├── kotlin
│ │ ├── ui
│ │ │ ├── JobsScreen.kt
│ │ │ ├── CoursesScreen.kt
│ │ │ ├── HomeScreen.kt
│ │ │ ├── Screen.kt
│ │ │ ├── internships
│ │ │ │ ├── InternshipViewModel.kt
│ │ │ │ ├── InternshipsScreen.kt
│ │ │ │ └── InternshipCard.kt
│ │ │ ├── theme
│ │ │ │ └── Color.kt
│ │ │ └── PagingListUI.kt
│ │ ├── domain
│ │ │ ├── usecase
│ │ │ │ └── StreamInternshipUseCase.kt
│ │ │ ├── Repository.kt
│ │ │ └── ResultPagingSource.kt
│ │ ├── data
│ │ │ ├── Service.kt
│ │ │ ├── RepositoryImpl.kt
│ │ │ └── InternshipApiModel.kt
│ │ ├── util
│ │ │ └── ApiClient.kt
│ │ ├── di
│ │ │ └── Koin.kt
│ │ └── App.kt
│ │ └── resources
│ │ ├── ic_arrow_up.xml
│ │ ├── ic_calendar.xml
│ │ ├── ic_clock.xml
│ │ ├── ic_play_circle.xml
│ │ └── ic_stipend.xml
└── build.gradle.kts
├── androidApp
├── src
│ └── main
│ │ ├── res
│ │ ├── values
│ │ │ ├── strings.xml
│ │ │ └── ic_launcher_background.xml
│ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.webp
│ │ │ ├── ic_launcher_round.webp
│ │ │ └── ic_launcher_foreground.webp
│ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.webp
│ │ │ ├── ic_launcher_round.webp
│ │ │ └── ic_launcher_foreground.webp
│ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.webp
│ │ │ ├── ic_launcher_round.webp
│ │ │ └── ic_launcher_foreground.webp
│ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.webp
│ │ │ ├── ic_launcher_round.webp
│ │ │ └── ic_launcher_foreground.webp
│ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.webp
│ │ │ ├── ic_launcher_round.webp
│ │ │ └── ic_launcher_foreground.webp
│ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ ├── drawable-v24
│ │ │ └── ic_launcher_foreground.xml
│ │ └── drawable
│ │ │ └── ic_launcher_background.xml
│ │ ├── java
│ │ └── com
│ │ │ └── learning
│ │ │ └── app
│ │ │ ├── App.kt
│ │ │ └── MainActivity.kt
│ │ └── AndroidManifest.xml
└── build.gradle.kts
├── gradle
├── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
└── libs.versions.toml
├── .gitignore
├── gradle.properties
├── settings.gradle.kts
├── gradlew.bat
└── gradlew
/iosApp/Configuration/Config.xcconfig:
--------------------------------------------------------------------------------
1 | TEAM_ID=
2 | BUNDLE_ID=com.learning.app
3 | APP_NAME=LearningApp
4 |
--------------------------------------------------------------------------------
/shared/src/iosMain/kotlin/Koin.ios.kt:
--------------------------------------------------------------------------------
1 | class Koin {
2 | fun initKoin() {
3 | di.initKoin()
4 | }
5 | }
--------------------------------------------------------------------------------
/androidApp/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Learning App
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Prashant-123/kmm-ktor-koin/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
--------------------------------------------------------------------------------
/shared/src/androidMain/kotlin/main.android.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.runtime.Composable
2 |
3 | @Composable fun MainView() = App()
4 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | .idea
4 | .DS_Store
5 | build
6 | captures
7 | .externalNativeBuild
8 | .cxx
9 | local.properties
10 | xcuserdata
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/ui/JobsScreen.kt:
--------------------------------------------------------------------------------
1 | package ui
2 |
3 | import androidx.compose.runtime.Composable
4 |
5 | @Composable
6 | fun JobsScreen() {
7 | }
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/ui/CoursesScreen.kt:
--------------------------------------------------------------------------------
1 | package ui
2 |
3 | import androidx.compose.runtime.Composable
4 |
5 | @Composable
6 | fun CoursesScreen() {
7 | }
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/ui/HomeScreen.kt:
--------------------------------------------------------------------------------
1 | package ui
2 |
3 | import androidx.compose.runtime.Composable
4 |
5 | @Composable
6 | fun HomeScreen() {
7 |
8 | }
--------------------------------------------------------------------------------
/androidApp/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Prashant-123/kmm-ktor-koin/HEAD/androidApp/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/androidApp/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Prashant-123/kmm-ktor-koin/HEAD/androidApp/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/androidApp/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Prashant-123/kmm-ktor-koin/HEAD/androidApp/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/iosApp/iosApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/shared/src/iosMain/kotlin/main.ios.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.ui.window.ComposeUIViewController
2 |
3 | fun MainViewController() = ComposeUIViewController { App() }
--------------------------------------------------------------------------------
/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Prashant-123/kmm-ktor-koin/HEAD/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Prashant-123/kmm-ktor-koin/HEAD/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/androidApp/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Prashant-123/kmm-ktor-koin/HEAD/androidApp/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/androidApp/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Prashant-123/kmm-ktor-koin/HEAD/androidApp/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Prashant-123/kmm-ktor-koin/HEAD/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Prashant-123/kmm-ktor-koin/HEAD/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/androidApp/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Prashant-123/kmm-ktor-koin/HEAD/androidApp/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/androidApp/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Prashant-123/kmm-ktor-koin/HEAD/androidApp/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Prashant-123/kmm-ktor-koin/HEAD/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Prashant-123/kmm-ktor-koin/HEAD/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Prashant-123/kmm-ktor-koin/HEAD/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/androidApp/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #4285F4
4 |
--------------------------------------------------------------------------------
/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Prashant-123/kmm-ktor-koin/HEAD/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Prashant-123/kmm-ktor-koin/HEAD/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/ui/Screen.kt:
--------------------------------------------------------------------------------
1 | package ui
2 |
3 | enum class Screen(val textValue: String) {
4 | HOME("Home"),
5 | INTERNSHIPS("Internships"),
6 | JOBS("Jobs"),
7 | COURSES("Courses")
8 | }
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
--------------------------------------------------------------------------------
/iosApp/iosApp/iOSApp.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 | import shared
3 |
4 | @main
5 | struct iOSApp: App {
6 | var body: some Scene {
7 | WindowGroup {
8 | ContentView()
9 | }
10 | }
11 |
12 | init() {
13 | KoinKt.doInitKoin()
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/domain/usecase/StreamInternshipUseCase.kt:
--------------------------------------------------------------------------------
1 | package domain.usecase
2 |
3 | import domain.Repository
4 |
5 | class StreamInternshipUseCase(private val repository: Repository) {
6 | operator fun invoke() = repository.streamInternships()
7 | }
--------------------------------------------------------------------------------
/androidApp/src/main/java/com/learning/app/App.kt:
--------------------------------------------------------------------------------
1 | package com.learning.app
2 |
3 | import android.app.Application
4 | import di.initKoin
5 |
6 | class App : Application() {
7 |
8 | override fun onCreate() {
9 | super.onCreate()
10 | initKoin()
11 | }
12 | }
--------------------------------------------------------------------------------
/iosApp/Learning App.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Nov 07 16:49:05 IST 2023
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/domain/Repository.kt:
--------------------------------------------------------------------------------
1 | package domain
2 |
3 | import androidx.paging.PagingData
4 | import data.InternshipApiModel
5 | import kotlinx.coroutines.flow.Flow
6 |
7 | interface Repository {
8 | fun streamInternships(): Flow>
9 | }
--------------------------------------------------------------------------------
/iosApp/Learning App.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "app-icon-1024.png",
5 | "idiom" : "universal",
6 | "platform" : "ios",
7 | "size" : "1024x1024"
8 | }
9 | ],
10 | "info" : {
11 | "author" : "xcode",
12 | "version" : 1
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/iosApp/Learning App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/ui/internships/InternshipViewModel.kt:
--------------------------------------------------------------------------------
1 | package ui.internships
2 |
3 | import com.rickclephas.kmm.viewmodel.KMMViewModel
4 | import domain.usecase.StreamInternshipUseCase
5 |
6 | class InternshipViewModel(streamInternshipUseCase: StreamInternshipUseCase) : KMMViewModel() {
7 | val internships = streamInternshipUseCase()
8 | }
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package ui.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | object Color {
6 | val primary = Color(0xFF00A5EC)
7 |
8 | val textOnPrimary = Color(0xFF0069AF)
9 | val textSecondary = Color(0xFF6C6C6C)
10 | val textTertiary = Color(0xFF8C8C8C)
11 |
12 | val background = Color(0xFFEEEEEE)
13 | }
--------------------------------------------------------------------------------
/shared/src/commonMain/resources/ic_arrow_up.xml:
--------------------------------------------------------------------------------
1 |
3 |
5 |
6 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/Service.kt:
--------------------------------------------------------------------------------
1 | package data
2 |
3 | import io.ktor.client.HttpClient
4 | import io.ktor.client.request.url
5 | import io.ktor.http.HttpMethod
6 | import util.fetch
7 |
8 | object Service {
9 | suspend fun HttpClient.getInternships(page: Int = 1) =
10 | fetch {
11 | url("/json/internships/page-$page")
12 | method = HttpMethod.Get
13 | }
14 | }
--------------------------------------------------------------------------------
/androidApp/src/main/java/com/learning/app/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.learning.app
2 |
3 | import MainView
4 | import android.os.Bundle
5 | import androidx.activity.compose.setContent
6 | import androidx.appcompat.app.AppCompatActivity
7 |
8 | class MainActivity : AppCompatActivity() {
9 | override fun onCreate(savedInstanceState: Bundle?) {
10 | super.onCreate(savedInstanceState)
11 |
12 | setContent {
13 | MainView()
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | #Gradle
2 | org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M"
3 |
4 | #Kotlin
5 | kotlin.code.style=official
6 |
7 | #Compose
8 | org.jetbrains.compose.experimental.uikit.enabled=true
9 |
10 | #Android
11 | android.useAndroidX=true
12 | android.nonTransitiveRClass=true
13 |
14 | #MPP
15 | kotlin.mpp.enableCInteropCommonization=true
16 |
17 | #Versions
18 | kotlin.version=1.9.10
19 | agp.version=8.0.2
20 | compose.version=1.5.3
--------------------------------------------------------------------------------
/shared/src/commonMain/resources/ic_calendar.xml:
--------------------------------------------------------------------------------
1 |
3 |
5 |
6 |
--------------------------------------------------------------------------------
/iosApp/iosApp/ContentView.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import SwiftUI
3 | import shared
4 |
5 | struct ComposeView: UIViewControllerRepresentable {
6 | func makeUIViewController(context: Context) -> UIViewController {
7 | Main_iosKt.MainViewController()
8 | }
9 |
10 | func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
11 | }
12 |
13 | struct ContentView: View {
14 | var body: some View {
15 | ComposeView()
16 | .ignoresSafeArea(.all, edges: .bottom) // Compose has own keyboard handler
17 | }
18 | }
--------------------------------------------------------------------------------
/shared/src/commonMain/resources/ic_clock.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/shared/src/commonMain/resources/ic_play_circle.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/RepositoryImpl.kt:
--------------------------------------------------------------------------------
1 | package data
2 |
3 | import androidx.paging.Pager
4 | import androidx.paging.PagingConfig
5 | import androidx.paging.PagingData
6 | import data.Service.getInternships
7 | import domain.Repository
8 | import domain.ResultPagingSource
9 | import io.ktor.client.HttpClient
10 | import kotlinx.coroutines.flow.Flow
11 | import util.map
12 |
13 | class RepositoryImpl(private val apiService: HttpClient) : Repository {
14 | override fun streamInternships(): Flow> = Pager(
15 | config = PagingConfig(
16 | pageSize = 10,
17 | initialLoadSize = 10,
18 | enablePlaceholders = false,
19 | ),
20 | pagingSourceFactory = {
21 | ResultPagingSource { page, _ ->
22 | apiService.getInternships(page).map { it.internshipsMeta.values.toList() }
23 | }
24 | }
25 | ).flow
26 | }
--------------------------------------------------------------------------------
/androidApp/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
14 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/shared/src/commonMain/resources/ic_stipend.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
2 |
3 | rootProject.name = "LearningApp"
4 |
5 | include(":androidApp")
6 | include(":shared")
7 |
8 | pluginManagement {
9 | repositories {
10 | google()
11 | gradlePluginPortal()
12 | mavenCentral()
13 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
14 | }
15 |
16 | plugins {
17 | val kotlinVersion = extra["kotlin.version"] as String
18 | val agpVersion = extra["agp.version"] as String
19 | val composeVersion = extra["compose.version"] as String
20 |
21 | kotlin("jvm").version(kotlinVersion)
22 | kotlin("multiplatform").version(kotlinVersion)
23 | kotlin("android").version(kotlinVersion)
24 |
25 | id("com.android.application").version(agpVersion)
26 | id("com.android.library").version(agpVersion)
27 | id("org.jetbrains.compose").version(composeVersion)
28 | }
29 | }
30 |
31 | dependencyResolutionManagement {
32 | repositories {
33 | google()
34 | mavenCentral()
35 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
36 | }
37 | }
38 |
39 | rootProject.name = "LearningApp"
40 | include(":shared")
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/util/ApiClient.kt:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import io.ktor.client.HttpClient
4 | import io.ktor.client.call.body
5 | import io.ktor.client.request.HttpRequestBuilder
6 | import io.ktor.client.request.request
7 | import io.ktor.client.statement.bodyAsText
8 | import io.ktor.http.HttpStatusCode
9 |
10 | suspend inline fun HttpClient.fetch(
11 | block: HttpRequestBuilder.() -> Unit
12 | ): Result = try {
13 | val response = request(block)
14 |
15 | if (response.status == HttpStatusCode.OK) {
16 | Result.Success(response.body())
17 | } else {
18 | Result.Error(Throwable("${response.status}: ${response.bodyAsText()}"))
19 | }
20 | } catch (e: Exception) {
21 | Result.Error(e)
22 | }
23 |
24 | sealed interface Result {
25 | class Success(val value: R) : Result
26 | data object Loading : Result
27 |
28 | class Error(val throwable: Throwable) : Result
29 | }
30 |
31 | inline fun Result.map(transform: (value: T) -> R): Result =
32 | when (this) {
33 | is Result.Success -> Result.Success(transform(value))
34 | is Result.Error -> Result.Error(throwable)
35 | is Result.Loading -> Result.Loading
36 | }
37 |
--------------------------------------------------------------------------------
/androidApp/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.androidApplication)
3 | alias(libs.plugins.kotlinAndroid)
4 | id("org.jetbrains.compose")
5 | }
6 |
7 | android {
8 | namespace = "com.learning.app.android"
9 | compileSdk = 34
10 | defaultConfig {
11 | applicationId = "com.learning.app.android"
12 | minSdk = 21
13 | targetSdk = 34
14 | versionCode = 1
15 | versionName = "1.0"
16 | }
17 | buildFeatures {
18 | compose = true
19 | }
20 | composeOptions {
21 | kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
22 | }
23 | packaging {
24 | resources {
25 | excludes += "/META-INF/{AL2.0,LGPL2.1}"
26 | }
27 | }
28 | buildTypes {
29 | getByName("release") {
30 | isMinifyEnabled = false
31 | }
32 | }
33 | compileOptions {
34 | sourceCompatibility = JavaVersion.VERSION_1_8
35 | targetCompatibility = JavaVersion.VERSION_1_8
36 | }
37 | kotlinOptions {
38 | jvmTarget = "1.8"
39 | }
40 | }
41 |
42 | dependencies {
43 | implementation(projects.shared)
44 | implementation(libs.compose.ui)
45 | implementation(libs.compose.ui.tooling.preview)
46 | implementation(libs.compose.material3)
47 | implementation(libs.androidx.activity.compose)
48 | implementation(libs.kotlinx.coroutines.android)
49 | implementation(libs.koin.android)
50 | implementation(libs.koin.androidx.compose)
51 | debugImplementation(libs.compose.ui.tooling)
52 | }
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/data/InternshipApiModel.kt:
--------------------------------------------------------------------------------
1 | package data
2 |
3 |
4 | import kotlinx.serialization.SerialName
5 | import kotlinx.serialization.Serializable
6 |
7 | @Serializable
8 | data class InternshipApiModel(
9 | @SerialName("internships_meta")
10 | val internshipsMeta: Map
11 | ) {
12 | @Serializable
13 | data class Internship(
14 | @SerialName("company_logo")
15 | val companyLogo: String,
16 | @SerialName("company_name")
17 | val companyName: String,
18 | @SerialName("duration")
19 | val duration: String,
20 | @SerialName("id")
21 | val id: Int,
22 | @SerialName("posted_by_label")
23 | val postedByLabel: String,
24 | @SerialName("profile_name")
25 | val profileName: String,
26 | @SerialName("start_date")
27 | val startDate: String,
28 | @SerialName("stipend")
29 | val stipend: Stipend?,
30 | @SerialName("title")
31 | val title: String,
32 | @SerialName("url")
33 | val url: String,
34 | @SerialName("labels_app_in_card")
35 | val labels: List? = null
36 | ) {
37 | @Serializable
38 | data class Stipend(
39 | @SerialName("large_stipend_text")
40 | val largeStipendText: Boolean?,
41 | @SerialName("salary")
42 | val salary: String?,
43 | @SerialName("salaryType")
44 | val salaryType: String?,
45 | @SerialName("tooltip")
46 | val tooltip: String?
47 | )
48 | }
49 | }
--------------------------------------------------------------------------------
/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 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 |
28 | UIRequiredDeviceCapabilities
29 |
30 | armv7
31 |
32 | UISupportedInterfaceOrientations
33 |
34 | UIInterfaceOrientationPortrait
35 | UIInterfaceOrientationLandscapeLeft
36 | UIInterfaceOrientationLandscapeRight
37 |
38 | UISupportedInterfaceOrientations~ipad
39 |
40 | UIInterfaceOrientationPortrait
41 | UIInterfaceOrientationPortraitUpsideDown
42 | UIInterfaceOrientationLandscapeLeft
43 | UIInterfaceOrientationLandscapeRight
44 |
45 | UILaunchScreen
46 |
47 |
48 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/domain/ResultPagingSource.kt:
--------------------------------------------------------------------------------
1 | package domain
2 |
3 | import androidx.paging.PagingSource
4 | import androidx.paging.PagingState
5 | import util.Result
6 |
7 | open class ResultPagingSource(private val fetch: suspend (page: Int, pageSize: Int) -> Result>) :
8 | PagingSource() {
9 |
10 | override fun getRefreshKey(state: PagingState): Int? = null
11 |
12 | override suspend fun load(params: LoadParams): LoadResult =
13 | (params.key ?: 1).let { _page ->
14 | try {
15 | fetch(_page, params.loadSize)
16 | .run {
17 | when (this) {
18 | /* success state */
19 | is Result.Success -> {
20 | LoadResult.Page(
21 | data = value,
22 | /* no previous pagination int as page */
23 | prevKey = _page.takeIf { it > 1 }?.dec(),
24 | /* no pagination if no results found else next page as +1 */
25 | nextKey = _page.takeIf { value.size >= params.loadSize }?.inc()
26 | )
27 | }
28 |
29 | /* error state */
30 | is Error -> LoadResult.Error(this)
31 |
32 | /* should not reach this point */
33 | else -> LoadResult.Error(IllegalStateException("$this type of [Result] is not allowed here"))
34 | }
35 | }
36 | } catch (e: Exception) {
37 | LoadResult.Error(e)
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/androidApp/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/di/Koin.kt:
--------------------------------------------------------------------------------
1 | package di
2 |
3 | import data.RepositoryImpl
4 | import domain.Repository
5 | import domain.usecase.StreamInternshipUseCase
6 | import io.ktor.client.HttpClient
7 | import io.ktor.client.plugins.HttpTimeout
8 | import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
9 | import io.ktor.client.plugins.defaultRequest
10 | import io.ktor.client.plugins.logging.LogLevel
11 | import io.ktor.client.plugins.logging.Logger
12 | import io.ktor.client.plugins.logging.Logging
13 | import io.ktor.http.URLBuilder
14 | import io.ktor.http.takeFrom
15 | import io.ktor.serialization.kotlinx.json.json
16 | import kotlinx.serialization.json.Json
17 | import org.koin.core.context.startKoin
18 | import org.koin.core.module.dsl.bind
19 | import org.koin.core.module.dsl.singleOf
20 | import org.koin.dsl.module
21 | import ui.internships.InternshipViewModel
22 |
23 | fun initKoin() = startKoin {
24 | modules(
25 | networkModule,
26 | repositoryModule,
27 | useCaseModule,
28 | viewModelModule
29 | )
30 | }
31 |
32 | private val networkModule = module {
33 | single {
34 | HttpClient {
35 | defaultRequest {
36 | url.takeFrom(URLBuilder().takeFrom("https://internshala.com/"))
37 | }
38 | install(HttpTimeout) {
39 | requestTimeoutMillis = 15_000
40 | }
41 | install(ContentNegotiation) {
42 | json(Json {
43 | ignoreUnknownKeys = true
44 | prettyPrint = true
45 | })
46 | }
47 | install(Logging) {
48 | level = LogLevel.ALL
49 | logger = object : Logger {
50 | override fun log(message: String) {
51 | println(message)
52 | }
53 | }
54 | }
55 | }
56 | }
57 | }
58 |
59 | private val useCaseModule = module {
60 | singleOf(::StreamInternshipUseCase)
61 | }
62 |
63 | private val repositoryModule = module {
64 | singleOf(::RepositoryImpl) { bind() }
65 | }
66 |
67 | private val viewModelModule = module {
68 | factory { InternshipViewModel(get()) }
69 | }
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/ui/internships/InternshipsScreen.kt:
--------------------------------------------------------------------------------
1 | @file:OptIn(ExperimentalResourceApi::class)
2 |
3 | package ui.internships
4 |
5 | import androidx.compose.material.Icon
6 | import androidx.compose.material.IconButton
7 | import androidx.compose.material.Scaffold
8 | import androidx.compose.material.Text
9 | import androidx.compose.material.TopAppBar
10 | import androidx.compose.material.icons.Icons
11 | import androidx.compose.material.icons.filled.Menu
12 | import androidx.compose.material.icons.filled.Search
13 | import androidx.compose.runtime.Composable
14 | import androidx.compose.runtime.getValue
15 | import androidx.compose.runtime.rememberUpdatedState
16 | import androidx.compose.ui.graphics.Color
17 | import androidx.compose.ui.unit.dp
18 | import app.cash.paging.compose.collectAsLazyPagingItems
19 | import org.jetbrains.compose.resources.ExperimentalResourceApi
20 | import org.koin.core.component.KoinComponent
21 | import org.koin.core.component.inject
22 | import ui.PagingListUI
23 |
24 | class InternshipsScreen : KoinComponent {
25 | private val viewModel: InternshipViewModel by inject()
26 |
27 | @Composable
28 | fun content() {
29 | val result by rememberUpdatedState(viewModel.internships.collectAsLazyPagingItems())
30 | return Scaffold(
31 | topBar = {
32 | TopAppBar(
33 | title = { Text("Internships") },
34 | elevation = 0.dp,
35 | navigationIcon = {
36 | IconButton(onClick = { println("Drawer clicked") }) {
37 | Icon(imageVector = Icons.Default.Menu, contentDescription = "Menu")
38 | }
39 | },
40 | actions = {
41 | IconButton(onClick = { println("Search Internships!") }) {
42 | Icon(imageVector = Icons.Default.Search, contentDescription = "Search")
43 | }
44 | },
45 | backgroundColor = Color.White
46 | )
47 | },
48 | drawerContent = { /*Drawer content*/ },
49 | content = { PagingListUI(data = result, content = { InternshipCard(it) }) },
50 | )
51 | }
52 | }
--------------------------------------------------------------------------------
/shared/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.kotlinMultiplatform)
3 | alias(libs.plugins.androidLibrary)
4 | id("org.jetbrains.compose")
5 | kotlin("plugin.serialization").version("1.9.10")
6 | }
7 |
8 | @OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
9 | kotlin {
10 | targetHierarchy.default()
11 |
12 | androidTarget {
13 | }
14 |
15 | listOf(
16 | iosX64(),
17 | iosArm64(),
18 | iosSimulatorArm64()
19 | ).forEach { iosTarget ->
20 | iosTarget.binaries.framework {
21 | baseName = "shared"
22 | isStatic = true
23 | }
24 | }
25 |
26 | sourceSets {
27 | val commonMain by getting {
28 | dependencies {
29 | implementation(compose.runtime)
30 | implementation(compose.foundation)
31 | implementation(compose.material)
32 | @OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class)
33 | implementation(compose.components.resources)
34 | implementation(libs.koin.core)
35 | implementation(libs.koin.test)
36 | implementation(libs.ktor.client.core)
37 | implementation(libs.ktor.serialization.kotlinx.json)
38 | implementation(libs.ktor.client.logging)
39 | implementation(libs.ktor.client.content.negotiation)
40 | implementation(libs.kotlinx.coroutines.core)
41 | implementation(libs.paging.compose.common)
42 | api(libs.kmm.viewmodel.core)
43 | implementation(libs.kamel.image)
44 | }
45 | }
46 | val commonTest by getting {
47 | dependencies {
48 | implementation(libs.kotlin.test)
49 | }
50 | }
51 | val androidMain by getting {
52 | dependencies {
53 | api(libs.appcompat)
54 | implementation(libs.ktor.client.okhttp)
55 | }
56 | }
57 | val iosX64Main by getting
58 | val iosArm64Main by getting
59 | val iosSimulatorArm64Main by getting
60 | val iosMain by getting {
61 | dependsOn(commonMain)
62 | iosX64Main.dependsOn(this)
63 | iosArm64Main.dependsOn(this)
64 | iosSimulatorArm64Main.dependsOn(this)
65 |
66 | dependencies {
67 | implementation(libs.ktor.client.darwin)
68 | }
69 | }
70 | }
71 | }
72 |
73 | android {
74 | namespace = "com.learning.app"
75 | compileSdk = 34
76 | sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
77 | sourceSets["main"].resources.srcDirs("src/commonMain/resources")
78 | defaultConfig {
79 | minSdk = 21
80 | }
81 | compileOptions {
82 | sourceCompatibility = JavaVersion.VERSION_17
83 | targetCompatibility = JavaVersion.VERSION_17
84 | }
85 | kotlin {
86 | jvmToolchain(17)
87 | }
88 | }
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/iosApp/Learning App.xcodeproj/xcshareddata/xcschemes/iosApp.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
42 |
44 |
50 |
51 |
52 |
53 |
59 |
61 |
67 |
68 |
69 |
70 |
72 |
73 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/App.kt:
--------------------------------------------------------------------------------
1 | import androidx.compose.foundation.background
2 | import androidx.compose.foundation.layout.height
3 | import androidx.compose.material.BottomNavigation
4 | import androidx.compose.material.BottomNavigationItem
5 | import androidx.compose.material.Icon
6 | import androidx.compose.material.MaterialTheme
7 | import androidx.compose.material.Scaffold
8 | import androidx.compose.material.Text
9 | import androidx.compose.material.icons.Icons
10 | import androidx.compose.material.icons.filled.AccountBox
11 | import androidx.compose.material.icons.filled.Add
12 | import androidx.compose.material.icons.filled.Home
13 | import androidx.compose.material.icons.filled.Notifications
14 | import androidx.compose.runtime.Composable
15 | import androidx.compose.runtime.getValue
16 | import androidx.compose.runtime.mutableStateOf
17 | import androidx.compose.runtime.remember
18 | import androidx.compose.runtime.setValue
19 | import androidx.compose.ui.Modifier
20 | import androidx.compose.ui.graphics.Color
21 | import androidx.compose.ui.graphics.vector.ImageVector
22 | import androidx.compose.ui.unit.dp
23 | import ui.CoursesScreen
24 | import ui.HomeScreen
25 | import ui.internships.InternshipsScreen
26 | import ui.JobsScreen
27 | import ui.Screen
28 |
29 | @Composable
30 | fun App() {
31 | MaterialTheme {
32 | val screens = Screen.values()
33 | var selectedScreen by remember { mutableStateOf(screens.first()) }
34 |
35 | Scaffold(
36 | bottomBar = {
37 | BottomNavigation(
38 | backgroundColor = Color.White,
39 | modifier = Modifier.height(64.dp)
40 | ) {
41 | screens.forEach { screen ->
42 | BottomNavigationItem(
43 | modifier = Modifier.background(Color.White),
44 | selectedContentColor = ui.theme.Color.textOnPrimary,
45 | unselectedContentColor = Color.Gray,
46 | icon = {
47 | Icon(
48 | imageVector = getIconForScreen(screen),
49 | contentDescription = screen.textValue
50 | )
51 | },
52 | label = { Text(screen.textValue) },
53 | selected = screen == selectedScreen,
54 | onClick = { selectedScreen = screen },
55 | )
56 | }
57 | }
58 | },
59 | content = { getScreen(selectedScreen) }
60 | )
61 | }
62 | }
63 |
64 | @Composable
65 | fun getIconForScreen(screen: Screen): ImageVector {
66 | return when (screen) {
67 | Screen.INTERNSHIPS -> Icons.Default.AccountBox
68 | Screen.JOBS -> Icons.Default.Add
69 | Screen.COURSES -> Icons.Default.Notifications
70 | else -> Icons.Default.Home
71 | }
72 | }
73 |
74 | @Composable
75 | fun getScreen(selectedScreen: Screen) = when (selectedScreen) {
76 | Screen.INTERNSHIPS -> InternshipsScreen().content()
77 | Screen.JOBS -> JobsScreen()
78 | Screen.COURSES -> CoursesScreen()
79 | else -> HomeScreen()
80 | }
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | agp = "8.1.1"
3 | kotlin = "1.9.10"
4 | compose = "1.5.4"
5 | compose-compiler = "1.5.3"
6 | compose-material3 = "1.1.2"
7 | androidx-activityCompose = "1.8.0"
8 | activityComposeVersion = "1.8.0"
9 | appcompatVersion = "1.6.1"
10 | kamelImageVersion = "0.8.2"
11 | kmmViewmodelCoreVersion = "1.0.0-ALPHA-14"
12 | koinCoreVersion = "3.5.0"
13 | kotlinxCoroutinesCoreVersion = "1.7.3"
14 | ktorVersion = "2.3.5"
15 | pagingCommonVersion = "3.3.0-alpha02-0.4.0"
16 |
17 | [libraries]
18 | appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompatVersion" }
19 | kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
20 | androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activityCompose" }
21 | compose-ui = { module = "androidx.compose.ui:ui", version.ref = "compose" }
22 | compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" }
23 | compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "compose" }
24 | compose-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "compose" }
25 | compose-material3 = { module = "androidx.compose.material3:material3", version.ref = "compose-material3" }
26 | kamel-image = { module = "media.kamel:kamel-image", version.ref = "kamelImageVersion" }
27 | kmm-viewmodel-core = { module = "com.rickclephas.kmm:kmm-viewmodel-core", version.ref = "kmmViewmodelCoreVersion" }
28 | koin-androidx-compose = { module = "io.insert-koin:koin-androidx-compose", version.ref = "koinCoreVersion" }
29 | koin-android = { module = "io.insert-koin:koin-android", version.ref = "koinCoreVersion" }
30 | koin-core = { module = "io.insert-koin:koin-core", version.ref = "koinCoreVersion" }
31 | koin-test = { module = "io.insert-koin:koin-test", version.ref = "koinCoreVersion" }
32 | kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutinesCoreVersion" }
33 | kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesCoreVersion" }
34 | ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktorVersion" }
35 | ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktorVersion" }
36 | ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktorVersion" }
37 | ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktorVersion" }
38 | ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktorVersion" }
39 | ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktorVersion" }
40 | paging-common = { module = "app.cash.paging:paging-common", version.ref = "pagingCommonVersion" }
41 | paging-runtime-uikit = { module = "app.cash.paging:paging-runtime-uikit", version.ref = "pagingCommonVersion" }
42 | paging-compose-common = { module = "app.cash.paging:paging-compose-common", version.ref = "pagingCommonVersion" }
43 |
44 | [plugins]
45 | androidApplication = { id = "com.android.application", version.ref = "agp" }
46 | androidLibrary = { id = "com.android.library", version.ref = "agp" }
47 | kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
48 | kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
49 | kotlinCocoapods = { id = "org.jetbrains.kotlin.native.cocoapods", version.ref = "kotlin" }
50 |
51 | [bundles]
52 |
53 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/ui/PagingListUI.kt:
--------------------------------------------------------------------------------
1 | package ui
2 |
3 | import androidx.compose.foundation.BorderStroke
4 | import androidx.compose.foundation.background
5 | import androidx.compose.foundation.border
6 | import androidx.compose.foundation.layout.Arrangement
7 | import androidx.compose.foundation.layout.Box
8 | import androidx.compose.foundation.layout.Column
9 | import androidx.compose.foundation.layout.PaddingValues
10 | import androidx.compose.foundation.layout.Row
11 | import androidx.compose.foundation.layout.fillMaxSize
12 | import androidx.compose.foundation.layout.fillMaxWidth
13 | import androidx.compose.foundation.layout.padding
14 | import androidx.compose.foundation.layout.wrapContentWidth
15 | import androidx.compose.foundation.lazy.LazyColumn
16 | import androidx.compose.material.CircularProgressIndicator
17 | import androidx.compose.material.Divider
18 | import androidx.compose.material.OutlinedButton
19 | import androidx.compose.material.Text
20 | import androidx.compose.runtime.Composable
21 | import androidx.compose.ui.Alignment
22 | import androidx.compose.ui.Modifier
23 | import androidx.compose.ui.graphics.Color
24 | import androidx.compose.ui.layout.onPlaced
25 | import androidx.compose.ui.text.style.TextAlign
26 | import androidx.compose.ui.unit.dp
27 | import app.cash.paging.LoadStateError
28 | import app.cash.paging.LoadStateLoading
29 | import app.cash.paging.LoadStateNotLoading
30 | import app.cash.paging.compose.LazyPagingItems
31 | import ui.theme.Color as UiColor
32 |
33 | @Composable
34 | fun PagingListUI(
35 | data: LazyPagingItems,
36 | content: @Composable (T) -> Unit
37 | ) {
38 | LazyColumn(
39 | modifier = Modifier
40 | .fillMaxSize()
41 | .background(Color.White),
42 | horizontalAlignment = Alignment.CenterHorizontally,
43 | ) {
44 |
45 | items(data.itemCount) { index ->
46 | val item = data[index]
47 | item?.let { content(it) }
48 | Divider(
49 | color = UiColor.background,
50 | thickness = 10.dp,
51 | modifier = Modifier.border(border = BorderStroke(0.5.dp, Color.LightGray))
52 | )
53 | }
54 |
55 | data.loadState.apply {
56 | when {
57 | refresh is LoadStateNotLoading && data.itemCount < 1 -> {
58 | item {
59 | Box(
60 | modifier = Modifier.fillParentMaxSize(),
61 | contentAlignment = Alignment.Center
62 | ) {
63 | Text(
64 | text = "No Items",
65 | modifier = Modifier.align(Alignment.Center),
66 | textAlign = TextAlign.Center
67 | )
68 | }
69 | }
70 | }
71 |
72 | refresh is LoadStateLoading -> {
73 | item {
74 | Box(
75 | modifier = Modifier.fillParentMaxSize(),
76 | contentAlignment = Alignment.Center
77 | ) {
78 | CircularProgressIndicator(
79 | color = UiColor.primary
80 | )
81 | }
82 | }
83 | }
84 |
85 | append is LoadStateLoading -> {
86 | item {
87 | CircularProgressIndicator(
88 | color = UiColor.primary,
89 | modifier = Modifier.fillMaxWidth()
90 | .padding(16.dp)
91 | .wrapContentWidth(Alignment.CenterHorizontally)
92 | )
93 | }
94 | }
95 |
96 | refresh is LoadStateError -> {
97 | item {
98 | ErrorView(
99 | message = "No Internet Connection.",
100 | onClickRetry = { data.retry() },
101 | modifier = Modifier.fillParentMaxSize()
102 | )
103 | }
104 | }
105 |
106 | append is LoadStateError -> {
107 | item {
108 | ErrorItem(
109 | message = "No Internet Connection",
110 | onClickRetry = { data.retry() },
111 | )
112 | }
113 | }
114 | }
115 | }
116 | }
117 | }
118 |
119 | @Composable
120 | private fun ErrorItem(
121 | message: String,
122 | modifier: Modifier = Modifier,
123 | onClickRetry: () -> Unit
124 | ) {
125 | Row(
126 | modifier = modifier.padding(16.dp),
127 | horizontalArrangement = Arrangement.SpaceBetween,
128 | verticalAlignment = Alignment.CenterVertically
129 | ) {
130 | Text(
131 | text = message,
132 | maxLines = 1,
133 | modifier = Modifier.weight(1f),
134 | color = androidx.compose.ui.graphics.Color.Red
135 | )
136 | OutlinedButton(onClick = onClickRetry) {
137 | Text(text = "Try again")
138 | }
139 | }
140 | }
141 |
142 | @Composable
143 | private fun ErrorView(
144 | message: String,
145 | modifier: Modifier = Modifier,
146 | onClickRetry: () -> Unit
147 | ) {
148 | Column(
149 | modifier = modifier.padding(16.dp).onPlaced { _ ->
150 | },
151 | verticalArrangement = Arrangement.Center,
152 | horizontalAlignment = Alignment.CenterHorizontally
153 | ) {
154 | Text(
155 | text = message,
156 | maxLines = 1,
157 | modifier = Modifier.align(Alignment.CenterHorizontally),
158 | color = androidx.compose.ui.graphics.Color.Red
159 | )
160 | OutlinedButton(
161 | onClick = onClickRetry, modifier = Modifier
162 | .fillMaxWidth()
163 | .padding(16.dp)
164 | .wrapContentWidth(Alignment.CenterHorizontally)
165 | ) {
166 | Text(text = "Try again")
167 | }
168 | }
169 | }
--------------------------------------------------------------------------------
/androidApp/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/shared/src/commonMain/kotlin/ui/internships/InternshipCard.kt:
--------------------------------------------------------------------------------
1 | package ui.internships
2 |
3 | import androidx.compose.foundation.Image
4 | import androidx.compose.foundation.layout.Arrangement
5 | import androidx.compose.foundation.layout.Column
6 | import androidx.compose.foundation.layout.Row
7 | import androidx.compose.foundation.layout.Spacer
8 | import androidx.compose.foundation.layout.fillMaxWidth
9 | import androidx.compose.foundation.layout.height
10 | import androidx.compose.foundation.layout.padding
11 | import androidx.compose.foundation.layout.width
12 | import androidx.compose.foundation.shape.RoundedCornerShape
13 | import androidx.compose.material.Icon
14 | import androidx.compose.material.MaterialTheme
15 | import androidx.compose.material.Surface
16 | import androidx.compose.material.Text
17 | import androidx.compose.material.icons.Icons
18 | import androidx.compose.material.icons.filled.Home
19 | import androidx.compose.runtime.Composable
20 | import androidx.compose.ui.Alignment
21 | import androidx.compose.ui.Modifier
22 | import androidx.compose.ui.draw.clip
23 | import androidx.compose.ui.graphics.Color
24 | import androidx.compose.ui.graphics.ColorFilter
25 | import androidx.compose.ui.layout.ContentScale
26 | import androidx.compose.ui.text.font.FontWeight
27 | import androidx.compose.ui.unit.dp
28 | import androidx.compose.ui.unit.sp
29 | import data.InternshipApiModel
30 | import io.kamel.image.KamelImage
31 | import io.kamel.image.asyncPainterResource
32 | import org.jetbrains.compose.resources.ExperimentalResourceApi
33 | import org.jetbrains.compose.resources.painterResource
34 | import ui.theme.Color as UiColor
35 |
36 | @ExperimentalResourceApi
37 | @Composable
38 | fun InternshipCard(internship: InternshipApiModel.Internship) {
39 | Column(modifier = Modifier.fillMaxWidth().padding(16.dp)) {
40 | Row(
41 | modifier = Modifier.fillMaxWidth(),
42 | horizontalArrangement = Arrangement.SpaceBetween,
43 | verticalAlignment = Alignment.CenterVertically
44 | ) {
45 | Column(modifier = Modifier.weight(0.8f)) {
46 | Text(
47 | text = internship.profileName,
48 | fontSize = 18.sp,
49 | modifier = Modifier.padding(end = 8.dp),
50 | fontWeight = FontWeight.SemiBold
51 | )
52 | Spacer(Modifier.height(8.dp))
53 | Text(
54 | text = internship.companyName,
55 | fontSize = 14.sp,
56 | color = UiColor.textTertiary
57 | )
58 | }
59 |
60 | KamelImage(
61 | resource = asyncPainterResource(data = "https://internshala.com/uploads/logo/${internship.companyLogo}"),
62 | contentDescription = "Company Logo",
63 | modifier = Modifier.height(48.dp).width(48.dp)
64 | .clip(shape = RoundedCornerShape(8.dp)),
65 | contentAlignment = Alignment.Center,
66 | contentScale = ContentScale.Fit
67 | )
68 | }
69 |
70 |
71 | Spacer(modifier = Modifier.height(16.dp))
72 | Row(verticalAlignment = Alignment.CenterVertically) {
73 | Icon(
74 | imageVector = Icons.Default.Home,
75 | tint = UiColor.textSecondary,
76 | contentDescription = "Menu",
77 | modifier = Modifier.height(18.dp).width(18.dp)
78 | )
79 | Spacer(Modifier.width(4.dp))
80 | Text(
81 | text = "Work from Home",
82 | fontSize = 15.sp,
83 | color = UiColor.textSecondary,
84 | )
85 | }
86 |
87 |
88 | Spacer(modifier = Modifier.height(16.dp))
89 | Row(verticalAlignment = Alignment.CenterVertically) {
90 | Image(
91 | painter = painterResource("ic_play_circle.xml"),
92 | contentDescription = "Menu",
93 | colorFilter = ColorFilter.tint(UiColor.textSecondary)
94 | )
95 | Spacer(modifier = Modifier.width(4.dp))
96 | Text(text = internship.startDate, fontSize = 15.sp, color = UiColor.textSecondary)
97 | Spacer(Modifier.width(24.dp))
98 | Image(
99 | painter = painterResource("ic_calendar.xml"),
100 | contentDescription = "Calendar",
101 | colorFilter = ColorFilter.tint(UiColor.textSecondary)
102 | )
103 | Spacer(modifier = Modifier.width(4.dp))
104 | Text(
105 | text = internship.duration,
106 | fontSize = 15.sp,
107 | color = UiColor.textSecondary
108 | )
109 | }
110 |
111 | Spacer(modifier = Modifier.height(16.dp))
112 | Row(verticalAlignment = Alignment.CenterVertically) {
113 | Image(
114 | painter = painterResource("ic_stipend.xml"),
115 | contentDescription = "Stipend",
116 | colorFilter = ColorFilter.tint(UiColor.textSecondary)
117 | )
118 | Spacer(Modifier.width(4.dp))
119 | Text(
120 | text = internship.stipend?.salary.orEmpty(),
121 | fontSize = 15.sp,
122 | color = UiColor.textSecondary,
123 | )
124 | }
125 |
126 | Spacer(Modifier.height(16.dp))
127 | Row {
128 | internship.labels?.map { label ->
129 | Surface(
130 | shape = MaterialTheme.shapes.medium,
131 | color = UiColor.background,
132 | contentColor = Color.DarkGray
133 | ) {
134 | Text(
135 | text = label,
136 | modifier = Modifier.padding(horizontal = 6.dp, vertical = 4.dp),
137 | fontSize = 13.sp
138 | )
139 | }
140 | Spacer(Modifier.width(8.dp))
141 | }
142 | }
143 |
144 | Spacer(Modifier.height(14.dp))
145 | Surface(
146 | shape = MaterialTheme.shapes.medium,
147 | color = Color(233, 241, 254, 205),
148 | contentColor = UiColor.primary
149 | ) {
150 | Row(
151 | verticalAlignment = Alignment.CenterVertically,
152 | modifier = Modifier.padding(horizontal = 4.dp, vertical = 2.dp)
153 | ) {
154 | Image(
155 | painterResource("ic_clock.xml"),
156 | contentDescription = null,
157 | colorFilter = ColorFilter.tint(UiColor.textOnPrimary)
158 | )
159 | Text(
160 | modifier = Modifier.padding(start = 4.dp),
161 | text = internship.postedByLabel,
162 | fontSize = 13.sp,
163 | color = UiColor.textOnPrimary
164 | )
165 | }
166 | }
167 | }
168 | }
--------------------------------------------------------------------------------
/iosApp/Learning App.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 54;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; };
11 | 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; };
12 | 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; };
13 | 7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; };
14 | /* End PBXBuildFile section */
15 |
16 | /* Begin PBXCopyFilesBuildPhase section */
17 | 7555FFB4242A642300829871 /* Embed Frameworks */ = {
18 | isa = PBXCopyFilesBuildPhase;
19 | buildActionMask = 12;
20 | dstPath = "";
21 | dstSubfolderSpec = 10;
22 | files = (
23 | );
24 | name = "Embed Frameworks";
25 | runOnlyForDeploymentPostprocessing = 0;
26 | };
27 | /* End PBXCopyFilesBuildPhase section */
28 |
29 | /* Begin PBXFileReference section */
30 | 058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
31 | 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
32 | 2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; };
33 | 7555FF7B242A565900829871 /* Learning App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Learning App.app"; sourceTree = BUILT_PRODUCTS_DIR; };
34 | 7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
35 | 7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
36 | /* End PBXFileReference section */
37 |
38 | /* Begin PBXFrameworksBuildPhase section */
39 | 7555FF78242A565900829871 /* Frameworks */ = {
40 | isa = PBXFrameworksBuildPhase;
41 | buildActionMask = 2147483647;
42 | files = (
43 | );
44 | runOnlyForDeploymentPostprocessing = 0;
45 | };
46 | /* End PBXFrameworksBuildPhase section */
47 |
48 | /* Begin PBXGroup section */
49 | 058557D7273AAEEB004C7B11 /* Preview Content */ = {
50 | isa = PBXGroup;
51 | children = (
52 | 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */,
53 | );
54 | path = "Preview Content";
55 | sourceTree = "";
56 | };
57 | 7555FF72242A565900829871 = {
58 | isa = PBXGroup;
59 | children = (
60 | 7555FF7D242A565900829871 /* iosApp */,
61 | 7555FF7C242A565900829871 /* Products */,
62 | 7555FFB0242A642200829871 /* Frameworks */,
63 | AD156C5EC3C69E8E80C52384 /* xcschemes */,
64 | );
65 | sourceTree = "";
66 | };
67 | 7555FF7C242A565900829871 /* Products */ = {
68 | isa = PBXGroup;
69 | children = (
70 | 7555FF7B242A565900829871 /* Learning App.app */,
71 | );
72 | name = Products;
73 | sourceTree = "";
74 | };
75 | 7555FF7D242A565900829871 /* iosApp */ = {
76 | isa = PBXGroup;
77 | children = (
78 | 058557BA273AAA24004C7B11 /* Assets.xcassets */,
79 | 7555FF82242A565900829871 /* ContentView.swift */,
80 | 7555FF8C242A565B00829871 /* Info.plist */,
81 | 2152FB032600AC8F00CF470E /* iOSApp.swift */,
82 | 058557D7273AAEEB004C7B11 /* Preview Content */,
83 | );
84 | path = iosApp;
85 | sourceTree = "";
86 | };
87 | 7555FFB0242A642200829871 /* Frameworks */ = {
88 | isa = PBXGroup;
89 | children = (
90 | );
91 | name = Frameworks;
92 | sourceTree = "";
93 | };
94 | AD156C5EC3C69E8E80C52384 /* xcschemes */ = {
95 | isa = PBXGroup;
96 | children = (
97 | );
98 | name = xcschemes;
99 | path = "Learning App.xcodeproj/project.xcworkspace/xcuserdata/prashant.xcuserdatad/xcschemes";
100 | sourceTree = "";
101 | };
102 | /* End PBXGroup section */
103 |
104 | /* Begin PBXNativeTarget section */
105 | 7555FF7A242A565900829871 /* Learning App */ = {
106 | isa = PBXNativeTarget;
107 | buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "Learning App" */;
108 | buildPhases = (
109 | 7555FFB5242A651A00829871 /* ShellScript */,
110 | 7555FF77242A565900829871 /* Sources */,
111 | 7555FF78242A565900829871 /* Frameworks */,
112 | 7555FF79242A565900829871 /* Resources */,
113 | 7555FFB4242A642300829871 /* Embed Frameworks */,
114 | );
115 | buildRules = (
116 | );
117 | dependencies = (
118 | );
119 | name = "Learning App";
120 | productName = "Learning App";
121 | productReference = 7555FF7B242A565900829871 /* Learning App.app */;
122 | productType = "com.apple.product-type.application";
123 | };
124 | /* End PBXNativeTarget section */
125 |
126 | /* Begin PBXProject section */
127 | 7555FF73242A565900829871 /* Project object */ = {
128 | isa = PBXProject;
129 | attributes = {
130 | BuildIndependentTargetsInParallel = YES;
131 | LastSwiftUpdateCheck = 1130;
132 | LastUpgradeCheck = 1500;
133 | ORGANIZATIONNAME = orgName;
134 | TargetAttributes = {
135 | 7555FF7A242A565900829871 = {
136 | CreatedOnToolsVersion = 11.3.1;
137 | };
138 | };
139 | };
140 | buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "Learning App" */;
141 | compatibilityVersion = "Xcode 9.3";
142 | developmentRegion = en;
143 | hasScannedForEncodings = 0;
144 | knownRegions = (
145 | en,
146 | Base,
147 | );
148 | mainGroup = 7555FF72242A565900829871;
149 | productRefGroup = 7555FF7C242A565900829871 /* Products */;
150 | projectDirPath = "";
151 | projectRoot = "";
152 | targets = (
153 | 7555FF7A242A565900829871 /* Learning App */,
154 | );
155 | };
156 | /* End PBXProject section */
157 |
158 | /* Begin PBXResourcesBuildPhase section */
159 | 7555FF79242A565900829871 /* Resources */ = {
160 | isa = PBXResourcesBuildPhase;
161 | buildActionMask = 2147483647;
162 | files = (
163 | 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */,
164 | 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */,
165 | );
166 | runOnlyForDeploymentPostprocessing = 0;
167 | };
168 | /* End PBXResourcesBuildPhase section */
169 |
170 | /* Begin PBXShellScriptBuildPhase section */
171 | 7555FFB5242A651A00829871 /* ShellScript */ = {
172 | isa = PBXShellScriptBuildPhase;
173 | buildActionMask = 12;
174 | files = (
175 | );
176 | inputFileListPaths = (
177 | );
178 | inputPaths = (
179 | );
180 | outputFileListPaths = (
181 | );
182 | outputPaths = (
183 | );
184 | runOnlyForDeploymentPostprocessing = 0;
185 | shellPath = /bin/sh;
186 | shellScript = "cd \"$SRCROOT/..\"\n./gradlew :shared:embedAndSignAppleFrameworkForXcode\n";
187 | };
188 | /* End PBXShellScriptBuildPhase section */
189 |
190 | /* Begin PBXSourcesBuildPhase section */
191 | 7555FF77242A565900829871 /* Sources */ = {
192 | isa = PBXSourcesBuildPhase;
193 | buildActionMask = 2147483647;
194 | files = (
195 | 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */,
196 | 7555FF83242A565900829871 /* ContentView.swift in Sources */,
197 | );
198 | runOnlyForDeploymentPostprocessing = 0;
199 | };
200 | /* End PBXSourcesBuildPhase section */
201 |
202 | /* Begin XCBuildConfiguration section */
203 | 7555FFA3242A565B00829871 /* Debug */ = {
204 | isa = XCBuildConfiguration;
205 | buildSettings = {
206 | ALWAYS_SEARCH_USER_PATHS = NO;
207 | CLANG_ANALYZER_NONNULL = YES;
208 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
209 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
210 | CLANG_CXX_LIBRARY = "libc++";
211 | CLANG_ENABLE_MODULES = YES;
212 | CLANG_ENABLE_OBJC_ARC = YES;
213 | CLANG_ENABLE_OBJC_WEAK = YES;
214 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
215 | CLANG_WARN_BOOL_CONVERSION = YES;
216 | CLANG_WARN_COMMA = YES;
217 | CLANG_WARN_CONSTANT_CONVERSION = YES;
218 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
219 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
220 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
221 | CLANG_WARN_EMPTY_BODY = YES;
222 | CLANG_WARN_ENUM_CONVERSION = YES;
223 | CLANG_WARN_INFINITE_RECURSION = YES;
224 | CLANG_WARN_INT_CONVERSION = YES;
225 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
226 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
227 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
228 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
229 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
230 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
231 | CLANG_WARN_STRICT_PROTOTYPES = YES;
232 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
233 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
234 | CLANG_WARN_UNREACHABLE_CODE = YES;
235 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
236 | COPY_PHASE_STRIP = NO;
237 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
238 | ENABLE_STRICT_OBJC_MSGSEND = YES;
239 | ENABLE_TESTABILITY = YES;
240 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
241 | EXCLUDED_ARCHS = "";
242 | GCC_C_LANGUAGE_STANDARD = gnu11;
243 | GCC_DYNAMIC_NO_PIC = NO;
244 | GCC_NO_COMMON_BLOCKS = YES;
245 | GCC_OPTIMIZATION_LEVEL = 0;
246 | GCC_PREPROCESSOR_DEFINITIONS = (
247 | "DEBUG=1",
248 | "$(inherited)",
249 | );
250 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
251 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
252 | GCC_WARN_UNDECLARED_SELECTOR = YES;
253 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
254 | GCC_WARN_UNUSED_FUNCTION = YES;
255 | GCC_WARN_UNUSED_VARIABLE = YES;
256 | IPHONEOS_DEPLOYMENT_TARGET = 14.1;
257 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
258 | MTL_FAST_MATH = YES;
259 | ONLY_ACTIVE_ARCH = YES;
260 | SDKROOT = iphoneos;
261 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
262 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
263 | };
264 | name = Debug;
265 | };
266 | 7555FFA4242A565B00829871 /* Release */ = {
267 | isa = XCBuildConfiguration;
268 | buildSettings = {
269 | ALWAYS_SEARCH_USER_PATHS = NO;
270 | CLANG_ANALYZER_NONNULL = YES;
271 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
272 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
273 | CLANG_CXX_LIBRARY = "libc++";
274 | CLANG_ENABLE_MODULES = YES;
275 | CLANG_ENABLE_OBJC_ARC = YES;
276 | CLANG_ENABLE_OBJC_WEAK = YES;
277 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
278 | CLANG_WARN_BOOL_CONVERSION = YES;
279 | CLANG_WARN_COMMA = YES;
280 | CLANG_WARN_CONSTANT_CONVERSION = YES;
281 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
282 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
283 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
284 | CLANG_WARN_EMPTY_BODY = YES;
285 | CLANG_WARN_ENUM_CONVERSION = YES;
286 | CLANG_WARN_INFINITE_RECURSION = YES;
287 | CLANG_WARN_INT_CONVERSION = YES;
288 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
289 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
290 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
291 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
292 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
293 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
294 | CLANG_WARN_STRICT_PROTOTYPES = YES;
295 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
296 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
297 | CLANG_WARN_UNREACHABLE_CODE = YES;
298 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
299 | COPY_PHASE_STRIP = NO;
300 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
301 | ENABLE_NS_ASSERTIONS = NO;
302 | ENABLE_STRICT_OBJC_MSGSEND = YES;
303 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
304 | EXCLUDED_ARCHS = "";
305 | GCC_C_LANGUAGE_STANDARD = gnu11;
306 | GCC_NO_COMMON_BLOCKS = YES;
307 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
308 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
309 | GCC_WARN_UNDECLARED_SELECTOR = YES;
310 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
311 | GCC_WARN_UNUSED_FUNCTION = YES;
312 | GCC_WARN_UNUSED_VARIABLE = YES;
313 | IPHONEOS_DEPLOYMENT_TARGET = 14.1;
314 | MTL_ENABLE_DEBUG_INFO = NO;
315 | MTL_FAST_MATH = YES;
316 | SDKROOT = iphoneos;
317 | SWIFT_COMPILATION_MODE = wholemodule;
318 | SWIFT_OPTIMIZATION_LEVEL = "-O";
319 | VALIDATE_PRODUCT = YES;
320 | };
321 | name = Release;
322 | };
323 | 7555FFA6242A565B00829871 /* Debug */ = {
324 | isa = XCBuildConfiguration;
325 | buildSettings = {
326 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
327 | CODE_SIGN_STYLE = Automatic;
328 | DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
329 | ENABLE_PREVIEWS = YES;
330 | FRAMEWORK_SEARCH_PATHS = (
331 | "$(inherited)",
332 | "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
333 | );
334 | INFOPLIST_FILE = iosApp/Info.plist;
335 | INFOPLIST_KEY_CFBundleDisplayName = "Learning App";
336 | INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education";
337 | LD_RUNPATH_SEARCH_PATHS = (
338 | "$(inherited)",
339 | "@executable_path/Frameworks",
340 | );
341 | OTHER_LDFLAGS = (
342 | "$(inherited)",
343 | "-framework",
344 | shared,
345 | );
346 | PRODUCT_BUNDLE_IDENTIFIER = com.learning.app;
347 | PRODUCT_NAME = "$(TARGET_NAME)";
348 | SWIFT_VERSION = 5.0;
349 | TARGETED_DEVICE_FAMILY = 1;
350 | };
351 | name = Debug;
352 | };
353 | 7555FFA7242A565B00829871 /* Release */ = {
354 | isa = XCBuildConfiguration;
355 | buildSettings = {
356 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
357 | CODE_SIGN_STYLE = Automatic;
358 | DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
359 | ENABLE_PREVIEWS = YES;
360 | FRAMEWORK_SEARCH_PATHS = (
361 | "$(inherited)",
362 | "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
363 | );
364 | INFOPLIST_FILE = iosApp/Info.plist;
365 | INFOPLIST_KEY_CFBundleDisplayName = "Learning App";
366 | INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education";
367 | LD_RUNPATH_SEARCH_PATHS = (
368 | "$(inherited)",
369 | "@executable_path/Frameworks",
370 | );
371 | OTHER_LDFLAGS = (
372 | "$(inherited)",
373 | "-framework",
374 | shared,
375 | );
376 | PRODUCT_BUNDLE_IDENTIFIER = com.learning.app;
377 | PRODUCT_NAME = "$(TARGET_NAME)";
378 | SWIFT_VERSION = 5.0;
379 | TARGETED_DEVICE_FAMILY = 1;
380 | };
381 | name = Release;
382 | };
383 | /* End XCBuildConfiguration section */
384 |
385 | /* Begin XCConfigurationList section */
386 | 7555FF76242A565900829871 /* Build configuration list for PBXProject "Learning App" */ = {
387 | isa = XCConfigurationList;
388 | buildConfigurations = (
389 | 7555FFA3242A565B00829871 /* Debug */,
390 | 7555FFA4242A565B00829871 /* Release */,
391 | );
392 | defaultConfigurationIsVisible = 0;
393 | defaultConfigurationName = Debug;
394 | };
395 | 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "Learning App" */ = {
396 | isa = XCConfigurationList;
397 | buildConfigurations = (
398 | 7555FFA6242A565B00829871 /* Debug */,
399 | 7555FFA7242A565B00829871 /* Release */,
400 | );
401 | defaultConfigurationIsVisible = 0;
402 | defaultConfigurationName = Debug;
403 | };
404 | /* End XCConfigurationList section */
405 | };
406 | rootObject = 7555FF73242A565900829871 /* Project object */;
407 | }
408 |
--------------------------------------------------------------------------------