Kotlin Multiplatform — отличный фреймворк, позволяющий создавать приложения для Android и iOS и одновременно писать общую бизнес-логику только один раз, поэтому для каждой из платформ требуется только написать код пользовательского интерфейса.

Но что произойдет, если нам потребуется доступ к конкретной информации об устройстве, такой как координаты GPS, для нашей бизнес-логики? Как общий код может получить доступ к этим данным?

Общий интерфейс

Ответом будет предоставление интерфейса внутри общего кода, который может реализовать каждая платформа. Чтобы получить GPS-координаты с устройства, вы можете сделать что-то вроде этого:

Обратите внимание, что userGPSCoordinates() — это функция приостановки. Причина этого в том, что получение GPS-координат может занять некоторое время в зависимости от устройства и его местоположения. Это позволит общему коду ждать, пока устройства предоставят GPSCoordinates, прежде чем запускать остальную часть кода. Но как мы реализуем эту функцию на каждой платформе и как мы обрабатываем переопределение функции приостановки?

Реализация Android

На Android это довольно просто, поскольку у нас есть доступ ко всему, что может предложить Kotlin. Поскольку нам нужно использовать обратный вызов LocationManager для получения координат GPS, мы можем использовать suspendCancellableCoroutine для отправки этих координат обратно в общий:

Таким образом, мы убеждаемся, что находимся в потоке пользовательского интерфейса, используя withContext(Dispatchers.Main.immediate), а затем окружаем остальную часть функции suspendCancellableCoroutine. Это означает, что как только код внутри блока будет запущен, он приостановит сопрограмму до тех пор, пока мы не вызовем continuation.resume() со значением. Это здорово, поскольку, как мы упоминали ранее, LocationManager.getCurrentLocation будет использовать обратный вызов для отправки координат GPS. Поэтому мы приостанавливаем сопрограмму до тех пор, пока не получим указанный обратный вызов, а затем возобновляем сопрограмму со значением. Если пользователь не дал нам разрешения на получение местоположения, мы просто возвращаем null.

Если по какой-то причине наша сопрограмма отменяется (например, если пользователь выходит из представления, в котором мы получаем GPSCoordinates), то вызывается continuation.invokeOnCancellation { }. Мы используем этот блок для отмены запроса местоположения с помощью CancelSignal.

iOS-реализация

В iOS функции приостановки становятся async await . Это будет выглядеть примерно так:

Как и в Android, мы запрашиваем местоположение и возвращаем объект GPSCoordinates, когда получаем его, или nil, если у нас нет необходимых разрешений.

Общий код

Поскольку userGPSCoordinates() возвращает объект, допускающий значение NULL, вы можете сделать что-то подобное в общем коде, пока ждете данные GPS:

В приведенном выше коде мы сначала выдаем (null), чтобы список ресторанов можно было отобразить, как только данные будут готовы с методом сортировки по умолчанию (например, мы могли бы по умолчанию выбрать город, указанный пользователем в другом месте приложения). ). Затем, как только gpsService получит местоположение, мы можем перестроить список с соответствующим userLocation.

Использование внедрения зависимостей для реализации службы

Чтобы убедиться, что общий код использует правильную реализацию вашего сервиса, вы можете использовать либо шаблон serviceLocator, либо внедрение зависимостей. В моем случае я обычно использую внедрение зависимостей, поскольку приложения, над которыми я работаю, обычно довольно большие. Вот небольшой пример того, как вы можете настроить это с помощью koin:

Так что все, что вам действительно нужно сделать, это создать экземпляры ваших сервисов внутри ваших реализаций BootStrap, а Koin должен позаботиться обо всем остальном! Благодаря этому ваш общий код сможет использовать правильные реализации ваших сервисов.

В итоге

Вы можете использовать «Platform Services» для выполнения большей части бизнес-логики в общем коде, создавая при этом только реализации для конкретных платформ на обеих платформах. В приведенном выше примере общий код не заботится о том, как мы получаем GPS-координаты, он просто использует значения для сортировки данных.

Он отлично работает для всего остального, что получает данные с определенной платформы, например для разрешений, получения уведомлений и даже для более простых вещей, таких как получение кода версии приложения или выбор языка устройства.