Что нам показывают карты

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Всем привет! Меня зовут Женя, я работаю iOS разработчиком в каршеринг-сервисе Ситидрайв, где мы с командой стремимся улучшить пользовательский опыт и сделать наше приложение более интуитивно понятным и функциональным. В этой статье я расскажу, как у нас организована работа с картой: как отображаем автомобили и другие объекты, какие проблемы возникали в процессе разработки, и почему мы выбрали формат данных GeoJSON. Также поделюсь особенностями работы с форматом, которые важно знать любому разработчику и расскажу о некоторых его преимуществах.

Как мы организовали свою работу

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

protocol MapProviderProtocol: AnyObject {
    var view: UIView { get }

    func show(wrappedPolygons: inout [PolygonWrapper])
    func show(wrappedPolylines: inout [PolylineWrapper])
    func show(wrappedCircle: inout CircleWrapper)

    func hide(wrappedPolygons: [PolygonWrapper])
    func hide(wrappedPolylines: [PolylineWrapper])
    func hide(wrappedCircle: CircleWrapper)

    func listenCameraPositions()
}

Протокол имеет и другие свойства:

  • Добавление маркеров авто и геолокации пользователя.

  • Обновление кластеризации.

  • Получение координат по нажатию  на слое с картой.

  • Перемещение viewport.

Готовый объект мы получаем от фабрики, которая резолвит условие от сервиса конфигурации. Например, мы можем выбрать на сервере карту какого провайдера отображать, если у пользователей неожиданно будут наблюдаться проблемы.

class MapFactory {
    func makeMap() -> MapProviderProtocol {
        toggleService.isEnabled(.googleMaps)
            ? GMaps()
            : DoubleGisMaps()
    }
}

Если позже мы захотим добавить работу с OSM (OpenStreetMap) или нативными картами (MapKit), нужно будет написать адаптер, который поддерживает и реализует методы MapProviderProtocol.

Далее управляемый экземпляр карты мы оборачиваем в класс, который соответствует MapProtocol. Этот протокол определяет поведение для объектов, управляющих бизнес данными:

protocol MapProtocol: AnyObject {
    var map: MapProviderProtocol { get }
    func show()
    func removeAllDrawnObjects()
}

Так, корневым объектом, который далее мы будем декорировать, является BaseMap класс. Он занимается обновлением и наблюдением за видимой областью карты в момент ее использования.

class BaseMap: MapProtocol {
    let map: MapProviderProtocol

    init(map: MapProviderProtocol) {
        self.map = map
    }
    
    func show() {
        map.listenCameraPositions()
    }

    func removeAllDrawnObjects() {}
}

Далее накидываем нужные слои, например, ParkingsMap, CarMap, LocationMap и так далее. Каждый из них отвечает за отображение своих собственных данных. Всего у нас есть 14 различных декораторов, которые инкапсулируют логику добавления графики на MapProviderProtocol.

Вот как может выглядеть реализация такого декоратора:

class ActiveCarMap: MapProtocol {
    private var selectedCar: MapMarker?
    private let car: Car
    private let decorator: MapProtocol
    
    let map: MapProviderProtocol

    init(decorator: MapProtocol, car: Car) {
        self.car = car
        self.map = decorator.map
        self.decorator = decorator
    }

    func show() {
        decorator.show()
        let marker = map.makeMarker(
            location: car.location,
            icon: car.icon
        )
        map.add(markers: [marker])
        selectedCar = marker
        selectedCar?.loadImage(from: car.pinImage)
    }

    func removeAllDrawnObjects() {
        decorator.removeAllDrawnObjects()
        if let marker = selectedCarMarker {
            map.remove(markers: [marker])
            selectedCar = nil
        }
    }
}

Новый слой принимает в инициализаторе MapProtocol объект –– это всегда ранее упомянутый BaseMap, который, как правило, мы передаём во view model при создании модуля. Вот как это выглядит на практике:

class MapViewModel {
    let map: MapProviderProtocol
    let baseMap: MapProtocol
    let mapComposite: MapCompositeProtocol
    let car: Car

    init(
        map: MapProviderProtocol,
        baseMap: MapCompositeProtocol,
        car: Car
    ) {
        self.map = map
        self.baseMap = BaseMap(map: map)
        self.mapComposite = mapComposite
        self.car = car
        
        setupMap()
    }

    func setupMap() {
        mapComposite.add(map: baseMap)
        
        let locationMap = LocationMap(decoratee: baseMap)
        mapComposite.add(map: locationMap)
        
        let activeCarMap = ActiveCarMap(decoratee: baseMap, car: car)
        mapComposite.add(map: activeCarMap)
    }
}

Источник: https://habr.com/ru/companies/citydrive/articles/815217/


Интересные статьи

Интересные статьи

Нижегородская компания «Новакард» выпустила на российский рынок сим-карты с отечественной операционной системой Simart Mi, сообщает ТАСС со ссылкой на пресс-службу организации...
В интернете появилось опасное вредоносное ПО под названием Erbium. Это инструмент для кражи личных данных, который нацелен на ваши пароли, данные банковских карт, куки, криптовалютные кошельки и, возм...
История об изысканиях возможных путей использования в видеокарте собственных шрифтов, которая увенчалась их полной интеграцией в ПЗУ VGA. На этом пути рассматривались различные варианты...
На протяжении нескольких лет я пытался разобраться в картах нормалей и в проблемах, которые обычно возникают при работе с ними. Большинство найденных объяснений было слишком техническим, непо...
Когда деревья были чуточку выше, трава зеленее, солнце ярче, а я учился в институте, у меня была социальная карта студента. Она мне нравилась своей функциональностью и продуманностью, но, как и в...