├── 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 | --------------------------------------------------------------------------------