Very often developers need to build dictionaries from optional values. Sure, it’s pretty easy to do it in Swift, right? It’s just [String: Any?] , so why write a blog post about it? We will use it as one of our use cases for our handy conditional assignment operator.

So let’s look at an example from a real life project – Zonky. The app presents a screen where you can select multiple loan parameters to filter the list of loans. 

 

All of these parameters are optional of course. In order to apply the selected filters, we need to send the parameters to a server as a dictionary. A filtering function with the dictionary can look like this:

func filter(rating: Stirng?, insured: Bool?, activeOnly: Bool?) {
	
	let parameters: [String: Any?] = [
		"rating": rating,
		"insured": insured,
		"active_only": activeOnly
	]

	// Alamofire.request...
}

It seems to be OK, but we don’t want optionals in our dictionary because Alamofire takes [String: Any]  as a type of parameter dictionary. How do we fix that? The most straightforward solution is pretty obvious:

func filter(rating: Stirng?, insured: Bool?, activeOnly: Bool?) {
	
	var parameters: [String: Any] = [:]

	if let rating = rating {
		parameters["rating"] = rating
	}

	if let insured = insured {
		parameters["insured"] = insured
	}

	if let activeOnly = activeOnly {
		parameters["activeOnly"] = activeOnly
	}

	Alamofire.request("https://example.com/filter", parameters: parameters)
}

Fine, it works, but imagine a larger set of parameters. That’s a lot of ugly and boring code! ? That’s what leads us to the idea behind our conditional assignment operator. We want to assign a value to the dictionary only if it’s not nil.

func filter(rating: Stirng?, insured: Bool?, activeOnly: Bool?) {
	
	var parameters: [String: Any] = [:]
	parameters["rating"] =? rating
	parameters["insured"] =? insured
	parameters["activeOnly"] =? activeOnly

	Alamofire.request("https://example.com/filter", parameters: parameters)
}

Voilà. Shorter, nicer, more readable ? But it’s not the standard operator included in Swift, so let’s look at how it works under the hood. The basic idea is simple- we just moved the unwrapping and assignment code used in the second example to the custom operator definition.

precedencegroup ConditionalAssignmentPrecedence {
    associativity: left
    assignment: true
    higherThan: AssignmentPrecedence
}

infix operator =?: ConditionalAssignmentPrecedence

/// Set value of left-hand side only if right-hand side differs from `nil`
public func =?<T>(variable: inout T, value: T?) {
    if let v = value {
        variable = v
    }
}

/// Set value of left-hand side only if right-hand side differs from `nil`
public func =?<T>(variable: inout T?, value: T?) {
    if let v = value {
        variable = v
    }
}

And that’s it! We have a small piece of code which makes our codebase much nicer. You can use this operator not only for building dictionaries (there are other and maybe even smarter ways to achieve this) but whenever you need to assign value only if it’s not nil, and trust me, it’s a very common case, and you will like it ?

If you like this simple trick, see our ACKategories – lightweight open source library full of similar useful tools and extensions, or just wait for our next blog posts, and we will show you more recipes from our kitchen ?‍?

 

Leave a Reply

Your email address will not be published. Required fields are marked *