SoFunction
Updated on 2025-04-11

Detailed analysis of limited extensions in Swift

Preface

Now many new iOS projects in companies have begun to use Swift to replace OC development. Swift brings many highlights and new functions, but I think the most important point is to guide our programming ideas to change, and transform the traditional object-oriented programming idea OOP (object-oriented programming) we use in OC to protocol-oriented programming ideas POP (protocol oriented programming) and value-oriented programming ideas VOP (value-oriented programming). Apple also allows us developers to "start with a protocol, not with a class" when programming. In addition, there are large-scale structure structs and enum types enums in Swift, so flexibly use various protocol protocols and value-oriented programming ideas to achieve functions that are both practical and elegant. The ubiquitous extension in the Swift programming process not only caused a huge change in our code structure, but also coincided with POP in programming philosophy.

extension

extension, as the name suggests, is an extension, similar to category in OC, but the extension in Swift is much more powerful, and it can extend class struc enum or even protocol to provide them with: providing convenient construction methods, adding operation attributes, defining instance methods and class methods, defining subscripts, making existing types follow the protocol, etc.

There is now a requirement. The three classes A, B and C all need to extend an attribute or method to handle the same function. OOP's approach: let A, B and C inherit from a base class D, and then add this attribute to D, or when the three classes A, B and C are inconvenient to inherit from a base class, each extends one attribute separately. But doing this feels rigid.

The practice of protocol-oriented programming POP: write a protocol someProtocol, declare this property in the protocol, and then let A, B, and C all follow this someProtocol, and then implement the properties in this protocol, as shown below:

//Define protocolprotocol 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){
  //Implementation code }
}

Limited extension...where..

At this time, a new requirement comes, and another attribute needs to be extended to Class A, but B and C are not needed for the time being. Most people's thinking is that since only A needs it, can we just extend an attribute to A alone? This will definitely work. But since we have all embarked on the path of POP programming, and Swift also encourages us to use protocol to deal with problems as much as possible, how should we make a fuss on protocol?

First of all, we thought of extending a property to someProtocol, but this is not only A can be used, but also follows B and C of the protocol, as well as all classes, structs, etc. that follow this protocol in the future, and obtain this property. This is not rigorous in terms of the logic of the code. So Apple added a new function in Swift 2.0, which can add a limitation when extending the protocol, that is, the attributes or methods under this extension can only be allowed to be used when the restriction condition is met (following another protocol or satisfying a certain type). This limitation is added through where, let's look at the code first:

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

 func handleError(error: String) {
  //Implementation code}}

The above code means: Self represents a class that follows someProtocol. Only when this class is inherited from UIViewController can you use otherProperty attribute and handleError. This is the basic way to define extensions. Of course, the UIViewController here can also be another protocol anotherProtocol. That is to say, only when you follow anotherProtocol, you can use the extension content in someProtocol.

Limited extension applications in RxSwift/RxCocoa

When Swift development reaches a certain stage, the RxSwift framework is usually introduced for responsive programming and agile development. Another library RxCocoa is RxCocoa, which is introduced in the code at the same time as RxSwift. RxCocoa is an extension of Rx to UIKit and views (UIView), control events (Control Event), key-value observation (KVO), notification (Notification) in the native API of iOS, so as to more conveniently apply these native components during development. For example:

//nameTextField is a UITextField control that can be directly obtained through . The event sequence of the input content in the control and then processed
let nameObservable =  .shareReplay(1).map {
 $0!. > 0 
}

// registerButton is a UIButton, through which the click event sequence of the button can be obtained .bindTo(someObservar) .addDisposableTo(disposeBag)

In the above exampleAnd how it is implemented, let’s take a look at the source code of RxCocoa:

/// Extend NSObject with `rx` proxy. All NSObject subclasses follow the ReactiveCompatible protocolextension NSObject: ReactiveCompatible { }
 //ReactiveCompatible This protocol extends the rx attribute, and Reactive of type struct type is Reactiveextension ReactiveCompatible {
 /// Reactive extensions.
 public var rx: Reactive {
 get {
  return Reactive(self) //Reactive entity is returned here and assign self to base attribute }
 set {
  // this enables using Reactive to "mutate" base object
 }
 }
}

Taking UIButton as an example, through the above implementation, we understand that by obtaining a Reactive type attribute,Reactive(self) Assign the button itself to the Base property of the Reactive struct, see 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) {  
   = base //The above return Reactive(self) assigned self to base }
}

Through Reactive's construction method, at this timeThe Base type in the Reactive entity obtained is UIButton, and base is the UIButton object itself. Then how do you get the click event? This uses the very practical function of limited extension. Let's look at the source code:

//Click on event extension, you can observe the subscriptionextension Reactive where Base: UIButton {
 /// Reactive wrapper for `TouchUpInside` control event.
 public var tap: ControlEvent {
  return controlEvent(.touchUpInside)
 } 
}

The above is the limited extension of Reactive in RxCocoa. Only when the Base type of Reactive is UIButton, can the tap attribute under Reactive be used, and this tap attribute is the click event sequence encapsulated by RxSwift. RxCocoa also expands the image and title of UIButton at the same time, as follows:

//Extension method, you can dynamically modify the title and image of UIButton by binding Observableextension Reactive where Base: UIButton {
 /// Reactive wrapper for `setTitle(_:for:)`
 public func title(for controlState: UIControlState = []) -> UIBindingObserver<Base, String?> {
  return UIBindingObserver<Base, String?>(UIElement: ) { (button, title) -> () in
   (title, for: controlState)
  }
 }

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

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

We can also perform other Rx extensions on UI controls to meet specific needs. For example, the login button can only click and change the background color when the username and password meet a certain condition at the same time. Therefore, we can convert a certain condition into a Bool-type observable sequenceObservabel<Bool>, and then perform an extension of the observer type btnValidState to UIButton, and then the binding can be monitored at any time.

//Use limited extensions to customize Reactive extensions to UIButton can be accessed through rxextension Reactive where Base:UIButton{
 var btnValidState:UIBindingObserver&lt;Base,Bool&gt;{
  return UIBindingObserver(UIElement: base, binding: { (button, valid) in
    = valid
    = valid ? mainColor : bgGrayColor2
  })
 }
}
//Convert String sequence of username and password into Bool sequencelet nameObservable =  .shareReplay(1).map {
   $0!. &gt; 0
 }
let passObservable =  .shareReplay(1).map {
   $0!. &gt; 0
 }
//Synthe above two sequences into one sequence and bind to our extended btnValidState property// You can see that you can get it at this time, which maintains the consistency of RxSwift code _ = (nameObservable,passObservable){$0 &amp;&amp; $1}.bind(to: ).disposed(by: disposeBag)

The above are the basic use of limited extensions and some practical applications. This can indeed change our coding thinking and methods to a certain extent. This is also a very flexible change that Swift brings to us.

Summarize

The above is the entire content of this article. I hope that the content of this article has a certain reference value for everyone's study or work. If you have any questions, you can leave a message to communicate. Thank you for your support.