Showing posts with label interface. Show all posts
Showing posts with label interface. Show all posts

Tuesday, February 20, 2018

Power of Extensions

Article is valid for Swift 4.0.

In this article I want to share my practical and emotional experience of using Swift extensions. I’m really impressed how powerful they become in comparison with old Objective-C categories.

To create a category in Objective-C you must create 2 files (.h and .m) and support both private and public interfaces. Categories are unsafe: compiler allows you to redefine any method in a class/subclass/superclass and according to Docs (Avoid Category Method Name Clashes paragraph) the behavior is undefined. Actually, it depends on the order of files in Compile Sources list. Also, the one moment is not so obvious, but very important: each Objective-C category should have a name. Why that's an issue? – you may ask. – Because the name usually corresponds to the category features so for each new feature you need a new category, that means 2 files. Such operations eat time, thats why.

Now compare it to Swift. Compiled extensions in swift are much safer and predictable. Try to redefine a method or variable – you will get an `Invalid redeclaration` compiler error. Swift extension doesn't need a name and to create it you usually don't need a separate file. Thanks to this I'm shooting extensions like from a machine gun.

Interesting, that Objective-C also has its own extensions which are just categories without a name. But such anonymous categories can be defined only in the .m file and therefore are limited.

Here I put some common extension applications:
  1. Code grouping. Implementing protocols in the separate blocks: // MARK: UITableViewDataSource extension ScheduleViewController: UITableViewDataSource { // ... } (Though protocol methods implemented in extensions couldn’t be overridden yet.)

    Thematic or logical scopes:
    // MARK: - PRIVATE private extension AppDelegate { func makeRootViewController() -> UIViewController { // ... } }
  2. New way to define constants:
    extension Int { static let maxLocalNotificationsCount = 64 } extension TimeInterval { static let secondsInMinute: TimeInterval = 60 static let secondsInHour: TimeInterval = 60 * .secondsInMinute }
    Usage: request.timeoutInterval = 2 * .secondsInMinute

  3. Default implementation. A powerful feature since Swift 3. protocol OffsetListParserProtocol: class { // ... func loadNext(completion: LoadHandler?) } extension OffsetListParserProtocol { func loadNext(completion: LoadHandler?) { guard !isFinished && !isLoading else { completion?(nil, nil) return } // Start loading... } }
    BTW, it's the only way to create a common implementation for struct's because they cannot inherit.

  4. And of course extending the abilities of existing system, third party or your own types: public extension UIViewController { func firstAncestorOfType(_ type: T.Type) -> T? where T: UIViewController { guard let someParent = parent else { return nil } if let requiredParent = someParent as? T { return requiredParent } else { return someParent.firstAncestorOfType(type) } } }

Handy extensions that we create from time to time can be aggregated in git submodules or Swift frameworks, allowing to share improvements among a whole bunch of projects, greatly rising the speed of development. Instead of copypasting the code snippets you just create extensions (less often types or global functions). Like this:
// Example from my SwiftEssentials framework public extension UIColor { var hexString: String { var r: CGFloat = 0 var g: CGFloat = 0 var b: CGFloat = 0 getRed(&r, green: &g, blue: &b, alpha: nil) let intR = Int(round(r * 255)) let intG = Int(round(g * 255)) let intB = Int(round(b * 255)) return String(format: "#%02lX%02lX%02lX", intR, intG, intB) } convenience init(Hº: CGFloat, S%: CGFloat, B%: CGFloat, A: CGFloat) { self.init(hue: Hº / 360, saturation: S% / 100, brightness: B% / 100, alpha: A) } convenience init(hex: UInt32, alpha: CGFloat) { self.init( red: CGFloat((hex & 0xFF0000) >> 16) / 255, green: CGFloat((hex & 0x00FF00) >> 8) / 255, blue: CGFloat(hex & 0x0000FF) / 255, alpha: alpha ) } }
I’m very excited about new possibilities that Swift provides. I have learned Swift since the origin, see how it grows and I don't stop to notice how fast, convenient and productive this language is. Due to extension's I don’t bother if I see any imperfections in the system libraries anymore. I just fix them and forget :)
public extension UIView { func removeSubviews() { for subview in subviews { subview.removeFromSuperview() } } }

How to Record Calls on iPhone