Skip to content

Commit 28c511e

Browse files
committed
Implemented deep linking with avivscr schema
- Updated environment values to support the deeplinking -
1 parent 2f9a546 commit 28c511e

13 files changed

Lines changed: 139 additions & 28 deletions

GoInfoGame/GoInfoGame/AppDelegate.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
6363
}
6464

6565
private func refreshToken() {
66-
TokenRefresher.shared.refreshToken { status in
66+
TokenRefresher.shared.refreshToken { status, _ in
6767
print("Refresh status \(status)")
6868
if status == false {
6969
DispatchQueue.main.async {

GoInfoGame/GoInfoGame/Helpers/Generated/Strings+Generated.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ internal enum L10n {
2121
internal static let composeMessage = L10n.tr("Localizable", "Compose message", fallback: "Compose message")
2222
/// Don't show again for this session
2323
internal static let dontShowAgain = L10n.tr("Localizable", "dont_show_again", fallback: "Don't show again for this session")
24+
/// Invalid credentials
25+
internal static let invalidCredentials = L10n.tr("Localizable", "Invalid credentials", fallback: "Invalid credentials")
2426
/// My Profile
2527
internal static let myProfile = L10n.tr("Localizable", "My Profile", fallback: "My Profile")
2628
/// OTHER ANSWERS...

GoInfoGame/GoInfoGame/Info.plist

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,19 @@
1111
<string>com.vindago.goinfogame</string>
1212
<key>CFBundleURLSchemes</key>
1313
<array>
14-
<string>goinfogame</string>
14+
<string>avivscr</string>
15+
</array>
16+
</dict>
17+
<dict>
18+
<key>CFBundleTypeRole</key>
19+
<string>Editor</string>
20+
<key>CFBundleURLIconFile</key>
21+
<string></string>
22+
<key>CFBundleURLName</key>
23+
<string>tech.opentoall.aviv.scoutroute</string>
24+
<key>CFBundleURLSchemes</key>
25+
<array>
26+
<string>avivscr</string>
1527
</array>
1628
</dict>
1729
</array>

GoInfoGame/GoInfoGame/Login/SessionManager.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ import UIKit
1212
final class SessionManager: ObservableObject {
1313
static let shared = SessionManager()
1414
private init() {
15-
username = KeychainManager.load(.username, for: APIConfiguration.shared.environment)
1615
}
1716

1817
@Published var isLoginSuccessful: Bool = false
1918
@Published var hasLoginFailed: Bool = false
20-
private(set) var username: String? = nil
19+
var username: String? {
20+
return KeychainManager.load(.username, for: APIConfiguration.shared.environment)
21+
}
2122

2223
var lastLoginPassword: String?
2324

@@ -36,7 +37,6 @@ final class SessionManager: ObservableObject {
3637
DispatchQueue.main.async { [weak self] in
3738
switch result {
3839
case .success(let response):
39-
self?.username = username
4040
_ = KeychainManager.save(key: "accessToken", data: response.accessToken)
4141
self?.lastLoginPassword = password
4242
UserDefaults.standard.setValue(response.expiresIn, forKey: "accessToken_expire_in")
@@ -53,7 +53,7 @@ final class SessionManager: ObservableObject {
5353
print("Login failed:", error)
5454
self?.isLoginSuccessful = false
5555
self?.hasLoginFailed = true
56-
completion(false, "Invalid credentials")
56+
completion(false, L10n.Localizable.invalidCredentials)
5757
}
5858
}
5959
}

GoInfoGame/GoInfoGame/Login/View/PosmLogin.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,12 @@ struct PosmLoginView: View {
6969
selectedEnvironment = environment
7070
APIConfiguration.shared.environment = environment
7171
}) {
72-
Text(environment.rawValue)
72+
Text(environment.displayString())
7373
}
7474
}
7575
} label: {
7676
HStack {
77-
Text("Environment: \(selectedEnvironment.rawValue)")
77+
Text("Environment: \(selectedEnvironment.displayString())")
7878
.foregroundColor(.black)
7979
Image(systemName: "chevron.down")
8080
}

GoInfoGame/GoInfoGame/Login/ViewModel/PosmLoginViewModel.swift

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,5 +100,70 @@ class PosmLoginViewModel: ObservableObject {
100100
self.isLoginSuccess = true
101101
}
102102
}
103+
104+
func loginWithRefreshToken(refreshToken: String) {
105+
isLoading = true
106+
107+
TokenRefresher.shared.refreshToken(refreshToken: refreshToken) { [weak self] result, error in
108+
DispatchQueue.main.async { [weak self, result, error] in
109+
self?.isLoading = false
110+
111+
self?.loggedIn = result
112+
self?.isLoginSuccess = result
113+
self?.hasLoginFailed = !result
114+
if let _ = error {
115+
self?.loginFailedMessage = L10n.Localizable.invalidCredentials
116+
}
117+
118+
guard result,
119+
let accessToken = KeychainManager.load(key: "accessToken"),
120+
let email = self?.emailIDfromJWT(token: accessToken) else {
121+
return
122+
}
123+
_ = KeychainManager.save(.username, value: email, for: APIConfiguration.shared.environment)
124+
}
125+
}
126+
}
127+
128+
private func emailIDfromJWT(token: String) -> String? {
129+
let segments = token.components(separatedBy: ".")
130+
guard segments.count == 3 else {
131+
debugPrint("Invalid JWT format.")
132+
return nil
133+
}
134+
135+
let payloadSegment = segments[1]
136+
var base64 = payloadSegment
137+
.replacingOccurrences(of: "-", with: "+")
138+
.replacingOccurrences(of: "_", with: "/")
139+
140+
let remainingLength = base64.count % 4
141+
if remainingLength > 0 {
142+
let padding = String(repeating: "=", count: 4 - remainingLength)
143+
base64 = base64 + padding
144+
}
145+
146+
guard let data = Data(base64Encoded: base64) else {
147+
debugPrint("Could not Base64URL decode the payload.")
148+
return nil
149+
}
150+
151+
do {
152+
guard let jsonObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
153+
debugPrint("Could not parse payload data as JSON object.")
154+
return nil
155+
}
156+
157+
if let email = jsonObject["email"] as? String {
158+
return email
159+
} else {
160+
debugPrint("Email claim not found in JWT payload, or is not a String.")
161+
return nil
162+
}
163+
} catch {
164+
debugPrint("Error parsing JSON payload: \(error)")
165+
return nil
166+
}
167+
}
103168
}
104169

GoInfoGame/GoInfoGame/Network/APIConfiguration.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class APIConfiguration {
1818
let savedEnvironment = APIEnvironment(rawValue: savedValue) {
1919
return savedEnvironment
2020
}
21-
return .staging // default value
21+
return .production // default value
2222
}
2323
set {
2424
UserDefaults.standard.set(newValue.rawValue, forKey: environmentKey)

GoInfoGame/GoInfoGame/Network/APIEnvironment.swift

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
import Foundation
99

1010
enum APIEnvironment: String, CaseIterable {
11-
case development = "Development"
12-
case staging = "Staging"
13-
case production = "Production"
11+
case development = "dev"
12+
case staging = "stage"
13+
case production = "prod"
1414
// case osm = "OSM"
1515
//
1616
var workspaceBaseURL: String {
@@ -67,4 +67,15 @@ enum APIEnvironment: String, CaseIterable {
6767
return "https://api.openstreetcam.org/1.0"
6868
}
6969
}
70+
71+
func displayString() -> String {
72+
switch self {
73+
case .development:
74+
return "Development"
75+
case .staging:
76+
return "Staging"
77+
case .production:
78+
return "Production"
79+
}
80+
}
7081
}

GoInfoGame/GoInfoGame/Network/APIHandler/NetworkManager.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ class NetworkManager: NetworkHandler {
147147
private func refreshSession<T: Decodable>(promise: @escaping (Result<T, Error>) -> Void,
148148
request: APIRequest,
149149
type: T.Type) {
150-
TokenRefresher.shared.refreshToken {[weak self] status in
150+
TokenRefresher.shared.refreshToken {[weak self] status, _ in
151151
guard let self = self else {
152152
return promise(.failure(NetworkError.badNetwrok))
153153
}

GoInfoGame/GoInfoGame/Network/ApiManager.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ class ApiManager {
151151
request.url!.lastPathComponent.contains("refresh-token") == false,
152152
request.url!.lastPathComponent.contains("authenticate") == false {
153153
print("Failed requests: \(String(describing: request.url))")
154-
TokenRefresher.shared.refreshToken { [weak self] status in
154+
TokenRefresher.shared.refreshToken { [weak self] status, _ in
155155
if status {
156156
let accessToken = KeychainManager.load(key: "accessToken") ?? ""
157157
var headers = endpoint.headers

0 commit comments

Comments
 (0)