Showing posts with label must use. Show all posts
Showing posts with label must use. Show all posts

Thursday, October 18, 2018

UIKit protocol of the month: StoryboardInstantiatableViewController

Github Gist.

I want to share this little invention with the community. I use it for the last month and find it notably helpful: protocol StoryboardInstantiatableViewController { static var storyboardId: String { get } } extension StoryboardInstantiatableViewController where Self: UIViewController { static func instantiate(from storyboard: UIStoryboard?) -> Self? { return storyboard?.instantiateViewController(withIdentifier: Self.storyboardId) as? Self } }
This protocol along with the extension provides some syntactic sugar and solves two common problems:
  • Shortens the instantiation
  • Answers, where to store the storyboard id

Usage example:

// Declaration: class TaskViewController: UIViewController, StoryboardInstantiatableViewController { // The same id as specified in Identity inspector static let storyboardId: String = "task" // ... } // Initializing from Storyboard: // ... guard let taskVC = TaskViewController.instantiate(from: storyboard) else { return } navigationController?.pushViewController(taskVC, animated: true)

Update:

Surprisingly, I bumped into another similar protocol, named StoryboardInstantiatable. The main difference that it's designed for use with NSViewController and Cocoa instead of Cocoa Touch. The fact is, that the gist was posted 5 months earlier than this blog post. So Satori Maru is the first one who invented the idea, although I came to it independently.

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 raywenderlich.com 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.

Saturday, January 27, 2018

Catching nil as Error

Github gist.

I've posted this as an answer on stackoverflow before, and the information was highly appreciated by some people. The question was like: “How to catch any error, specially unexpectedly found nil in Swift?" Though the author was asking about catching system errors, I decided to post my super-handy Unwrap Snippet there.
struct UnwrapError: Error, CustomStringConvertible { let optional: T? public var description: String { return "Found nil while unwrapping \(String(describing: optional))!" } } public func unwrap(_ optional: T?) throws -> T { if let real = optional { return real } else { throw UnwrapError(optional: optional) } }
The idea of this approach is to replace multiple if let and guard let statements with a single do-try-catch block. You can:
do { // Parse JSON and assign variables which were defined somewhere above let dictionary = try unwrap(JSONSerialization.jsonObject( with: data, options: .allowFragments) as? [String: Any]) isAdsEnabled = try unwrap(dictionary["isAdsEnabled"] as? Bool) // While calling function which require a non-optional parameter in one line of code imageView.image = try UIImage(data: unwrap(receivedData)) // And also you can simplify the building of multipart data var data = Data() data += try unwrap(dispositionString.data(using: .utf8)) data += someContent data += try unwrap("\r\n".data(using: .utf8)) } catch error { // Handle error // ... // and exit return nil }
Those who make apps with web backend will definitely like.

How to Record Calls on iPhone