Showing posts with label design. Show all posts
Showing posts with label design. Show all posts

Tuesday, May 22, 2018

Swift guidelines to follow

I started using Swift in my projects in 2015 and, as any Swift newbie, I was surprised by the lack of code style guidelines. Here I will share my experience of 3 years and point guidelines that worth following in different aspects of development.

Apple Swift API Design Guidelines provide great rules for naming. I also recommend to use Objective-C style prefixes for public extensions to avoid possible conflicts: /// MARK: Module XXX extension UIImageView { func xxx_loadImageFromURL(_ url: URL) { // ... } } // ... customImageView.xxx_loadImageFromURL(url)
Built-in Xcode formatter (^I) closes most questions with indentation. Just accept, that sometimes it can produce strange results, like this:
URLSession.shared.dataTask(with: apiURL) { (data, _, err) in }.resume() instead of this:
URLSession.shared.dataTask(with: apiURL) { (data, _, err) in }.resume()
Considering newlines, I recommend to follow good old Google Objective-C Style Guide, which is stating: "Use vertical whitespace sparingly."

All the rest is covered by The Official Guide. This guide is consistent and detailed, yet simple. It's widely adopted in different projects and tutorials, so it's well tested and recognizable. Note, that the guide is optimized for tutorials and sample code, which is good, because it's easy to read. But you can ignore some rules, like Minimal Imports or Use Type Inferred Context, because they don't improve reading but can slow down the writer.

While writing this post I found another style guide from LinkedIn. I don't recommend to follow it. Some rules listed there are obvious, some duplicate Xcode warnings, and some rules are disputable. The guide is way too strict, too large to remember, and thus it will be hard to adopt in your team.

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() } } }

Saturday, February 3, 2018

Changing view's type on the Storyboard

Suppose the design of your app changed, and now you need to replace the Table View with a Collection View keeping all the constraints along with views hierarchy.
Drop a new View Controller on the storyboard and put a Collection View inside. Rename it in the left panel (called Document Outline). Name it "The Replacement" so you will be able recognize it later. Now switch to the Source Code through the Open As.
Now the storyboard looks like XML markup. Find the Table View you need to replace, it will be scoped within the tableView tag.

Copy its id (example: id="8Oa-xL-ums"). It points to the view in constraints and outlets. Now find the new Collection View under the collectionView tag.

Copy everything including the enclosing tags: <collectionView ...</collectionView> and paste it instead of the tableView. Replace Collection View's id with the value you copied from the Table View. Before switching back to Interface Builder you need to clean up the storyboard. Remove the scene of the temporary View Controller that you created at the beginning of the tutorial. Delete everything between tags and the preceding comment: <!--View Controller><scene ...</scene>. If you don't do this you will have an error because of duplicated id.

Now you will have the new Collection View constrained like the Table View but misplaced, because it's was copied with the rect.
Click Update Frames and enjoy the result!

Here is the list of storyboard tags for different UI classes. Who knows, may be it will be useful.

arscnView ARSCNView
arskView ARSKView
glkView GLKView
mapView MKMapView
mtkView MTKView
sceneKitView SCNView
skView SKView
activityIndicatorView UIActivityIndicatorView
button UIButton
collectionView UICollectionView
datePicker UIDatePicker
imageView UIImageView
label UILabel
navigationBar UINavigationBar
pageControl UIPageControl
pickerView UIPickerView
progressView UIProgressView
scrollView UIScrollView
searchBar UISearchBar
segmentedControl UISegmentedControl
slider UISlider
stackView UIStackView
stepper UIStepper
switch UISwitch
tabBar UITabBar
tableView UITableView
textField UITextField
textView UITextView
toolbar UIToolbar
view UIView
containerView UIView (with embedded View Controller)
visualEffectView UIVisualEffectView
webView UIWebView
wkWebView WKWebView

How to Record Calls on iPhone