This PR contains the work done to add a location at a time and updates the locations in the list of locations screen right after. To give further details about the work done: - [x] implemented the `LocationProvider` provider in the **Persistence** library; - [x] implemented the `SaveLocalLocationUseCase` use case; - [x] defined the properties and functions of the `LocationsAddViewModeling` protocol to support the clean, updating and saving of locations; - [x] implemented the `LocationsAddViewModel` view model; - [x] implemented the `LocationsAddViewController` view controller; - [x] implemented the dismissal of the `LocationsAddCoordinator` coordinator. Co-authored-by: Javier Cicchelli <javier@rock-n-code.com> Reviewed-on: rock-n-code/deep-linking-assignment#11
139 lines
3.5 KiB
Swift
139 lines
3.5 KiB
Swift
//
|
|
// LocationsAddViewController.swift
|
|
// Locations
|
|
//
|
|
// Created by Javier Cicchelli on 11/04/2023.
|
|
// Copyright © 2023 Röck+Cöde. All rights reserved.
|
|
//
|
|
|
|
import Combine
|
|
import Core
|
|
import UIKit
|
|
import MapKit
|
|
|
|
class LocationsAddViewController: BaseViewController {
|
|
|
|
// MARK: Properties
|
|
|
|
private let viewModel: LocationsAddViewModeling
|
|
|
|
private var cancellables: Set<AnyCancellable> = []
|
|
|
|
// MARK: Outlets
|
|
|
|
private lazy var map = {
|
|
let map = MKMapView()
|
|
|
|
map.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
map.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapOnMap)))
|
|
|
|
return map
|
|
}()
|
|
|
|
// MARK: Initialisers
|
|
|
|
init(viewModel: LocationsAddViewModeling) {
|
|
self.viewModel = viewModel
|
|
|
|
super.init()
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
// MARK: UIViewController
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
setupBar()
|
|
setupView()
|
|
bindViewModel()
|
|
}
|
|
|
|
}
|
|
|
|
// MARK: - Helpers
|
|
|
|
private extension LocationsAddViewController {
|
|
|
|
// MARK: Functions
|
|
|
|
func bindViewModel() {
|
|
viewModel
|
|
.locationExistsPublisher
|
|
.receive(on: RunLoop.main)
|
|
.sink { locationExists in
|
|
self.navigationItem
|
|
.rightBarButtonItems?
|
|
.forEach { $0.isEnabled = locationExists }
|
|
}
|
|
.store(in: &cancellables)
|
|
}
|
|
|
|
func setupBar() {
|
|
title = "Add a location"
|
|
navigationController?.navigationBar.prefersLargeTitles = false
|
|
navigationController?.navigationBar.backgroundColor = .systemBackground
|
|
navigationController?.navigationBar.isTranslucent = true
|
|
navigationController?.navigationBar.tintColor = .red
|
|
navigationItem.rightBarButtonItems = [
|
|
.init(
|
|
title: "Save",
|
|
style: .plain,
|
|
target: self,
|
|
action: #selector(saveButtonPressed)
|
|
),
|
|
.init(
|
|
title: "Clean",
|
|
style: .plain,
|
|
target: self,
|
|
action: #selector(cleanButtonPressed)
|
|
),
|
|
]
|
|
}
|
|
|
|
func setupView() {
|
|
view.addSubview(map)
|
|
|
|
NSLayoutConstraint.activate([
|
|
view.bottomAnchor.constraint(equalTo: map.bottomAnchor),
|
|
view.leadingAnchor.constraint(equalTo: map.leadingAnchor),
|
|
view.topAnchor.constraint(equalTo: map.topAnchor),
|
|
view.trailingAnchor.constraint(equalTo: map.trailingAnchor),
|
|
])
|
|
}
|
|
|
|
// MARK: Actions
|
|
|
|
@objc func cleanButtonPressed() {
|
|
map.removeAnnotations(map.annotations)
|
|
|
|
viewModel.cleanLocation()
|
|
}
|
|
|
|
@objc func saveButtonPressed() {
|
|
viewModel.saveLocation()
|
|
}
|
|
|
|
@objc func tapOnMap(recognizer: UITapGestureRecognizer) {
|
|
let tapOnView = recognizer.location(in: map)
|
|
let mapCoordinates = map.convert(tapOnView, toCoordinateFrom: map)
|
|
let annotation = MKPointAnnotation()
|
|
|
|
annotation.coordinate = mapCoordinates
|
|
|
|
map.removeAnnotations(map.annotations)
|
|
map.addAnnotation(annotation)
|
|
map.setCenter(mapCoordinates, animated: true)
|
|
|
|
viewModel.setLocation(
|
|
latitude: Float(mapCoordinates.latitude),
|
|
longitude: Float(mapCoordinates.longitude)
|
|
)
|
|
}
|
|
|
|
}
|