Very interesting article, but I have a few questions about the necessity of changing the structure of your code to accommodate edge cases. My question is specific to your example of birds, and may in fact be invalid with another example, so be mindful of that.
In your example, you found that it would be significantly more changes using OOP, to correctly design a “Penguin” class that is a child of the class “Bird” because they can’t fly and don’t have feathers. I found this assessment to be incorrect, you could actually leave the Bird class alone. Since the two arguments that the Bird class’ init method takes are String object for its feather data, I would simple pass in null, and use that to indicate the lack of feathers. Furthermore, I would just leave the fly method unimplemented since Penguins have wings/flippers but they cannot fly (at least above waters, because their wings allow them maneuver through water the same way a fowl does in the air). This makes since (at least to me it does) because Penguins are birds, and birds fly (general rule), and penguins are the exception to that rule.
It seems to me that you are making significant changes to your code, specifically the class structure, and losing a bit of the clarity just to match edges cases. More birds are able to fly than there are birds that cannot fly. Plus, splitting the flying attributes out of the Bird class doesn’t allow you to generically to iterate over a collection of every bird and perform a specific action based on the ability of flight, at least not without Runtime type checking (this may not be a huge factor in Swift).
Another solution would be to add a boolean flag to the Bird class that simple indicates if flight is possible.
Lastly, the design your introduced to demonstrate POP, can easily be done with with OOP. I’m not familiar with Swift, because I have never used the language, but in Java there are several classes that inherit from several interfaces to provide/ or at least indicate the presence of certain functionality. An example would be the two collection classes, ArrayList and LinkedList. They both inherit from the interface List to provide common methods that are expected of a collection class such as get() and add(). But ArrayList, unlike LinkedList also inherits from the interface, RandomAccess. The RandomAccess interface indicates that the elements inside of the collection can be accessed in any order.