So, you’ve been around with Swift for a couple of months. Now, you want to become a better Swift Developer?
Improve the Readability of Constants
import Foundation
struct Constants {
struct FoursquareApi {
static let BaseUrl = "https://api.foursquare.com/v2/"
}
struct TwitterApi {
static let BaseUrl = "https://api.twitter.com/1.1/"
}
struct Configuration {
static let UseWorkaround = true
}
}
we could use Constants.FoursquareApi.BaseUrl to access Foursquare’s BaseUrl constant.
Avoid NSObject and @objc to Improve Performance
Swift allows us to extend classes from NSObject to get Objective-C runtime features for an object. It also allows us to annotate Swift methods with @objc to allow the methods to be used by the Objective-C runtime.
Supporting the Objective-C runtime means that method calls are going to be using dynamic dispatch instead of static or vtable dispatch. This end result is that methods that support the Objective-C runtime have a four times performance penalty when called. In practical usage, the performance hit may be negligible but the cool thing is that armed with this knowledge, we now know that method execution in Swift is four times faster than Objective-C.
Use Method Swizzling in Swift
Method swizzling is a technique that substitutes one method implementation for another.
So by default swizzling doesn’t work in Swift classes unless we:
1Disable this optimization with the dynamic keyword.
2Extend NSObject.
3Use the @objc annotation on the method being swizzled.
Clean Up Asynchronous Code
Swift has a neat syntax for writing completion functions.
[self loginViaHttpWithRequest:request completionBlockWithSuccess:^(LoginOperation *operation, id responseObject) {
[self showMainScreen];
} failure:^(LoginOperation *operation, NSError *error) {
[self showFailedLogin];
}];
loginViaHttp(request) { response in
if response.success {
showMainScreen()
} else {
showFailedLogin()
}
}
Control Access to Our Code
We should always be using the appropriate access control modifier to encapsulate code. Good encapsulation helps us understand how pieces of code interact without having to remember our thought process
Swift has common access control mechanisms of private, internal, and public, but Swift doesn’t have the protected access control modifier that is common in other Object-oriented programming languages.
Experiment and Validate with Playgrounds
A playground is an interactive coding environment for Swift. We can create playgrounds to test and validate ideas, learn Swift, and share concepts with each other. This can all be done without needing to create a new project.
Use Optionals Safely
An optional property is a property that can have a valid value or have no value (nil). We can implicitly unwrap an optional by following the optional’s name with an exclamation point, as in optionalProperty!. This is generally something you want to avoid, as hinted at by the exclamation point meaning “danger!”
Leave NSNumber Behind
Objective-C used C primitives for numbers, and the Foundation Objective-C API provided the NSNumber type to box and unbox primitives. We had code that looked like [array addObject:@(intPrimitive)] and [array[0] intValue] when we needed to go back and forth between primitives and object types. Swift doesn’t have this awkward mechanism. Instead, we can actually add Int / Float / AnyObject values to Swift dictionaries and arrays.
Here are the most common Swift types used in place of NSNumber:
Swift: Objective-C
Int: [NSNumber integerValue]
UInt: [NSNumber unsignedIntegerValue]
Float: [NSNumber floatValue]
Bool: [NSNumber boolValue]
Double: [NSNumber doubleValue]
Reduce Boilerplate Code with Default Arguments
With Swift, function arguments can now have default values. These default arguments reduce clutter. If the callee of the function chooses to use the default values, the function call is shorter because the default arguments can be omitted. For example:
func printAlertWithMessage(message: String, title: String = "title") {
print("Alert: \(title) | \(message)")
}
printAlertWithMessage("message") // prints: Alert: title | message
printAlertWithMessage("message", title: "non-default title") // prints: Alert: non-default title | messagex
Validate Methods with Guard
Swift’s guard statement helps clean up code and make it safer. A guard statement checks one or more conditions and if any of the conditions are not met an else block is called. The else block requires a return, break, continue, or throw statement that ends the method’s execution, or in other words, transfers program control out of scope.
class ProjectManager {
func increaseProductivityOfDeveloper(developer: Developer) {
guard let developerName = developer.name else {
print("Papers, please!")
return
}
let slackMessage = SlackMessage(message: "\(developerName) is a great iOS Developer!")
slackMessage.send()
}
}
Manage Program Control Flow with Defer
A defer statement defers execution of the code contained in its block until the end of the current scope. What this means is that cleanup logic can be placed inside a defer statement, and it is guaranteed to be called when the current function leaves scope. This can help reduce redundant steps, and more importantly, increase safety.
func deferExample() {
defer {
print("Leaving scope, time to cleanup!")
}
print("Performing some operation...")
}
// Prints:
// Performing some operation...
// Leaving scope, time to cleanup!
Simplify Singletons
The implementation of a singleton in Objective-C involves several steps to ensure that the singleton class cannot be created more than once. Swift greatly simplifies this.
Objective-C:
@implementation MySingletonClass
+(id)sharedInstance {
static MySingletonClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
Swift:
class MySingletonClass {
static let sharedInstance = MySingletonClass()
private init() {
}
}
Reduce Duplicate Code with Protocol Extensions
We can extend existing types with categories in Objective-C, but the same affordance isn’t given to protocols. Swift allows us to add functionality to a protocol. With Swift we can extend a single protocol (even ones in the standard library!), and have it apply to any class that implements the protocol.
Create Global Helper Functions
Global variables and functions are often conflated as a “bad thing,” but the truth is that both can help us make clean code. What’s truly a bad thing is global state. Global functions often need global state to do their work, so it’s easy to see why they get such a bad rap.
import Foundation
/**
Executes the closure on the main queue after a set amount of seconds.
– parameter delay: Delay in seconds
– parameter closure: Code to execute after delay
*/
func delayOnMainQueue(delay: Double, closure: ()->()) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))), dispatch_get_main_queue(), closure)
}
/**
Executes the closure on a background queue after a set amount of seconds.
– parameter delay: Delay in seconds
– parameter closure: Code to execute after delay
*/
func delayOnBackgroundQueue(delay: Double, closure: ()->()) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))), dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), closure)
}
Here’s an example of how our new wrapper function looks:
delayOnBackgroundQueue(5) {
showView()
}
Here’s an example of how the unwrapped function looks:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))), dispatch_get_global_queue(QOS_CLASS_UTILITY, 0)) {
showView()
}
Discover more from mycodetips
Subscribe to get the latest posts sent to your email.