Emerging
Best Practices
in Swift
Ash Furrow
Afraid
I was
Everything turned out
Fine
Best Practices in Swift
What do they look like?
Also, how do we find new ones?
Agenda
Weve been here before
Learning is forever, deal with it
Never throw ideas away
How to force yourself to think
Always be abstracting
Lets go!
This looks strangely familiar
Those who dont study history
are doomed to repeat it.
Lots of people, for hundreds of years
Wow, thats depressing.
Those who dont understand the past
cant make informed decisions about the present.
Me, today
iOS 5 or earlier?
Before Object Literals
NSArray *array = [NSArray arrayWithObjects:
@"This",
@"is",
@"so",
@"tedious", nil];
NSDictionary *dictionary = [NSDictionary
dictionaryWithObjectsAndKeys:
@"Who would do this?", @"Not me", nil];
NSNumber *number = [NSNumber numberWithInt:401];
Before Object Literals and ARC
NSArray *array = [[NSArray arrayWithObjects:
@"This",
@"is",
@"so",
@"tedious", nil] alloc];
NSDictionary *dictionary = [[NSDictionary
dictionaryWithObjectsAndKeys:
@"Who would do this?", @"Not me", nil] alloc];
NSNumber *number = [[NSNumber numberWithInt:401] alloc];
After Object Literals
NSArray *array =
@[ @"This", @"is", @"much", @"better" ];
NSDictionary *dictionary =
@{ @"Who likes this?": @"Me!" };
NSNumber *number = @(401);
Object Literals
Clearly way better
Adopted by everyone almost immediately
Became a best practice
^{
Blocks & GCD
Introduced in iOS 4
Adopted slowly, but surely
Required new ways of thinking
Did using blocks became a best practice?
Sort of
Blocks
Enable
other best practices
Contextual Code Execution
^{
Functional Reactive Programming
Futures
Inline Network Operations
Generic Datasource Objects
Callbacks
Deferred Customization Promises
Collections Operations
Change
Embrace
Swift 2
Swift 2
Lots of new syntax
New syntax lets us do new things
However! Syntax is only a tool
Like blocks, Swift 2 syntax is most useful when it enables new ideas
Swift 2
guard
defer
throws
etc
Should I use guard?
What can I do with guard?
Examples
Pyramid of Doom
if let thing = optionalThing {
if thing.shouldDoThing {
if let otherThing = thing.otherThing {
doStuffWithThing(otherThing)
}
}
}
Clause Applause
if let thing = optionalThing,
let otherThing = thing.otherThing
where thing.shoudDoThing {
doStuffWithThing(otherThing)
}
Avoid Mutability
func strings(
parameter: [String],
startingWith prefix: String) -> [String] {
var mutableArray = [String]()
for string in parameter {
if string.hasPrefix(prefix) {
mutableArray.append(string)
}
_
}
return mutableArray
}
Avoid Mutability
func strings(
parameter: [String],
startingWith prefix: String) -> [String] {
return parameter.filter { $0.hasPrefix(prefix) }
}
Currying
One of those weird words you avoid because people who say it are sometimes jerks
Its actually a pretty straightforward concept
Currying is a function that returns another function
Useful for sharing code thats mostly the same
Before Currying
func containsAtSign(string: String) -> Bool {
return string.characters.contains("@")
}
...
input.filter(containsAtSign)
Currying
func contains(substring: String) -> (String -> Bool) {
return { string -> Bool in
return string.characters.contains(substring)
}
}
...
input.filter(contains("@"))
Currying
func contains(substring: String)(string: String) -> Bool {
return string.characters.contains(substring)
}
...
input.filter(contains("@"))
Extract Associated Values
Use Swift enums
Attach associated values
Extract using a case
Extract Associated Values
enum Result {
case Success
case Failure(reason: String)
}
switch doThing() {
case .Success:
print("")
case .Failure(let reason):
print("Oops: \(reason)")
}
Extract Associated Values
enum Result {
case Success
case Failure(reason: String)
}
if case .Failure(let reason) = doThing() {
print(" \(reason)")
}
Thats all just
Syntax
What matters are
Ideas
Protocol-Oriented
Programming
just go watch the WWDC video.
Others
Lets ask
Syntax vs Idea
How to tell if something is universally a good idea, or just enables other ideas?
You cant
Its a false dichotomy
I lied to you
Im so sorry
Youve just got to
Try stuff
Never throw away
Ideas
Never Throw Away Ideas
Swift was released
We treated Swift like object literals instead of like blocks
Some of us thought Swift was universally better
My fault, oops
Merit
Older ideas have
iOS developers throw things away
A lot
Why?
Beginner learns thing
Is bad at thing
Blames thing
Thing must be bad
Beginner gets more experience
New thing comes out
Learning new thing is easier than old thing
New thing must be good
New ideas are
Appealing
iOS is constantly changing
Always a fresh supply of old APIs
for us to blame
Refactoring
Lets talk about
What is Not Refactor?
Refactoring does not add new functionality
Refactoring does not change a types interface
Refactoring does not change a types behaviour
Changing a unit test?
No Yes
Refactoring Rewriting
Rewrites are
Bad
Favour incremental change
Code isnt necessarily valuable
But throwing it away is dangerous
&
Things to never throw away:
Code
Ideas
Change
Unit tests will help
Unit Testing & Thinking
So, uhh, unit testing
Controversial in iOS
Not so much everywhere else
Why?
Well get to that
Benefits of Testing
(Lets presume that unit testing is a good idea)
I really dont care that much about the tests
I care more about how writing tests makes me think about what Im writing
Benefits of Testing
Limited object scope is good
High cohesion, low coupling
How to limit scope?
Controlling public interface and dependencies
Dependency injection?
Dependency Injection
5 word for a 5 idea
Your things shouldnt create the things they need
Example
Without Dependency Injection
class ViewController: UIViewController {
let networkController = NetworkController()
func viewDidLoad() {
super.viewDidLoad()
networkController.fetchStuff {
self.showStuff()
}
}
}
With Dependency Injection
class ViewController: UIViewController {
var networkController: NetworkController?
func viewDidLoad() {
super.viewDidLoad()
networkController?.fetchStuff {
self.showStuff()
}
}
}
Dependency Injection
Rely on someone else to configure your instance
Could be another part of your app (eg: prepareForSegue)
Could be a unit test
Protocols work really well for this
Dependency Injection
protocol NetworkController {
func fetchStuff(completion: () -> ())
}
...
class APINetworkController: NetworkController {
func fetchStuff(completion: () -> ()) {
// TODO: fetch stuff and call completion()
}
}
Dependency Injection
protocol NetworkController {
func fetchStuff(completion: () -> ())
}
...
class TestNetworkController: NetworkController {
func fetchStuff(completion: () -> ()) {
// TODO: stub fetched stuff
completion()
}
}
Dependency Injection
Use of protocols limits coupling between types
Adding a method to a protocol becomes a decision you have to make
Dependency injection can also be used for shared state, like singletons
Without Dependency Injection
func loadAppSetup() {
let defaults =
NSUserDefaults.standardUserDefaults()
if defaults.boolForKey("launchBefore") == false {
runFirstLaunch()
}
}
How would you even test that?
With Dependency Injection
func loadAppSetup(defaults: NSUserDefaults) {
if defaults.boolForKey("launchBefore") == false {
runFirstLaunch()
}
}
Dont be an ideologue
Cheat with Dependency Injection
func loadAppSetup(
defaults: NSUserDefaults = .standardUserDefaults()){
if defaults.boolForKey("launchBefore") == false {
runFirstLaunch()
}
}
Cheat with Dependency Injection
loadAppSetup() // In your app
loadAppSetup(stubbedUserDefaults) // In your tests
Cheat with Dependency Injection
class ViewController: UIViewController {
lazy var networkController: NetworkController =
APINetworkController()
func viewDidLoad() {
super.viewDidLoad()
networkController.fetchStuff {
self.showStuff()
}
}
}
TDD if necessary, but not necessarily TDD.
Mackenzie King (Canadas Winston Churchill)
Unit Testing
Dont test private functions
Also, start marking functions as private
Remember, we want to avoid rewriting
Dont test the implementation
Dont use partial mocks
See @searls post on partial mocks
Unit Testing
So why dont iOS developers do unit testing?
Its unfamiliar and no one forces us to do it
Better
Testing code makes me a
Developer
Abstract
Everything
Two or more lines of repeated code?
Find a better way
(
Look for Abstractions
Youre already learning new syntax
Look for new abstractions along the way
Not all ideas will work out
But you should still do it
Experiment!
No such thing
as a
Failed experiment
Learn
Always opportunities to
Wrap Up
We have a history of being awesome, lets keep it up
Learning isnt just for when Xcode is in beta
Ideas are more valuable than code, but throwing away either is dangerous
Effective unit tests make it easy to change code
Operate at the highest level of abstraction you can at any given time
Make
Better Mistakes
Tomorrow