Перейти к основному содержимому

Общие принципы

Отложенные результаты

Некоторые методы SDK (например, методы, которые обращаются к удалённому серверу) возвращают отложенные результаты (Future). Для работы с ними создайте обработчик получения данных и обработчик ошибок. Обработать результат в главной очереди можно с помощью DispatchQueue.

Пример получения объекта из справочника:

// Создать объект для поиска по справочнику
let searchManager = SearchManager.createOnlineManager(context: sdk.context)

// Получить объект из справочника по идентификатору
let future = searchManager.searchByDirectoryObjectIds(objectIds: [object.id])

// Обработать результат поиска в главной очереди
// Сохранить результат вызова, так как его удаление отменяет обработку
self.searchDirectoryObjectCancellable = future.sink(
receiveValue: {
[weak self] directoryObjects in
guard let directoryObject = directoryObjects.first else { return }
DispatchQueue.main.async {
self.handle(directoryObject)
}
},
failure: { error in
DispatchQueue.main.async {
self.handle(error)
}
}
)

Для упрощения работы с отложенными результатами вы можете создать расширение:

extension DGis.Future {
func sinkOnMainThread(
receiveValue: @escaping (Value) -> Void,
failure: @escaping (Error) -> Void
) -> DGis.Cancellable {
self.sink(on: .main, receiveValue: receiveValue, failure: failure)
}

func sink(
on queue: DispatchQueue,
receiveValue: @escaping (Value) -> Void,
failure: @escaping (Error) -> Void
) -> DGis.Cancellable {
self.sink { value in
queue.async {
receiveValue(value)
}
} failure: { error in
queue.async {
failure(error)
}
}
}
}

self.searchDirectoryObjectCancellable = future.sinkOnMainThread(
receiveValue: {
[weak self] directoryObject in
guard let directoryObject = directoryObject else { return }
self.handle(directoryObject)
},
failure: { error in
self.handle(error)
}
)
Обновление в версии 13.x

В версии SDK 13.0.0 и выше в рамках перехода на Swift 6 обычный Future.sink был удалён. Для получения отложенных результатов используйте Future.sink(on queue: DispatchQueue, ...) и Future.sinkOnMainThread.

Также вы можете использовать Combine:

// Создать Combine.Future из DGis.Future
extension DGis.Future {
func asCombineFuture() -> Combine.Future<Value, Error> {
Combine.Future { [self] promise in
// Удерживать ссылку на Cancellable, пока не будет вызван обработчик
// Combine.Future не позволяет конфигурировать отмену напрямую
var cancellable: DGis.Cancellable?
cancellable = self.sink {
promise(.success($0))
_ = cancellable
} failure: {
promise(.failure($0))
_ = cancellable
}
}
}
}

// Создать Combine.Future
let combineFuture = future.asCombineFuture()

// Обработать результат поиска в главной очереди
combineFuture.receive(on: DispatchQueue.main).sink {
[weak self] completion in
switch completion {
case .failure(let error):
self?.handle(error)
case .finished:
break
}
} receiveValue: {
[weak self] directoryObject in
self?.handle(directoryObject)
}.store(in: &self.subscriptions)

Подробнее о работе со справочником объектов см. в разделе Справочник.

Потоки значений

Некоторые объекты SDK предоставляют потоки значений, которые можно обработать. На поток значений можно подписаться, указав функцию-обработчик данных. От потока можно отписаться, когда обработка данных больше не требуется. Для работы с потоками значений используется класс Channel.

Пример подписки на изменение видимой области карты (поток новых прямоугольных областей):

// Выбрать канал (прямоугольники видимой области карты)
let visibleRectChannel = map.camera.visibleRectChannel

// Подписаться и обрабатывать результаты в главной очереди
// Значения будут присылаться при любом изменении видимой области до момента отписки
// Важно сохранить Cancellable, иначе подписка будет уничтожена
self.cancellable = visibleRectChannel.sink { [weak self] visibleRect in
DispatchQueue.main.async {
self?.handle(visibleRect)
}
}

Чтобы избежать утечки памяти, после окончания работы с каналом отпишитесь от него:

self.cancellable.cancel()

Для упрощения работы с потоками значений вы можете создать расширение:

extension Channel {
func sinkOnMainThread(receiveValue: @escaping (Value) -> Void) -> DGis.Cancellable {
self.sink(on: .main, receiveValue: receiveValue)
}

func sink(on queue: DispatchQueue, receiveValue: @escaping (Value) -> Void) -> DGis.Cancellable {
self.sink { value in
queue.async {
receiveValue(value)
}
}
}
}

self.cancellable = visibleRectChannel.sinkOnMainThread { [weak self] visibleRect in
self?.handle(visibleRect)
}
Обновление в версии 13.x

В версии SDK 13.0.0 и выше в рамках перехода на Swift 6 обычный Channel.sink был удалён. Для подписки на потоки значений используйте Channel.sink(on queue: DispatchQueue, ...) и Channel.sinkOnMainThread.