General principles
Deferred results
Some SDK methods (e.g., those that access a remote server) return deferred results (Future). To work with them, create a data obtaining handler and an error handler. To process the result in the main thread, use DispatchQueue.
Example of obtaining an object from the directory:
- For SDK version 14.0.0 or later
- For SDK version 13.x
// Create an object for directory search
let searchManager = SearchManager.createOnlineManager(context: sdk.context)
// Get the directory object by its identifier
let future = searchManager.searchByDirectoryObjectIds(objectIds: [object.id])
// Process the search result in the main thread
// Save the call result, as deleting it will cancel the processing
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)
}
}
)
// Create an object for directory search
let searchManager = SearchManager.createOnlineManager(context: sdk.context)
// Get the directory object by its identifier
let future = searchManager.searchByDirectoryObjectId(objectId: object.id)
// Process the search result in the main thread
// Save the call result, as deleting it will cancel the processing
self.searchDirectoryObjectCancellable = future.sink(
receiveValue: {
[weak self] directoryObject in
guard let directoryObject = directoryObject else { return }
DispatchQueue.main.async {
self.handle(directoryObject)
}
},
failure: { error in
DispatchQueue.main.async {
self.handle(error)
}
}
)
To simplify working with deferred results, you can create an extension:
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)
}
)
In SDK version 13.0.0 or later Future.sink is removed during the transition to Swift 6. To obtain deferred results, use Future.sink(on queue: DispatchQueue, ...) and Future.sinkOnMainThread.
Also, you can use the Combine framework:
// Create Combine.Future from DGis.Future
extension DGis.Future {
func asCombineFuture() -> Combine.Future<Value, Error> {
Combine.Future { [self] promise in
// Save the Cancellable object until the callback function is called
// Combine.Future does not support configuring cancellation directly
var cancellable: DGis.Cancellable?
cancellable = self.sink {
promise(.success($0))
_ = cancellable
} failure: {
promise(.failure($0))
_ = cancellable
}
}
}
}
// Create Combine.Future
let combineFuture = future.asCombineFuture()
// Process the search result in the main thread
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)
For more information on working with the object directory, see the Directory section.
Data channels
Some SDK objects provide data channels that can be processed. To subscribe to a data channel, specify a handler function. You can unsubscribe from a channel when data processing is no longer required. To work with the data channels, use the Channel class.
Example of subscribing to change of the visible map area (channel of new rectangular areas):
// Choose a data channel (visible area rectangles)
let visibleRectChannel = map.camera.visibleRectChannel
// Subscribe and process the results in the main thread
// Values will be sent on any change of the visible area until unsubscribed
// It is important to keep the Cancellable, otherwise the subscription will be destroyed
self.cancellable = visibleRectChannel.sink { [weak self] visibleRect in
DispatchQueue.main.async {
self?.handle(visibleRect)
}
}
To avoid memory leaks, unsubscribe from a channel after finishing working with it:
self.cancellable.cancel()
To simplify working with data channels, you can create an extension:
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)
}
In SDK version 13.0.0 or later Channel.sink is removed during the transition to Swift 6. To subscribe to data channels, use Channel.sink(on queue: DispatchQueue, ...) and Channel.sinkOnMainThread.