Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Building 5 Calls for iOS
Search
Ben Scheirman
April 27, 2017
Programming
170
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Building 5 Calls for iOS
A talk I gave in Barcelona. About 1/3 political, 1/3 technical, and 1/3 tactical.
Ben Scheirman
April 27, 2017
More Decks by Ben Scheirman
See All by Ben Scheirman
A Promise for a Better Future
subdigital
0
210
Bézier Curves
subdigital
1
5.8k
Buckets of Code
subdigital
0
2k
Swift on Linux
subdigital
1
920
tvOS Workshop
subdigital
1
200
Swift Solutions
subdigital
2
510
iOS 8 Networking
subdigital
4
980
iOS 8 App Extensions
subdigital
1
410
Effective Networking with iOS 8 and Swift
subdigital
4
1.7k
Other Decks in Programming
See All in Programming
When benchmarks go bad - what I learned from measuring performance wrong
hollycummins
0
150
Webの地図
yosuke_furukawa
PRO
6
5.1k
Herb in Rails 8.2: Your ERB views, now HTML-aware @ Rails World 2026, Austin, Texas
marcoroth
0
190
Rでドレミ/Do-Re-Mi_with_R
florets1
0
120
mrbgem 三角測量 開発
ogom
0
200
Vue Fes Japan の “作る側”に関わってみて
naokihaba
0
110
Everything will be SERVERLESS — 信じて運用した10年の経験値 / Everything Will be Serverless — Lessons Learned from 10 Years of Operational Experience
seike460
PRO
1
750
仕様駆動開発による爆速プロダクト開発 / Bakusoku Spec Driven Development
kobakei
0
190
WebRTC映像をAirPlayに対応させる挑戦.pdf
monolithic_adam
0
340
ソニーのクラウド共通基盤の変遷とAI時代の開発スタイルに合わせた進化 / The Journey of Sony’s Common Cloud Platform and Its Evolution for AI-Native Development
kenjiyoneyama
0
230
phperだってパンチカードでコーディングしたい
o0h
PRO
0
210
Embedded Swiftで作る自作USBデバイス によるiOSデバイスの自動テスト / iOSDC Japan 2026 glassfiber
glassfiber
0
300
Featured
See All Featured
RailsConf 2023
tenderlove
30
1.6k
30 Presentation Tips
portentint
PRO
1
420
Tell your own story through comics
letsgokoyo
1
1.1k
Mobile First: as difficult as doing things right
swwweet
225
10k
YesSQL, Process and Tooling at Scale
rocio
174
15k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
133
20k
Highjacked: Video Game Concept Design
rkendrick25
PRO
1
470
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
4
3.2k
How to build a perfect <img>
jonoalderson
1
6.1k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
203
76k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.6k
Transcript
Building
Ben Scheirman benscheirman.com @subdigital
ficklebits.com
260+ screencasts on iOS development nsscreencast.com
None
None
None
What do we do?
None
None
None
What do we do?
What can we do?
What can I do?
None
None
United States Congress House of Representatives Senate 435 Members 100
Members
None
None
None
5calls.org
None
None
None
Launch ASAP
None
Wanted it to be a GOOD iOS app
None
github.com/5calls/ios
None
Let people help you!
None
Built on NSOperation Storyboards UITableView CocoaPods Vibrancy / Blur Dynamic
Type Custom UIViewController Containment Local Push Notifications Fastlane Pantry Swift 3 Universal app (split view) Fabric / Crashlytics R.swift Buddybuild
NSOperations class BaseOperation : Operation { override var isAsynchronous: Bool
{ return true } private var _executing = false { willSet { willChangeValue(forKey: "isExecuting") } didSet { didChangeValue(forKey: "isExecuting") } } override var isExecuting: Bool { return _executing } private var _finished = false { willSet { willChangeValue(forKey: "isFinished") } didSet { didChangeValue(forKey: "isFinished") } } override var isFinished: Bool { return _finished } class BaseOperation : Operation { func execute() { fatalError("You must override this") } func finish() { _executing = false _finished = true } }
NSOperations class FetchIssuesOperation : BaseOperation { let location: UserLocation? init(location:
UserLocation?) { self.location = location } ... }
NSOperations override func execute() { let task = session.dataTask(with: url)
{ (data, response, error) in if let e = error { print("Error fetching issues: \ (e.localizedDescription)”) } else { self.handleResponse(data: data, response: response) } self.finish() } task.resume() }
NSOperations self.issuesList = IssuesList(dictionary: json)
NSOperations func fetchIssues(completion: @escaping (IssuesLoadResult) -> Void) { let operation
= FetchIssuesOperation(location: userLocation) operation.completionBlock = { [weak self, weak operation] in if let issuesList = operation?.issuesList { self?.issuesList = issuesList DispatchQueue.main.async { completion(.success) } } else { // … } } OperationQueue.main.addOperation(operation) }
NSOperations FetchIssuesOperation FetchStatsOperation ReportOutcomeOperation
NSOperations Composable Dependencies Cancellable Control Concurrency Quality of Service
NSOperations https://developer.apple.com/videos/play/wwdc2015/226/ WWDC Session 226 - Advanced NSOperations http://nsscreencast.com/ NSScreencast
Episodes 175-177, 180
Dynamic Type Dynamic Type Dynamic Type
Using a custom font made this more difficult
None
None
Getting Dynamic Type to work with UITableViewCells Define height with
constraints
Getting Dynamic Type to work with UITableViewCells
Respond to Dynamic Type Changes UIContentSizeCategoryDidChangeNotification adjustsFontsForContentSizeCategory iOS 10.0
None
Getting dynamic fonts in code headline.font = UIFont.preferredFont( forTextStyle: UIFontTextStyleHeadline)
subhead.font = UIFont.preferredFont( forTextStyle: UIFontTextStyleSubheadline) body.font = UIFont.preferredFont( forTextStyle: UIFontTextStyleBody)
https://github.com/nickoneill/Pantry if let available: Bool = Pantry.unpack("promptAvailable") { completion(available: available)
} else { anExpensiveOperationToDetermineAvailability({ (available) -> () in Pantry.pack(available, key: "promptAvailable", expires: .Seconds(60 * 10)) completion(available: available) }) }
var autopersist: String? { set { if let newValue =
newValue { Pantry.pack(newValue, key: "autopersist") } } get { return Pantry.unpack("autopersist") } } ...later... autopersist = "Hello!" // restart app, reboot phone, etc print(autopersist) // Hello!
SIMPLE MODEL OBJECT…
CONFORM TO STORABLE
USAGE IS REALLY SIMPLE:
CAREFUL YOU DON’T STORE USER DATA IN CACHES
R.swift
R.swift let icon = UIImage(named: "settings-icon") let icon = R.image.settingsIcon()
becomes…
R.swift let viewController = CustomViewController(nibName: "CustomView", bundle: nil) let viewController
= CustomViewController(nib: R.nib.customView) becomes…
R.swift let string = String(format: NSLocalizedString("welcome.withName", comment: ""), locale:
NSLocale.current, "Arthur Dent") let string = R.string.localizable.welcomeWithName("Arthur Dent") becomes…
R.swift
None
None
# Fastfile desc "Runs all the tests" lane :test
do scan(workspace: workspace, scheme: scheme) end
$ fastlane test
desc "Increments build number" lane :increment_build do increment_build_number(xcodeproj: xcodeproj) end
None
desc "Submit a new Beta Build to Apple TestFlight" lane
:beta do ensure_git_status_clean increment_build commit_version_bump badge(shield: "Version-#{app_version}-red", shield_no_resize: true) gym(workspace: workspace, scheme: scheme) changelog = prompt_for_release_notes pilot(changelog: changelog) git_commit(path: 'fastlane/changelog.txt', message: 'Updated changelog.txt') reset_git_repo(files: app_icon_files) add_git_tag push_git_tags end
desc "Submit a new Beta Build to Apple TestFlight" lane
:beta do ensure_git_status_clean increment_build commit_version_bump badge(shield: "Version-#{app_version}-red", shield_no_resize: true) gym(workspace: workspace, scheme: scheme) changelog = prompt_for_release_notes pilot(changelog: changelog) git_commit(path: 'fastlane/changelog.txt', message: 'Updated changelog.txt') reset_git_repo(files: app_icon_files) add_git_tag push_git_tags end
desc "Submit a new Beta Build to Apple TestFlight" lane
:beta do ensure_git_status_clean increment_build commit_version_bump badge(shield: "Version-#{app_version}-red", shield_no_resize: true) gym(workspace: workspace, scheme: scheme) changelog = prompt_for_release_notes pilot(changelog: changelog) git_commit(path: 'fastlane/changelog.txt', message: 'Updated changelog.txt') reset_git_repo(files: app_icon_files) add_git_tag push_git_tags end
desc "Submit a new Beta Build to Apple TestFlight" lane
:beta do ensure_git_status_clean increment_build commit_version_bump badge(shield: "Version-#{app_version}-red", shield_no_resize: true) gym(workspace: workspace, scheme: scheme) changelog = prompt_for_release_notes pilot(changelog: changelog) git_commit(path: 'fastlane/changelog.txt', message: 'Updated changelog.txt') reset_git_repo(files: app_icon_files) add_git_tag push_git_tags end
desc "Submit a new Beta Build to Apple TestFlight" lane
:beta do ensure_git_status_clean increment_build commit_version_bump badge(shield: "Version-#{app_version}-red", shield_no_resize: true) gym(workspace: workspace, scheme: scheme) changelog = prompt_for_release_notes pilot(changelog: changelog) git_commit(path: 'fastlane/changelog.txt', message: 'Updated changelog.txt') reset_git_repo(files: app_icon_files) add_git_tag push_git_tags end
def prompt_for_release_notes `open changelog.txt -W` File.read('changelog.txt') end
desc "Submit a new Beta Build to Apple TestFlight" lane
:beta do ensure_git_status_clean increment_build commit_version_bump badge(shield: "Version-#{app_version}-red", shield_no_resize: true) gym(workspace: workspace, scheme: scheme) changelog = prompt_for_release_notes pilot(changelog: changelog) git_commit(path: 'fastlane/changelog.txt', message: 'Updated changelog.txt') reset_git_repo(files: app_icon_files) add_git_tag push_git_tags end
desc "Submit a new Beta Build to Apple TestFlight" lane
:beta do ensure_git_status_clean increment_build commit_version_bump badge(shield: "Version-#{app_version}-red", shield_no_resize: true) gym(workspace: workspace, scheme: scheme) changelog = prompt_for_release_notes pilot(changelog: changelog) git_commit(path: 'fastlane/changelog.txt', message: 'Updated changelog.txt') reset_git_repo(files: app_icon_files) add_git_tag push_git_tags end
Automatic Screenshots # Snapfile devices([ "iPhone 7" "iPhone 7 Plus",
"iPhone SE", "iPad Pro (12.9 inch)" ]) languages(["en-US"]) workspace "./FiveCalls/FiveCalls.xcworkspace" scheme "FiveCallsUITests" output_directory "./screenshots" clear_previous_screenshots true launch_arguments(["-hasShownWelcomeScreen false"])
Automatic Screenshots
Deterministic Data? How do we ALWAYS choose the same item,
despite the server changing?
Stubs! { “issues”: [ … ] } issues.json INTERNET
GET /issues UI TESTING? YES NO
None
None
SeededURLSession SeededDataTask dataTask(with:completion:)
None
None
HOST APP TESTS ENV
TESTS
None
None
None
The Stats
Week 1 6,300 downloads
None
None
Month 1 76,916 downloads
None
All Time (4/21/2017) 105,000 downloads
44.2k Monthly Active Users 2.4k Daily Active Users
None
Over 1 million calls to congress
Final Thoughts
!
Track Useful Analytics *but don’t be scummy
None
None
None
REDMAP
None
Software Development is an incredibly valuable skill.
Software Development is an incredibly valuable skill. Use your powers
for good, not evil.
Ben Scheirman benscheirman.com @subdigital nsscreencast.com ¡Gracias!