Files
Woka_Native_iOS/WOKA/Network Adapter/NetworkManager.swift
2024-09-05 20:01:07 +05:30

297 lines
13 KiB
Swift

//
// NetworkManager.swift
// WOKA
//
// Created by MacBook Pro on 06/05/24.
//
import Alamofire
class NetworkManager{
static let shareInstance = NetworkManager()
private let alamofireLogger = AlamofireLogger()
private init() {}
enum APIError: Error, LocalizedError {
case networkError
case noNetwork(message:String)
case invalidURL
case parameterEncodingFailed
case responseValidationFailed
case unknown(message: String)
case custom(message: String)
var errorDescription: String? {
switch self {
case .networkError:
return "A network error occurred."
case .noNetwork(let message):
return "No network: \(message)"
case .invalidURL:
return "The URL provided is invalid."
case .parameterEncodingFailed:
return "Failed to encode the parameters."
case .responseValidationFailed:
return "Response validation failed."
case .unknown(let message):
return "Unknown error: \(message)"
case .custom(let message):
return "Custom error: \(message)"
}
}
}
/// This function will do the network call for HTTPMethod & Encoding is URLEncoding with contentType ["application/json"]
///
///
/// - Warning: The returned string is not localized.
///
/// Usage:
///
/// Alamofire network call Generic (.get , .post, .put.).
///
/// - Parameter (header : HTTPHeaders , Params :[String : Any] , URL , Dedocable Generic T Struct. , queue : dispatchQueue decide)
/// - NOTE Oncompletion will be called on main thread so no need to specify the main thread for UI updates
/// - Returns: This function returns a GENERIC response base on the T Model & APIError .
func apiRequest<T: Codable & ResponseProtocol>(
url: URLConvertible,
method: HTTPMethod,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil,
queue: DispatchQueue = QueueHelper.background,
completionHandler: @escaping (Result<T, APIError>) -> Void
) {
// Stop monitoring network reachability
NetworkReachibility.shared.stopMonitoring()
let loginCred = getLoginIDPass()
// Execute the request on the specified queue
queue.async {
AF.request(url, method: method, parameters: parameters, encoding: encoding, headers: headers, requestModifier: { $0.timeoutInterval = 20 })
.authenticate(username: loginCred.0, password: loginCred.1)
.validate(statusCode: 200..<300)
.responseDecodable(of: T.self) { response in
// alamofireLogger.request(response.da, didParseResponse: response)
switch response.result {
case .success(let value):
/*
if Sucess == 4 menas user has logined in some other device, logout that user by saying session timeout
*/
if value.success == 4{
if let topController = UIApplication.topViewController() {
Utilities.dismissProgressHUD()
let sb = UIStoryboard(name: K.StoryBoard.customAlerts, bundle: nil)
let vcPush = sb.instantiateViewController(withIdentifier: K.StoryBoardID.CustomAlerts.alertCustomVC) as! AlertCustomVC
vcPush.contentLabel = "Please Login Again"
vcPush.mainTitleText = "Session Expired."
vcPush.yesBtnText = "Login"
vcPush.onDoneBlock = { isDone in
AuthFunc.shareInstance.logout()
UIApplication.setRootView(LoginNavVC.instantiate(from: .AuthenticationSB))
}
vcPush.modalPresentationStyle = .overCurrentContext
vcPush.modalTransitionStyle = .crossDissolve
topController.present(vcPush, animated: true)
// Utilities.alertWithBtnCompletion(title: "Session Expired", msgBody: "Please Login Again", okBtnStr: "OK", vc: topController) { isDone in
// AuthFunc.shareInstance.logout()
// UIApplication.setRootView(LoginNavVC.instantiate(from: .AuthenticationSB))
// }
}
return
}
// Handle successful response on the main thread
DispatchQueue.main.async {
completionHandler(.success(value))
}
case .failure(let error):
// Handle failure cases
if let afError = error as? AFError {
switch afError {
case .sessionTaskFailed(let urlError):
if urlError._code == -1020 || urlError._code == -1009 {
// Handle network error on the main thread
DispatchQueue.main.async {
completionHandler(.failure(.noNetwork(message: K.ConstantString.noInternet)))
}
} else {
// Handle other network errors on the main thread
DispatchQueue.main.async {
completionHandler(.failure(.networkError))
}
}
case .invalidURL:
// Handle invalid URL error on the main thread
DispatchQueue.main.async {
completionHandler(.failure(.invalidURL))
}
case .parameterEncodingFailed:
// Handle parameter encoding failure on the main thread
DispatchQueue.main.async {
completionHandler(.failure(.parameterEncodingFailed))
}
case .responseValidationFailed:
// Handle response validation failure on the main thread
DispatchQueue.main.async {
completionHandler(.failure(.responseValidationFailed))
}
default:
// Handle other network errors on the main thread
DispatchQueue.main.async {
completionHandler(.failure(.networkError))
}
}
} else {
// Handle unrecognized errors on the main thread
DispatchQueue.main.async {
completionHandler(.failure(.unknown(message: K.ConstantString.unRecognised)))
}
}
}
}
}
}
func getLoginIDPass()-> (String, String){
if let loginID = Bundle.main.infoDictionary?["API_KEY_ID"] as? String , let loginPaass = Bundle.main.infoDictionary?["API_KEY_PASS"] as? String{
let cleanedLoginID = loginID.trimmingCharacters(in: CharacterSet(charactersIn: "\""))
let cleanedLoginPass = loginPaass.trimmingCharacters(in: CharacterSet(charactersIn: "\""))
return (cleanedLoginID, cleanedLoginPass)
}
return ("","")
}
/// This function will do the network call for POST With RAWJSON & Encoding is URLEncoding with contentType ["application/json"]
///
///
/// - Warning: The returned string is not localized.
///
/// Usage:
///
/// Alamofire network call POST(RawJSON).
///
/// - Parameter (header : HTTPHeaders , Params :[String : Any] , URL , Dedocable Generic T Struct.)
///
/// - Returns: This function returns a GENERIC response base on the T Model & ERROR .
func nwCallRawJSON(clicksData : [ClicksAnalytics], onCompletion : @escaping (Bool) -> Void){
let loginCred = getLoginIDPass()
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
guard let jsonData = try? encoder.encode(clicksData),
let jsonArray = try? JSONSerialization.jsonObject(with: jsonData, options: .allowFragments) as? [[String: Any]] else {
print("Failed to encode totalClicks array to JSON")
return
}
let url = APIEndPoints.Analytics.user_clicks
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.headers = ["device-id" : AuthFunc.shareInstance.getDeviceUUID(),
"access-token" : AuthFunc.shareInstance.getAccessToken()]
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
do {
// Set the HTTP body with the JSON data
request.httpBody = try JSONSerialization.data(withJSONObject: jsonArray)
} catch let error {
print("Error: \(error.localizedDescription)")
return
}
AF.request(request).authenticate(username: loginCred.0, password: loginCred.1)
.validate(statusCode: 200..<300)
.responseDecodable(of: CommonResponseModel.self) { response in
switch response.result {
case .success(let data):
onCompletion(true)
case .failure(let error):
onCompletion(false)
}
}
}
func nwCallRawJSONAds(adsData : [AdsClickImpressionsData], onCompletion : @escaping (Bool) -> Void){
let loginCred = getLoginIDPass()
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
guard let jsonData = try? encoder.encode(adsData),
let jsonArray = try? JSONSerialization.jsonObject(with: jsonData, options: .allowFragments) as? [[String: Any]] else {
print("Failed to encode totalClicks array to JSON")
return
}
let url = APIEndPoints.Analytics.update_ad_count
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.headers = ["device-id" : AuthFunc.shareInstance.getDeviceUUID(),
"access-token" : AuthFunc.shareInstance.getAccessToken()]
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
do {
// Set the HTTP body with the JSON data
request.httpBody = try JSONSerialization.data(withJSONObject: jsonArray)
} catch let error {
print("Error: \(error.localizedDescription)")
return
}
AF.request(request).authenticate(username: loginCred.0, password: loginCred.1)
.validate(statusCode: 200..<300)
.responseDecodable(of: CommonResponseModel.self) { response in
switch response.result {
case .success(let data):
onCompletion(true)
case .failure(let error):
onCompletion(false)
}
}
}
}
final class AlamofireLogger: EventMonitor {
func requestDidResume(_ request: Request) {
let allHeaders = request.request.flatMap { $0.allHTTPHeaderFields.map { $0.description } } ?? "None"
let headers = """
⚡️⚡️⚡️⚡️ Request Started: \(request)
⚡️⚡️⚡️⚡️ Headers: \(allHeaders)
"""
NSLog(headers)
let body = request.request.flatMap { $0.httpBody.map { String(decoding: $0, as: UTF8.self) } } ?? "None"
let message = """
⚡️⚡️⚡️⚡️ Request Started: \(request)
⚡️⚡️⚡️⚡️ Body Data: \(body)
"""
NSLog(message)
}
func request<Value>(_ request: DataRequest, didParseResponse response: AFDataResponse<Value>) {
NSLog("⚡️⚡️⚡️⚡️ Response Received: \(response.debugDescription)")
NSLog("⚡️⚡️⚡️⚡️ Response All Headers: \(String(describing: response.response?.allHeaderFields))")
}
}
extension UIApplication {
class func topViewController(_ base: UIViewController? = UIApplication.shared.mainKeyWindow?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(nav.visibleViewController)
}
if let tab = base as? UITabBarController {
if let selected = tab.selectedViewController {
return topViewController(selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(presented)
}
return base
}
}