Support universal link (to report)

This commit is contained in:
Selim Mustafaev 2020-09-20 23:38:43 +03:00
parent 717138bb3e
commit 7a090e8827
12 changed files with 116 additions and 23 deletions

View File

@ -34,6 +34,7 @@
7A11474923FF2B2D00B424AF /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A11474823FF2B2D00B424AF /* Response.swift */; }; 7A11474923FF2B2D00B424AF /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A11474823FF2B2D00B424AF /* Response.swift */; };
7A11474B23FF368B00B424AF /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A11474A23FF368B00B424AF /* Settings.swift */; }; 7A11474B23FF368B00B424AF /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A11474A23FF368B00B424AF /* Settings.swift */; };
7A15051224DB3E3000F39631 /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A15051124DB3E3000F39631 /* AnyEncodable.swift */; }; 7A15051224DB3E3000F39631 /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A15051124DB3E3000F39631 /* AnyEncodable.swift */; };
7A1DC38E2517ED98002E9C99 /* BlockBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1DC38D2517ED98002E9C99 /* BlockBarButtonItem.swift */; };
7A21112A24FC3D7E003BBF6F /* AudioEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A21112924FC3D7E003BBF6F /* AudioEngine.swift */; }; 7A21112A24FC3D7E003BBF6F /* AudioEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A21112924FC3D7E003BBF6F /* AudioEngine.swift */; };
7A27ADC7249D43210035F39E /* RegionsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27ADC6249D43210035F39E /* RegionsController.swift */; }; 7A27ADC7249D43210035F39E /* RegionsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27ADC6249D43210035F39E /* RegionsController.swift */; };
7A27ADF3249F8B650035F39E /* RecordsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27ADF2249F8B650035F39E /* RecordsController.swift */; }; 7A27ADF3249F8B650035F39E /* RecordsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27ADF2249F8B650035F39E /* RecordsController.swift */; };
@ -129,6 +130,7 @@
7A11474A23FF368B00B424AF /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = "<group>"; }; 7A11474A23FF368B00B424AF /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = "<group>"; };
7A11474D23FFEE8800B424AF /* SVProgressHUD.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SVProgressHUD.framework; path = Carthage/Build/iOS/SVProgressHUD.framework; sourceTree = "<group>"; }; 7A11474D23FFEE8800B424AF /* SVProgressHUD.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SVProgressHUD.framework; path = Carthage/Build/iOS/SVProgressHUD.framework; sourceTree = "<group>"; };
7A15051124DB3E3000F39631 /* AnyEncodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyEncodable.swift; sourceTree = "<group>"; }; 7A15051124DB3E3000F39631 /* AnyEncodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyEncodable.swift; sourceTree = "<group>"; };
7A1DC38D2517ED98002E9C99 /* BlockBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockBarButtonItem.swift; sourceTree = "<group>"; };
7A21112924FC3D7E003BBF6F /* AudioEngine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioEngine.swift; sourceTree = "<group>"; }; 7A21112924FC3D7E003BBF6F /* AudioEngine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioEngine.swift; sourceTree = "<group>"; };
7A27ADC6249D43210035F39E /* RegionsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegionsController.swift; sourceTree = "<group>"; }; 7A27ADC6249D43210035F39E /* RegionsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegionsController.swift; sourceTree = "<group>"; };
7A27ADF2249F8B650035F39E /* RecordsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordsController.swift; sourceTree = "<group>"; }; 7A27ADF2249F8B650035F39E /* RecordsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordsController.swift; sourceTree = "<group>"; };
@ -415,6 +417,7 @@
7AB67E8D2435D1A000258F61 /* CustomButton.swift */, 7AB67E8D2435D1A000258F61 /* CustomButton.swift */,
7A1090EB24A4E3E100B4F0B2 /* CellProgressView.swift */, 7A1090EB24A4E3E100B4F0B2 /* CellProgressView.swift */,
7ADF6C96250F41B000F237B2 /* PNKeyboard.swift */, 7ADF6C96250F41B000F237B2 /* PNKeyboard.swift */,
7A1DC38D2517ED98002E9C99 /* BlockBarButtonItem.swift */,
); );
path = Views; path = Views;
sourceTree = "<group>"; sourceTree = "<group>";
@ -562,6 +565,7 @@
7AE26A3524F31B0700625033 /* EventsController.swift in Sources */, 7AE26A3524F31B0700625033 /* EventsController.swift in Sources */,
7AB67E8C2435C38700258F61 /* CustomTextField.swift in Sources */, 7AB67E8C2435C38700258F61 /* CustomTextField.swift in Sources */,
7A27ADF5249FD2F90035F39E /* FileManagerExt.swift in Sources */, 7A27ADF5249FD2F90035F39E /* FileManagerExt.swift in Sources */,
7A1DC38E2517ED98002E9C99 /* BlockBarButtonItem.swift in Sources */,
7AE26A3324EEF9EC00625033 /* UIViewControllerExt.swift in Sources */, 7AE26A3324EEF9EC00625033 /* UIViewControllerExt.swift in Sources */,
7A27ADF3249F8B650035F39E /* RecordsController.swift in Sources */, 7A27ADF3249F8B650035F39E /* RecordsController.swift in Sources */,
7A8A2209248D10EC0073DFD9 /* ResizeImage.swift in Sources */, 7A8A2209248D10EC0073DFD9 /* ResizeImage.swift in Sources */,

View File

@ -73,10 +73,10 @@
filePath = "AutoCat/Utils/Api.swift" filePath = "AutoCat/Utils/Api.swift"
startingColumnNumber = "9223372036854775807" startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807"
startingLineNumber = "228" startingLineNumber = "232"
endingLineNumber = "228" endingLineNumber = "232"
landmarkName = "add(event:to:)" landmarkName = "add(event:to:)"
landmarkType = "7"> landmarkType = "9">
</BreakpointContent> </BreakpointContent>
</BreakpointProxy> </BreakpointProxy>
</Breakpoints> </Breakpoints>

View File

@ -13,6 +13,7 @@ enum QuickAction {
case check case check
case checkNumber(String, VehicleEvent?) case checkNumber(String, VehicleEvent?)
case addVoiceRecord case addVoiceRecord
case openReport(String)
} }
@UIApplicationMain @UIApplicationMain

View File

@ -2,9 +2,9 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>com.apple.developer.applesignin</key> <key>com.apple.developer.associated-domains</key>
<array> <array>
<string>Default</string> <string>applinks:auto.aliencat.pro</string>
</array> </array>
<key>com.apple.security.app-sandbox</key> <key>com.apple.security.app-sandbox</key>
<true/> <true/>

View File

@ -99,6 +99,12 @@ class CheckController: UIViewController, UITableViewDelegate, UITextFieldDelegat
case .addVoiceRecord: case .addVoiceRecord:
self.tabBarController?.selectedIndex = 1 self.tabBarController?.selectedIndex = 1
break break
case .openReport(let number):
ad.quickAction = .none
if let sd = self.view.window?.windowScene?.delegate as? SceneDelegate {
sd.openReport(with: number)
}
break
default: default:
break break
} }

View File

@ -421,7 +421,7 @@ class ReportController: UIViewController, UICollectionViewDataSource, UICollecti
let shareLink = UIAlertAction(title: "As link", style: .default) { _ in let shareLink = UIAlertAction(title: "As link", style: .default) { _ in
guard let vehicle = self.vehicle else { return } guard let vehicle = self.vehicle else { return }
if let jwt = try? JWT.generate(for: vehicle.number), let url = URL(string: Constants.reportLinkBaseURL + "?token=" + jwt) { if let jwt = try? JWT<EmptyPayload>.generate(for: vehicle.number), let url = URL(string: Constants.reportLinkBaseURL + "?token=" + jwt) {
let controller = UIActivityViewController(activityItems: [url], applicationActivities: nil) let controller = UIActivityViewController(activityItems: [url], applicationActivities: nil)
controller.popoverPresentationController?.barButtonItem = sender controller.popoverPresentationController?.barButtonItem = sender
self.present(controller, animated: true) self.present(controller, animated: true)

View File

@ -14,7 +14,7 @@ class SettingsController: FormViewController {
} }
<<< LabelRow("GoogleAccount") { row in <<< LabelRow("GoogleAccount") { row in
row.title = "Google" row.title = "Google"
if let jwtString = Settings.shared.user.firebaseIdToken, let jwt = JWT(string: jwtString) { if let jwtString = Settings.shared.user.firebaseIdToken, let jwt = JWT<FirebasePayload>(string: jwtString) {
row.value = jwt.payload.email row.value = jwt.payload.email
} else { } else {
row.value = "Log In" row.value = "Log In"
@ -92,7 +92,7 @@ class SettingsController: FormViewController {
} }
func displayLogoutSheet(for row: String) { func displayLogoutSheet(for row: String) {
guard let jwtString = Settings.shared.user.firebaseIdToken, let jwt = JWT(string: jwtString) else { return } guard let jwtString = Settings.shared.user.firebaseIdToken, let jwt = JWT<FirebasePayload>(string: jwtString) else { return }
let sheet = UIAlertController(title: jwt.payload.name, message: "You are currently signed in with email \(jwt.payload.email). It will help to gather more data about vehicles.", preferredStyle: .actionSheet) let sheet = UIAlertController(title: jwt.payload.name, message: "You are currently signed in with email \(jwt.payload.email). It will help to gather more data about vehicles.", preferredStyle: .actionSheet)
@ -116,7 +116,7 @@ class SettingsController: FormViewController {
if let vc = storyboard.instantiateViewController(identifier: "GoogleSignInController") as? GoogleSignInController { if let vc = storyboard.instantiateViewController(identifier: "GoogleSignInController") as? GoogleSignInController {
vc.completion = { vc.completion = {
guard let googleRow = self.form.rowBy(tag: "GoogleAccount") as? LabelRow else { return } guard let googleRow = self.form.rowBy(tag: "GoogleAccount") as? LabelRow else { return }
if let jwtString = Settings.shared.user.firebaseIdToken, let jwt = JWT(string: jwtString) { if let jwtString = Settings.shared.user.firebaseIdToken, let jwt = JWT<FirebasePayload>(string: jwtString) {
googleRow.value = jwt.payload.email googleRow.value = jwt.payload.email
} else { } else {
googleRow.value = "Log In" googleRow.value = "Log In"

View File

@ -1,6 +1,7 @@
import UIKit import UIKit
import os.log import os.log
import AVFoundation import AVFoundation
import RxSwift
class SceneDelegate: UIResponder, UIWindowSceneDelegate { class SceneDelegate: UIResponder, UIWindowSceneDelegate {
@ -17,6 +18,14 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
if activity.activityType == "pro.aliencat.autocat.addVoiceRecord" { if activity.activityType == "pro.aliencat.autocat.addVoiceRecord" {
ad.quickAction = .addVoiceRecord ad.quickAction = .addVoiceRecord
} }
if let url = activity.webpageURL {
if let param = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems?.first, let token = param.value {
if let jwt = JWT<NumberPayload>(string: token) {
ad.quickAction = .openReport(jwt.payload.plateNumber)
}
}
}
} }
self.window = UIWindow(windowScene: windowScene) self.window = UIWindow(windowScene: windowScene)
@ -104,6 +113,14 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
if userActivity.activityType == "pro.aliencat.autocat.addVoiceRecord" { if userActivity.activityType == "pro.aliencat.autocat.addVoiceRecord" {
self.handleAddVoiceRecordAction() self.handleAddVoiceRecordAction()
} }
if let url = userActivity.webpageURL {
if let param = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems?.first, let token = param.value {
if let jwt = JWT<NumberPayload>(string: token) {
self.openReport(with: jwt.payload.plateNumber)
}
}
}
} }
func handleAddVoiceRecordAction() { func handleAddVoiceRecordAction() {
@ -124,5 +141,23 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
} }
} }
} }
func openReport(with number: String) {
guard let rootController = self.window?.rootViewController else { return }
IHProgressHUD.show()
_ = Api.getReport(for: number).observeOn(MainScheduler.instance).subscribe { vehicle in
let sb = UIStoryboard(name: "Main", bundle: nil)
let controller = sb.instantiateViewController(identifier: "ReportController") as ReportController
controller.vehicle = vehicle
let nav = UINavigationController(rootViewController: controller)
nav.modalPresentationStyle = .fullScreen
controller.navigationItem.leftBarButtonItem = BlockBarButtonItem(barButtonSystemItem: .close) { _ in nav.dismiss(animated: true) }
rootController.present(nav, animated: true)
IHProgressHUD.dismiss()
} onError: { error in
IHProgressHUD.show(error: error)
}
}
} }

View File

@ -80,7 +80,7 @@ class Api {
// MARK: - Firebase API // MARK: - Firebase API
public static func refreshFbToken() -> Single<Void> { public static func refreshFbToken() -> Single<Void> {
guard let token = Settings.shared.user.firebaseIdToken, let refreshToken = Settings.shared.user.firebaseRefreshToken, let jwt = JWT(string: token), jwt.expired else { guard let token = Settings.shared.user.firebaseIdToken, let refreshToken = Settings.shared.user.firebaseRefreshToken, let jwt = JWT<FirebasePayload>(string: token), jwt.expired else {
return .just(()) return .just(())
} }
@ -208,6 +208,10 @@ class Api {
} }
} }
public static func getReport(for number: String) -> Single<Vehicle> {
return self.makeGetRequest(api: "vehicles/report", params: ["number": number])
}
public static func getBrands() -> Single<[String]> { public static func getBrands() -> Single<[String]> {
return self.makeEmptyGetRequest(api: "vehicles/brands") return self.makeEmptyGetRequest(api: "vehicles/brands")
} }

View File

@ -3,9 +3,9 @@ import Foundation
enum Constants { enum Constants {
static var baseUrl: String { static var baseUrl: String {
#if DEBUG #if DEBUG
//return "http://127.0.0.1:3000/" return "http://127.0.0.1:3000/"
//return "http://192.168.1.67:3000/" //return "http://192.168.1.67:3000/"
return "https://vps.aliencat.pro:8443/" //return "https://vps.aliencat.pro:8443/"
#else #else
return "https://vps.aliencat.pro:8443/" return "https://vps.aliencat.pro:8443/"
#endif #endif

View File

@ -61,15 +61,28 @@ extension String {
} }
} }
struct JwtPayload: Codable { protocol JwtPayload: Codable {
var email: String var exp: Int { get }
var name: String
var exp: Int
} }
class JWT { extension JwtPayload {
var exp: Int { 0 }
}
struct EmptyPayload: JwtPayload { }
struct FirebasePayload: JwtPayload {
var email: String
var name: String
}
struct NumberPayload: JwtPayload {
var plateNumber: String
}
class JWT<T> where T: JwtPayload {
private var jwt: String private var jwt: String
public let payload: JwtPayload public let payload: T
public var expired: Bool { public var expired: Bool {
return TimeInterval(self.payload.exp) < Date().timeIntervalSince1970 return TimeInterval(self.payload.exp) < Date().timeIntervalSince1970
} }
@ -80,13 +93,14 @@ class JWT {
let parts = string.split(separator: ".") let parts = string.split(separator: ".")
if parts.count == 3 { if parts.count == 3 {
var payloadStr = String(parts[1]) var payloadStr = String(parts[1])
if (payloadStr.count % 2 != 0) { if (payloadStr.count % 4 != 0) {
payloadStr += String(repeating: "=", count: (payloadStr.count % 2)) payloadStr += String(repeating: "=", count: (payloadStr.count % 4))
} }
if let json = Data(base64Encoded: payloadStr, options: [.ignoreUnknownCharacters]) { if let json = Data(base64Encoded: payloadStr, options: [.ignoreUnknownCharacters]) {
if let resp = try? JSONDecoder().decode(JwtPayload.self, from: json) { do {
self.payload = resp self.payload = try JSONDecoder().decode(T.self, from: json)
} else { } catch {
print(error)
return nil return nil
} }
} else { } else {

View File

@ -0,0 +1,29 @@
import UIKit
class BlockBarButtonItem: UIBarButtonItem {
typealias ActionHandler = (UIBarButtonItem) -> Void
private var actionHandler: ActionHandler?
convenience init(image: UIImage?, style: UIBarButtonItem.Style, actionHandler: ActionHandler?) {
self.init(image: image, style: style, target: nil, action: #selector(barButtonItemPressed(sender:)))
target = self
self.actionHandler = actionHandler
}
convenience init(title: String?, style: UIBarButtonItem.Style, actionHandler: ActionHandler?) {
self.init(title: title, style: style, target: nil, action: #selector(barButtonItemPressed(sender:)))
target = self
self.actionHandler = actionHandler
}
convenience init(barButtonSystemItem systemItem: UIBarButtonItem.SystemItem, actionHandler: ActionHandler?) {
self.init(barButtonSystemItem: systemItem, target: nil, action: #selector(barButtonItemPressed(sender:)))
target = self
self.actionHandler = actionHandler
}
@objc func barButtonItemPressed(sender: UIBarButtonItem) {
actionHandler?(sender)
}
}