1. Core Principle of Method Chaining: Returning self 1. Why does returning self enable method chaining?
Continuity of Method Calls : In Swift, the return type of a method determines what can be called next. If a method returns the current instance (self), the following method call can act on that same instance.
Syntax Structure : Swift allows consecutive dot syntax, e.g., a().b().c(). Each method must return a type compatible with the next method’s calling context (typically the same type).
2. Example Code Explained: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Builder { var result: String = "" @discardableResult func addText (_ text : String ) -> Builder { result += text + " " return self } func build () -> String { return result.trimmingCharacters(in: .whitespaces) } } let builtString = Builder () .addText("Hello" ) .addText("World" ) .build()
Key Points :
addText returns self of type Builder, so .addText("World") acts on the same instance.
build() returns a String, ending the chain.
2. Method Chaining with Closures 1. Returning self in Closures
Closure as a Parameter : A method can take a closure and still return self to enable chaining.
Example Code: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Adder { var currentValue: Double = 0 func add (value : Double , then action : (Adder ) -> Void ) -> Adder { currentValue += value action(self ) return self } func getResult () -> String { return String (format: "%.2f" , currentValue) } } let result = Adder () .add(value: 1.111 ) { _ in } .add(value: 2.222 ) { _ in } .getResult()
Key Points :
The closure receives self, enabling internal state modification.
Returning self allows the next add call to proceed.
3. Optional Chaining Explained 1. Syntax
Syntax : optional?.propertyOrMethod()
Purpose : If the optional is nil, the chain safely returns nil without crashing.
Example Code: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Person { var residence: Residence ? } class Residence { var address: Address ? } class Address { var street: String ? } let person = Person ()person.residence = Residence () person.residence? .address = Address () person.residence? .address? .street = "Main St" if let street = person.residence? .address? .street { print (street) } else { print ("Address not available" )
Key Points :
If residence is nil, further access returns nil safely.
Each ? checks for nil before proceeding.
4. Method Chaining via Extensions 1. Syntax
Syntax : extension TypeName { ... }
Purpose : Add chainable methods to existing types.
Example Code: 1 2 3 4 5 6 7 8 9 10 11 12 extension String { @discardableResult func appendIfNotEmpty (_ text : String ) -> String { if ! text.isEmpty { return self + text } return self } } let message = "Hello" .appendIfNotEmpty(" World" ).appendIfNotEmpty("" )print (message)
Key Points :
@discardableResult avoids compiler warnings if return value is unused.
The method returns a String, enabling further chaining.
5. Role and Importance of @discardableResult 1. What is @discardableResult?
Purpose : Marks that a method’s return value can be ignored.
Necessity : In chaining, intermediate returns (usually self) are often not explicitly used.
Code Comparison: Without @discardableResult:
1 2 3 4 func addText (_ text : String ) -> Builder { } _ = Builder ().addText("Hello" )
With @discardableResult:
1 2 3 4 5 @discardableResult func addText (_ text : String ) -> Builder { } Builder ().addText("Hello" )
6. Real-World Use Cases 1. UI Building 1 2 3 4 5 let button = UIButton () .setTitle("Login" , for: .normal) .setTitleColor(.blue, for: .normal) .setBackgroundColor(.lightGray) .addTarget(self , action: #selector (buttonTapped), for: .touchUpInside)
Key Points :
Each method returns UIButton to support chaining.
Methods like addTarget may need to be extended to support chaining.
2. Data Processing 1 2 3 4 let result = [1 , 2 , 3 , 4 , 5 ] .filter { $0 % 2 == 0 } .map { $0 * 2 } .reduce(0 , + )
Key Points :
Each high-order function returns a value that supports further chaining.
filter and map return arrays; reduce returns a final result.
Memory Usage : Intermediate objects (like arrays/strings) may increase memory usage.
Closure Captures : Avoid strong reference cycles by using [weak self] or [unowned self] inside closures.
2. Best Practices
Avoid Over-Chaining : Excessive chaining may hurt readability. Break complex logic into parts.
Error Handling : If methods can throw errors, use do-catch to handle gracefully.
8. Summary: Key Elements and Techniques
Element
Explanation
return self
Enables continuous method calls on the same instance.
@discardableResult
Allows ignoring intermediate return values.
Optional Chaining ?
Safely accesses optional properties/methods.
Extensions
Adds chaining support to existing types.
Closure Chaining
Allows flexible logic within chaining context.
Performance/Readability
Balance code elegance and maintainability.