Alamofire: [Alamofire 5]: curl command could not be created

Created on 25 Apr 2019  路  14Comments  路  Source: Alamofire/Alamofire

What did you do?

let task = AF.request(url).responseData { response in
   //
}
print(task.debugDescription)

What did you expect to happen?

Expected cURL expression in console log

What happened instead?

console output curl command could not be created

Alamofire Environment

Alamofire version: 5.0.0-beta.6
Xcode version: 10.1 (10B61)
Swift version: 5.0.1 Release 2019-04-18 (a)
Platform(s) running Alamofire: iOS 12.1
macOS version running Xcode: 10.13.6

Additional Info

I know why it is showing this message but did not found any advice how it should be called in 5.0.0.

func cURLRepresentation() -> String {
        guard
            let request = lastRequest,
            let url = request.url,
            let host = url.host,
            let method = request.httpMethod else { return "$ curl command could not be created" }
//....
}

lastRequest is nil. Why? Because

//Sessions.swift:
func perform(_ request: DataRequest) {
        requestQueue.async {
            guard !request.isCancelled else { return }

            self.performSetupOperations(for: request, convertible: request.convertible)
        }
    }

func performSetupOperations(for request: Request, convertible: URLRequestConvertible) {
        do {
            let initialRequest = try convertible.asURLRequest()
            rootQueue.async { request.didCreateURLRequest(initialRequest) }
//.....
}

//Request.swift
func didCreateURLRequest(_ request: URLRequest) {
        protectedMutableState.write { $0.requests.append(request) } // <- this is my lastRequest

        eventMonitor?.request(self, didCreateURLRequest: request)
    }

First idea was to wrap print with Session.default.requestQueue.async, but there were 2 async calls inside Alamofire before lastRequest set and print called between them.

cURL Command Output section in docs should be updated

needs investigation

Most helpful comment

@xGoPox

let task = AF.request(url)
task.responseData { response in
   print(task.debugDescription)
}

All 14 comments

In your case, you can print the debugDescription in the response handler.

Generally, we should see if there's anything we can do to improve this. At the least we should improve the message printed when we haven't gotten a URLRequest yet.

Seems like to me that we should consider adding a chained API on Request that will print or debugPrint once the URLRequest has been finalized and the task can be or has been created. Otherwise, users will continue to run into this issue without having a very good option from the previous behavior.

In your case, you can print the debugDescription in the response handler.

Sorry, I don't see suitable way to make it.

let task = AF.request(url).responseData { response in
   print(task.debugDescription)           // this will not compile
   print(response.debugDescription) // not cURL
}

to print cURL, I need access to AF's Request class, not URLRequest (no access to Request in responseData callback)

the only way that comes to mind is to move task variable somewhere outside context (and access it via self.task from callback), but this is totally bad idea :hankey: .

UPD: well, this works:

let task = AF.request(url)
task.responseData { response in
   print(task.debugDescription)
}

Seems like to me that we should consider adding a chained API on Request that will print or debugPrint once the URLRequest has been finalized and the task can be or has been created. Otherwise, users will continue to run into this issue without having a very good option from the previous behavior.

If we did, it would likely need to be integrated into the existing EventMonitor support so we don't need to add new state to support it. I'm also not sure I'd want to add such direct support for a debug feature.

In the meantime, using an EventMonitor would allow the printing of all Request cURL descriptions. Adding a monitor for logging would be a great place to also print this information.

I agree. The EventMonitor is a great way to implement such a feature.

The real question is convenience. Do we feel that we should try and match the convenience of debugPrint(request) in AF4 in AF5 to be reliable? It's a great feature that I would hate to see slowly die off just because we moved task creation to be async. Also, event monitors are not per-request types, so with the current behavior, getting a reliable debugPrint(request) type of functionality can only be achieved for all requests.

As a debug feature, I'm not sure the immediate printing of the debugDescription is the primary use of the this feature, or that not being able to so is a big issue. Mainly, that's an issue with the usage in the documentation. For my use, it either comes from my logger or my debugger.

Perhaps a way to generate the cURL command from a Response would help? Problem there is grabbing info from the Request and even the Session may be necessary to generate an accurate command. So ultimately, an async cURLDescription() method would probably be best for direct usage, but that wouldn't work in the debugger.

I think there is a large portion of the community that has been leveraging the print(request) and debugPrint(request) functionality over the past 4 AF versions, myself included. It's awesome!

IMO, it's probably important to offer some sort of transition from that pattern to a new one that's reliable, just because it has been around for so long. I think that if we don't, and simply point them to EventMonitor, it would be a miss on our part.

Is the cURLDescription API you're thinking of an API on Request or Response or both? Is it a chained API?

I think there is a large portion of the community that has been leveraging the print(request) and debugPrint(request) functionality over the past 4 AF versions, myself included. It's awesome!

IMO, it's probably important to offer some sort of transition from that pattern to a new one that's reliable, just because it has been around for so long. I think that if we don't, and simply point them to EventMonitor, it would be a miss on our part.

Immediate, synchronous usage is always going to be broken, no real way around that. If we need a method right off of Request to do this, an async cURLDescription method could work, which I guess would make it a chainable API, since it would have to wait for internal events before being able to print the description. What replacement would be acceptable here?

Yeah, the immediate case is definitely broken which I think is fine as long as we have an easy alternative. The best way I can think of to do that would be a chainable API on Request that internally uses an EventMonitor to print out the cURL command or a less verbose representation of the Request.

I'm not really sure what those API would be, but here are a few ideas:

func print() -> Self
func debugPrint() -> Self

func printRequest() -> Self
func debugPrintRequest() -> Self

func logRequest() -> Self
func logDebugRequest() -> Self

func logBasicRequest() -> Self
func logCURLRequest() -> Self

func printBasic() -> Self
func printCURL() -> Self

do you guys have an alternative for me in order to have cURL expression printed in console log?
thanks

@xGoPox

let task = AF.request(url)
task.responseData { response in
   print(task.debugDescription)
}

oh thanks @Kirow

This has been addressed through a refactoring of the cURL APIs, including the addition of sync and async cURLDescription methods in #2863.

as I always come back to this post when looking on how to print cURL, here is how it is with Alamofire 5 beta

 request?.cURLDescription(calling: { (curl) in
            print(curl)
        })
Was this page helpful?
0 / 5 - 0 ratings