diff --git a/Apps/Locations/Resources/Info.plist b/Apps/Locations/Resources/Info.plist
index 0c67376..bb76b77 100644
--- a/Apps/Locations/Resources/Info.plist
+++ b/Apps/Locations/Resources/Info.plist
@@ -1,5 +1,10 @@
-
+
+ LSApplicationQueriesSchemes
+
+ wikipedia
+
+
diff --git a/Apps/Locations/Sources/Coordinators/LocationsAddCoordinator.swift b/Apps/Locations/Sources/Coordinators/LocationsAddCoordinator.swift
index 7d8891d..7666f15 100644
--- a/Apps/Locations/Sources/Coordinators/LocationsAddCoordinator.swift
+++ b/Apps/Locations/Sources/Coordinators/LocationsAddCoordinator.swift
@@ -42,7 +42,7 @@ extension LocationsAddCoordinator: LocationsAddCoordination {
// MARK: Functions
- func closeAddLocation() {
+ func closeLocationsAddScreen() {
router.dismiss(animated: true)
}
diff --git a/Apps/Locations/Sources/Coordinators/LocationsListCoordinator.swift b/Apps/Locations/Sources/Coordinators/LocationsListCoordinator.swift
index a7d2592..05d5ff5 100644
--- a/Apps/Locations/Sources/Coordinators/LocationsListCoordinator.swift
+++ b/Apps/Locations/Sources/Coordinators/LocationsListCoordinator.swift
@@ -48,7 +48,7 @@ extension LocationsListCoordinator: LocationsListCoordination {
// MARK: Functions
- func openAddLocation() {
+ func openLocationsAddScreen() {
guard let viewController else {
return
}
@@ -61,4 +61,12 @@ extension LocationsListCoordinator: LocationsListCoordination {
)
}
+ func openWikipediaApp(with url: URL) {
+ guard UIApplication.shared.canOpenURL(url) else {
+ return
+ }
+
+ UIApplication.shared.open(url)
+ }
+
}
diff --git a/Apps/Locations/Sources/Extensions/Location+URLs.swift b/Apps/Locations/Sources/Extensions/Location+URLs.swift
new file mode 100644
index 0000000..a0b3123
--- /dev/null
+++ b/Apps/Locations/Sources/Extensions/Location+URLs.swift
@@ -0,0 +1,46 @@
+//
+// Location+URLs.swift
+// Locations
+//
+// Created by Javier Cicchelli on 13/04/2023.
+// Copyright © 2023 Röck+Cöde. All rights reserved.
+//
+
+import Foundation
+import Persistence
+
+extension Location {
+
+ var wikipediaPlacesURL: URL? {
+ var urlComponents = URLComponents()
+
+ urlComponents.scheme = .Scheme.wikipedia
+ urlComponents.host = .Host.places
+ urlComponents.queryItems = [
+ .init(
+ name: .Query.key,
+ value: .init(format: .Query.value, latitude, longitude)
+ )
+ ]
+
+ return urlComponents.url
+ }
+
+}
+
+// MARK: - String+Constants
+
+private extension String {
+ enum Scheme {
+ static let wikipedia = "wikipedia"
+ }
+
+ enum Host {
+ static let places = "places"
+ }
+
+ enum Query {
+ static let key = "coordinates"
+ static let value = "%f,%f"
+ }
+}
diff --git a/Apps/Locations/Sources/Protocols/Coordination/LocationsAddCoordination.swift b/Apps/Locations/Sources/Protocols/Coordination/LocationsAddCoordination.swift
index 9819245..52354a3 100644
--- a/Apps/Locations/Sources/Protocols/Coordination/LocationsAddCoordination.swift
+++ b/Apps/Locations/Sources/Protocols/Coordination/LocationsAddCoordination.swift
@@ -10,6 +10,6 @@ protocol LocationsAddCoordination: AnyObject {
// MARK: Functions
- func closeAddLocation()
+ func closeLocationsAddScreen()
}
diff --git a/Apps/Locations/Sources/Protocols/Coordination/LocationsListCoordination.swift b/Apps/Locations/Sources/Protocols/Coordination/LocationsListCoordination.swift
index bafe329..96cc545 100644
--- a/Apps/Locations/Sources/Protocols/Coordination/LocationsListCoordination.swift
+++ b/Apps/Locations/Sources/Protocols/Coordination/LocationsListCoordination.swift
@@ -6,10 +6,13 @@
// Copyright © 2023 Röck+Cöde. All rights reserved.
//
+import Foundation
+
protocol LocationsListCoordination: AnyObject {
// MARK: Functions
- func openAddLocation()
+ func openLocationsAddScreen()
+ func openWikipediaApp(with url: URL)
}
diff --git a/Apps/Locations/Sources/Protocols/ViewModeling/LocationsListViewModeling.swift b/Apps/Locations/Sources/Protocols/ViewModeling/LocationsListViewModeling.swift
index 385b599..4073b1c 100644
--- a/Apps/Locations/Sources/Protocols/ViewModeling/LocationsListViewModeling.swift
+++ b/Apps/Locations/Sources/Protocols/ViewModeling/LocationsListViewModeling.swift
@@ -17,14 +17,15 @@ protocol LocationsListViewModeling: AnyObject {
var coordinator: LocationsListCoordination? { get set }
var locationsDidChangePublisher: PassthroughSubject<[Change], Never> { get }
+ var numberOfLocationSections: Int { get }
var viewStatusPublisher: Published.Publisher { get }
- var numberOfSectionsInData: Int { get }
-
+
// MARK: Functions
-
- func openAddLocation()
+
func loadLocations()
- func numberOfDataItems(in section: Int) -> Int
- func dataItem(at indexPath: IndexPath) -> Location
+ func location(at indexPath: IndexPath) -> Location
+ func numberOfLocations(in section: Int) -> Int
+ func openLocationsAdd()
+ func openWikipedia(at indexPath: IndexPath)
}
diff --git a/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewModel.swift b/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewModel.swift
index b447c1c..78e8294 100644
--- a/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewModel.swift
+++ b/Apps/Locations/Sources/Screens/LocationsAdd/LocationsAddViewModel.swift
@@ -54,7 +54,7 @@ extension LocationsAddViewModel: LocationsAddViewModeling {
longitude: location.longitude
)
- coordinator?.closeAddLocation()
+ coordinator?.closeLocationsAddScreen()
}
func setLocation(latitude: Float, longitude: Float) {
diff --git a/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewController.swift b/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewController.swift
index e20b00e..5fb6e90 100644
--- a/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewController.swift
+++ b/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewController.swift
@@ -67,14 +67,14 @@ extension LocationsListViewController: UITableViewDataSource {
// MARK: Functions
func numberOfSections(in tableView: UITableView) -> Int {
- viewModel.numberOfSectionsInData
+ viewModel.numberOfLocationSections
}
func tableView(
_ tableView: UITableView,
numberOfRowsInSection section: Int
) -> Int {
- viewModel.numberOfDataItems(in: section)
+ viewModel.numberOfLocations(in: section)
}
func tableView(
@@ -88,7 +88,7 @@ extension LocationsListViewController: UITableViewDataSource {
return .init()
}
- let entity = viewModel.dataItem(at: indexPath)
+ let entity = viewModel.location(at: indexPath)
cell.update(
iconName: entity.source == .remote ? "network" : "house",
@@ -104,7 +104,20 @@ extension LocationsListViewController: UITableViewDataSource {
// MARK: - UITableViewDelegate
-extension LocationsListViewController: UITableViewDelegate {}
+extension LocationsListViewController: UITableViewDelegate {
+
+ // MARK: Functions
+
+ func tableView(
+ _ tableView: UITableView,
+ didSelectRowAt indexPath: IndexPath
+ ) {
+ viewModel.openWikipedia(at: indexPath)
+
+ tableView.deselectRow(at: indexPath, animated: true)
+ }
+
+}
// MARK: - Helpers
@@ -163,7 +176,7 @@ private extension LocationsListViewController {
.store(in: &cancellables)
viewModel
- .controllerDidChangePublisher
+ .locationsDidChangePublisher
.sink(receiveValue: { [weak self] updates in
var movedToIndexPaths = [IndexPath]()
@@ -199,7 +212,7 @@ private extension LocationsListViewController {
}
@objc func addLocationPressed() {
- viewModel.openAddLocation()
+ viewModel.openLocationsAdd()
}
}
diff --git a/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewModel.swift b/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewModel.swift
index b5e2384..dedcbdc 100644
--- a/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewModel.swift
+++ b/Apps/Locations/Sources/Screens/LocationsList/LocationsListViewModel.swift
@@ -42,15 +42,11 @@ extension LocationsListViewModel: LocationsListViewModeling {
// MARK: Properties
var locationsDidChangePublisher: PassthroughSubject<[Persistence.Change], Never> { locationProvider.didChangePublisher }
- var numberOfSectionsInData: Int { locationProvider.numberOfSections }
+ var numberOfLocationSections: Int { locationProvider.numberOfSections }
var viewStatusPublisher: Published.Publisher { $viewStatus }
// MARK: Functions
- func openAddLocation() {
- coordinator?.openAddLocation()
- }
-
func loadLocations() {
Task {
do {
@@ -67,12 +63,25 @@ extension LocationsListViewModel: LocationsListViewModeling {
}
}
- func numberOfDataItems(in section: Int) -> Int {
+
+ func location(at indexPath: IndexPath) -> Location {
+ locationProvider.location(at: indexPath)
+ }
+
+ func numberOfLocations(in section: Int) -> Int {
locationProvider.numberOfLocationsInSection(section)
}
- func dataItem(at indexPath: IndexPath) -> Location {
- locationProvider.location(at: indexPath)
+ func openLocationsAdd() {
+ coordinator?.openLocationsAddScreen()
+ }
+
+ func openWikipedia(at indexPath: IndexPath) {
+ guard let url = locationProvider.location(at: indexPath).wikipediaPlacesURL else {
+ return
+ }
+
+ coordinator?.openWikipediaApp(with: url)
}
}
diff --git a/DeepLinking.xcodeproj/project.pbxproj b/DeepLinking.xcodeproj/project.pbxproj
index 00cc327..22e7f75 100644
--- a/DeepLinking.xcodeproj/project.pbxproj
+++ b/DeepLinking.xcodeproj/project.pbxproj
@@ -12,9 +12,10 @@
02031EC929E60B29003C108C /* DependencyService+Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02031EC829E60B29003C108C /* DependencyService+Keys.swift */; };
02031EE829E68D9B003C108C /* LoadingSpinnerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02031EE729E68D9B003C108C /* LoadingSpinnerView.swift */; };
02031EEA29E6B495003C108C /* ErrorMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02031EE929E6B495003C108C /* ErrorMessageView.swift */; };
+ 02031F0829E75EF0003C108C /* SaveLocalLocationUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02031F0729E75EED003C108C /* SaveLocalLocationUseCase.swift */; };
+ 02031F0A29E7645F003C108C /* Location+URLs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02031F0929E7645F003C108C /* Location+URLs.swift */; };
4656CBC229E6D33C00600EE6 /* LoadRemoteLocationsUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4656CBC129E6D33C00600EE6 /* LoadRemoteLocationsUseCase.swift */; };
4656CBC829E6F2E400600EE6 /* LocationViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4656CBC729E6F2E400600EE6 /* LocationViewCell.swift */; };
- 4656CBE629E7360B00600EE6 /* SaveLocalLocationUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4656CBE529E7360B00600EE6 /* SaveLocalLocationUseCase.swift */; };
46C3B7C629E5BF1500F8F57C /* LocationsListCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7C529E5BF1500F8F57C /* LocationsListCoordinator.swift */; };
46C3B7CB29E5CD3200F8F57C /* LocationsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */; };
46C3B7CF29E5D00E00F8F57C /* LocationsAddViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C3B7CE29E5D00E00F8F57C /* LocationsAddViewModel.swift */; };
@@ -131,9 +132,10 @@
02031EC829E60B29003C108C /* DependencyService+Keys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DependencyService+Keys.swift"; sourceTree = ""; };
02031EE729E68D9B003C108C /* LoadingSpinnerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingSpinnerView.swift; sourceTree = ""; };
02031EE929E6B495003C108C /* ErrorMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorMessageView.swift; sourceTree = ""; };
+ 02031F0729E75EED003C108C /* SaveLocalLocationUseCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SaveLocalLocationUseCase.swift; sourceTree = ""; };
+ 02031F0929E7645F003C108C /* Location+URLs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Location+URLs.swift"; sourceTree = ""; };
4656CBC129E6D33C00600EE6 /* LoadRemoteLocationsUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadRemoteLocationsUseCase.swift; sourceTree = ""; };
4656CBC729E6F2E400600EE6 /* LocationViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationViewCell.swift; sourceTree = ""; };
- 4656CBE529E7360B00600EE6 /* SaveLocalLocationUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveLocalLocationUseCase.swift; sourceTree = ""; };
46C3B7C529E5BF1500F8F57C /* LocationsListCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListCoordinator.swift; sourceTree = ""; };
46C3B7CA29E5CD3200F8F57C /* LocationsListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsListViewModel.swift; sourceTree = ""; };
46C3B7CE29E5D00E00F8F57C /* LocationsAddViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsAddViewModel.swift; sourceTree = ""; };
@@ -180,6 +182,7 @@
isa = PBXGroup;
children = (
02031EC829E60B29003C108C /* DependencyService+Keys.swift */,
+ 02031F0929E7645F003C108C /* Location+URLs.swift */,
);
path = Extensions;
sourceTree = "";
@@ -225,7 +228,7 @@
isa = PBXGroup;
children = (
4656CBC129E6D33C00600EE6 /* LoadRemoteLocationsUseCase.swift */,
- 4656CBE529E7360B00600EE6 /* SaveLocalLocationUseCase.swift */,
+ 02031F0729E75EED003C108C /* SaveLocalLocationUseCase.swift */,
);
path = "Use Cases";
sourceTree = "";
@@ -547,7 +550,8 @@
02031EBF29E5F949003C108C /* LocationsAddViewModeling.swift in Sources */,
4656CBC829E6F2E400600EE6 /* LocationViewCell.swift in Sources */,
46C3B7DE29E5ED2E00F8F57C /* LocationsAddCoordinator.swift in Sources */,
- 4656CBE629E7360B00600EE6 /* SaveLocalLocationUseCase.swift in Sources */,
+ 02031F0A29E7645F003C108C /* Location+URLs.swift in Sources */,
+ 02031F0829E75EF0003C108C /* SaveLocalLocationUseCase.swift in Sources */,
02031EEA29E6B495003C108C /* ErrorMessageView.swift in Sources */,
46C3B7DC29E5ED2300F8F57C /* LocationsAddCoordination.swift in Sources */,
46C3B7D829E5E55000F8F57C /* LocationsListCoordination.swift in Sources */,