Giter Site home page Giter Site logo

Comments (5)

MohammadAlBarari avatar MohammadAlBarari commented on May 25, 2024

nserror-domain | Request explicitly cancelled.

from alamofire.

jshier avatar jshier commented on May 25, 2024

Without any of your code I can't be sure, but most likely you're not properly retaining the cancellation token from your Combine stream, and it's being deinited, leading to the cancellation. In fact, some number of cancellations are expected when you scope requests to the lifetimes of things like view controllers or SwiftUI views.

Feel free to investigate further and respond with additional details if you find this cancellation is happening without the stream being cancelled or another cancel() call somewhere.

from alamofire.

MohammadAlBarari avatar MohammadAlBarari commented on May 25, 2024

Also, I got Domain: URL is not valid error
although the domain was valid.

from alamofire.

jshier avatar jshier commented on May 25, 2024

In that case you likely need to properly escape your URL, or use URLComponents to build it and have the escaping and validation done for you.

from alamofire.

MohammadAlBarari avatar MohammadAlBarari commented on May 25, 2024

I believe the issue is that the request was not explicitly cancelled it something else.

What did you do?

class NetworkManager {
    static let shared = NetworkManager()
    private let session = Alamofire.Session(interceptor: interceptor)

    private static let credential = OAuthCredential()

    // Create the interceptor
    private static let authenticator = OAuthAuthenticator(endpoint: RefreshTokenEndpoint.refreshToken)
    private static let interceptor = AuthenticationInterceptor(authenticator: authenticator,
                                                               credential: credential)

    private init() {}

    func request<T: Decodable>(_ endpoint: Endpoint) -> AnyPublisher<Result<T, NetworkError>, Never> {
        let requestModifier: Session.RequestModifier = { request in
            if let dicToStream = endpoint.bodyToStream {
                let jsonData = try? JSONSerialization.data(withJSONObject: dicToStream, options: [])
                if let jsonData = jsonData {
                    let inputStream = InputStream(data: jsonData)
                    request.httpBodyStream = inputStream
                }
            }
        }
        NetworkManager.authenticator.endPoint = endpoint

        return session.request(endpoint.url,
                               method: endpoint.httpMethod,
                               parameters: endpoint.parameters,
                               encoding: endpoint.httpMethod == .get ? URLEncoding.default : JSONEncoding.default,
                               headers: HTTPHeaders(endpoint.headers ?? [:]),
                               requestModifier: requestModifier)
            .customValidate(url: endpoint.url)
            .cURLDescription { description in
                self.logRequest(endpoint, description)
            }
            .publishResponse(using: CustomResponseSerializable<T>(endpoint: endpoint))
            .map { result in
                result.value!
            }
            .eraseToAnyPublisher()
    }
}
struct OAuthCredential: AuthenticationCredential {
    var accessToken: Token? {
        SessionManager.shared.getCurrentUser()?.accessToken
    }

    var refreshToken: Token? {
        SessionManager.shared.getCurrentUser()?.refreshToken
    }

    // Require refresh if within 5 minutes of expiration
    var requiresRefresh: Bool {
        guard let refreshToken else { return false }

        if let accessToken {
            let expiryDate = Date(timeIntervalSince1970: Double(accessToken.expiry / 1000))
            return Date(timeIntervalSinceNow: 60 * 5) > expiryDate
        }
        return false
    }
}

class OAuthAuthenticator: Authenticator {
    var endPoint: Endpoint
    var subscriptions = Set<AnyCancellable>()
    var refreshTokenIfNeededUseCase = RefreshTokenIfNeededUseCase()

    init(endpoint: Endpoint) {
        self.endPoint = endpoint
    }

    func apply(_ credential: OAuthCredential, to urlRequest: inout URLRequest) {
        guard let refreshTokenEndPoint = (endPoint as? RefreshTokenProtocol)?.refreshTokenEndPoint
        else {
            /// If the request does not require authentication, we can directly return it as unmodified.
            return
        }

        urlRequest.setValue("" + (credential.accessToken?.value ?? ""), forHTTPHeaderField: "interceptor-token")
    }

    func refresh(_ credential: OAuthCredential, for session: Session, completion: @escaping (Result<OAuthCredential, Error>) -> Void) {
        guard let refreshTokenEndPoint = ((endPoint as? RefreshTokenProtocol)?.refreshTokenEndPoint as? RefreshTokenEndpoint)
        else {
            /// If the request does not require authentication, we can directly return it as unmodified.
            return
        }

        refreshTokenIfNeededUseCase.execute(value: .init(forceRefresh: true)).sink { result in
            switch result {
            case .success:
                completion(.success(OAuthCredential()))
            case .failure(let error):
                completion(.failure(error))
            }
        }.store(in: &subscriptions)
    }

    func didRequest(_ urlRequest: URLRequest, with response: HTTPURLResponse, failDueToAuthenticationError error: Error) -> Bool {
        return response.statusCode == 401
    }

    func isRequest(_ urlRequest: URLRequest, authenticatedWith credential: OAuthCredential) -> Bool {
        let token = credential.accessToken?.value ?? ""
        return urlRequest.headers["interceptor-token"] == token
    }
}

What did you expect to happen?

All the request should be sent successfully
And if it needs to refresh the token and add the authorization token, it should be added. if not, it should not be there

What happened instead?

All the expected work is fine but we have five users with the below issue
The users stuck on api call that was a get request that didn't need to add tokens using the interceptor. "It returned from the apply method without changing the request."
We sent that request to alamofire using the request method in our NetworkManager and it has not returned to success or failure block
But when we removed the authentication interceptor from the session property for this get request for those users, it worked, and when we put it back, it happened again.
So the problem happend if I use the interceptor in session proberty
private let session = Alamofire.Session(interceptor: interceptor)
But if I removed the interceptor as below it worked

    static var session : Session = {
        let configuration = URLSessionConfiguration.default
        configuration.requestCachePolicy = .reloadIgnoringLocalAndRemoteCacheData
        configuration.urlCache = nil // Disable URLCache
        
        return Alamofire.Session(configuration: configuration)
    }()

Users devices

Model: iPad 7th Generation (Cellular)
iOS Version: 17.1.1

Model: iPad 9th generation (Cellular)
iOS Version: 17.1.1

Model: iPad 9th generation (Cellular)
iOS Version: 17.1.1

iPad 9th generation (Cellular)
iPad12,2
iOS Version: 17.0.3

Alamofire Environment

**Alamofire Version:5.8.0
Dependency Manager:
**Xcode Version:**15.0.1 (15A507)
Swift Version:
Platform(s) Running Alamofire:
macOS Version Running Xcode:

Demo Project

ℹ Please link to or upload a project we can download that reproduces the issue.

from alamofire.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.