Detailed analysis of the finite extension in Swift

  • 2020-06-03 08:33:11
  • OfStack

preface

Now many companies are replacing OC with Swift in their new iOS projects. Swift brings a lot of highlights and new features, but I think the most important one is to lead to a change in our programming thinking. Shifting from the traditional object-oriented (ES9en-ES10en programming) approach used in OC to the protocol-oriented (protocol oriented programming) and value-oriented (ES17en-ES18en programming) approach, Apple also told us developers to "start with 1 protocol, don't start with 1 class", In addition, struct and the enumeration type enum account for a large proportion of Swift's standard library, so it is practical and elegant to use various protocols protocol and value type value flexibly in Swift. extension, which is ubiquitous in Swift programming, not only changed the structure of our code, but also coincided with POP's programming philosophy.

extension

extension, as the name suggests, is an extension, similar to category in OC, but extension in Swift is much more powerful, extending class struc enum and even protocol to provide convenience constructors, add arithmetic attributes, define instance and class methods, define subscripts, make existing types conform to protocols, and so on.

Now there is a demand, A, B and C3 class, all need to extend a property or method used to deal with the same function, OOP: let A, B and C inherited from a base class D, then adding this attribute to D, or when A, B and C3 class is not convenient to inherit from a base class, each extension 1 attribute, respectively. But this feels very rigid.

Protocol oriented programming POP: write a protocol someProtocol, declare this attribute in the protocol, then make A, B and C3 classes follow this someProtocol, and implement the attribute in this protocol respectively, as shown below:


// define protocol
protocol someProtocol {
 var clickCount: Int { set get }
 func ClickEvent(action: String,value: NSNumber)
}

extension A: someProtocol{
 var clickCount: int { set get } 
 func ClickEvent(action: String,value: NSNumber){
  // The implementation code 
 }
}

Limited extension extesion... where..

And at this point to the new requirements, need to A class extends the other one attribute, but B and C temporarily do not need, since most people's thinking is certainly only A need, then we will separate to A to extend one attribute is not ok, it must work, but now that we've all down the path of POP programming, but also encourages us to try to handle with protocol Swift problem, that we should how to make an issue of protocol?

First of all, we came up with to someProtocol extension 1 attribute, but it is not just a A can, follow the same protocol B and C, and follow the agreement in the future all class, struct extension got this property, it from the code logic is not strict, so the apple in Swift2. Zero joined a new feature, You can add a qualification to the protocol extension, that is, you can only allow the use of properties or methods under the extension if the qualification is met (by another protocol or by a certain type). The qualification is added through where.


extension someProtocol where Self: UIViewController{
 var otherProperty: String{
  return "something you want"
 }

 func handleError(error: String) {
  // The implementation code 
}}

Code above means: Self represent a certain class of followed someProtocol, only this class is inherited in UIViewController otherProperty this property and handleError may only be used this way, this is the basic limit extension method and the UIViewController here, of course, also can be other anotherProtocol one agreement, means you only follow the anotherProtocol premise, you can use the extension someProtocol content.

Qualified extension applications in RxSwift/RxCocoa

When Swift is developed to a certain stage, RxSwift framework is usually introduced for responsive programming and agile development. RxCocoa is another library introduced with RxSwift in the code. RxCocoa is the extension of UIKit (UIView), control events (Control Event), key-value observation (KVO), notification (Notification) and so on in iOS's native API. In order to facilitate the development of these native components Rx applications. Such as:


//nameTextField is 1 a UITextField Control, can be directly through .rx.text Gets the sequence of events into the input to the control, and then processes it 

let nameObservable = nameTextField.rx.text .shareReplay(1).map {
 $0!.characters.count > 0 
}

//registerButton is 1 a UIButton Through the .rx.tap I can get that button Of the click event sequence 
registerButton.rx.tap .bindTo(someObservar) .addDisposableTo(disposeBag)

And in the example above rx.text And rx. tap is how to achieve, let's take a look at RxCocoa source code 1 to see:


/// Extend NSObject with `rx` proxy. Will all NSObject Subclasses follow ReactiveCompatible agreement  
extension NSObject: ReactiveCompatible { }
 //ReactiveCompatible This protocol is extended rx Property of type struct The type of Reactive
extension ReactiveCompatible {
 /// Reactive extensions.
 public var rx: Reactive {
 get {
  return Reactive(self) // Return here Reactive Entities, self Assigned to base attribute 
 }
 set {
  // this enables using Reactive to "mutate" base object
 }
 }
}

Taking UIButton as an example, through the above implementation, we understand that an Reactive type attribute can be obtained through ES139en.rx Reactive(self) button itself is assigned to Reactive, the struct attribute. Look at the source code:


public struct Reactive<Base>{ 
 /// Base object to extend. 
 public let base: Base 
 /// Creates extensions with base object. 
 ///
 /// - parameter base: Base object. 
 public init(_ base: Base) {  
  self.base = base // The above return Reactive(self) That will be self Assigned to the base
 }
}

By Reactive construction method, at this time UIButton.rx The Base type obtained in the Reactive entity is UIButton, and base is the UIButton object itself, and then through ES157en.tap is how to get the click event, which USES the qualified extension this very useful function, then look at the source code:


// Click event Extension to go through button.rx.tap To look at the subscription 
extension Reactive where Base: UIButton {
 /// Reactive wrapper for `TouchUpInside` control event.
 public var tap: ControlEvent {
  return controlEvent(.touchUpInside)
 } 
}

The above is the qualified extension of Reactive in RxCocoa. The tap attribute under Reactive can only be used if the Base type of Reactive is UIButton, and the tap attribute is the sequence of click events encapsulated by RxSwift. RxCocoa also extends image and title which change UIButton, as follows:


// Extend the method by binding Observable To modify dynamically UIButton the title and image
extension Reactive where Base: UIButton {
 /// Reactive wrapper for `setTitle(_:for:)`
 public func title(for controlState: UIControlState = []) -> UIBindingObserver<Base, String?> {
  return UIBindingObserver<Base, String?>(UIElement: self.base) { (button, title) -> () in
   button.setTitle(title, for: controlState)
  }
 }

 /// Reactive wrapper for `setImage(_:for:)`
 public func image(for controlState: UIControlState = []) -> UIBindingObserver<Base, UIImage?> {
  return UIBindingObserver<Base, UIImage?>(UIElement: self.base) { (button, image) -> () in
   button.setImage(image, for: controlState)
  }
 }

 /// Reactive wrapper for `setBackgroundImage(_:for:)`
 public func backgroundImage(for controlState: UIControlState = []) -> UIBindingObserver<Base, UIImage?> {
  return UIBindingObserver<Base, UIImage?>(UIElement: self.base) { (button, image) -> () in
   button.setBackgroundImage(image, for: controlState)
  }
 }
}

We can also extend the UI control to other Rx extensions ourselves to meet specific needs. For example, the login button can only be clicked and the background color can be changed if both the username and password meet a certain condition, so we can convert a certain condition to an observable sequence of type Bool Observabel<Bool> , UIButton is then extended by an observer type btnValidState, and the binding can be monitored at any time.


// Customize the definition pair with qualified extensions UIButton the Reactive extension   Can be achieved by rx To access the 
extension Reactive where Base:UIButton{
 var btnValidState:UIBindingObserver<Base,Bool>{
  return UIBindingObserver(UIElement: base, binding: { (button, valid) in
   button.isEnabled = valid
   button.backgroundColor = valid ? mainColor : bgGrayColor2
  })
 }
}
// Put the username and password in String Sequence conversion to Bool The sequence 
let nameObservable = userNameText.rx.text .shareReplay(1).map {
   $0!.characters.count > 0
 }
let passObservable = passwordText.rx.text .shareReplay(1).map {
   $0!.characters.count > 0
 }
// Combine the above two sequences 1 Sequence, bound to our extended btnValidState On the properties 
// And you can see that we can pass it loginBtn.rx.btnValidState I got it. I kept it RxSwift The code of 1 consistency 
 _ = Observable.combineLatest(nameObservable,passObservable){$0 && $1}.bind(to: loginBtn.rx.btnValidState).disposed(by: disposeBag)

These are the basic USES of the limited extension and a few real-world applications, and to a certain extent it does change the way we think and code, which is a very flexible change that Swift has brought to us.

conclusion


Related articles: