Thanks to visit codestin.com
Credit goes to github.com

Skip to content
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
5 changes: 4 additions & 1 deletion Sources/SwiftNetKit/BaseRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,21 @@ public struct BaseRequest<Response: Decodable>: RequestProtocol {
let parameters: [String : Any]?
let headers: [String : String]?
let body: RequestBody?
let cacheConfiguration: CacheConfiguration?

init(
url: URL,
method: MethodType,
parameters: [String : Any]? = nil,
headers: [String : String]? = nil,
body: RequestBody? = nil
body: RequestBody? = nil,
cacheConfiguration: CacheConfiguration? = nil
) {
self.url = url
self.method = method
self.parameters = parameters
self.headers = headers
self.body = body
self.cacheConfiguration = cacheConfiguration
}
}
25 changes: 25 additions & 0 deletions Sources/SwiftNetKit/CacheConfiguration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// CacheConfiguration.swift
//
//
// Created by Sam Gilmore on 7/19/24.
//

import Foundation

public struct CacheConfiguration {
var memoryCapacity: Int
var diskCapacity: Int
var diskPath: String?
var cachePolicy: URLRequest.CachePolicy

public init(memoryCapacity: Int = 20 * 1024 * 1024, // 20 MB
diskCapacity: Int = 100 * 1024 * 1024, // 100 MB
diskPath: String? = nil,
cachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy) {
self.memoryCapacity = memoryCapacity
self.diskCapacity = diskCapacity
self.diskPath = diskPath
self.cachePolicy = cachePolicy
}
}
35 changes: 30 additions & 5 deletions Sources/SwiftNetKit/NetworkService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public struct NetworkService: NetworkServiceProtocol {
sessionConfiguration = URLSessionConfiguration.background(withIdentifier: identifier)
}

// Default: 60.0s
if let timeoutInterval = timeoutInterval {
sessionConfiguration.timeoutIntervalForRequest = timeoutInterval
sessionConfiguration.timeoutIntervalForResource = timeoutInterval
Expand All @@ -30,18 +31,41 @@ public struct NetworkService: NetworkServiceProtocol {
self.session = URLSession(configuration: sessionConfiguration)
}

private func configureCache(for urlRequest: inout URLRequest, using request: any RequestProtocol) {
if let cacheConfig = request.cacheConfiguration {
let cache = URLCache(
memoryCapacity: cacheConfig.memoryCapacity,
diskCapacity: cacheConfig.diskCapacity,
diskPath: cacheConfig.diskPath
)

// Configure the URLSession's URLCache with the specified memory and disk capacity
self.session.configuration.urlCache = cache

// Set the cache policy for the individual URLRequest, determining how the URLRequest uses the URLCache
urlRequest.cachePolicy = cacheConfig.cachePolicy
} else {
// If no custom cache configuration is provided for this request,
// then reset the session's URLCache to the system-wide default cache.
// This ensures that subsequent requests use the default caching behavior
// provided by URLCache.shared.
self.session.configuration.urlCache = URLCache.shared
}
}

func start<Request: RequestProtocol>(
_ request: Request,
retries: Int = 0,
retryInterval: TimeInterval = 1.0
) async throws -> Request.ResponseType {
var urlRequest = request.buildURLRequest()
self.configureCache(for: &urlRequest, using: request)

var currentAttempt = 0
var lastError: Error?

while currentAttempt <= retries {
do {
let urlRequest = request.buildURLRequest()

let (data, response) = try await session.data(for: urlRequest)

guard let httpResponse = response as? HTTPURLResponse else {
Expand Down Expand Up @@ -76,12 +100,13 @@ public struct NetworkService: NetworkServiceProtocol {
retryInterval: TimeInterval = 1.0,
completion: @escaping (Result<Request.ResponseType, Error>) -> Void
) {
var urlRequest = request.buildURLRequest()
self.configureCache(for: &urlRequest, using: request)

var currentAttempt = 0

func attempt() {
let urlRequest = request.buildURLRequest()

session.dataTask(with: urlRequest) { data, response, error in
self.session.dataTask(with: urlRequest) { data, response, error in
if let error = error {
if currentAttempt < retries {
currentAttempt += 1
Expand Down
3 changes: 3 additions & 0 deletions Sources/SwiftNetKit/Protocols/RequestProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ protocol RequestProtocol {
var parameters: [String: Any]? { get }
var headers: [String: String]? { get }
var body: RequestBody? { get }
var cacheConfiguration: CacheConfiguration? { get }

func buildURLRequest() -> URLRequest
}
Expand All @@ -23,6 +24,8 @@ extension RequestProtocol {
func buildURLRequest() -> URLRequest {
var urlRequest = URLRequest(url: self.url)

urlRequest.cachePolicy = self.cacheConfiguration?.cachePolicy ?? .useProtocolCachePolicy

if let parameters = self.parameters {
let queryItems = parameters.map { key, value in
URLQueryItem(name: key, value: "\(value)")
Expand Down
39 changes: 39 additions & 0 deletions Tests/SwiftNetKitTests/NetworkServiceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,45 @@ final class NetworkServiceTests: XCTestCase {

wait(for: [expectation], timeout: 5.0)
}

func testCachingBehavior() {
// Disclaimer: This test doesn't necessarily prove that the request was cached

let expectation = XCTestExpectation(description: "Fetch data and cache it")
let cacheConfiguration = CacheConfiguration(
memoryCapacity: 10_000_000,
diskCapacity: 100_000_000,
cachePolicy: .returnCacheDataElseLoad
)

let firstRequest = BaseRequest<Post>(
url: self.getURL,
method: .get,
cacheConfiguration: cacheConfiguration
)

Task {
do {
let post: Post = try await self.networkService.start(firstRequest)
XCTAssertEqual(post.userId, 1)
XCTAssertEqual(post.id, 1)

try await Task.sleep(nanoseconds: 1_000_000_000)

let secondRequest = firstRequest
let cachedPost: Post = try await self.networkService.start(secondRequest)

XCTAssertEqual(post.userId, cachedPost.userId)
XCTAssertEqual(post.id, cachedPost.id)

expectation.fulfill()
} catch {
XCTFail("Failed with error: \(error)")
}
}

wait(for: [expectation], timeout: 10.0)
}
}

// 'Post' for testing jsonplaceholder.typicode.com data
Expand Down