mobile-ios/CovidSafe/API/MessageAPI.swift

200 lines
7.2 KiB
Swift
Raw Permalink Normal View History

2020-07-21 05:42:48 +00:00
//
// MessageAPI.swift
// CovidSafe
//
// Copyright © 2020 Australian Government. All rights reserved.
//
import Foundation
import Alamofire
2020-11-10 00:51:00 +00:00
class MessageAPI: CovidSafeAuthenticatedAPI {
2020-07-21 05:42:48 +00:00
static let keyLastApiUpdate = "keyLastApiUpdate"
2020-08-18 00:52:17 +00:00
static let keyLastVersionChecked = "keyLastVersionChecked"
2020-09-14 01:23:11 +00:00
2020-11-10 00:51:00 +00:00
static func getMessagesIfNeeded(completion: @escaping (MessageResponse?, CovidSafeAPIError?) -> Void) {
2020-07-21 05:42:48 +00:00
if shouldGetMessages() {
2020-09-14 01:23:11 +00:00
getMessages(completion: completion)
}
}
2020-11-10 00:51:00 +00:00
static func getMessages(completion: @escaping (MessageResponse?, CovidSafeAPIError?) -> Void) {
2020-09-14 01:23:11 +00:00
guard let token = UserDefaults.standard.string(forKey: "deviceTokenForAPN") else {
completion(nil, .RequestError)
return
}
//Get relevat encounter data
guard let persistentContainer =
EncounterDB.shared.persistentContainer else {
completion(nil, .RequestError)
2020-08-03 06:01:39 +00:00
return
2020-09-14 01:23:11 +00:00
}
let managedContext = persistentContainer.newBackgroundContext()
guard let encounterLastWeekRequest = Encounter.fetchEncountersInLast(days: 7) else {
completion(nil, .RequestError)
return
}
do {
//fetch last week encounters count
let weekEncounters = try managedContext.count(for: encounterLastWeekRequest)
let healthcheck = BluetraceManager.shared.isBluetoothOn() && BluetraceManager.shared.isBluetoothAuthorized() ?
healthCheckParamValue.OK :
healthCheckParamValue.POSSIBLE_ERROR
let encounterCheck = weekEncounters > 0 ? healthCheckParamValue.OK : healthCheckParamValue.POSSIBLE_ERROR
2020-08-03 06:01:39 +00:00
2020-09-14 01:23:11 +00:00
// Make API call to get messages
let messageRequest = MessageRequest(remotePushToken: token, healthcheck: healthcheck, encountershealth: encounterCheck)
getMessages(msgRequest: messageRequest, completion: completion)
} catch let error as NSError {
completion(nil, .RequestError)
DLog("Could not fetch encounter(s) from db. \(error), \(error.userInfo)")
2020-07-21 05:42:48 +00:00
}
}
private static func shouldGetMessages() -> Bool {
2021-03-18 03:16:35 +00:00
guard isBusy == false else {
return false
}
2020-07-21 05:42:48 +00:00
let lastChecked = UserDefaults.standard.double(forKey: keyLastApiUpdate)
2020-08-18 00:52:17 +00:00
let versionChecked = UserDefaults.standard.integer(forKey: keyLastVersionChecked)
2020-07-21 05:42:48 +00:00
var shouldGetMessages = true
let calendar = NSCalendar.current
2021-05-13 00:39:38 +00:00
let currentDate = Date()
2020-07-21 05:42:48 +00:00
2020-08-18 00:52:17 +00:00
// if the current version is newer than the last version checked, allow messages call
if let currVersionStr = Bundle.main.version, let currVersion = Int(currVersionStr), currVersion > versionChecked {
return true
}
2020-07-21 05:42:48 +00:00
if lastChecked > 0 {
let lastCheckedDate = Date(timeIntervalSince1970: lastChecked)
2020-09-14 01:23:11 +00:00
let components = calendar.dateComponents([.hour], from: lastCheckedDate, to: currentDate)
2020-07-21 05:42:48 +00:00
2020-09-14 01:23:11 +00:00
if let numHours = components.hour {
2021-05-13 00:39:38 +00:00
shouldGetMessages = numHours >= 4
2020-07-21 05:42:48 +00:00
}
}
return shouldGetMessages
}
2020-08-03 06:01:39 +00:00
private static func getMessages(msgRequest: MessageRequest,
2020-11-10 00:51:00 +00:00
completion: @escaping (MessageResponse?, CovidSafeAPIError?) -> Void) {
2020-08-03 06:01:39 +00:00
guard let apiHost = PlistHelper.getvalueFromInfoPlist(withKey: "API_Host", plistName: "CovidSafe-config") else {
2020-09-14 01:23:11 +00:00
completion(nil, .RequestError)
2020-07-21 05:42:48 +00:00
return
}
2020-09-14 01:23:11 +00:00
let preferredLanguages = Locale.preferredLanguages.count > 5 ? Locale.preferredLanguages[0...5].joined(separator: ",") : Locale.preferredLanguages.joined(separator: ",")
2020-08-03 06:01:39 +00:00
var params: [String : Any] = [
"os" : "ios-\(UIDevice.current.systemVersion)",
"healthcheck" : msgRequest.healthcheck.rawValue,
2020-09-14 01:23:11 +00:00
"encountershealth" : msgRequest.encountershealth.rawValue,
"preferredlanguages": preferredLanguages
]
2020-07-21 05:42:48 +00:00
if let buildString = Bundle.main.version {
params["appversion"] = "\(buildString)"
}
if let remoteToken = msgRequest.remotePushToken {
params["token"] = remoteToken
}
2021-03-18 03:16:35 +00:00
isBusy = true
2021-05-13 00:39:38 +00:00
guard let authHeaders = try? authenticatedHeaders() else {
completion(nil, .RequestError)
return
}
2020-07-21 05:42:48 +00:00
CovidNetworking.shared.session.request("\(apiHost)/messages",
method: .get,
parameters: params,
2021-05-13 00:39:38 +00:00
headers: authHeaders,
2021-03-18 03:16:35 +00:00
interceptor: CovidRequestRetrier(retries: 3)
2020-07-21 05:42:48 +00:00
).validate().responseDecodable(of: MessageResponse.self) { (response) in
2020-09-14 01:23:11 +00:00
switch response.result {
case .success:
guard let messageResponse = response.value else { return }
// save successful timestamp
2021-05-13 00:39:38 +00:00
let minutesToDefer = Int.random(in: 0..<30)
2020-09-14 01:23:11 +00:00
let calendar = NSCalendar.current
let currentDate = Date()
if let deferredDate = calendar.date(byAdding: .minute, value: minutesToDefer, to: currentDate) {
UserDefaults.standard.set(deferredDate.timeIntervalSince1970, forKey: keyLastApiUpdate)
} else {
2020-07-21 05:42:48 +00:00
UserDefaults.standard.set(currentDate.timeIntervalSince1970, forKey: keyLastApiUpdate)
}
2020-09-14 01:23:11 +00:00
UserDefaults.standard.set(Bundle.main.version, forKey: keyLastVersionChecked)
2021-03-18 03:16:35 +00:00
isBusy = false
2020-09-14 01:23:11 +00:00
completion(messageResponse, nil)
case .failure(_):
guard let statusCode = response.response?.statusCode else {
completion(nil, .UnknownError)
return
}
2020-11-10 00:51:00 +00:00
2020-09-14 01:23:11 +00:00
if (statusCode == 200) {
completion(nil, .ResponseError)
2020-11-10 00:51:00 +00:00
return
}
if statusCode == 401, let respData = response.data {
completion(nil, processUnauthorizedError(respData))
return
2020-09-14 01:23:11 +00:00
}
if (statusCode >= 400 && statusCode < 500) {
completion(nil, .RequestError)
2020-11-10 00:51:00 +00:00
return
2020-09-14 01:23:11 +00:00
}
2021-03-18 03:16:35 +00:00
isBusy = false
2020-09-14 01:23:11 +00:00
completion(nil, .ServerError)
}
2020-07-21 05:42:48 +00:00
}
}
}
2020-08-03 06:01:39 +00:00
enum healthCheckParamValue: String {
case OK = "OK"
case POSSIBLE_ERROR = "POSSIBLE_ERROR"
case ERROR = "ERROR"
}
2020-07-21 05:42:48 +00:00
struct MessageRequest {
var remotePushToken: String?
2020-08-03 06:01:39 +00:00
var healthcheck: healthCheckParamValue
2020-09-14 01:23:11 +00:00
var encountershealth: healthCheckParamValue
2020-07-21 05:42:48 +00:00
}
struct MessageResponse: Decodable {
2020-08-18 00:52:17 +00:00
let messages: [Message]?
let forceappupgrade: Bool
enum CodingKeys: String, CodingKey {
case messages
case forceappupgrade
}
}
struct Message: Decodable {
let title: String?
let body: String?
let destination: String?
enum CodingKeys: String, CodingKey {
case title
case body
case destination
}
2020-07-21 05:42:48 +00:00
}