Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/download support #21

Merged
merged 3 commits into from
Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 38 additions & 41 deletions Sources/ACMNetworking/ACMNetworking.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,57 +6,54 @@ import Foundation

/// ACMNetworking, make requests easily
public class ACMNetworking: NSObject {
private var task: URLSessionDataTask?
var mainEndpoint: ACMBaseEndpoint?
var session: URLSession?
var requestTask: URLSessionDataTask?
var downloadTask: URLSessionDownloadTask?
var taskProgress: NSKeyValueObservation?

/// Predefined variables
var logger: ACMBaseLogger? {
mainEndpoint?.logger
}

var stringUtils: ACMStringUtils? {
mainEndpoint?.stringUtils
}

/// Public Init function
/// For creating object with SDK
override public init() {
super.init()
}

/// Main request function
///
/// - Parameters:
/// - endpoint: base endpoint that keeps all endpoint information
/// - currentRetryCount(Optional): retry request count
/// - onSuccess: Callback for success scenario
/// - onError: Callback for error scenario
///
/// - Returns:
/// - Void
public func request<T: Decodable>(to endpoint: ACMBaseEndpoint,
currentRetryCount: Int? = 0,
onSuccess: ACMGenericCallbacks.ResponseCallback<T>,
onError: ACMGenericCallbacks.ErrorCallback)
{
guard let urlRequest = generateURLRequest(endpoint: endpoint) else { return }

task = endpoint.session(delegate: self).dataTask(with: urlRequest) { [weak self] data, response, error in
guard let self else { return }

self.handleNilErrorResponse(with: endpoint, error: error, onError: onError)
self.handleNilResponse(with: endpoint, response: response, onError: onError)
self.handleConnectivityError(with: endpoint, error: error, onError: onError)

guard let data = self.handleData(with: endpoint, data: data, onError: onError) else { return }
guard let httpResponse = self.handleHttpResponse(with: endpoint, response: response, onError: onError) else { return }

// Check if response is in valid http range
guard self.validateResponse(with: httpResponse) else {
self.executeRetry(with: endpoint, httpResponse: httpResponse, data: data, currentRetryCount: currentRetryCount, onSuccess: onSuccess, onError: onError)
return
}

self.cancel()

self.handleResult(with: endpoint, data: data, onSuccess: onSuccess, onError: onError)
}
task?.resume()
/// Public destroy function
deinit {
print("ACMNetworking deinited")
}

/// Cancels the current network request
public func cancel() {
task?.cancel()
task = nil
cancelRequestTask()
cancelDownloadTask()
session?.invalidateAndCancel()
session = nil
taskProgress?.invalidate()
taskProgress = nil
mainEndpoint = nil
}

private func cancelRequestTask() {
if requestTask?.state == .running {
requestTask?.cancel()
}
requestTask = nil
}

private func cancelDownloadTask() {
if downloadTask?.state == .running {
downloadTask?.cancel()
}
downloadTask = nil
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ public enum ACMGenericCallbacks {
public typealias InfoCallback = ((Bool?, Error?) -> Void)?
/// Success callback with generic response for closures
public typealias ResponseCallback<T> = ((T) -> Void)?
/// Success callback with generic response for closures
public typealias DownloadCallback = ((ACMDownloadModel) -> Void)?
/// Progress callback with generic response for closures
public typealias ProgressCallback = ((ACMProgressModel) -> Void)?
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
//

enum ACMNetworkingConstants {
static var configOverride: Bool = false
static var configOverride: Bool? = false
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public enum ACMNetworkConstants {
public static var httpBodyMultipart = "Multipart data, length: %@"
/// Info message for retry mechanism
public static var httpRetryCount = "Current retry count is %d, total retry count is %d"
/// Error message if data is invalid and could not be parsed
public static var genericErrorMessage = "Generic error : %@"
}

public extension ACMNetworkConstants {
Expand All @@ -52,12 +54,12 @@ public extension ACMNetworkConstants {
}

/// Header for holding multipart header with content type
static func multipartHeader(model: ACMMultipartContentTypeModel) -> ACMHeaderModel {
ACMHeaderModel(field: "Content-Type", value: ACMStringUtils.shared.merge(list: [
static func multipartHeader(model: ACMMultipartContentTypeModel, utils: ACMStringUtils?) -> ACMHeaderModel {
ACMHeaderModel(field: "Content-Type", value: utils?.merge(list: [
model.type,
" ",
model.boundary,
]))
]) ?? "")
}

/// Header for holding multipart accept type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,60 +7,65 @@ import Foundation
extension ACMNetworking {
func baseRequest(to endpoint: ACMBaseEndpoint) -> URLRequest? {
guard let urlRequest = endpoint.urlRequest else {
ACMBaseLogger.error(ACMNetworkConstants.urlRequestErrorMessage)
endpoint.logger?.error(ACMNetworkConstants.urlRequestErrorMessage)
return nil
}

let info = ACMStringUtils.shared.merge(list: [
var infoList = [
ACMNetworkConstants.httpRequestType,
endpoint.method.rawValue,
])
ACMBaseLogger.info(info)
]

if let methodRaw = endpoint.method?.rawValue {
infoList.append(methodRaw)
}

let info = endpoint.stringUtils?.merge(list: infoList)
endpoint.logger?.info(info)

if let url = endpoint.url {
let info = ACMStringUtils.shared.merge(list: [
let info = endpoint.stringUtils?.merge(list: [
ACMNetworkConstants.httpURLMessage,
"\(url)",
])
ACMBaseLogger.info(info)
endpoint.logger?.info(info)
}

if let authHeader = endpoint.authHeader {
let info = ACMStringUtils.shared.merge(list: [
let info = endpoint.stringUtils?.merge(list: [
ACMNetworkConstants.httpAuthHeadersMessage,
"\(authHeader)",
])
ACMBaseLogger.info(info)
endpoint.logger?.info(info)
}

if let headers = endpoint.headers, headers.count > 0 {
let info = ACMStringUtils.shared.merge(list: [
let info = endpoint.stringUtils?.merge(list: [
ACMNetworkConstants.httpHeadersMessage,
"\(headers)",
])
ACMBaseLogger.info(info)
endpoint.logger?.info(info)
}

if let queryItems = endpoint.queryItems, queryItems.count > 0 {
let info = ACMStringUtils.shared.merge(list: [
let info = endpoint.stringUtils?.merge(list: [
ACMNetworkConstants.httpQueryItemsMessage,
"\(queryItems)",
])
ACMBaseLogger.info(info)
endpoint.logger?.info(info)
}

if let params = endpoint.params {
let info = ACMStringUtils.shared.merge(list: [
let info = endpoint.stringUtils?.merge(list: [
ACMNetworkConstants.httpBodyMessage,
params.paramsRaw,
])
ACMBaseLogger.info(info)
endpoint.logger?.info(info)
} else if let data = endpoint.mediaData {
let info = ACMStringUtils.shared.merge(list: [
let info = endpoint.stringUtils?.merge(list: [
ACMNetworkConstants.httpBodyMessage,
String(format: ACMNetworkConstants.httpBodyMultipart, "\(data.length)"),
])
ACMBaseLogger.info(info)
endpoint.logger?.info(info)
}

return urlRequest
Expand All @@ -75,13 +80,13 @@ extension ACMNetworking: URLSessionTaskDelegate {
/// - task: URL session task
/// - didFinishCollecting: Metrics that gathered
public func urlSession(_: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
let message = ACMStringUtils.shared.merge(list: [
let message = mainEndpoint?.stringUtils?.merge(list: [
"didFinishCollecting",
task.description,
"metrics",
"\(metrics.taskInterval)",
])
ACMBaseLogger.info(message)
logger?.info(message)
}

/// URL Session taskIsWaitingForConnectivity
Expand All @@ -90,11 +95,11 @@ extension ACMNetworking: URLSessionTaskDelegate {
/// - session: URL Session
/// - task: URL session task
public func urlSession(_: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) {
let message = ACMStringUtils.shared.merge(list: [
let message = mainEndpoint?.stringUtils?.merge(list: [
"taskIsWaitingForConnectivity",
task.description,
])
ACMBaseLogger.info(message)
logger?.info(message)
}

/// URL Session didSendBodyData
Expand All @@ -106,7 +111,7 @@ extension ACMNetworking: URLSessionTaskDelegate {
/// - totalBytesSent
/// - totalBytesExpectedToSend
public func urlSession(_: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
let message = ACMStringUtils.shared.merge(list: [
let message = mainEndpoint?.stringUtils?.merge(list: [
"task",
task.description,
"didSendBodyData",
Expand All @@ -116,6 +121,6 @@ extension ACMNetworking: URLSessionTaskDelegate {
"totalBytesExpectedToSend",
"\(totalBytesExpectedToSend)",
])
ACMBaseLogger.info(message)
logger?.info(message)
}
}
Loading
Loading