Alamofire 5: One Year in the Making, Now in Beta!

I'm happy to announce that we shipped Alamofire 5 beta 1 just over a week ago! This is the most significant release of Alamofire since the original in 2014! I've been working on this release on and off for a year now and I'm hoping you'll all take a look.

During the beta process I hope to gather feedback about migration and what we can do to help the process given the substantial renames for some of the APIs. Also, I'll be working to rewrite the documentation, including a migration guide, for modern Alamofire usage. So please, post your experiences adopting Alamofire 5 in a new topics here so we can help everyone make the migration.

So what does Alamofire 5 include? Well, here are the brief release notes, but I'll provide more detail here:

  • Rewritten Core: Over the years, the core types of Alamofire had grown far beyond their original designs. Much of this complexity was due to added features that touched all of the internal types, but some was due to issues with the original design. One of those issues was improper separation of concerns, where features were spread over multiple types, making it hard to understand and maintain over time. Another issue was haphazard thread-safety, as Alamofire was originally developed without the benefit of the Thread Sanitizer. This meant threading fixes were grafted on over the years and Alamofire's queue usage became hazardous to the system if the user tried to enqueue many requests at the same time, as every Alamofire Request had it's own queue, starving the system of resources. These rewritten types were able to rectify these issues by ensuring features were implemented in a single place, removing some complex but less used API, and reengineering Alamofire to use a single serial queue by default (as recommend in Modernizing Grand Central Dispatch from WWDC '17). I gave a presentation about this rewrite at dev/world 2018 in Melbourne, Australia. Overall these core types are faster, lighter, and smaller, while enabling more features and higher fidelity.
  • Encodable Parameters: Alamofire 5 now supports making requests with parameters from Encodable types. The implementation of this features includes a URLEncodedFormEncoder for query and body encoding, largely based off of Vapor's type of the same name. This feature actually turned out to be more powerful than I had initially thought and it enables some very elegant possibilities for generic request abstractions. In fact, this is so powerful that we'll likely deprecate the older [String: Any] parameter type in a later 5.X please and remove it in a future major release.
  • Decodable Responses: Alamofire 5 now supports generic response serialization of Decodable types. This is enabled through Alamofire's new DataDecoder protocol, which abstracts over any Decoder that can decode Data. This protocol allows you to use any Decoder with a single response method, maximizing flexibility for users which need to handle multiple response types, but at the same time defaulting to JSON for simplicity.
  • Customizable Empty Response Handling: With the addition of Decodable response parsing, it was necessary for Alamofire to further enhance its empty response handling. This now includes exposing default response codes and request methods that may return empty responses as well as customization points for the built in response serializers. More flexibility is coming in a future release.
  • HTTPHeaders: Alamofire 5 includes simple HTTPHeaders and HTTPHeader types to provide a single header API for all Alamofire and Foundation types, as well as convenience API for the most popular headers.
  • Certificate Pinning API Rewritten: Alamofire 5 rewrites the top level certificate pinning APIs while keeping the tested core implementations, creating a much more flexible design. With some of the changes enabled by the core type rewrite, Alamofire 5 can now return individual errors for certificate pinning failures, rather than the previous cancellation error that was easily confused with manual and automatic cancellation of tasks. This ability, along with the new protocol abstraction, enables powerful pinning capabilities that can return informative errors.
  • EventMonitor: Alamofire 5 removes the previous closure API that was sometimes used to allow visibility into certain Alamofire events and replaces it with a standardized protocol abstraction that allows read-only access to 36 different events between both URLSession and Alamofire. This new abstraction allowed us to clean up our notification integration that was spread between multiple files into a single type that hooks into just the events that it needs. I've used this protocol on my own projects to enable easy logging of specific events in only a few lines of code.
  • Asynchronous Request Pipeline: In Alamofire 5, all request creation is now asynchronous, allowing us to unify the awkwardly different multipart form encoding process with Alamofire's standard request creation process. This means that not only is multipart form encoding much simpler now (no encoding completion!) but request creation no longer blocks the calling thread. These changes also allow for full fidelity request retry. Previous versions of Alamofire lost data when converting incoming parameters into URLRequests to be performed. Now, all parameters are maintained and every step of the process is retried when RequestRetriers fire.
  • Integrated URLSessionTaskMetrics: Alamofire 5 now captures and exposes all URLSessionTaskMetrics for every request and removes the custom Timeline type that previously captured just a fraction of the same data. Not only are metrics captured for the last URLSessionTask issued, but since Alamofire 5 now captures all tasks associated with a Request, even during retry, the full history of associated values is available, including metrics. This makes is very easy to see the local performance of your requests without additional analytics libraries.
  • Top Level API Removed from Global Namespace: Previous versions of Alamofire's documentation introduced users to making requests with the Alamofire.request() API. This dates back to version 1 and I think it reflects Mattt's belief that Swift would gain qualified module references, allowing users to refer to modules that weren't imported by just using the module name. This never came to pass, so this API was actually misleading. What was actually happening was that import Alamofire was exposing global request() API everywhere, and the Alamofire. wasn't actually necessary. This API, since it's valuable simple introduction to Alamofire, has been moved inside an AF enum (attempt to use an Alamofire enum failed due to collision with the module name) as static methods.
  • RequestAdapter Now Asynchronous: As part of the new asynchronous request pipeline, the requirement for the RequestAdapter protocol was changed to be asynchronous as well, allowing adapters to grab async resources to add to a request.
  • Removals:
    • Older OS support: Alamofire 5 supports iOS 10+, macOS 10.12+, watchOS 3+, and tvOS 10+. Due to missing functionality in Foundation on Linux, Alamofire does not yet support Linux.
    • PropertyListResponseSerializer and responsePropertyList, as we're pretty sure no one ever used them.
    • Support for URLSessionStreamTask, as that integration was never tested and yet had no issues filed against it, so we're pretty sure it was also never used.
    • Alamofire's closure API, which allowed users to mutate Alamofire's core types by setting closures that could override specific behaviors. While this was a final escape hatch for full control over Alamofire's interaction with URLSession, it was bug-prone and invasive. We hope to replace most of the functionality users gained by using the API by offering specific functionality that's better, but that will come in the future. This includes things like full control over response caching at the URLSession level, as well as client certificate verification.

As you can see, this is a huge list, so please ask questions in topics here, and if you find bugs or want to submit a feature, feel free to create issues and PRs on our GitHub.

38 Likes

As there aren't updated docs yet for Alamofire 5, wondering if anyone can point to examples of the new patterns, e.g. router, JSON to Swift serialization using Codable, etc.? The only examples I was able to find was this series of posts, which are from 2017: Write a Networking Layer in Swift 4 using Alamofire 5 and Codable Part 1: API Router | by Alaeddine Messaoudi | Medium

I'll see what I can put together as a thread here and probably use it in our documentation as well.

Out of curiosity, is security framework support still the issue? https://github.com/Alamofire/Alamofire/issues/1935

I’ve gotten it working based on updating the code in the linked blog series. I’ll add a link to the repo after making it public

I think I’ve abstracted it enough to work around more easily, but we’ve integrated URLSessionTaskMetrics now, which is unimplemented on Linux last I checked. Honestly I haven’t tried in a while as I work towards release but it is something we want to take a look at in the future.

1 Like

In particular, I'd like to see an example of how to use the new signature for RequestAdapter. How do you build Result<URLRequest>?

You've likely figured it out, but you can use any existing RequestAdapter and just call the completion handler:

do {
    // Attempt to add something to the URLRequest.
    completionHandler(.success(request))
catch {
    completionHandler(.failure(error))
}

Awesome, I'm eagerly waiting for the official release!

Do you think SwiftUI integration is an area you are going to be moving in considering the lack of async image I/O tools in SwiftUI itself?

Any Combine integration will come in a later version (probably 5.1), which should allow easier integration with SwiftUI. We'll be reevaluating our AlamofireImage library after we release the version compatible with Alamofire 5. Follow up releases of that library may include SwiftUI integrations similar to what we have for UIKit, but nothing's final yet.

Feel free to try the latest beta (5.0.0-beta.7) in the meantime!

Beside security, does anyone have a list of issues that is preventing Alamofire from using s-c-f's URLSession implementation?

I'll need to give the build another go, it's just been lower priority than finishing 5.0. Biggest missing piece, last I checked, is URLSessionTaskMetrics. Gathering it is now a critical event in the lifetime of Alamofire's Request classes, so as long as the type exists and can be generated matching the Apple implementation, it should work even if it doesn't have all of the properties.

I'll need to add a pthread_mutex implementation for our Protector type. (Really just bringing back what we used to have instead of os_unfair_lock.)

There were some OperationQueue issues that were blocking, but those have both been fixed and we removed our use that had issues.

There will likely be a bunch of explicit Dispatch and Foundation imports to add.

Our tests are not set up at all to run on Linux, so hopefully we can use the test autodiscovery. Has that shipped on Linux at all? I really don't want to manually find and setup all 530+ tests we have.

Understood. I don't think I have a parity task, and I should add one.

Please use NSLock if possible instead; it will pick up a pthread_mutex implementation on Linux and also the equivalent setup on Windows, where pthreads isn't available.

OQ should be back up to par in 5.1.

@Aciid?

Sounds good. We've abstracted around anything that can lock and unlock, so this shouldn't be a problem.

I have created a ticket.