Практическое задание
Нужно разработать мультиплатформенное приложение для просмотра GitHub репозиториев.
Во время работы над практическим заданием настоятельно рекомендуем обращаться к разделу Памятки для разработчика
Функциональные возможности
- Авторизация пользователя (personal access token)
- Просмотр списка репозиториев пользователя (первые 10)
- Просмотр детальной информации выбранного репозитория
- статистика (forks, stars, watchers)
- ссылка на web страницу репозитория
- лицензия
- readme
Технические требования
- В качестве шаблонного проекта использовать
mobile-moko-boilerplate
- После настройки нового проекта по инструкции он должен успешно проходить CI
- Создать отдельные модули для:
common
кода, фичи авторизации, и фичи репозитория. - Сохранять токен авторизации в хранилище устройства:
SharedPreferences
дляAndroid
иNSUserDefaults
дляiOS
. Работу с хранилищем делегировать классуKeyValueStorage
- Использовать
multiplatform-settings
для работы с хранилищем устройства - Использовать
moko-mvvm
для внедрения всех ее возможностей, о которых вы узнали статьи - Использовать
moko-resources
для использования строк локализации приложения - Использовать
moko-units
для реализации списка репозиториев - Использовать
ExceptionMappersStorage
изmoko-errors
(не используйтеExceptionHandler
) - Вся логика должна находиться в
common
коде - Навигация на
iOS
должна быть реализована используяAppCoordinator
, безstoryboards
- Логика хранения данных должна находиться в
common
коде - Логика работы с сетью должна находиться в
common
коде - Для работы с сетью использовать
Ktor Client
- Используйте доменные сущности, вместо сетевых
- При перезапуске приложения авторизация должна сохраняться
- Использовать локализацию для всех строк, показываемых пользователю
- Использовать векторную графику везде, где это возможно
- Обеспечить поддержку Android API 21
- Локализовать проект используя
sheets-localizations-generator
- обеспечьте поддержку русского и английского языков
- Обеспечить поддержку iOS 13.0
Классы приложения
mpp-library
class AppRepository {
@Throws(Exception::class)
suspend fun getRepositories(): List<Repo> {
// TODO:
}
@Throws(Exception::class)
suspend fun getRepository(repoId: String): RepoDetails {
// TODO:
}
@Throws(Exception::class)
suspend fun getRepositoryReadme(
ownerName: String,
repositoryName: String,
branchName: String
): String {
// TODO:
}
@Throws(Exception::class)
suspend fun signIn(token: String): UserInfo {
// TODO:
}
// TODO:
}
class KeyValueStorage {
var authToken: String?
}
mpp-library-feature-auth
class AuthViewModel {
val token: MutableLiveData<String>
val state: LiveData<State>
val actions: Flow<Action>
fun onSignButtonPressed() {
// TODO:
}
sealed interface State {
object Idle : State
object Loading : State
data class InvalidInput(val reason: String) : State
}
sealed interface Action {
data class ShowError(val message: String) : Action
object RouteToMain : Action
}
// TODO:
}
mpp-library-feature-repo
class RepositoryInfoViewModel {
val state: LiveData<State>
sealed interface State {
object Loading : State
data class Error(val error: String) : State
data class Loaded(
val githubRepo: Repo,
val readmeState: ReadmeState
) : State
}
sealed interface ReadmeState {
object Loading : ReadmeState
object Empty : ReadmeState
data class Error(val error: String) : ReadmeState
data class Loaded(val markdown: String) : ReadmeState
}
// TODO:
}
class RepositoriesListViewModel {
val state: LiveData<State>
sealed interface State {
object Loading : State
data class Loaded(val repos: List<Repo>) : State
data class Error(val error: String) : State
object Empty : State
}
// TODO:
}
android-app
class MainActivity: AppCompatActivity {
// TODO:
}
class AuthFragment: Fragment {
// TODO:
}
class RepositoriesListFragment: Fragment {
// TODO:
}
class DetailInfoFragment: Fragment {
// TODO:
}
ios-app
class RepositoriesListViewController: UIViewController {
// TODO:
}
class RepositoryDetailInfoViewController: UIViewController {
// TODO:
}
class AuthViewController: UIViewController {
// TODO:
}
Диаграмма классов
На графе отображена зависимость компонентов KMM приложения друг от друга, цветами выделены подграфы:
Фиолетовый - Common, Зеленый - Android, Синий - iOS
classDiagram
class AuthViewModel:::common
class RepositoryInfoViewModel:::common
class RepositoriesListViewModel:::common
class GitHubRepoRepository:::common
class KeyValueStorage:::common
class MainActivity:::android
class RepositoriesListFragment:::android
class DetailInfoFragment:::android
class AuthFragment:::android
class RepositoriesListViewController:::ios
class RepositoryDetailInfoViewController:::ios
class AuthViewController:::ios
MainActivity --> AuthFragment
MainActivity --> RepositoriesListFragment
MainActivity --> DetailInfoFragment
RepositoriesListFragment --> RepositoriesListViewModel
DetailInfoFragment --> RepositoryInfoViewModel
AuthFragment --> AuthViewModel
RepositoriesListViewModel --> GitHubRepoRepository
AuthViewModel --> GitHubRepoRepository
RepositoryInfoViewModel --> GitHubRepoRepository
RepositoriesListViewController --> RepositoriesListViewModel
RepositoryDetailInfoViewController --> RepositoryInfoViewModel
AuthViewController --> AuthViewModel
GitHubRepoRepository --> KeyValueStorage
Материалы
- mobile-moko-boilerplate
- инструкция по созданию и настройке проекта на основе
mobile-moko-boilerplate
- инструкция по созданию и настройке проекта на основе
- GitHub REST API
- GitHub Basic Authorization
- GitHub user repositories
- Kotlinx.Serialization guide
- Подключение Ktor Client
- Настройке запросов в Ktor Client
- multiplatform-settings
- Android Дизайн
- iOS Дизайн