Skip to content

Commit e09ad46

Browse files
authored
Merge pull request #276 from TaskarCenterAtUW/TASK-2091-Implement-aerial/satellite-imagery-layer-logic-
Task 2091 implement aerial/satellite imagery layer logic
2 parents 8cf0a87 + d4e1de0 commit e09ad46

17 files changed

+844
-97
lines changed

GoInfoGame/GoInfoGame.xcodeproj/project.pbxproj

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,15 @@
142142
A4E711A82B57CA4300C9DE08 /* QuestsRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4E711A72B57CA4300C9DE08 /* QuestsRepository.swift */; };
143143
B0CCB98C2B8626AE00AA73DE /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0CCB98B2B8626AE00AA73DE /* ProfileView.swift */; };
144144
B0CCB98E2B8626C600AA73DE /* ProfileViewVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0CCB98D2B8626C600AA73DE /* ProfileViewVM.swift */; };
145+
C71EB5AC2E2100510009E610 /* WMTSServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C71EB5AA2E2100510009E610 /* WMTSServer.swift */; };
146+
C71EB5AD2E2100510009E610 /* SatellitePickerSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C71EB5A82E2100510009E610 /* SatellitePickerSheet.swift */; };
147+
C71EB5AE2E2100510009E610 /* SatelliteServerResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = C71EB5A92E2100510009E610 /* SatelliteServerResponse.swift */; };
148+
C71EB5B02E2100B80009E610 /* MapRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = C71EB5AF2E2100B80009E610 /* MapRepository.swift */; };
149+
C71EB5B62E2100E30009E610 /* ResponseHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C71EB5B42E2100E30009E610 /* ResponseHandler.swift */; };
150+
C71EB5B72E2100E30009E610 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C71EB5B22E2100E30009E610 /* NetworkManager.swift */; };
151+
C71EB5B82E2100E30009E610 /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C71EB5B32E2100E30009E610 /* NetworkMonitor.swift */; };
152+
C71EB5B92E2100E30009E610 /* APIManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C71EB5B12E2100E30009E610 /* APIManager.swift */; };
153+
C71EB5BD2E2113500009E610 /* WMTSLayers.json in Resources */ = {isa = PBXBuildFile; fileRef = C71EB5BC2E2113500009E610 /* WMTSLayers.json */; };
145154
C75666D02E0D068900D36C41 /* OHHTTPStubs in Frameworks */ = {isa = PBXBuildFile; productRef = C75666CF2E0D068900D36C41 /* OHHTTPStubs */; };
146155
C75666D22E0D068900D36C41 /* OHHTTPStubsSwift in Frameworks */ = {isa = PBXBuildFile; productRef = C75666D12E0D068900D36C41 /* OHHTTPStubsSwift */; };
147156
C75CC5D92E0D22F4008A94A0 /* LongQuestsResponse.json in Resources */ = {isa = PBXBuildFile; fileRef = C75CC5D82E0D22F4008A94A0 /* LongQuestsResponse.json */; };
@@ -162,7 +171,6 @@
162171
FA18CAEB2CD0F718008247F2 /* CameraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA18CAEA2CD0F718008247F2 /* CameraView.swift */; };
163172
FA1992ED2BB1A78C003B4719 /* KeychainManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA1992EC2BB1A78C003B4719 /* KeychainManager.swift */; };
164173
FA50718C2D54E08800CE9798 /* AddFeatureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA50718B2D54E08800CE9798 /* AddFeatureView.swift */; };
165-
FA50718E2D54E1D200CE9798 /* BingTileOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA50718D2D54E1D200CE9798 /* BingTileOverlay.swift */; };
166174
FA5071902D5B31CC00CE9798 /* CreateNoteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA50718F2D5B31CC00CE9798 /* CreateNoteView.swift */; };
167175
FA5071962D6DD31A00CE9798 /* LongElementQuest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5071952D6DD31A00CE9798 /* LongElementQuest.swift */; };
168176
FA5071982D71F1B000CE9798 /* SyncLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5071972D71F1B000CE9798 /* SyncLogger.swift */; };
@@ -477,6 +485,15 @@
477485
B0CCB98B2B8626AE00AA73DE /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = "<group>"; };
478486
B0CCB98D2B8626C600AA73DE /* ProfileViewVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewVM.swift; sourceTree = "<group>"; };
479487
C4F37DA661B22FA87DF8282F /* Pods_osmapi.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_osmapi.framework; sourceTree = BUILT_PRODUCTS_DIR; };
488+
C71EB5A82E2100510009E610 /* SatellitePickerSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SatellitePickerSheet.swift; sourceTree = "<group>"; };
489+
C71EB5A92E2100510009E610 /* SatelliteServerResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SatelliteServerResponse.swift; sourceTree = "<group>"; };
490+
C71EB5AA2E2100510009E610 /* WMTSServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTSServer.swift; sourceTree = "<group>"; };
491+
C71EB5AF2E2100B80009E610 /* MapRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapRepository.swift; sourceTree = "<group>"; };
492+
C71EB5B12E2100E30009E610 /* APIManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIManager.swift; sourceTree = "<group>"; };
493+
C71EB5B22E2100E30009E610 /* NetworkManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = "<group>"; };
494+
C71EB5B32E2100E30009E610 /* NetworkMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitor.swift; sourceTree = "<group>"; };
495+
C71EB5B42E2100E30009E610 /* ResponseHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponseHandler.swift; sourceTree = "<group>"; };
496+
C71EB5BC2E2113500009E610 /* WMTSLayers.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = WMTSLayers.json; sourceTree = "<group>"; };
480497
C75CC5D62E0D22F4008A94A0 /* Workspaces response.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "Workspaces response.json"; sourceTree = "<group>"; };
481498
C75CC5D72E0D22F4008A94A0 /* SCLIO Seattle pins response.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "SCLIO Seattle pins response.json"; sourceTree = "<group>"; };
482499
C75CC5D82E0D22F4008A94A0 /* LongQuestsResponse.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = LongQuestsResponse.json; sourceTree = "<group>"; };
@@ -494,7 +511,6 @@
494511
FA18CAEA2CD0F718008247F2 /* CameraView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraView.swift; sourceTree = "<group>"; };
495512
FA1992EC2BB1A78C003B4719 /* KeychainManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainManager.swift; sourceTree = "<group>"; };
496513
FA50718B2D54E08800CE9798 /* AddFeatureView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddFeatureView.swift; sourceTree = "<group>"; };
497-
FA50718D2D54E1D200CE9798 /* BingTileOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BingTileOverlay.swift; sourceTree = "<group>"; };
498514
FA50718F2D5B31CC00CE9798 /* CreateNoteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateNoteView.swift; sourceTree = "<group>"; };
499515
FA5071952D6DD31A00CE9798 /* LongElementQuest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongElementQuest.swift; sourceTree = "<group>"; };
500516
FA5071972D71F1B000CE9798 /* SyncLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncLogger.swift; sourceTree = "<group>"; };
@@ -663,6 +679,7 @@
663679
0536DD952B0BD91F00B04C4B /* Network */ = {
664680
isa = PBXGroup;
665681
children = (
682+
C71EB5B52E2100E30009E610 /* APIHandler */,
666683
A48037342BBA8DCB007EE7E4 /* models */,
667684
0536DD962B0BD95100B04C4B /* URLSession+Extension.swift */,
668685
A48037302BBA8C25007EE7E4 /* ApiManager.swift */,
@@ -807,8 +824,9 @@
807824
971575152B5FFD5F0044797C /* Map */ = {
808825
isa = PBXGroup;
809826
children = (
827+
C71EB5AF2E2100B80009E610 /* MapRepository.swift */,
828+
C71EB5AB2E2100510009E610 /* Satellite Server */,
810829
971575182B5FFE910044797C /* LocationManagerCoordinator.swift */,
811-
FA50718D2D54E1D200CE9798 /* BingTileOverlay.swift */,
812830
971575162B5FFD6F0044797C /* MapView.swift */,
813831
970D5BD22B67638400C20BE7 /* MapViewModel.swift */,
814832
A495DF2C2B6A190400478E44 /* CustomMapView.swift */,
@@ -1264,9 +1282,31 @@
12641282
path = Profile;
12651283
sourceTree = "<group>";
12661284
};
1285+
C71EB5AB2E2100510009E610 /* Satellite Server */ = {
1286+
isa = PBXGroup;
1287+
children = (
1288+
C71EB5A82E2100510009E610 /* SatellitePickerSheet.swift */,
1289+
C71EB5A92E2100510009E610 /* SatelliteServerResponse.swift */,
1290+
C71EB5AA2E2100510009E610 /* WMTSServer.swift */,
1291+
);
1292+
path = "Satellite Server";
1293+
sourceTree = "<group>";
1294+
};
1295+
C71EB5B52E2100E30009E610 /* APIHandler */ = {
1296+
isa = PBXGroup;
1297+
children = (
1298+
C71EB5B12E2100E30009E610 /* APIManager.swift */,
1299+
C71EB5B22E2100E30009E610 /* NetworkManager.swift */,
1300+
C71EB5B32E2100E30009E610 /* NetworkMonitor.swift */,
1301+
C71EB5B42E2100E30009E610 /* ResponseHandler.swift */,
1302+
);
1303+
path = APIHandler;
1304+
sourceTree = "<group>";
1305+
};
12671306
C75CC5D52E0D22E0008A94A0 /* SampleResponses */ = {
12681307
isa = PBXGroup;
12691308
children = (
1309+
C71EB5BC2E2113500009E610 /* WMTSLayers.json */,
12701310
C75CC5D62E0D22F4008A94A0 /* Workspaces response.json */,
12711311
C75CC5D72E0D22F4008A94A0 /* SCLIO Seattle pins response.json */,
12721312
C75CC5D82E0D22F4008A94A0 /* LongQuestsResponse.json */,
@@ -1851,6 +1891,7 @@
18511891
files = (
18521892
973FC00A2B4D2A6D00878269 /* Localizable.strings in Resources */,
18531893
FAD5C4FF2AFCBE720040C61A /* LaunchScreen.storyboard in Resources */,
1894+
C71EB5BD2E2113500009E610 /* WMTSLayers.json in Resources */,
18541895
FAD5C4FC2AFCBE720040C61A /* Assets.xcassets in Resources */,
18551896
FAD5C4FA2AFCBE700040C61A /* Main.storyboard in Resources */,
18561897
C75CC5D92E0D22F4008A94A0 /* LongQuestsResponse.json in Resources */,
@@ -2147,7 +2188,6 @@
21472188
05DBBB622B164D9A00B6F110 /* RealOPElement.swift in Sources */,
21482189
97AC1C0A2B6B7F10004F0BF4 /* CustomVerticalButtonsList.swift in Sources */,
21492190
FAE258522D3A780F00D2BB12 /* FloatingActionButtonStack.swift in Sources */,
2150-
FA50718E2D54E1D200CE9798 /* BingTileOverlay.swift in Sources */,
21512191
FA8C74C22C4E89C800D28220 /* LongForm.swift in Sources */,
21522192
FA9F38222DC93F5300D7AABF /* UndoSidebarView.swift in Sources */,
21532193
FAC9E60F2B04F9C800E2C608 /* OverpassRequestManager.swift in Sources */,
@@ -2175,6 +2215,9 @@
21752215
FA5853C12B21F17F00301CDA /* OnboardingView.swift in Sources */,
21762216
A4B83AF32B5F9385006684CA /* StoredWay.swift in Sources */,
21772217
FA18CAE52CC7D0DD008247F2 /* SequenceModel.swift in Sources */,
2218+
C71EB5AC2E2100510009E610 /* WMTSServer.swift in Sources */,
2219+
C71EB5AD2E2100510009E610 /* SatellitePickerSheet.swift in Sources */,
2220+
C71EB5AE2E2100510009E610 /* SatelliteServerResponse.swift in Sources */,
21782221
FAE481A22DD5EF3800149A48 /* PasswordAuthenticationPopupView.swift in Sources */,
21792222
05DBBB5F2B1263FF00B6F110 /* DatabaseConnector.swift in Sources */,
21802223
FAF44FC12B30850A004FE664 /* OnboardingView3.swift in Sources */,
@@ -2189,7 +2232,12 @@
21892232
973FC04C2B5A5F5F00878269 /* RadioItem.swift in Sources */,
21902233
FA18CAE92CCFD212008247F2 /* FinishUploadingModel.swift in Sources */,
21912234
C7ED070F2D70E2EC001FFFE2 /* TokenRefresher.swift in Sources */,
2235+
C71EB5B62E2100E30009E610 /* ResponseHandler.swift in Sources */,
2236+
C71EB5B72E2100E30009E610 /* NetworkManager.swift in Sources */,
2237+
C71EB5B82E2100E30009E610 /* NetworkMonitor.swift in Sources */,
2238+
C71EB5B92E2100E30009E610 /* APIManager.swift in Sources */,
21922239
FA50718C2D54E08800CE9798 /* AddFeatureView.swift in Sources */,
2240+
C71EB5B02E2100B80009E610 /* MapRepository.swift in Sources */,
21932241
A495DF2D2B6A190400478E44 /* CustomMapView.swift in Sources */,
21942242
05DBBB662B17209600B6F110 /* RealmOPMeta.swift in Sources */,
21952243
B0CCB98E2B8626C600AA73DE /* ProfileViewVM.swift in Sources */,
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
[
2+
{
3+
"attribution": {
4+
"required": true,
5+
"text": "Hexagon Imagery",
6+
"url": "https://hexagon.com/products/aerial-imagery"
7+
},
8+
"description": "Hexagon Imagery for Washington state in USA",
9+
"extent": {
10+
"max_zoom": 19,
11+
"polygon": [
12+
[
13+
[
14+
-124.84,
15+
48.99
16+
],
17+
[
18+
-124.78,
19+
47.46
20+
],
21+
[
22+
-124.7,
23+
47.15
24+
],
25+
[
26+
-124.47,
27+
46.95
28+
],
29+
[
30+
-124.15,
31+
46.75
32+
],
33+
[
34+
-124.05,
35+
46.28
36+
],
37+
[
38+
-124.05,
39+
45.94
40+
],
41+
[
42+
-122.4,
43+
45.94
44+
],
45+
[
46+
-122.3,
47+
46
48+
],
49+
[
50+
-121.5,
51+
45.67
52+
],
53+
[
54+
-120.5,
55+
45.7
56+
],
57+
[
58+
-119.9,
59+
46.05
60+
],
61+
[
62+
-118.99,
63+
46.28
64+
],
65+
[
66+
-117.03,
67+
46.34
68+
],
69+
[
70+
-117.04,
71+
47.76
72+
],
73+
[
74+
-117.05,
75+
49
76+
],
77+
[
78+
-118.7,
79+
49
80+
],
81+
[
82+
-120,
83+
49
84+
],
85+
[
86+
-122.76,
87+
49
88+
],
89+
[
90+
-124.84,
91+
48.99
92+
]
93+
]
94+
]
95+
},
96+
"icon": "https://ichs-p-001.sitecorecontenthub.cloud/api/public/content/a-s-s-e-t---3-9-4-2-4-39424?v=ba59b6e9",
97+
"id": "HX_IMG",
98+
"name": "Hexagon Imagery",
99+
"type": "wmts",
100+
"url": "https://waprovisoimg.centralindia.cloudapp.azure.com/streaming/wmts?/1.0.0/HxGN_Imagery/default/WebMercator/{z}/{y}/{x}.png"
101+
}
102+
]

GoInfoGame/GoInfoGame/KeychainManager.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import Security
1111

1212
struct KeychainManager {
1313

14+
enum Keys: String {
15+
case accessToken = "accessToken"
16+
}
17+
1418
static func save(key: String, data: String) -> Bool {
1519
guard let data = data.data(using: .utf8) else { return false }
1620

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//
2+
// APIManager.swift
3+
// GoInfoGame
4+
//
5+
// Created by Prashamsa on 29/06/25.
6+
//
7+
8+
import Foundation
9+
import Combine
10+
11+
// MARK: - APIRequest
12+
13+
protocol APIRequest {
14+
var urlRequest: URLRequest? { get }
15+
}
16+
17+
// MARK: - APIRequest Extension
18+
19+
extension APIRequest {
20+
func setDefaultValues(urlRequest: URLRequest) -> URLRequest {
21+
var request: URLRequest = urlRequest
22+
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
23+
if let jwtAccessToken = KeychainManager.load(key: KeychainManager.Keys.accessToken.rawValue) {
24+
request.setValue(jwtAccessToken, forHTTPHeaderField: "Authorization")
25+
}
26+
return request
27+
}
28+
}
29+
30+
// MARK: - APIHaandler
31+
32+
protocol APIHandler {
33+
/**
34+
Fetches raw data from an API request asynchronously using Combine.
35+
36+
This method performs a network request based on the given `APIRequest` and returns a `Future` publisher that either emits the retrieved `Data` or an `Error`.
37+
38+
- Parameters:
39+
- request: The `APIRequest` object containing request details such as endpoint, headers, and parameters.
40+
41+
- Returns:
42+
A `Future<Data, Error>` publisher that emits raw `Data` on success or an `Error` on failure.
43+
44+
- Note: The caller is responsible for decoding the returned `Data` into the desired model.
45+
*/
46+
func fetchData(from request: APIRequest) -> Future<Data, Error>
47+
48+
func fetchData(from request: APIRequest) async throws -> (Data, URLResponse)
49+
}
50+
51+
// MARK: - APIManager
52+
53+
class APIManager: APIHandler {
54+
/**
55+
A set to store Combine `AnyCancellable` instances.
56+
57+
This property holds subscriptions to Combine publishers, ensuring they remain active for the lifecycle of the object.
58+
Automatically cancels subscriptions when the instance is deinitialized.
59+
60+
- Note: Use this set to manage memory and prevent subscriptions from being prematurely deallocated.
61+
*/
62+
private var cancellables: Set<AnyCancellable> = []
63+
64+
func fetchData(from request: any APIRequest) -> Future<Data, Error> {
65+
return Future<Data, Error> { [weak self] promise in
66+
guard let self = self, let req = request.urlRequest else {
67+
return promise(.failure(NetworkError.invalidURLRequest))
68+
}
69+
70+
URLSession.shared.dataTaskPublisher(for: req)
71+
.tryMap { (data, response) -> Data in
72+
debugPrint("Response \n\(String(data: data, encoding: .utf8) ?? "")")
73+
guard let response = response as? HTTPURLResponse, (200..<300).contains(response.statusCode) else {
74+
throw NetworkError.badNetwrok
75+
}
76+
return data
77+
}
78+
.sink { completion in
79+
switch completion {
80+
case .failure(let error):
81+
promise(.failure(error))
82+
case .finished:
83+
break
84+
}
85+
} receiveValue: { data in
86+
promise(.success(data))
87+
}
88+
.store(in: &cancellables)
89+
}
90+
}
91+
92+
func fetchData(from request: any APIRequest) async throws -> (Data, URLResponse) {
93+
guard let req = request.urlRequest else {
94+
throw NetworkError.invalidURLRequest
95+
}
96+
97+
return try await URLSession.shared.data(for: req)
98+
}
99+
}

0 commit comments

Comments
 (0)