Skip to main content

Практическое задание

Нужно разработать мультиплатформенное приложение для просмотра GitHub репозиториев.

Во время работы над практическим заданием настоятельно рекомендуем обращаться к разделу Памятки для разработчика

Функциональные возможности

  1. Авторизация пользователя (personal access token)
  2. Просмотр списка репозиториев пользователя (первые 10)
  3. Просмотр детальной информации выбранного репозитория
    1. статистика (forks, stars, watchers)
    2. ссылка на web страницу репозитория
    3. лицензия
    4. readme

Технические требования

  1. В качестве шаблонного проекта использовать mobile-moko-boilerplate
  2. После настройки нового проекта по инструкции он должен успешно проходить CI
  3. Создать отдельные модули для: common кода, фичи авторизации, и фичи репозитория.
  4. Сохранять токен авторизации в хранилище устройства: SharedPreferences для Android и NSUserDefaults для iOS. Работу с хранилищем делегировать классу KeyValueStorage
  5. Использовать multiplatform-settings для работы с хранилищем устройства
  6. Использовать moko-mvvm для внедрения всех ее возможностей, о которых вы узнали статьи
  7. Использовать moko-resources для использования строк локализации приложения
  8. Использовать moko-units для реализации списка репозиториев
  9. Использовать ExceptionMappersStorage из moko-errors (не используйте ExceptionHandler)
  10. Вся логика должна находиться в common коде
  11. Навигация на iOS должна быть реализована используя AppCoordinator, без storyboards
  12. Логика хранения данных должна находиться в common коде
  13. Логика работы с сетью должна находиться в common коде
  14. Для работы с сетью использовать Ktor Client
  15. Используйте доменные сущности, вместо сетевых
  16. При перезапуске приложения авторизация должна сохраняться
  17. Использовать локализацию для всех строк, показываемых пользователю
  18. Использовать векторную графику везде, где это возможно
  19. Обеспечить поддержку Android API 21
  20. Локализовать проект используя sheets-localizations-generator
    • обеспечьте поддержку русского и английского языков
  21. Обеспечить поддержку 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

Материалы

  1. mobile-moko-boilerplate
    • инструкция по созданию и настройке проекта на основе mobile-moko-boilerplate
  2. GitHub REST API
  3. GitHub Basic Authorization
  4. GitHub user repositories
  5. Kotlinx.Serialization guide
  6. Подключение Ktor Client
  7. Настройке запросов в Ktor Client
  8. multiplatform-settings
  9. Android Дизайн
  10. iOS Дизайн