Manjusaka

Manjusaka

Good and Bad, A Comprehensive Look at Swift Part 2

Not long ago, in my article Good and Bad, A Comprehensive Look at Swift Part 1, I introduced some tips on how to write excellent code in Swift. In the two years since Swift was released, I have spent a long time mastering the best practices. For more details, please refer to this article: Good and Bad, A Comprehensive Look at Swift Part 1.

In this series of articles, I will try to summarize the good and bad parts of the Swift language. Well, I hope there will be excellent Swift in the future to help me conquer Swift (well, young man, don't look, the central government has decided it's you, recite a poem quickly). If you have any ideas or want to share some life experiences as a developer, please contact me on Twitter. My account is ksmandersen.

Enough nonsense, let's start today's lesson.

guard is great for guarding safety#

In Swift 2.0, Swift introduced a set of features that may be unfamiliar to developers. The guard statement plays a significant role in defensive programming. (Translator's note 1: Defensive programming is a specific manifestation of defensive design. It is to ensure that the program will not be damaged by unforeseen use of the program. It can be seen as an idea to reduce or eliminate the effectiveness of Murphy's Law. Defensive programming is mainly used in programs that may be abused, pranked, or inadvertently cause catastrophic effects. Sourced from Wikipedia). Every Objective-C developer may be familiar with defensive programming. By using this technique, you can ensure that your code does not throw exceptions when handling unexpected input data.

The guard statement allows you to set conditions and rules for the following code, and you must specify how to handle the conditions (or rules) when they are not met. In addition, the guard statement must return a value. In early Swift programming, you may use if-else statements to handle these situations in advance. However, if you use the guard statement, the compiler will help you handle exceptional data when you forget to consider certain situations.

The next example is a bit long, but it is a very good example of the use of guard. The didPressLogIn function is called when the button on the screen is clicked. We expect that when this function is called, if the program generates additional requests, no additional logs will be generated. Therefore, we need to do some preprocessing on the code. Then we need to validate the log. If this log is not what we need, then we don't need to send this log. But more importantly, we need to return an executable statement to ensure that we don't send this log. The guard statement will throw an exception when we forget to return.

    @objc func didPressLogIn(sender: AnyObject?) {
            guard !isPerformingLogIn else { return }
            isPerformingLogIn = true

            let email = contentView.formView.emailField.text
            let password = contentView.formView.passwordField.text

            guard validateAndShowError(email, password: password) else {
                isPerformingLogIn = false
                return
            }

            sendLogInRequest(ail, password: password)
    }

The combination of let and guard is very effective. In the following example, we will bind the result of the request to a variable user, and then use it in the finishSignUp method. If result.okValue is empty, the guard statement will take effect. If it is not empty, this value will be assigned to user. We use where to restrict the guard statement.

    currentRequest?.getValue { [weak self] result in
      guard let user = result.okValue where result.errorValue == nil else {
        self?.showRequestError(result.errorValue)
        self?.isPerformingSignUp = false
        return
      }

      self?.finishSignUp(user)
    }

To be honest, guard is very powerful. Well, if you haven't used it yet, then you should really consider it carefully.

Declare and configure subviews at the same time#

As mentioned in the previous articles, when developing views, I am more accustomed to generating code. Because I am familiar with the configuration of views, I can quickly locate the problem when there are layout issues or improper configurations.

During the development process, I found it important to put different configuration processes together. In my early Swift programming experience, I usually declared a configureView function and put the configuration process here during initialization. But in Swift, we can use property declaration code blocks to configure views (actually I don't know what to call this (escape)).

Well, in the example below, there is an AwesomeView view that contains two subviews, bestTitleLabel and otherTitleLabel. The configuration process for both subviews is done in one place. We integrate the configuration process in the configureView method. Therefore, if I want to change the textColor property of a label, I know exactly where to make the modification.

    class AwesomeView: GenericView {
        let bestTitleLabel = UILabel().then {
            $0.textAlignment = .Center
            $0.textColor = .purpleColor()tww
        }

        let otherTitleLabel = UILabel().then {
            $0.textAlignment = .
            $0.textColor = .greenColor()
        }

        override func configureView() {
            super.configureView()

            addSubview(bestTitleLabel)
            addSubview(otherTitleLabel)

            // Configure constraints
        }
    }

For the above code, I don't like the type label in the declaration of the label, and then initialize and return a value in the code block. By using the library Then, we can make a small improvement. You can use this small function to associate code blocks with the declaration of objects in your project. This reduces redundant declarations.

    class AwesomeView: GenericView {
        let bestTitleLabel = UILabel().then {
            $0.textAlignment = .Center
            $0.textColor = .purpleColor()tww
        }

        let otherTitleLabel = UILabel().then {
            $0.textAlignment = .
            $0.textColor = .greenColor()
        }

        override func configureView() {
            super.configureView()

            addSubview(bestTitleLabel)
            addSubview(otherTitleLabel)

            // Configure constraints
        }
    }

Classify class members by different access levels#

Well, recently, something important happened to me. I used a special method to combine class and struct members. This is a habit I developed when developing with Objective-C. I usually put private methods at the bottom, and public and initialization methods in the middle. Then I place the properties in the order of public properties to private properties. Well, you can organize your code according to the following structure.

  • Public properties
  • Inline properties
  • Private properties
  • Initialization container
  • Public methods
  • Inline methods
  • Private methods

You can also sort them according to static/class properties/fixed values. Different people may add different things based on this. But for me, I always program according to the above method.

Well, that's the end of this issue. If you have any good ideas or something to say, please feel free to contact me through the contact information below the screen. Of course, you are welcome to use this method to tip and subscribe to my articles (heavy fog).

Next episode preview: I will continue to talk about the bits and pieces in Swift. Don't go away, the next episode will be even more exciting.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.