Swift’s type inference capabilities have been a core part of the language since the beginning, greatly reducing the amount of work we have to do to manually specify types when declaring variables and properties with default values. For example, the expression var number = 7 does not need to include any type annotations, because the compiler is able to infer that the value 7 is an Int and our number variable should be typed accordingly. Swift 5.6, released as part of Xcode 13.3, continues to expand these type inference capabilities by introducing the concept of “type placeholders”, which are very useful when dealing with collections and other generic types. For example, suppose we want to create an instance of CurrentValueSubject inside a Combine with a default integer value. An initial thought about how to do this might be to simply pass our default value to the subject's initializer and then store the result in a local let-declared property (just like when creating a normal Int value). However, doing so would give us the following compiler error: // Error: "Generic parameter 'Failure' could not be inferred" This is because CurrentValueSubject is a generic type, and instantiation requires not only the Output type, but also the Failure type - this is the type of error that the subject can throw. Since we don’t want our body to throw any errors in this case, we’ll give it a value of type Failure Never (a common convention when using Combine in Swift). But in order to do this, prior to Swift 5.6, we needed to explicitly specify our Int output type — like this: let counterSubject = CurrentValueSubject < Int , Never > ( 0 ) However, as of Swift 5.6, this is no longer the case — because we can now use a type placeholder to represent the Output type of our body, which allows us to once again take advantage of the compiler automatically inferring the type for us, just like when declaring a normal Int value: let counterSubject = CurrentValueSubject < _ , Never > ( 0 ) This is nice, but arguably not a huge improvement in Swift. After all, we only saved two characters by using _ instead of Int, and manually specifying simple types like Int isn’t problematic in the first place. But now let's see how this functionality extends to more complex types, which is where it really starts to shine. For example, suppose our project contains the following function, which lets us load a user-annotated PDF file: func loadAnnotatedPDF ( named : String ) - > Resource < PDF < UserAnnotations >> { The function above uses a rather complex generic type as its return type, probably because we need to reuse our Resource type in multiple places, and also because we chose to use a *phantom type* to specify which PDF we are currently dealing with. Now let's see what our previous CurrentValueSubject-based code would look like if we called the above function when creating the subject, instead of just using a simple integer: // Before Swift 5.6: This is a pretty big improvement! Not only does the Swift 5.6 version save us some typing, but since the type of pdfSubject is now derived entirely from loadAnnotatedPDF , this will likely make iterating on that function (and its related code) much easier — since there will be fewer manual type annotations to update if we ever change the return type of that function. However, it’s worth pointing out that there’s another way to take advantage of Swift’s type inference capabilities in situations like the one above — that’s to use type aliases instead of type placeholders. For example, we can define an UnfailingValueSubject type alias here that we can use to easily create subjects that don’t generate any errors: typealias UnfailingValueSubject < T > = CurrentValueSubject < T , Never > With the above in place, we can now create our pdfSubject without any generic annotations - since the compiler is able to infer what type T refers to, and the failure type Never is hardcoded into our new type alias: let pdfSubject = UnfailingValueSubject ( loadAnnotatedPDF ( named : name )) This doesn't mean that type aliases are always better than type placeholders, because if we were to define new type aliases for each specific case, it would make our codebase more complex. Sometimes, it's definitely a good idea to specify everything inline (like when using type placeholders), because it allows us to define completely independent expressions. Before we wrap up, let's also look at how type placeholders are used with collection literals - for example when creating a dictionary. Here, we choose to manually specify the type of our dictionary's Key (in order to be able to use dot syntax to refer to the various cases of the enumeration), while using a type placeholder for the dictionary's value: enum UserRole { That’s what type placeholders are — a new feature introduced in Swift 5.6 that can be really useful when dealing with slightly more complex generic types. But it’s worth pointing out that these placeholders can only be used at call sites, not when specifying the return type of a function or computed property. |
<<: Apple iOS 15.5 is officially released. Learn what’s new in this article
>>: The evolution and thinking of Taobao Native R&D model
I have obviously gotten enough sleep and eaten en...
Faceless monster slide spacewalk teaching video +...
Now everyone has begun to pay attention to SEO op...
On July 12, 2021, in order to thoroughly implemen...
This article mainly records: Three ways to use In...
The magic weight loss drug supports a country: in...
Private domain operations , in the final analysis...
...
In the Chinese civilization origin exploration pr...
Is Japan no longer able to print money? Next year...
: : : : : : : : : : : : : : : : : : : : : : : : : ...
From any perspective, Audi can be regarded as the...
On September 1, the 2024 "First Lesson of th...
We try to reflect the value of our product throug...
Recently, at a scientific event, Chu Junhao, an a...