From 1df7ead4bf1aa2efeff088f25f87644cc9ca01d6 Mon Sep 17 00:00:00 2001 From: Ali Moazenzadeh Date: Sat, 25 Sep 2021 08:43:52 +0330 Subject: [PATCH 1/3] Save modifiers on disk --- Sources/NetShears/NetShears.swift | 5 +- .../NetShears/NetworkInterceptorConfig.swift | 12 +- .../NetShears/Protocols/PersistHelper.swift | 105 ++++++++++++++++++ .../NetShearsModfierProtocol.swift | 18 ++- .../RequestEvaluatorModifierEndpoint.swift | 6 +- .../RequestEvaluatorModifierHeader.swift | 9 +- 6 files changed, 144 insertions(+), 11 deletions(-) create mode 100644 Sources/NetShears/Protocols/PersistHelper.swift diff --git a/Sources/NetShears/NetShears.swift b/Sources/NetShears/NetShears.swift index 9b49f9c..9de4e93 100644 --- a/Sources/NetShears/NetShears.swift +++ b/Sources/NetShears/NetShears.swift @@ -11,7 +11,10 @@ public final class NetShears: NSObject { public static let shared = NetShears() let networkRequestInterceptor = NetworkRequestInterceptor() - var config: NetworkInterceptorConfig = NetworkInterceptorConfig(modifiers: []) + lazy var config: NetworkInterceptorConfig = { + var savedModifiers = [RequestEvaluatorModifier]().retrieveFromDisk() + return NetworkInterceptorConfig(modifiers: savedModifiers) + }() public func startRecording(){ diff --git a/Sources/NetShears/NetworkInterceptorConfig.swift b/Sources/NetShears/NetworkInterceptorConfig.swift index da5002e..33d23f5 100644 --- a/Sources/NetShears/NetworkInterceptorConfig.swift +++ b/Sources/NetShears/NetworkInterceptorConfig.swift @@ -7,7 +7,7 @@ import Foundation -public struct RedirectedRequestModel: Equatable { +public struct RedirectedRequestModel: Codable, Equatable { public let originalUrl: String public let redirectUrl: String @@ -17,7 +17,7 @@ public struct RedirectedRequestModel: Equatable { } } -public struct HeaderModifyModel: Equatable { +public struct HeaderModifyModel: Codable, Equatable { public let key: String public let value: String @@ -28,7 +28,11 @@ public struct HeaderModifyModel: Equatable { } public final class NetworkInterceptorConfig { - var modifiers: [RequestEvaluatorModifier] = [] + var modifiers: [RequestEvaluatorModifier] = [] { + didSet { + modifiers.store() + } + } init(modifiers: [RequestEvaluatorModifier] = []) { self.modifiers = modifiers @@ -46,7 +50,7 @@ public final class NetworkInterceptorConfig { guard index <= modifiers.count - 1 else { return } modifiers.remove(at: index) } - + } diff --git a/Sources/NetShears/Protocols/PersistHelper.swift b/Sources/NetShears/Protocols/PersistHelper.swift new file mode 100644 index 0000000..4a2263a --- /dev/null +++ b/Sources/NetShears/Protocols/PersistHelper.swift @@ -0,0 +1,105 @@ +// +// PersistHelper.swift +// +// +// Created by Ali Moazenzadeh on 9/23/21. +// + +import Foundation + +public class PersistHelper { + + fileprivate init() { } + + /// Returns URL constructed from specified directory + static fileprivate func getURL() -> URL { + let searchPathDirectory: FileManager.SearchPathDirectory = .documentDirectory + + if let url = FileManager.default.urls(for: searchPathDirectory, in: .userDomainMask).first { + return url + } else { + fatalError("Could not create URL for specified directory!") + } + } + + + /// Store an encodable struct to the specified directory on disk + /// + /// - Parameters: + /// - object: the encodable struct to store + /// - directory: where to store the struct + /// - fileName: what to name the file where the struct data will be stored + static func store(_ object: T, as fileName: String) { + let url = getURL().appendingPathComponent(fileName, isDirectory: false) + + let encoder = JSONEncoder() + do { + let data = try encoder.encode(object) + if FileManager.default.fileExists(atPath: url.path) { + try FileManager.default.removeItem(at: url) + } + FileManager.default.createFile(atPath: url.path, contents: data, attributes: nil) + } catch { + fatalError(error.localizedDescription) + } + } + + /// Retrieve and convert a struct from a file on disk + /// + /// - Parameters: + /// - fileName: name of the file where struct data is stored + /// - directory: directory where struct data is stored + /// - type: struct type (i.e. Message.self) + /// - Returns: decoded struct model(s) of data + static func retrieve(_ fileName: String, as type: T.Type) -> T { + let url = getURL().appendingPathComponent(fileName, isDirectory: false) + + if !FileManager.default.fileExists(atPath: url.path) { + fatalError("File at path \(url.path) does not exist!") + } + + if let data = FileManager.default.contents(atPath: url.path) { + let decoder = JSONDecoder() + do { + let model = try decoder.decode(type, from: data) + return model + } catch { + fatalError(error.localizedDescription) + } + } else { + fatalError("No data at \(url.path)!") + } + } + + /// Remove all files at specified directory + static func clear() { + let url = getURL() + do { + let contents = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: []) + for fileUrl in contents { + try FileManager.default.removeItem(at: fileUrl) + } + } catch { + fatalError(error.localizedDescription) + } + } + + /// Remove specified file from specified directory + static func remove(_ fileName: String) { + let url = getURL().appendingPathComponent(fileName, isDirectory: false) + if FileManager.default.fileExists(atPath: url.path) { + do { + try FileManager.default.removeItem(at: url) + } catch { + fatalError(error.localizedDescription) + } + } + } + + /// Returns BOOL indicating whether file exists at specified directory with specified file name + static func fileExists(_ fileName: String) -> Bool { + let url = getURL().appendingPathComponent(fileName, isDirectory: false) + return FileManager.default.fileExists(atPath: url.path) + } +} + diff --git a/Sources/NetShears/RequestModifier/NetShearsModfierProtocol.swift b/Sources/NetShears/RequestModifier/NetShearsModfierProtocol.swift index e4c0910..2a2fc98 100644 --- a/Sources/NetShears/RequestModifier/NetShearsModfierProtocol.swift +++ b/Sources/NetShears/RequestModifier/NetShearsModfierProtocol.swift @@ -15,6 +15,22 @@ public protocol RequestModifier { func modify(request: inout URLRequest) } -public protocol RequestEvaluatorModifier : RequestEvaluator, RequestModifier {} +public protocol RequestEvaluatorModifier : RequestEvaluator, RequestModifier, Codable { + static var storeFileName: String { get } +} +extension Array where Element == RequestEvaluatorModifier { + func store() { + let headers: [RequestEvaluatorModifierHeader] = compactMap { $0 as? RequestEvaluatorModifierHeader } + let endpoints: [RequestEvaluatorModifierEndpoint] = compactMap { $0 as? RequestEvaluatorModifierEndpoint } + PersistHelper.store(headers, as: RequestEvaluatorModifierHeader.storeFileName) + PersistHelper.store(endpoints, as: RequestEvaluatorModifierEndpoint.storeFileName) + } + func retrieveFromDisk() -> [RequestEvaluatorModifier] { + var modifiers = [RequestEvaluatorModifier]() + modifiers.append(contentsOf: PersistHelper.retrieve(RequestEvaluatorModifierHeader.storeFileName, as: [RequestEvaluatorModifierHeader].self)) + modifiers.append(contentsOf: PersistHelper.retrieve(RequestEvaluatorModifierEndpoint.storeFileName, as: [RequestEvaluatorModifierEndpoint].self)) + return modifiers + } +} diff --git a/Sources/NetShears/RequestModifier/RequestEvaluatorModifierEndpoint.swift b/Sources/NetShears/RequestModifier/RequestEvaluatorModifierEndpoint.swift index d8a9f4c..3cf4db2 100644 --- a/Sources/NetShears/RequestModifier/RequestEvaluatorModifierEndpoint.swift +++ b/Sources/NetShears/RequestModifier/RequestEvaluatorModifierEndpoint.swift @@ -7,9 +7,13 @@ import Foundation -public struct RequestEvaluatorModifierEndpoint: RequestEvaluatorModifier, Equatable { +public struct RequestEvaluatorModifierEndpoint: RequestEvaluatorModifier, Equatable, Codable { public var redirectedRequest: RedirectedRequestModel + + public static var storeFileName: String { + "Modifier.txt" + } public init(redirectedRequest: RedirectedRequestModel) { self.redirectedRequest = redirectedRequest diff --git a/Sources/NetShears/RequestModifier/RequestEvaluatorModifierHeader.swift b/Sources/NetShears/RequestModifier/RequestEvaluatorModifierHeader.swift index ffb45c8..38575ba 100644 --- a/Sources/NetShears/RequestModifier/RequestEvaluatorModifierHeader.swift +++ b/Sources/NetShears/RequestModifier/RequestEvaluatorModifierHeader.swift @@ -7,22 +7,23 @@ import Foundation -public struct RequestEvaluatorModifierHeader: RequestEvaluatorModifier, Equatable { - +public struct RequestEvaluatorModifierHeader: RequestEvaluatorModifier, Equatable, Codable { public var header: HeaderModifyModel + + public static var storeFileName: String { + "Header.txt" + } public init(header: HeaderModifyModel) { self.header = header } public func modify(request: inout URLRequest) { - request.modifyURLRequestHeader(header: header) } public func isActionAllowed(urlRequest: URLRequest) -> Bool { return true } - } From e91b5a00b4c29e408bae5e49c82a6636ce96d7db Mon Sep 17 00:00:00 2001 From: Ali Moazenzadeh Date: Sat, 25 Sep 2021 09:01:45 +0330 Subject: [PATCH 2/3] Fix null probability --- Sources/NetShears/NetShears.swift | 2 +- Sources/NetShears/Protocols/PersistHelper.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/NetShears/NetShears.swift b/Sources/NetShears/NetShears.swift index 9de4e93..daa7405 100644 --- a/Sources/NetShears/NetShears.swift +++ b/Sources/NetShears/NetShears.swift @@ -13,7 +13,7 @@ public final class NetShears: NSObject { let networkRequestInterceptor = NetworkRequestInterceptor() lazy var config: NetworkInterceptorConfig = { var savedModifiers = [RequestEvaluatorModifier]().retrieveFromDisk() - return NetworkInterceptorConfig(modifiers: savedModifiers) + return NetworkInterceptorConfig(modifiers: savedModifiers ?? []) }() diff --git a/Sources/NetShears/Protocols/PersistHelper.swift b/Sources/NetShears/Protocols/PersistHelper.swift index 4a2263a..1485d24 100644 --- a/Sources/NetShears/Protocols/PersistHelper.swift +++ b/Sources/NetShears/Protocols/PersistHelper.swift @@ -51,11 +51,11 @@ public class PersistHelper { /// - directory: directory where struct data is stored /// - type: struct type (i.e. Message.self) /// - Returns: decoded struct model(s) of data - static func retrieve(_ fileName: String, as type: T.Type) -> T { + static func retrieve(_ fileName: String, as type: T.Type) -> T? { let url = getURL().appendingPathComponent(fileName, isDirectory: false) if !FileManager.default.fileExists(atPath: url.path) { - fatalError("File at path \(url.path) does not exist!") + return nil } if let data = FileManager.default.contents(atPath: url.path) { From 53bc9a66a8009d342637e2d6fbc8738ebd992d17 Mon Sep 17 00:00:00 2001 From: Ali Moazenzadeh Date: Sat, 25 Sep 2021 09:02:43 +0330 Subject: [PATCH 3/3] . --- Sources/NetShears/NetShears.swift | 2 +- .../NetShears/RequestModifier/NetShearsModfierProtocol.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/NetShears/NetShears.swift b/Sources/NetShears/NetShears.swift index daa7405..9de4e93 100644 --- a/Sources/NetShears/NetShears.swift +++ b/Sources/NetShears/NetShears.swift @@ -13,7 +13,7 @@ public final class NetShears: NSObject { let networkRequestInterceptor = NetworkRequestInterceptor() lazy var config: NetworkInterceptorConfig = { var savedModifiers = [RequestEvaluatorModifier]().retrieveFromDisk() - return NetworkInterceptorConfig(modifiers: savedModifiers ?? []) + return NetworkInterceptorConfig(modifiers: savedModifiers) }() diff --git a/Sources/NetShears/RequestModifier/NetShearsModfierProtocol.swift b/Sources/NetShears/RequestModifier/NetShearsModfierProtocol.swift index 2a2fc98..17e425e 100644 --- a/Sources/NetShears/RequestModifier/NetShearsModfierProtocol.swift +++ b/Sources/NetShears/RequestModifier/NetShearsModfierProtocol.swift @@ -29,8 +29,8 @@ extension Array where Element == RequestEvaluatorModifier { func retrieveFromDisk() -> [RequestEvaluatorModifier] { var modifiers = [RequestEvaluatorModifier]() - modifiers.append(contentsOf: PersistHelper.retrieve(RequestEvaluatorModifierHeader.storeFileName, as: [RequestEvaluatorModifierHeader].self)) - modifiers.append(contentsOf: PersistHelper.retrieve(RequestEvaluatorModifierEndpoint.storeFileName, as: [RequestEvaluatorModifierEndpoint].self)) + modifiers.append(contentsOf: PersistHelper.retrieve(RequestEvaluatorModifierHeader.storeFileName, as: [RequestEvaluatorModifierHeader].self) ?? []) + modifiers.append(contentsOf: PersistHelper.retrieve(RequestEvaluatorModifierEndpoint.storeFileName, as: [RequestEvaluatorModifierEndpoint].self) ?? []) return modifiers } }