

Swift Associated Types With Default Values
source link: https://swiftrocks.com/swift-associated-types-with-default-values
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Swift Associated Types With Default Values
I was browsing the Swift forums when I stumbled across a discussion regarding an undocumented feature of Swift (as of this post's date): the ability to give associated types a default value.
I've shown associated types in action before in my article about loadView() - they are a great tool to define generic behaviour inside protocols in the shape of a specific type. As the feature implies, setting a default value for an associated type will allow the children of the protocol to skip having to define a typealias for the type, unless they specifically want to use a different type.
This is the regular way to define a associated type:
protocol Foo {
associatedtype FooType
}
struct Bar: Foo {
typealias Foo = BarType
}
With just an equal sign, you can define a default type to it.
protocol Foo {
associatedtype FooType = BarType
}
struct Bar: Foo {} //Automatically sets FooType to BarType
//unless another type is specified
It is a very simple thing in nature, but I've found this undocumented feature very interesting. Having to set typealiases really bothered me - being forced to provide a typealias means that the type inherting the protocol is certainly going to do something unique, which might not really be true. Consider this structure used to define a HTTP request:
/// The representation of a HTTPClient's request.
public protocol HTTPRequest {
/// The Value of a HTTPRequest is the response object retrieved after parsing the request's response.
associatedtype Value
/// The endpoint path of the request, to be appended after the HTTPClient's baseURL property.
var path: String { get }
/// Serializes the response of this request to its associated value type.
func serialize(data: Data) throws -> Value
}
extension HTTPRequest where Value: Unboxable {
public func serialize(data: Data) throws -> Value {
let value: Value = try unbox(data: data)
return value
}
}
This structure works very well for my current project because defining path
and the Value
type is all a request needs to do in order to work, and if Value
conforms to Unboxable
, the object is even retrieved automatically. This works perfectly for requests such as this one that retrieves an user:
struct UserRequest: HTTPRequest {
typealias Value = User // is Unboxable
let path: String = "v1/profiles"
}
But that implementation is not perfect: Using associated types like that means that I can't test an endpoint without defining a fully fledgled response type to it.
Worse: What if I don't need my request to return something meaningful? Perhaps I don't care about the response, perhaps I just need to cache some Data
on the device, or a plain dictionary is enough. For all these cases, I have to explicitly set Value
to something and code a custom serialize()
method.
If that situation was common, I would simply not use associated types for my requests. Luckily, the existence of default values for associated types solve this problem entirely. Now, I can solve it by making all HTTPRequest
objects return a Data
object by default:
/// The representation of a HTTPClient's request.
public protocol HTTPRequest {
/// The Value of a HTTPRequest is the response object retrieved after parsing the request's response.
associatedtype Value = Data
/// The endpoint path of the request, to be appended after the HTTPClient's baseURL property.
var path: String { get }
/// Serializes the response of this request to it's associated value type.
func serialize(data: Data) throws -> Value
}
extension HTTPRequest where Value: Data {
public func serialize(data: Data) throws -> Value {
return data
}
}
extension HTTPRequest where Value: Unboxable {
public func serialize(data: Data?, error: Error?) throws -> Value {
let value: Value = try unbox(data: data)
return value
}
}
Plain requests can now work purely with a path
property (and return a plain Data
object) while still allowing the regular requests to provide their custom responses.
struct ABTestDataRequest: HTTPRequest {
let path: String = "v1/abtest"
}
Follow me on my Twitter - @rockbruno_, and let me know of any suggestions and corrections you want to share.
Reference
SR-8761Recommend
-
16
This post is a continuation of my posts discussing the topic of associated type constructors (ATC) and higher-kinded types (HKT):
-
9
Comparing enums in Swift is very straightforward – as long as they don’t have associated values. In this post we will discuss, what you can do in that case. Hint: This post is using Swift 4 Content
-
10
Default zero values for all Go types yourbasic.org/golang Variables declared without an initial value are set to t...
-
5
On Generics and Associated TypesA Rust language feature adventure!
-
13
Generic Associated Types - Learn Rust378 views•Dec 21, 2020200ShareSave ...
-
17
&Notepad Generic associated types encode higher-order functions on types Will Crichton — January 4, 2021 GAT...
-
8
Swift: Comparing Enums With Associated Values Compari...
-
3
Today I want to show you a rather obscure feature of Swift. You will learn a bit about Swift's type system, type inference and other nice powers that the language has.Type InferenceWe know Swift is a strongly typed programming...
-
7
Codable synthesis for enums with associated values in Swift 5.5 27 Dec 2021 ⋅ 4 min read ⋅ Swift
-
14
How to customize automatic synthesizing Codable for enums with associated values 13 Jan 2022 ⋅ 5 min read ⋅ Swift
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK