Alamofire: Swift 4.1 and ImplicitlyUnwrappedOptional using URLEncoding

Created on 8 May 2018  路  16Comments  路  Source: Alamofire/Alamofire

What did you do?

After upgrading to Swift 4.1, every place I was calling Alamofire with URLEncoding using an ImplicitlyUnwrappedOptional broke. Indeed, some("") is now added to IUO strings when printed (at least when using a Parameters Dictionary because of the any value), which is the case in the Alamofire code when the HTTP query string is built.

More info here: https://stackoverflow.com/questions/49609528/why-does-implicitly-unwrapped-optional-not-unwrap-in-dictionary-of-type-string and https://stackoverflow.com/questions/50051964/implicitlyunwrappedoptional-in-init-vs-later

What did you expect to happen?

Should Alamofire make an additional check to try and unwrap the IUO when printing / building the query string in ParameterEncoding.swift?, or we should handle this ourself before calling Alamofire?

Alamofire Environment

Alamofire version: 4.7.2
Xcode version: 9.3
Swift version: 4.1
Platform(s) running Alamofire: iOS
macOS version running Xcode:

bug help wanted

All 16 comments

I'm guessing it's this:

private func query(_ parameters: [String: Any]) -> String {
    var components: [(String, String)] = []

    for key in parameters.keys.sorted(by: <) {
        let value = parameters[key]!
        components += queryComponents(fromKey: key, value: value)
    }
    return components.map { "\($0)=\($1)" }.joined(separator: "&")
}

Definitely something we should fix in Alamofire.

@jshier Thanks for the quick reply!

I did some testing and I could get it to work by doing:

    private func query(_ parameters: [String: Any]) -> String {
        var components: [(String, String)] = []

        for key in parameters.keys.sorted(by: <) {
            guard let value = parameters[key] as? String else { return "" }
            components += queryComponents(fromKey: key, value: value)
        }
        return components.map { "\($0)=\($1)" }.joined(separator: "&")
    }

But I'm unsure if it's safe to cast to String. I think it is since we are building a query string?

I'm pretty sure we don't need the cast, it's just that parameters[key]! now results in the IUO type, as the unwrapped type isn't necessary there. We might be able to fix it by doing let value: Any = parameters[key]! but there's probably a cleaner solution.

@jshier You're right, let value: Any = parameters[key]! did work! There might be a better way, but I think short term this is a perfectly fine fix.

I think I spoke too soon, I'm still getting Swift.ImplicitlyUnwrappedOptional<Swift.String>.some("5d5694e7-862c-4037-92d5-df480e5ae19a") even when using:

           let value: Any = parameters[key]!

            print("--------------")
            debugPrint(value)

Yeah, I think we'll have to refactor query entirely to actually unwrap. Another option, short term, is let value = parameters[key].unsafelyUnwrapped would should replicate the forced unwrapping.

@jshier I just tried unsafelyUnwrapped and same result, not working. The only way I found to make this work is to explicitly unwrap when I declare my params dictionary:

        var params: Parameters = [
            "shift_id": shift.id!,
            "justification": justificationTextView.text!,
            "suggestions": suggestions
        ]

But I would rather find a way to fix this temporally in Alamofire to avoid having to change this everywhere.

Thanks!

Strange, there should be no ImplicitlyUnwrappedOptional from unsafelyUnwrapped.

Are your sure your shift.id valid isn't already an ImplicitlyUnwrappedOptional before you send it to Alamofire? I can't replicate your issue with just a simple parameter dictionary, and our tests don't indicate any failures.

@jshier I'm doing something like this:


        var shiftId: String!

        shiftId = "12345abcd"

        var params: Parameters = [
            "shift_id": shiftId,
            "p": page + 1,
            "ipp": ipp
        ]

        debugPrint(params)
        // ["shift_id": Swift.ImplicitlyUnwrappedOptional<Swift.String>.some("12345abcd"), "ipp": 25, "p": 1]

Before Swift 4.1, this used to work. Now I need to manually unwrap shiftId in my dictionary to make it work.

Unfortunately, there isn't anything Alamofire can do about IUOs coming into it. I would suggest you rethink your data model so you don't have IUOs as a type at all. Thanks for letting us know about the workaround! Cheers! 馃嵒

I don't think the issue is coming from Alamofire per se, but the fact that Parameters use Any is going to bite people like me because of this:

Under the rules set out by SE-0054, IUOs are only force unwrapped in contexts that demand their unwrapped type. In your case, the IUO doesn't need to be force unwrapped in order to be coerced to Any (as Any can represent any value), so it isn't.

I suppose the right way to fix it is to do like I did, to explicitly unwrap in the dictionary the IUO, like we had to do with string interpolation (https://oleb.net/blog/2016/12/optionals-string-interpolation/)

I don't think it will be a problem with Swift 4.2, when they remove ImplicitlyUnwrappedOptional completely, but yes, it could catch some people unaware. Unfortunately there isn't much we can do here. Making parameters [String: Any?] would fix the IUO issue but would make the dictionary print every element as Optional. It may still work for encoding, but it would be a breaking change, so we'd need to do it for Alamofire 5.

@jshier Cool, maybe something to keep in mind for version 5. Thanks for your time 馃嵒.

I'm using ObjectMapper and I'm seriously considering getting rid of all my IUO in my models. This type is supposed to be convenient, but so far it has created more headaches.

Yeah, IUOs have always been awkward in Swift, but it's generally recommended to avoid them if you can, mainly for safety, but also because they sometimes behave unexpectedly, especially when they change the language like this.

For any newer project I'd recommend using Codable for parsing JSON, unless you need something that ObjectMapper provides.

a

Was this page helpful?
0 / 5 - 0 ratings