This is a premium alert message you can set from Layout! Get Now!

Mastering typealias in Swift

0

Alias generally refers to an assumed identity or an alternate name that a person is known by — maybe a nickname, pen name, nom de plume, or pseudonym. In programming terms, an alias is very similar.

In Swift, typealias is a function that gives a new name, or an alias, to an existing type. This type can be a concrete type, like Double or a custom structure, a compound type, like tuples, or a complex closure type. By providing an alias for existing types, typealias helps make our code more readable and easier to maintain.

Here’s a fun fact: there are 363 typealias present in the Foundation framework as of this writing! So, let’s take a look at the power of typealias and consider how we can strike a balance between its function and its usefulness!

Jump ahead:

Syntax

The syntax for declaring a new typealias in Swift looks like this:

typealias aliasName = existingType

Note that aliasName is not a new type; it refers to the existing type.

Let’s say our project has messages in the form of a String:

var message: String?

Instead, we can use a typealias to call the String with an alias name, Message:

typealias Message = String

Then, we can use it in our codebase, like so:

var message: Message?

Basic examples

Here are some basic examples of instances in which typealias can be particularly helpful:

Time interval

A very basic, but widely used, example is to specify a time interval with a particular alias even though the type is Double:

typealias TimeInterval = Double

User ID

Another example is when we use a unique identifier for a user. We can create a typealias, like so:

public typealias UserID = String

Then, whenever we declare a user ID, we can simply use the typealias name:

public let userID: UserID

var currentUserID: UserID?

func createUser(for id: UserID) { 
/// implementation
}

Score

Apps that rely heavily on displaying and calculating scores are great candidates for typealias. We can create a typealias for the type of the score, like so:

typealias Score = Double

Then, we can use Score everywhere instead of using Double:

struct RGBColorScore {
  var red: Score 
  var green: Score
  var blue: Score 

Or, use it while defining a variable:

var currentScore: Score

Or, use it as the return type of a method:

func calculateMaximumScore(for score: RGBColorScore) -> Score {
  max(score.red, score.green, score.blue)
} 

Password

typealias is helpful when we want to give appropriate naming to the type that fits better contextually. For example, if we’re working on a screen related to login and sign up, we can create a typealias for the password:

typealias Password = String

We can perform various validation checks on the password and use Password instead of String because it is more contextually appropriate in this situation:

func passwordLengthValidation(for password: Password) -> Bool {  
  password.count > 8
}

Now, let’s look at some more advanced scenarios where typealias can come in handy in our codebase.

Reducing verbosity

When working with types that are just too wordy, you may find it helpful to use an alternative name instead. For example, working with diffable data sources is amazing, but the name of the generic classes are verbose.

Having these wordy types sprinkled across our codebase can cause headaches.

One solution to address excess verbosity is to use typealias. Here’s an example of using typealias with a diffable data source:

typealias DataSource = UICollectionViewDiffableDataSource<Section, Item> 

private var dataSource: DataSource?

Here’s an example of using it in our codebase:

typealias CurationSnapshot = NSDiffableDataSourceSnapshot<CurationCardSection, CurationCardModel>

typealias CurationDataSource = UICollectionViewDiffableDataSource<CurationCardSection, CurationCardModel>

If you have worked with the reducer pattern, you’re familiar with the state, action, and environment arguments. It can get cumbersome to continually write these arguments.

Instead, we can use typealias:

typealias AppReducer = Reducer<AppState, AppAction, AppEnvironment> 

Another use case is when you’re working with a protocol in which you use the same type repeatedly.

For example, let’s say we create a protocol Stack that uses Element. All the functions and variables would have to use the associated type as Iterator.Element instead of Element.

Instead, we can use typealias, like so:

protocol Stack {
    associatedtype Iterator: IteratorProtocol
    typealias Element = Iterator.Element

    var array: [Element] { get set }

    func push(_ element: Element)
    func pop() -> Element?
    func peak() -> Element?
}

Improving readability

typealias can improve readability for any named types that have long names; both user-defined types and those provided by the Foundation framework.

Many instances of long type names can be found in the Foundation framework. For example, different enumerations were introduced by Apple at its 2021 Worldwide Developers Conference (WWDC21) for formatting and parsing numeric values for numeric precision, rounding, and scale:

enum NumberFormatStyleConfiguration 

enum CurrencyFormatStyleConfiguration

To access each configuration, we need to use the dot syntax, such as NumberFormatStyleConfiguration.Grouping. But, the dot syntax makes the name even longer, and using the longer name everywhere is cumbersome.

Instead, we can create custom typealias with shorter, more explicit names:

public typealias NumberGrouping = NumberFormatStyleConfiguration.Grouping

public typealias CurrencyPrecision = CurrencyFormatStyleConfiguration.Precision

Here are some of many similar examples available from the Foundation framework:

public typealias EncodingConversionOptions = NSString.EncodingConversionOptions

public typealias EnumerationOptions = NSString.EnumerationOptions

public typealias CompareOptions = NSString.CompareOptions

As another example, Shopify’s iOS SDK uses typealias to create shorter names:

public typealias Query = FulfillmentLineItemConnectionQuery 

public typealias Response = FulfillmentLineItemConnection 

There are many instances with first-party frameworks where we can make a name more concise by introducing typealias when accessing types of that particular framework.

Let’s take the example of Apple’s MusicKit framework. It has a generic structure, MusicItemCollection<MusicItemType>, where MusicItemCollection is a collection of music items and MusicItemType conforms to MusicItem.

To fetch multiple songs from Apple Music matching the particular identifiers, we write the following method:

func catalogSongs(ids: [MusicItemID]) async throws -> MusicItemCollection<Song> {
    let musicRequest = MusicCatalogResourceRequest<Song>(matching: \.id, memberOf: ids)
    let response = try await musicRequest.response()
    return response.items
}

In our model, we pass the ids and then set the songs returned by this method to a variable:

var songs: MusicItemCollection<Song>?

songs = try await catalogSongs(ids: ["1109658204", "1508562321"])

Now, let’s use typealias to create a shorter name, Songs, for MusicItemCollection<Song>.

We can use this shorter name everywhere to improve readability:

typealias Songs = MusicItemCollection<Song>

func catalogSongs(ids: [MusicItemID]) async throws -> Songs {
    let musicRequest = MusicCatalogResourceRequest<Song>(matching: \.id, memberOf: ids)
    let response = try await musicRequest.response()
    return response.items
}

var songs: Songs?
songs = try await catalogSongs(ids: ["1109658204", "1508562321"])

/// More examples
var recommendedSongs: Songs?
var recentlyPlayedSongs: Songs?
var frequentlyPlayedSongs: Songs?

This strategy can be applied to all generic structures, making them easier to read and understand:

public typealias Artists = MusicItemCollection<Artist>
public typealias Genres = MusicItemCollection<Genre>
public typealias Albums = MusicItemCollection<Album>

Shorter, more concise aliases improve code readability!

Reducing complexity

We can utilize the power of typealias when working with complex types that have several arguments. For example:

typealias QuoteCompletion = (Result<Quote, Error>) -> () 
typealias QuotesCompletion = (Result<[Quote], Error>) -> ()

Then, in our methods, we can use the more succinct typealias name:

typealias QuoteID = String

func fetchAllQuotes(completion: @escaping QuotesCompletion) {   
/// implementation
}          

func fetchQuote(for ID: QuoteID, completion: @escaping QuoteCompletion) {  
/// implementation
}

Now, let’s look at a more complex example simplified using typealias. Here’s a generic closure that has constraints on the type:

typealias Parser<A> = (String) -> [(A, String)] where A: Equatable

We can use it like so:

func parsing<A>(_ string: String, for parser: Parser<A>) where A: Equatable {
}

Improving clarity

You may have had cases where your class or struct conforms to many protocols. For clarity, we can combine the series of protocol conformances into a single typealias and then use that alias everywhere.

typealias CombinedType = FooProtocol & BarProtocol

A classic example is when we are conforming our UIViewController to different delegates. For example, say we have a controller that is presented as a popover, and we want to conform to UIPopoverPresentationControllerDelegate to get the delegate methods.

If we have many controllers, we can create a typealias:

typealias PresentableViewController = UIViewController & UIPopoverPresentationControllerDelegate

In the MusicKit framework, Apple takes a similar approach with typealias. In this case, MusicTokenProvider is a typealias for a type that needs to be a subclass of MusicUserTokenProvider, which conforms to the MusicDeveloperTokenProvider protocol:

public typealias MusicTokenProvider = MusicUserTokenProvider & MusicDeveloperTokenProvider

Another example of combining is when we want our struct to conform to Codable. If we’re creating a custom structure in the MusicKit framework, we can make it conform to MusicItem and Codable by providing a typealias:

public typealias MusicCodableItem = MusicItem & Codable

Here, Codable is a typealias too!

We go through its declaration:

typealias Codable = Decodable & Encodable 

Then use it, like so:

public struct UserMusicItem: MusicCodableItem {
// MusicItem requirements
// Codable requirements
}

We can also separate the protocol conformances into different extensions for better clarity:

public struct UserMusicItem {}

extension UserMusicItem: MusicItem {
// MusicItem requirements
}

extension UserMusicItem: Decodable {
// Decodable requirements
}

extension UserMusicItem: Encodable {
// Decodable requirements
}

It is up to you how to use typealias in your codebase to maintain a balance between clarity and usefulness.

A similar example is conforming a type to Decodable and ExpressibleByBooleanLiteral:

typealias DecodableBooleanLiteral = Decodable & ExpressibleByBooleanLiteral

Using typealias with caution

Now that you have a better understanding of how typealias can make your codebase more readable, it may be tempting to use this function everywhere. However, there can be disadvantages to using typealias indiscriminately.

For example, you will personally be familiar with the alias name that you give to specific types on a given project. But, issues could arise when an entire team of developers is working on a project that uses typealias, or when a new member joins the team.

Even worse, some alias could confuse other developers. For example, let’s say use a generic typealias for a completion handler:

typealias Completion = (String?, AnyObject?) -> ()

It may not initially be clear to other developers what Completion does at first glance.

Let’s say you rename it to StringCompletion:

typealias StringCompletion = (String?, AnyObject?) -> ()

This is better, but someone new to the codebase would still need to check to see the parameters.

It may be best to exercise caution when adding typealias to your codebase. Try to only where it is specifically needed and where it makes the most sense.

Conclusion

In this article, we looked at basic and advanced examples of the typealias function in Swift. typealias can be useful for reducing verbosity and complexity and improving readability and clarity. typealias is especially powerful when working with complex closure types and those conforming to multiple protocols.

However, despite its many advantages, it’s best not to introduce typealias everywhere. If your colleagues have to look up the actual type for each typealias, this will waste valuable time in context switching and will defeat the purpose of typealias in the first place.

To learn more about the typealias in Swift, see the official docs.

The post Mastering <code>typealias</code> in Swift appeared first on LogRocket Blog.



from LogRocket Blog https://ift.tt/tTzEQrZ
via Read more

Post a Comment

0 Comments
* Please Don't Spam Here. All the Comments are Reviewed by Admin.
Post a Comment

Search This Blog

To Top