Skip to content

Commit

Permalink
🎷 [UPDATE] Download progress completed, memory leaks resolved.
Browse files Browse the repository at this point in the history
  • Loading branch information
Burak Colak committed Dec 28, 2023
1 parent 95be7d4 commit 2957996
Show file tree
Hide file tree
Showing 12 changed files with 140 additions and 91 deletions.
21 changes: 21 additions & 0 deletions Sources/ACMNetworking/ACMNetworking.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,41 @@ import Foundation

/// ACMNetworking, make requests easily
public class ACMNetworking: NSObject {
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()
}

/// Public destroy function
deinit {
print("ACMNetworking deinited")
}

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

private func cancelRequestTask() {
Expand Down
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 @@ -54,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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation
extension ACMNetworking {
func generateURLRequest(endpoint: ACMBaseEndpoint) -> URLRequest? {
guard let urlRequest = baseRequest(to: endpoint) else {
ACMBaseLogger.error(ACMNetworkConstants.urlRequestErrorMessage)
endpoint.logger?.error(ACMNetworkConstants.urlRequestErrorMessage)
return nil
}
return urlRequest
Expand All @@ -22,11 +22,12 @@ extension ACMNetworking {
func handleNilErrorResponse(with endpoint: ACMBaseEndpoint, error: Error?, onError: ACMGenericCallbacks.ErrorCallback) {
guard error == nil else {
cancel()
let message = ACMStringUtils.shared.merge(list: [

let message = endpoint.stringUtils?.merge(list: [
ACMNetworkConstants.errorMessage,
error?.localizedDescription ?? "",
])
ACMBaseLogger.error(message)
endpoint.logger?.error(message)
onError?(ACMBaseNetworkError(message: ACMNetworkConstants.errorMessage, log: error?.localizedDescription, endpoint: endpoint))
return
}
Expand All @@ -38,11 +39,11 @@ extension ACMNetworking {
func handleNilResponse(with endpoint: ACMBaseEndpoint, response: URLResponse?, onError: ACMGenericCallbacks.ErrorCallback) {
guard response != nil else {
cancel()
let message = ACMStringUtils.shared.merge(list: [
let message = endpoint.stringUtils?.merge(list: [
ACMNetworkConstants.errorMessage,
ACMNetworkConstants.responseNullMessage,
])
ACMBaseLogger.error(message)
endpoint.logger?.error(message)
onError?(ACMBaseNetworkError(message: ACMNetworkConstants.errorMessage, log: ACMNetworkConstants.responseNullMessage, endpoint: endpoint))
return
}
Expand All @@ -54,11 +55,11 @@ extension ACMNetworking {
func handleConnectivityError(with endpoint: ACMBaseEndpoint, error: Error?, onError: ACMGenericCallbacks.ErrorCallback) {
if error?.isConnectivityError ?? false {
cancel()
let message = ACMStringUtils.shared.merge(list: [
let message = endpoint.stringUtils?.merge(list: [
ACMNetworkConstants.errorMessage,
ACMNetworkConstants.dataNullMessage,
])
ACMBaseLogger.error(message)
endpoint.logger?.error(message)
onError?(ACMBaseNetworkError(message: ACMNetworkConstants.errorMessage, log: ACMNetworkConstants.dataNullMessage, endpoint: endpoint))
return
}
Expand All @@ -70,11 +71,11 @@ extension ACMNetworking {
func handleData(with endpoint: ACMBaseEndpoint, data: Data?, onError: ACMGenericCallbacks.ErrorCallback) -> Data? {
guard let data = data else {
cancel()
let message = ACMStringUtils.shared.merge(list: [
let message = endpoint.stringUtils?.merge(list: [
ACMNetworkConstants.errorMessage,
ACMNetworkConstants.dataNullMessage,
])
ACMBaseLogger.error(message)
endpoint.logger?.error(message)
onError?(ACMBaseNetworkError(message: ACMNetworkConstants.errorMessage, log: ACMNetworkConstants.dataNullMessage, endpoint: endpoint))
return nil
}
Expand All @@ -87,11 +88,11 @@ extension ACMNetworking {
func handleHttpResponse(with endpoint: ACMBaseEndpoint, response: URLResponse?, onError: ACMGenericCallbacks.ErrorCallback) -> HTTPURLResponse? {
guard let httpResponse = response as? HTTPURLResponse else {
cancel()
let message = ACMStringUtils.shared.merge(list: [
let message = endpoint.stringUtils?.merge(list: [
ACMNetworkConstants.errorMessage,
ACMNetworkConstants.httpStatusError,
])
ACMBaseLogger.error(message)
endpoint.logger?.error(message)
onError?(ACMBaseNetworkError(message: ACMNetworkConstants.errorMessage, log: ACMNetworkConstants.httpStatusError, endpoint: endpoint))
return nil
}
Expand All @@ -109,14 +110,14 @@ extension ACMNetworking {
extension ACMNetworking {
/// Execute retry mechanism
func executeRetry<T: Decodable>(with endpoint: ACMBaseEndpoint, httpResponse: HTTPURLResponse, data: Data, currentRetryCount: Int?, onSuccess: ACMGenericCallbacks.ResponseCallback<T>, onError: ACMGenericCallbacks.ErrorCallback) {
let message = ACMStringUtils.shared.merge(list: [
let message = endpoint.stringUtils?.merge(list: [
ACMNetworkConstants.errorMessage,
ACMNetworkConstants.httpStatusError,
"-\(httpResponse.statusCode)",
ACMNetworkConstants.responseInfoMessage,
String(data: data, encoding: .utf8) ?? "",
])
ACMBaseLogger.error(message)
endpoint.logger?.error(message)

// MARK: Retry mechanism

Expand All @@ -128,7 +129,7 @@ extension ACMNetworking {

if let currentRetryCount = currentRetryCount, currentRetryCount < maxRetryCount {
let nextRetryCount = currentRetryCount + 1
ACMBaseLogger.info(ACMStringUtils.shared.merge(list: [
endpoint.logger?.info(endpoint.stringUtils?.merge(list: [
String(format: ACMNetworkConstants.httpRetryCount, nextRetryCount, maxRetryCount),
]))
request(to: endpoint, currentRetryCount: nextRetryCount, onSuccess: onSuccess, onError: onError)
Expand All @@ -143,11 +144,11 @@ extension ACMNetworking {
func handleResult<T: Decodable>(with endpoint: ACMBaseEndpoint, data: Data, onSuccess: ACMGenericCallbacks.ResponseCallback<T>, onError: ACMGenericCallbacks.ErrorCallback) {
do {
let dataString = String(data: data, encoding: .utf8) ?? ""
let info = ACMStringUtils.shared.merge(list: [
let info = endpoint.stringUtils?.merge(list: [
ACMNetworkConstants.responseInfoMessage,
dataString,
])
ACMBaseLogger.info(info)
endpoint.logger?.info(info)

if endpoint.isStream == true {
let components = dataString
Expand All @@ -169,31 +170,31 @@ extension ACMNetworking {
onSuccess?(responseObject)
}
} catch let DecodingError.dataCorrupted(context) {
let message = ACMStringUtils.shared.merge(list: [
let message = endpoint.stringUtils?.merge(list: [
context.debugDescription,
])
ACMBaseLogger.error(message)
endpoint.logger?.error(message)
} catch let DecodingError.keyNotFound(key, context) {
let message = ACMStringUtils.shared.merge(list: [
let message = endpoint.stringUtils?.merge(list: [
"Key \(key) not found: \(context.debugDescription)",
"codingPath: \(context.codingPath)",
])
ACMBaseLogger.error(message)
endpoint.logger?.error(message)
} catch let DecodingError.valueNotFound(value, context) {
let message = ACMStringUtils.shared.merge(list: [
let message = endpoint.stringUtils?.merge(list: [
"Value \(value) not found: \(context.debugDescription)",
"codingPath: \(context.codingPath)",
])
ACMBaseLogger.error(message)
endpoint.logger?.error(message)
} catch let DecodingError.typeMismatch(type, context) {
let message = ACMStringUtils.shared.merge(list: [
let message = endpoint.stringUtils?.merge(list: [
"Type \(type) mismatch: \(context.debugDescription)",
"codingPath: \(context.codingPath)",
])
ACMBaseLogger.error(message)
endpoint.logger?.error(message)
} catch let e {
let errorMessage = String(format: ACMNetworkConstants.dataParseErrorMessage, e.localizedDescription)
ACMBaseLogger.warning(errorMessage)
endpoint.logger?.warning(errorMessage)
onError?(ACMBaseNetworkError(message: ACMNetworkConstants.errorMessage, log: errorMessage, endpoint: endpoint))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ public extension ACMNetworking {
{
guard let urlRequest = generateURLRequest(endpoint: endpoint) else { return }

downloadTask = endpoint.session(delegate: self).downloadTask(with: urlRequest) { [weak self] url, urlResponse, error in
session = endpoint.session(delegate: self)
downloadTask = session?.downloadTask(with: urlRequest) { [weak self] url, urlResponse, error in
guard let self else { return }

self.handleNilErrorResponse(with: endpoint, error: error, onError: onError)
Expand All @@ -43,8 +44,6 @@ public extension ACMNetworking {
}

self.cancel()
self.taskProgress?.invalidate()
self.taskProgress = nil

do {
let searchUrl = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
Expand All @@ -66,7 +65,7 @@ public extension ACMNetworking {
onSuccess?(model)
} catch let e {
let errorMessage = String(format: ACMNetworkConstants.genericErrorMessage, e.localizedDescription)
ACMBaseLogger.warning(errorMessage)
endpoint.logger?.warning(errorMessage)
onError?(ACMBaseNetworkError(message: ACMNetworkConstants.errorMessage, log: errorMessage, endpoint: endpoint))
}
}
Expand Down
Loading

0 comments on commit 2957996

Please sign in to comment.