Author: Andrew Jaffee, original text link, original text date: 2018/03/28 Translator: Yang Zai; Proofreading: Numbbbbb, Lision; Finalized: Forelax
In this article, we’ll take a closer look at protocol-oriented programming in Swift 4. This is the second article in a two-part series. If you haven’t read the previous introductory article, please read the previous one before moving on.
In this article, we’ll explore why Swift is considered a “protocol-oriented” language; Contrast protocol oriented programming (POP) with object Oriented programming (OOP); Compare “value semantics” and “reference semantics”; Discuss local reasoning; Implement proxy mode with protocol; Replace types with protocols; Use protocol polymorphism; Review my actual protocol-oriented code; Finally, why I didn’t program 100% with POP.
In this two-part series on POP, I’ve added links to at least three Apple Worldwide Developers Conference (WWDC) videos. Clicking on these links in Safari takes you straight to specific sections of the video (and often starts playing the video from there). If you’re not using Safari, you’ll need to browse the video, manually jump to a section, or view a text version of the video.
Why is Swift a “protocol oriented” language?
In my previous POP introduction, Apple claimed that “At its core, Swift is protocol-oriented.” Trust me, it is. Why is that? Before we answer that question, let’s compare several programming languages.
It’s a good idea to have some knowledge of other languages as this will be useful in some situations, such as when you need to link C++ libraries into an iOS application. Many of my iOS and OSX apps link to the C++ library because they are available for Windows. Over the years, I’ve supported many “platform-independent” apps.
OOP languages have long supported interfaces. Interfaces are similar to, but not identical to, the protocols in Swift.
Interfaces in these languages specify which methods and/or properties must be implemented by classes and/or structures that follow that interface. I use “and (or)” here because, for example, C++ doesn’t have the concept of an interface, but instead uses abstract classes. Also, a C++ struct can inherit from a class. An interface in C# allows you to specify properties and methods within it, and structs can follow the interface. A protocol can also specify methods and attributes that are required to be implemented, but only classes can declare compliance with interfaces, not structs.
These interfaces and the protocols in Objective-C, there’s no implementation of methods. They simply specify requirements that serve as a “blueprint” for the implementation of classes/structures that follow the protocol.
Protocols form the basis of the Swift standard library. As I showed in my first article, protocols are key to the POP methodology and paradigm.
The protocol in Swift has one feature that no other language supports: protocol extension. Here’s an excerpt from Apple’s official description:
The protocol can be extended to provide concrete implementations of methods, initialization methods, subscripts, and computed properties for types that follow the protocol. This allows the protocol itself to define some behavior, rather than being implemented by the types themselves or by a global method. By extension, we can provide a default implementation for any method and calculation properties required by the protocol. If a protocol-compliant type provides its own implementation for a method or property, that implementation replaces the implementation in the protocol extension.
You saw how I used protocol extensions in the last article, and you’ll see it again in this article. They are the secret that makes Swift POP so powerful.
Protocols were important in iOS long before Swift came along. Remember my discussion of protocols like UITableViewDataSource and UITableViewDelegate that iOS developers have adopted for years? Think again about the Swift code you write every day.
It is impossible to program in Swift without using the protocols in the standard library. For example, Array (a struct that inherits 10 protocols), Bool (a struct that inherits 7 protocols), Comparable (a protocol that inherits from another protocol and is an inherited ancestor of many other Swift types), And Equatable, an inherited ancestor of many Swift protocols and types.
Take some time to browse the Swift standard library and follow the links to see all types, protocols, operators, global variables, functions. Be sure to look at the Inheritance section on almost any page and click on the VIEW PROTOCOL HIERARCHY -> link. You’ll see a lot of protocols, definitions of protocols, and diagrams of protocol inheritance relationships.
It’s important to remember that most of the code in the iOS (and OSX) SDK is implemented in a class inheritance hierarchy. I believe many of the core frameworks we use are still written in Objective-C (and some C++ and C), such as Fundation and UIKit. Let’s take UIbutton in UIKit. Using the inherit from link in the official Apple documentation page, we can look up the inheritance chain from the leaf UIButton to the root NSObject: UIButton to UIControl to UIView to UIResponder to NSObject. Can be graphically expressed as:
POP and OOP
The benefits of OOP have been discussed a lot by developers, so I’ll just list them here. For more details, see this detailed introduction I wrote about implementing OOP in Swift.
Note: If you don’t know OOP when you read this, I recommend that you learn OOP before you even think about learning POP.
The benefits of OOP include reusability, inheritance, maintainability, hiding (encapsulation) of complexity, abstraction, polymorphism, and control of access to a class’s properties and methods. I might be missing something here, because developers have already figured out too many advantages of OOP.
In short, OOP and POP share most of the above characteristics, with the main difference being that classes can only inherit from one other class, whereas protocols can inherit from multiple protocols.
Because of the nature of OOP inheritance, it is best to limit the inheritance relationship to single inheritance in development. Because multiple inheritance can make your code a mess very quickly.
However, protocols can inherit from one or more different protocols.
Why do we need to push protocol oriented programming? When we build a large class hierarchy, many properties and methods are inherited. Developers tend to add some generic functionality to the top-level — mostly to the top-level parent classes (and continue to do so). The responsibilities of the middle – and low-level classes are more explicit and specific. New functionality is put into the parent class, which often makes the parent class “polluted” or “bloated” with extra, extraneous responsibilities. The middle – and low-level classes inherit a lot of functionality that they don’t need.
These concerns about OOP are not written rules. A good developer can avoid many of the pitfalls mentioned above. It takes time, practice and experience. For example, developers can solve the problem of overloading parent classes by adding instances of other classes as member variables of the current class instead of inheriting those classes (that is, using composition instead of inheritance).
There is another benefit to using POP in Swift: not just classes, but value types can follow protocols, such as struct and enum. We discuss some of the advantages of using value types below.
But I do have some concerns about following the multi-protocol approach. Are we just shifting the complexity and difficulty of the code into another form? That is, shifting the “vertical” complexity of OOP inheritance to the “horizontal” complexity of POP inheritance?
Compare UIButton’s class inheritance structure shown earlier with the protocol that Array in Swift follows:
Image source: swiftdoc.org/v3.1/type/A…
Local reasoning doesn’t work in either case because there are too many individuals and relationships.
Value semantics vs. reference semantics
As I mentioned in my last article, Apple is heavily promoting the concepts related to POP and value semantics (they are also promoting another POP-related paradigm, which will be covered below). I showed you the code last time, and again I’ll use the code to explicitly show the difference between “reference semantics” and “value semantics.” See the ObjectThatFlies protocol in my article last week and List, FIFO, LIFO, and related protocols in today’s article.
Alex, an Apple engineer, says we “should use value types and protocols to make applications better.” A section of code documentation on Apple Sample Playground entitled “Understanding value types” says this:
Sequences and containers in the library use value types, which make writing code much easier. Each variable has a separate value, and references to that value are not shared. For example, when you pass an array to a function, the function does not cause the caller’s copy of the array to suddenly change.
This is of course true for all data types that use value semantics. I strongly encourage you to download and read the entire playground in its entirety.
I’m not throwing away the concept of classes using reference semantics. I can’t do it. I’ve written too much class-based code myself. I helped my clients organize millions of lines of class-based code. I agree that value types are generally safer than reference types. When I write new code, or refactor existing code, I consider actively experimenting in certain cases.
With reference semantics, class instances (references) lead to “unexpected data sharing.” Others call it an “unexpected change.” There are ways to minimize the side effects of reference semantics, but I’ll be using value semantics more and more.
Value semantics protect variables from unexpected changes, which is great. Because “each variable has a separate value and references to that value are not shared,” we can avoid the side effects of such unpredictable changes.
Because structs in Swift are a value type and protocol compliant, Apple is also pushing POP to replace OOP, and you can find the reasons behind this in protocol and value oriented programming.
Local reasoning
Let’s explore a great topic Apple calls Local Reasoning. This was introduced by an Apple engineer named Alex in WWDC 2016-Session 419, “Protocol and Value Oriented Programming in UIKit Applications.” It’s a concept that Both Apple and POP are pushing hard.
I don’t think it’s a new concept. Years ago, professors, colleagues, mentors, and developers all talked about this: Never write a function that is longer than a screen (i.e., no longer than a page, maybe less); Breaking up big functions into smaller ones; Breaking up large code files into smaller code files; Use meaningful variable names; Take the time to design code before you write it; Keep spacing and indentation consistent; Integrate related attributes and behaviors into classes and/or structures; Integrate related classes and/or structures into a framework or library. But Apple made it official when it explained POP. Alex tells us:
Local reasoning means that when you look at the code in front of you, you don’t have to think about how the rest of the code is going to interact with the function. Maybe you’ve felt this way before. For example, when you join a new team and have a lot of code to look at, and context information is scarce, can you understand what a particular function does? The ability to do Local Reasoning is important because it makes it easier to maintain, write, and test your code.
Ha-ha, have you ever felt that way? I’ve read some really good code written by other people. I’ve also written some code that is very readable. To be honest, in 30 years of working experience, the vast majority of existing code I would support and upgrade would not make me feel the way Alex describes. Instead, I often get very confused because when I look at a piece of code, I often have no clue what it does.
The source code for the Swift language is open source. Take a quick look at the following functions and don’t spend three hours trying to understand them:
public mutating func next(a) -> Any? {
if index + 1 > count {
index = 0
// Make sure that no member variables of self are captured
let enumeratedObject = enumerable
var localState = state
var localObjects = objects
(count, useObjectsBuffer) = withUnsafeMutablePointer(to: &localObjects) {
let buffer = AutoreleasingUnsafeMutablePointer<AnyObject? > ($0)
return withUnsafeMutablePointer(to: &localState) { (statePtr: UnsafeMutablePointer<NSFastEnumerationState>) - > (Int.Bool) in
let result = enumeratedObject.countByEnumerating(with: statePtr, objects: buffer, count: 16)
if statePtr.pointee.itemsPtr == buffer {
// Most Cocoa classes return their own internal pointer cache and do not use the default path to get the value. There are exceptions, such as NSDictionary and NSSet.
return (result, true)}else {
// Here is the usual case, such as NSArray.
return (result, false)
}
}
}
state = localState // Reset the value of state
objects = localObjects // Copy the object pointer back to self
if count= =0 { return nil}}defer { index += 1 }
if! useObjectsBuffer {returnstate.itemsPtr! [index] }else {
switch index {
case 0: return objects.0! .takeUnretainedValue()case 1: return objects.1! .takeUnretainedValue()case 2: return objects.2! .takeUnretainedValue()case 3: return objects.3! .takeUnretainedValue()case 4: return objects.4! .takeUnretainedValue()case 5: return objects.5! .takeUnretainedValue()case 6: return objects.6! .takeUnretainedValue()case 7: return objects.7! .takeUnretainedValue()case 8: return objects.8! .takeUnretainedValue()case 9: return objects.9! .takeUnretainedValue()case 10: return objects.10! .takeUnretainedValue()case 11: return objects.11! .takeUnretainedValue()case 12: return objects.12! .takeUnretainedValue()case 13: return objects.13! .takeUnretainedValue()case 14: return objects.14! .takeUnretainedValue()case 15: return objects.15! .takeUnretainedValue()default: fatalError("Access beyond storage buffer")}}}Copy the code
After you’ve gone through it, honestly, can you understand the code? I didn’t. I had to spend some time reading it over and over again and looking up code like function definitions. In my experience, this kind of code is ubiquitous and inevitably needs to be patched frequently.
Now, let’s consider understanding a Swift type (not a function). View the definition of Array in Swift. My God, it inherits 10 protocols:
BidirectionalCollection
Collection
CustomDebugStringConvertible
CustomReflectable
CustomStringConvertible
ExpressibleByArrayLiteral
MutableCollection
RandomAccessCollection
RangeReplaceableCollection
Sequence
Click the “VIEW PROTOCOL HIERARCHY ->” button at the bottom — oh, look at this spaghetti line!
Local Reasoning is much easier when you’re working on a new project and the team follows a set of best practices. Small code refactoring is also a good opportunity for local Reasoning. For me, like most things, refactoring code requires discretion and care, and moderation.
Keep in mind that you will almost always have to deal with very complex business logic, which, if written in code and understood fluently by a new team member, will require some training and coaching in business knowledge. He or she will most likely need to look up the definition of some function, class, structure, enumeration value, variable.
Agency and Agreement
The proxy mode is widely used in iOS, where a required component is the protocol. We don’t need to repeat it here. You can read my AppCoda blog on the subject.
Protocol type and protocol polymorphism
I’m not going to spend much time on these topics. I’ve talked a lot about protocols and shown you a lot of code. As a task, I’d like you to investigate for yourself the importance of the Swift protocol types (as in the proxy), the flexibility they can give us, and the polymorphism they demonstrate.
Protocol Types In my article on proxies, I defined an attribute:
var delegate: LogoDownloaderDelegate?
Copy the code
LogoDownloaderDelegate is a protocol. I then called a method of the protocol.
Protocol polymorphism Just as in object-oriented, we can interact with data types of multiple subprotocols that follow the same protocol family by following the data types of the parent protocol. Use code examples to illustrate:
protocol Top {
var protocolName: String { get}}protocol Middle: Top {}protocol Bottom: Middle {}struct TopStruct : Top {
var protocolName: String = "TopStruct"
}
struct MiddleStruct : Middle {
var protocolName: String = "MiddleStruct"
}
struct BottomStruct : Bottom {
var protocolName: String = "BottomStruct"
}
let top = TopStruct(a)let middle = MiddleStruct(a)let bottom = BottomStruct(a)var topStruct: Top
topStruct = bottom
print("\(topStruct)\n")
BottomStruct(protocolName: "BottomStruct")"
topStruct = middle
print("\(topStruct)\n")
(protocolName: "MiddleStruct")"
topStruct = top
print("\(topStruct)\n")
// Output "TopStruct(protocolName: "TopStruct")"
let protocolStructs:[Top] = [top,middle,bottom]
for protocolStruct in protocolStructs {
print("\(protocolStruct)\n")}Copy the code
If you run the code in Playground, this is the output from the terminal:
BottomStruct(protocolName: "BottomStruct")
MiddleStruct(protocolName: "MiddleStruct")
TopStruct(protocolName: "TopStruct")
TopStruct(protocolName: "TopStruct")
MiddleStruct(protocolName: "MiddleStruct")
BottomStruct(protocolName: "BottomStruct")
Copy the code
Protocol in a real UIKit application
Now, let’s get down to the nitty gritty and write some Swift 4 code — code that I actually use in my own application. This code should get you started thinking about building and/or extending your code with protocols. This is what I’ve been describing in these two articles, “protocol-oriented programming,” or POP.
I chose to show you how to extend or extend (whatever you want to call it) UIKit classes because 1) you’re probably pretty used to them and 2) extending classes in the iOS SDK like UIView is a little more difficult than using your own classes.
All UIView extensions are written using the Single View App template in the Xcode 9 project.
I use the default protocol extension to extend UIView — the key to doing so is a practice Apple calls “conditional compliance” (also see here). Since I only want to extend UIView, we can have the compiler make this mandatory.
I often use UIView as a container to organize other UI elements on the screen. Sometimes I use these container views to better see, feel, and arrange my UI views.
Here is a GIF showing the result of customizing the appearance of UIView using the three protocols created below:
Note that I also abide by the Local Reasoning principle. Each of my protocol-based functions is controlled in one screen. I want you to read each one, because they don’t have a lot of code, but they work.
Add a default border to UIView
Suppose I want to have many INSTANCES of UIView with the same border — for example, in an application that supports a color theme. An example of this is the green view at the top of the image above.
protocol SimpleViewWithBorder {}
// Safe: the "addBorder" method is only added to instances of UIView.
extension SimpleViewWithBorder where Self : UIView {
func addBorder(a) -> Void {
layer.borderColor = UIColor.green.cgColor
layer.borderWidth = 10.0}}class SimpleUIViewWithBorder : UIView.SimpleViewWithBorder {}Copy the code
To create, configure, and display an instance of SimpleUIViewWithBorder, I wrote the following code in IBAction of my ViewController subclass:
@IBAction func addViewButtonTapped(_ sender: Any) {
let customFrame0 = CGRect(x: 110, y: 100, width: 100, height: 100)
let customView0 = SimpleUIViewWithBorder(frame: customFrame0)
customView0.addBorder()
self.view.addSubview(customView0)
Copy the code
I don’t need to create a special initialization method for this UIView subclass.
Add a default background color to UIView
Let’s say I want many instances of UIView to have the same background color. An example of this is the blue view in the middle of the image above. Notice that I’m one step closer to a configurable UIView.
protocol ViewWithBackground {
var customBackgroundColor: UIColor { get}}extension ViewWithBackground where Self : UIView {
func addBackgroundColor(a) -> Void {
backgroundColor = customBackgroundColor
}
}
class UIViewWithBackground : UIView.ViewWithBackground {
let customBackgroundColor: UIColor = .blue
}
Copy the code
To create, configure, and display an instance of UIViewWithBackground, I wrote the following code in my ViewController subclass IBAction:
let customFrame1 = CGRect(x: 110, y: 210, width: 100, height: 100)
let customView1 = UIViewWithBackground(frame: customFrame1)
customView1.addBackgroundColor()
self.view.addSubview(customView1)
Copy the code
I don’t need to create a special initialization method for this UIView subclass.
Add a configurable border color to UIView
Now, I want to be able to configure the color and width of the UIView border. With the following implementation code, I can create views with different border colors and widths. An example of this is the red view at the bottom of the image above. Adding configurable properties to my protocol was a little costly, and I needed to be able to initialize those properties, so I added an init method to my protocol. That means I can also call UIView’s initialization method. After reading the code, you will understand:
protocol ViewWithBorder {
var borderColor: UIColor { get }
var borderThickness: CGFloat { get }
init(borderColor: UIColor, borderThickness: CGFloat, frame: CGRect)}extension ViewWithBorder where Self : UIView {
func addBorder(a) -> Void {
layer.borderColor = borderColor.cgColor
layer.borderWidth = borderThickness
}
}
class UIViewWithBorder : UIView.ViewWithBorder {
let borderColor: UIColor
let borderThickness: CGFloat
// the required UIView initialization method
required init(borderColor: UIColor, borderThickness: CGFloat, frame: CGRect) {
self.borderColor = borderColor
self.borderThickness = borderThickness
super.init(frame: frame)
}
// the required UIView initialization method
required init? (coder aDecoder:NSCoder) {
fatalError("init(coder:) has not been implemented")}}Copy the code
To create, configure, and display an instance of UIViewWithBorder, I wrote the following code in IBAction of my ViewController subclass:
let customFrame2 = CGRect(x: 110, y: 320, width: 100, height: 100)
let customView2 = UIViewWithBorder(borderColor: .red, borderThickness: 10.0, frame: customFrame2)
customView2.addBorder()
self.view.addSubview(customView2)
Copy the code
Something I don’t want to do
I don’t want to create code like this:
extension UIView {
func addBorder(a){... }func addBackgroundColor(a){... }}Copy the code
This may work in some cases, but I feel that this implementation is too broad and loses a lot of control over fineness. This implementation also tends to make the constructor a garbage dump for UIView-related extension methods, in other words, the code tends to become bloated. As more methods proliferate, the code becomes harder to read and maintain.
In all of the above UIKit-based protocols, I used a subclass of UIView called reference types. Subclassing gives me direct access to anything in the parent UIView class, making my code clear, short, and easy to read. If I’m using a struct, my code gets a lot wordier, but I’ll leave that as an exercise for you.
The things I do
Keep in mind that all of these default protocol extensions can be overridden in class extensions. Use an example and picture to illustrate:
protocol SimpleViewWithBorder {}
extension SimpleViewWithBorder where Self : UIView {
func addBorder(a) -> Void {
layer.borderColor = UIColor.green.cgColor
layer.borderWidth = 10.0}}class SimpleUIViewWithBorder : UIView.SimpleViewWithBorder {
// Override the default implementation in Extension
func addBorder(a) -> Void {
layer.borderColor = UIColor.darkGray.cgColor
layer.borderWidth = 20.0}}Copy the code
Notice my comment in SimpleUIViewWithBorder. Take a look at the top view below:
Real, protocol-based generic data structures
I am very proud that in my own application, I was able to write as little POP code as possible to create fully functional generic stack and queue data structures. To learn more about generics in Swift, read my article in AppCoda.
Note that I used protocol inheritance to help me create more concrete FIFO and LIFO protocols using the abstract List protocol. I then use protocol extensions to implement the Queue and Stack value types. You can see examples of these structs in Xcode 9 Playground below.
What I want to show you is how to implement your own custom protocols through other protocols like Apple suggested, so I’ve created ListSubscript, Listprintforward, ListPrintBackwards, ListCount protocols. They are simple now, but will be useful in a practical application.
This practice of inheriting multiple protocols allows developers to add new functionality to existing code without “contaminating” or “bloating” the code with too many additional, unrelated functions. Each of these agreements is independent. If added as classes above the middle of the inheritance hierarchy, these functions will be automatically inherited by at least some other classes, depending on where they are.
I’ve said enough about POP to help you read and understand the code. One more reminder of how I made my data structures generic-friendly: the definition of the associated type.
When defining a protocol, one or more association types can sometimes be declared as part of the protocol definition. An association type provides a placeholder name to represent a type in the protocol. The true data type of this association type is not determined until the protocol is used. Use the associatedType keyword to specify an association type.
The code is as follows:
protocol ListSubscript {
associatedtype AnyType
var elements : [AnyType] { get}}extension ListSubscript {
subscript(i: Int) - >Any {
return elements[i]
}
}
protocol ListPrintForwards {
associatedtype AnyType
var elements : [AnyType] { get}}extension ListPrintForwards {
func showList(a) {
if elements.count > 0 {
var line = ""
var index = 1
for element in elements {
line += "\(element) "
index += 1
}
print("\(line)\n")}else {
print("EMPTY\n")}}}protocol ListPrintBackward {
associatedtype AnyType
var elements : [AnyType] { get}}extension ListPrintBackwards {
func showList(a) {
if elements.count > 0 {
var line = ""
var index = 1
for element in elements.reversed() {
line += "\(element) "
index += 1
}
print("\(line)\n")}else {
print("EMPTY\n")}}}protocol ListCount {
associatedtype AnyType
var elements : [AnyType] { get}}extension ListCount {
func count(a) -> Int {
return elements.count}}protocol List {
associatedtype AnyType
var elements : [AnyType] { get set }
mutating func remove(a) -> AnyType
mutating func add(_ element: AnyType)
}
extension List {
mutating func add(_ element: AnyType) {
elements.append(element)
}
}
protocol FIFO : List.ListCount.ListPrintForwards.ListSubscript {}extension FIFO {
mutating func remove(a) -> AnyType {
if elements.count > 0 {
return elements.removeFirst()
} else {
return "******EMPTY******" as! AnyType}}}struct Queue<AnyType> :FIFO {
var elements: [AnyType] = []}var queue = Queue<Any>()
queue.add("Bob")
queue.showList()
queue.add(1)
queue.showList()
queue.add(3.0)
_ = queue[0] // This subscript outputs "Bob"
_ = queue.count()
queue.showList()
queue.remove()
queue.showList()
queue.remove()
queue.showList()
queue.remove()
queue.showList()
_ = queue.count(a)protocol LIFO : List.ListCount.ListPrintBackwards.ListSubscript {}extension LIFO {
mutating func remove(a) -> AnyType {
if elements.count > 0 {
return elements.removeLast()
} else {
return "******EMPTY******" as! AnyType}}}struct Stack<AnyType> :LIFO {
var elements: [AnyType] = []}var stack = Stack<Any>()
stack.add("Bob")
stack.showList()
stack.add(1)
stack.showList()
stack.add(3.0)
_ = stack[0] // The subscript outputs 3
_ = stack.count()
stack.showList()
stack.remove()
stack.showList()
stack.remove()
stack.showList()
stack.remove()
stack.showList()
_ = stack.count(a)Copy the code
This code snippet is printed on the console as follows:
Bob Bob 1 Bob 1 3.0 3.0 EMPTY Bob 1 Bob 3.0 1 Bob 1 Bob Bob EMPTYCopy the code
I don’t use POP 100%
In one of the WWDC videos on POP, an engineer/instructor says, “In Swift we have a saying, don’t start with a class, start with a protocol.” Well, maybe. This guy starts a lengthy discussion about how to use the protocol to write a binary lookup. I’m a little skeptical that this is what many of my readers remember most. Did you lose sleep over it?
It’s kind of a contrived problem in search of a POP solution. Maybe the problem is real, maybe the solution has merit, I don’t know. My time is too precious to waste on ivory tower theory. If it took me more than five minutes to read a piece of code, IT violated Apple’s local Reasoning principle.
If you’re a software developer like me, it’s best to keep an open mind to new methodologies and keep complexity control your main focus. I’m in no way against making money, but it’s good to think bigger and bigger. Remember, Apple is a company, a big company whose main mission is to make a lot of money, with a market cap of nearly $837 billion on Friday and hundreds of billions of dollars in cash and cash equivalents. They want everyone to use Swift, and one of the ways these companies attract people to their ecosystems is by offering products and services that no one else can. Yes, Swift is open source, but Apple makes a lot of money from the App Store, so apps are the key to making all Apple devices useful, and many developers are migrating to Swift.
I don’t see any reason to program only with POP. I think POP, like many other technologies I use, even OOP, has some problems. We’re modeling reality, or at least we’re fitting reality. There is no perfect solution. So use POP as a tool in your development toolbox, just like other great solutions that people have come up with over the years.
conclusion
After 30 years of development experience, I can say with a little peace of mind that ** you should understand protocols and POP. ** Start designing and writing your own POP code.
I’ve spent a lot of time experimenting with POP and have already used the protocols in this article in my own applications, such as SimpleViewWithBorder, ViewWithBackground, ViewWithBorder, List, FIFO, LIFO. POP is powerful.
As I mentioned in my previous article, learning and embracing a new method, like POP, is not a right or wrong thing. POP and OOP not only coexist, they complement each other.
So experiment, practice, and learn. Finally, enjoy life and work to the fullest.
This article is translated by SwiftGG translation team and has been authorized to be translated by the authors. Please visit swift.gg for the latest articles.