preface

If iOS development didn’t start directly with Swift, then anyone familiar with OC would probably know the OC Category.

With categories we can extend the methods and attributes of the class (read-only calculation attributes and runtime attributes added) to make development more efficient.

However, there are more ways to use Extension in Swift. It has many more uses.

As for the enlightenment of the use of Extension, I started with the following article, which you can also have a look at. Maybe in front of this great god’s article, what I wrote after is just a drop in the bucket.

The “error” of using Extension in Swift

How can we use Extension gracefully? I’m just talking about the idea and style of coding, the logic of the code and you’ll see.

At the end of this article, I will talk about Extension in Flutter Dart.

Use Extension when complying with proxies or data sources

This is a very common coding requirement. Create a tableView in the controller, set the data source and proxy of tableView to the controller.

You could code it like this:

class ViewController: UIViewController, UITableViewDataSource.  UITableViewDelegate { override func viewDidLoad() { super.viewDidLoad() let tableView = UITableView() tableView.dataSource = self tableView.delegate = self view.addSubview(tableView) } func tableView(_ tableView: UITableView, numberOfRowsInSection Section: Int) -> Int {return 5} func tableView(_ tableView: UITableView, cellForRowAt indexPath: indexPath) -> UITableViewCell {return UITableViewCell()}}Copy the code

Or this:

class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let tableView = UITableView() tableView.dataSource = self tableView.delegate = self view.addSubview(tableView) } } extension ViewController: UITableViewDataSource, UITableViewDelegate {func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {return 5} func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { return UITableViewCell() } }Copy the code

However, I would prefer to write it this way, and use an Extension to comply with the data source and the agent respectively. This way, although there are too many lines of code, it is very clear to separate the business from the function.

class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let tableView = UITableView() tableView.dataSource = self tableView.delegate = self view.addSubview(tableView) } } extension ViewController: UITableViewDataSource {func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {return 5} func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { return UITableViewCell() } } extension ViewController: UITableViewDelegate { }Copy the code

Extension can be used to comply with brokers and data sources, and it is recommended that a separate classification be created for each protocol that Extension complies with, because one of the purposes of Extension is to separate different businesses.

Plus use //MARK: – Well, extremely comfortable.

Different businesses use Extension

In fact, I used this principle when using Extension for compliance brokers and data sources above.

Let’s say I’m in a controller, and I have a lot of different business logic, like network requests, like click events with multiple buttons.

Then you can use Extension this way to make your code more readable and maintainable.

// MARK: - Extension ViewController {} // MARK: - Extension ViewController {}..Copy the code

You can continue along the same lines by adding extensions to separate different business layers.

Private apis and external apis use Extension

When my colleagues first went from OC to Swift, they often complained to me, OC has dot h and dot m files, and when YOU use a class, you click on the dot h file and you can see very clearly which apis of that class I can use, whereas Swift only has one dot Swift file, you can’t tell at a glance that those are private apis and those are external apis. You need to look at the private and fileprivate keywords.

It is recommended to use Extension to separate private apis from external apis. Of course, this is a bit against the separation of different businesses using Extension, but you can agree on a rule when working together to facilitate style management.

// MARK: - private API private extension ViewController {} private extension ViewController {} - API extension ViewController {}Copy the code

Or write all external API functions in curly braces of the declared class and private API functions in Extension.

// MARK: - API class ViewController: UIViewController {}Copy the code

Protocol is used with Extension to realize multiple inheritance

Note that the Protocol here is not a proxy or data source, as mentioned above. You can interpret it as more like ‘inheritance’.

The following example comes from the famous Alamofire, which will focus more on the use of protocol and not be detailed.

public protocol URLConvertible {

    func asURL() throws -> URL
}

extension String: URLConvertible {
    public func asURL() throws -> URL {
        guard let url = URL(string: self) else { throw AFError.invalidURL(url: self) }
        return url
    }
}

extension URL: URLConvertible {
    public func asURL() throws -> URL { return self }
}

extension URLComponents: URLConvertible {
    public func asURL() throws -> URL {
        guard let url = url else { throw AFError.invalidURL(url: self) }
        return url
    }
}

Copy the code

The URLConvertible protocol is defined so that String, URL, and URLComponents are eventually flattened into urlconvertibly types, giving users more options when they enter parameters. So we can see that the request Api in Alamofire reads something like this:

@discardableResult
public func request(
    _ url: URLConvertible,
    method: HTTPMethod = .get,
    parameters: Parameters? = nil,
    encoding: ParameterEncoding = URLEncoding.default,
    headers: HTTPHeaders? = nil)
    -> DataRequest
{
    return SessionManager.default.request(
        url,
        method: method,
        parameters: parameters,
        encoding: encoding,
        headers: headers
    )
}
Copy the code

I don’t care whether the url’s entry is a String, URL, or any other type, as long as you comply with URLConvertible, you can even roll out custom classes yourself by complying with URLConvertible.

I have the opportunity to talk about Protocol in detail.

Considerations for using Extension

  • Class Extension cannot write a first-level constructor or destructor. These two functions must be used in the declared class. Constructors can be written in Extension of struct.

  • You can write convenience constructors (secondary constructors) in class Extension.

  • Attributes cannot be defined in Extension; if you must, use the Runtime principles.

  • You can define read-only computing properties in Extension.

What is the meaning of Extension?

I can make a vivid example of the use of Extension:

Just like the shadow body of Naruto, each Extension is like an additional shadow body, and this shadow body complies with the agent, data source, protocol, etc., to achieve different functions. Each ghost would then have different skills, and its own body would be able to invoke those skills at will.

So let’s try the following code in viewDidLoad where both if methods are going to go.

class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let tableView = UITableView() tableView.dataSource = self tableView.delegate = self view.addSubview(tableView) if self is UITableViewDataSource { Print (" it is UITableViewDataSource ")} if self is UITableViewDelegate {print(" it is UITableViewDelegate ")}} extension ViewController: UITableViewDataSource {func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {return 5} func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { return UITableViewCell() } } extension ViewController: UITableViewDelegate { }Copy the code

self is UITableViewDataSource

self is UITableViewDelegate

also self is UIViewController

So self is what?

Use of Extension in Dart

In the process of learning Flutter, I actually used Dart as Swift for lifting, so naturally I thought of using Extension.

It should be noted that Extension in Dart is currently closer to the Category function in OC, and can only be used as an Extension method and attribute, and there are some differences in writing.

Here’s an example:

extension SomeName on DateTime { String get formateMonthAndDay { if (this == null) return ""; var month = this.month; var day = this.day; return "$month" + "/" + "$day"; }}Copy the code

The first format is the extension class name on the class to be extended

Dart: DateTime: DateTime: DateTime: DateTime: DateTime: DateTime: DateTime: DateTime: DateTime: DateTime: DateTime: DateTime: DateTime: DateTime: DateTime: DateTime: DateTime: DateTime: DateTime: DateTime: DateTime: DateTime

It is also worth noting that Dart Extension use requires Dart SDK≥2.7.0 to be available.