Build Features Not Apps

Natasha Murashev
Natasha MurashevSenior Software Engineer - iOS at Capital One
BUILD FEATURES,
NOT APPS
@NATASHATHEROBOT
SWIFT ROBOT
▸ NatashaTheRobot.com
▸ This Week in Swift
▸ Swift Jobs
▸ @NatashaTheNomad
▸ try! Swift
Build Features Not Apps
Most smartphone users download 0 apps per month
An average app loses up to 95% of users within the first month
Build Features Not Apps
!
Build Features Not Apps
Build Features Not Apps
!"
FEATURE:
NOTIFICATIONS
Build Features Not Apps
Build Features Not Apps
FEATURE:
SPEECH
RECOGNITION
Build Features Not Apps
Build Features Not Apps
"As speech recognition accuracy goes from say 95% to 99%, all of us in
the room will from barely using it today to using it all the time. Most
people underestimate the difference between 95% and 99% accuracy -
99% is a game changer" - Andrew NG, Chief Scientist at Baidu
SIRI INTENTS:
▸ Audio or video calling
▸ Messaging
▸ Payments
▸ Searching photos
▸ Workouts
▸ Ride booking
FEATURE:
EXTENSIONS
Build Features Not Apps
Build Features Not Apps
Build Features Not Apps
Build Features Not Apps
"It took Line Messenger almost four
months to find its first two million
users ...
… but after stickers were launched, it
took only two days to find the next
million...
The company now makes over $270M a
year just from selling stickers."
THE FUTURE?
Build Features Not Apps
! -> "
ARCHITECTING
FOR FEATURES
▸ Frameworks all the things!
▸ Decouple sign in
▸ NSUserActivity FTW
Build Features Not Apps
Build Features Not Apps
!
Build Features Not Apps
Build Features Not Apps
OPEN VS PUBLIC
Build Features Not Apps
let rootURL = FileManager.default().
containerURLForSecurityApplicationGroupIdentifier("group.com.NatashaTheRobot.MyFavoriteGelato")
let defaults = UserDefaults(suiteName: "group.com.NatashaTheRobot.MyFavoriteGelato")
Build Features Not Apps
! + ⌚ + #
Build Features Not Apps
Build Features Not Apps
Build Features Not Apps
MULTIPLATFORM, SINGLE-SCHEME XCODE PROJECTS
by Max Howell on PromiseKit.org
FRAMEWORKS
▸ DRY
▸ App Groups
▸ iCloud Key-Value Storage
▸ Cross-Platform
SIGN IN
Build Features Not Apps
Build Features Not Apps
Build Features Not Apps
!
Build Features Not Apps
Build Features Not Apps
Build Features Not Apps
Build Features Not Apps
▸ Decouple Sign In
▸ Profit !
NSUSERACTIVITY FTW
// AppDelegate
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([Any]?) -> Void) -> Bool
{
if userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let webpageURL = userActivity.webpageURL
{
// separate webpageURL using NSURLComponents
// present the correct View Controller if valid
// otherwise, open link in Safari
}
return false
}
Build Features Not Apps
Build Features Not Apps
Build Features Not Apps
Build Features Not Apps
Build Features Not Apps
CREATING AN ACTIVITY
// GelatoDetailViewController
override func viewDidLoad() {
super.viewDidLoad()
// other config here
let activity = NSUserActivity(activityType: "com.natashatherobot.GelatoFinder.gelato")
// will show up as this in Spotlight Search Results
activity.title = gelato.name
// Other keywords to search by
activity.keywords = Set([gelato.name, "gelato"])
// should be handed off to another device?
activity.isEligibleForHandoff = false
// should be indexed in App History?
activity.isEligibleForSearch = true
// should be eligible for indexing for any user of this application?
activity.isEligibleForPublicIndexing = true
// Avoid deallocating before indexing,
// global variable declared in UIResponder class
userActivity = activity
// don't forget to activate!
userActivity!.becomeCurrent()
}
SAVE UNIQUE INFO
// GelatoDetailViewController
override func viewDidLoad() {
// create activity and other config here
activity.delegate = self
activity.needsSave = true
// assign to userActivity, etc
}
extension GelatoDetailViewController: NSUserActivityDelegate {
func userActivityWillSave(_ userActivity: NSUserActivity) {
// info needed to recreate activity!
userActivity.userInfo = ["index": gelatoIndex]
}
}
RESTORE ACTIVITY
// AppDelegate
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([Any]?) -> Void) -> Bool
{
let mainController = (window!.rootViewController! as! UINavigationController).viewControllers.first
mainController?.restoreUserActivityState(userActivity)
return true
}
RESTORE ACTIVITY
// GelatoListTableViewController
override func restoreUserActivityState(_ activity: NSUserActivity) {
if let index = activity.userInfo?["index"] as? Int {
searchedGelatoIdentifier = index
performSegue(withIdentifier: "showGelato", sender: self)
}
}
RESTORE ACTIVITY
// GelatoListTableViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let gelatoDetailVC = segue.destination as? GelatoDetailViewController {
let index = tableView.indexPathForSelectedRow?.row ?? searchedGelatoIdentifier ?? 0
gelatoDetailVC.gelato = gelatoFlavors[index]
gelatoDetailVC.gelatoIndex = index
}
}
Build Features Not Apps
NSUSERACTIVITY
▸ Handoff
▸ Universal Links
▸ Search
▸ Location
▸ Contextual Reminders
▸ Contact Interactions
Build Features Not Apps
▸ Frameworks all the things!
▸ Decouple sign In
▸ NSUserActivity FTW
BUILD FEATURES,
NOT APPS
@NATASHATHEROBOT
1 of 74

More Related Content

Viewers also liked(20)

Swift Delhi: Practical POPSwift Delhi: Practical POP
Swift Delhi: Practical POP
Natasha Murashev3.1K views
Practical Protocols with Associated TypesPractical Protocols with Associated Types
Practical Protocols with Associated Types
Natasha Murashev6.5K views
How to Win on the Apple WatchHow to Win on the Apple Watch
How to Win on the Apple Watch
Natasha Murashev1.7K views
The Secret Life of a Digital NomadThe Secret Life of a Digital Nomad
The Secret Life of a Digital Nomad
Natasha Murashev1.9K views
Hello watchOS2 Hello watchOS2
Hello watchOS2
Natasha Murashev1.3K views
Hello, WatchKitHello, WatchKit
Hello, WatchKit
Natasha Murashev2.8K views
The Zen Guide to WatchOS 2The Zen Guide to WatchOS 2
The Zen Guide to WatchOS 2
Natasha Murashev2.8K views
Unleash the Power of PlaygroundsUnleash the Power of Playgrounds
Unleash the Power of Playgrounds
Natasha Murashev2.1K views
The Swift ArchitectThe Swift Architect
The Swift Architect
Natasha Murashev3.5K views
Practical Protocol-Oriented-ProgrammingPractical Protocol-Oriented-Programming
Practical Protocol-Oriented-Programming
Natasha Murashev90.3K views
HealthKit Deep DiveHealthKit Deep Dive
HealthKit Deep Dive
Natasha Murashev2.1K views
Using Parse in HackathonsUsing Parse in Hackathons
Using Parse in Hackathons
Natasha Murashev1.5K views
Protocol-Oriented MVVMProtocol-Oriented MVVM
Protocol-Oriented MVVM
Natasha Murashev5.3K views
Visual Design with DataVisual Design with Data
Visual Design with Data
Seth Familian2.9M views
Hello, WatchKitHello, WatchKit
Hello, WatchKit
Natasha Murashev1.3K views
Hello, WatchKitHello, WatchKit
Hello, WatchKit
Natasha Murashev1.5K views
Protocol-Oriented MVVM (extended edition)Protocol-Oriented MVVM (extended edition)
Protocol-Oriented MVVM (extended edition)
Natasha Murashev5.2K views

Similar to Build Features Not Apps(20)

More from Natasha Murashev(11)

Digital Nomad: The New NormalDigital Nomad: The New Normal
Digital Nomad: The New Normal
Natasha Murashev1.3K views
AltConf 2015: Swift ThinkingAltConf 2015: Swift Thinking
AltConf 2015: Swift Thinking
Natasha Murashev1.2K views
Swift ThinkingSwift Thinking
Swift Thinking
Natasha Murashev9.4K views
Intro To SwiftIntro To Swift
Intro To Swift
Natasha Murashev974 views
Learning.... Swift functions!Learning.... Swift functions!
Learning.... Swift functions!
Natasha Murashev1.6K views
The Many Faces of Swift FunctionsThe Many Faces of Swift Functions
The Many Faces of Swift Functions
Natasha Murashev2.6K views
Getting Started with Open SourceGetting Started with Open Source
Getting Started with Open Source
Natasha Murashev766 views
iOSDevCampDC: A Swift IntroductioniOSDevCampDC: A Swift Introduction
iOSDevCampDC: A Swift Introduction
Natasha Murashev2K views

Recently uploaded(20)

Build Features Not Apps

  • 2. SWIFT ROBOT ▸ NatashaTheRobot.com ▸ This Week in Swift ▸ Swift Jobs ▸ @NatashaTheNomad ▸ try! Swift
  • 4. Most smartphone users download 0 apps per month
  • 5. An average app loses up to 95% of users within the first month
  • 7. !
  • 10. !"
  • 17. "As speech recognition accuracy goes from say 95% to 99%, all of us in the room will from barely using it today to using it all the time. Most people underestimate the difference between 95% and 99% accuracy - 99% is a game changer" - Andrew NG, Chief Scientist at Baidu
  • 18. SIRI INTENTS: ▸ Audio or video calling ▸ Messaging ▸ Payments ▸ Searching photos ▸ Workouts ▸ Ride booking
  • 24. "It took Line Messenger almost four months to find its first two million users ...
  • 25. … but after stickers were launched, it took only two days to find the next million...
  • 26. The company now makes over $270M a year just from selling stickers."
  • 31. ▸ Frameworks all the things! ▸ Decouple sign in ▸ NSUserActivity FTW
  • 34. !
  • 39. let rootURL = FileManager.default(). containerURLForSecurityApplicationGroupIdentifier("group.com.NatashaTheRobot.MyFavoriteGelato")
  • 40. let defaults = UserDefaults(suiteName: "group.com.NatashaTheRobot.MyFavoriteGelato")
  • 42. ! + ⌚ + #
  • 46. MULTIPLATFORM, SINGLE-SCHEME XCODE PROJECTS by Max Howell on PromiseKit.org
  • 47. FRAMEWORKS ▸ DRY ▸ App Groups ▸ iCloud Key-Value Storage ▸ Cross-Platform
  • 52. !
  • 57. ▸ Decouple Sign In ▸ Profit !
  • 59. // AppDelegate func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool { if userActivity.activityType == NSUserActivityTypeBrowsingWeb, let webpageURL = userActivity.webpageURL { // separate webpageURL using NSURLComponents // present the correct View Controller if valid // otherwise, open link in Safari } return false }
  • 65. CREATING AN ACTIVITY // GelatoDetailViewController override func viewDidLoad() { super.viewDidLoad() // other config here let activity = NSUserActivity(activityType: "com.natashatherobot.GelatoFinder.gelato") // will show up as this in Spotlight Search Results activity.title = gelato.name // Other keywords to search by activity.keywords = Set([gelato.name, "gelato"]) // should be handed off to another device? activity.isEligibleForHandoff = false // should be indexed in App History? activity.isEligibleForSearch = true // should be eligible for indexing for any user of this application? activity.isEligibleForPublicIndexing = true // Avoid deallocating before indexing, // global variable declared in UIResponder class userActivity = activity // don't forget to activate! userActivity!.becomeCurrent() }
  • 66. SAVE UNIQUE INFO // GelatoDetailViewController override func viewDidLoad() { // create activity and other config here activity.delegate = self activity.needsSave = true // assign to userActivity, etc } extension GelatoDetailViewController: NSUserActivityDelegate { func userActivityWillSave(_ userActivity: NSUserActivity) { // info needed to recreate activity! userActivity.userInfo = ["index": gelatoIndex] } }
  • 67. RESTORE ACTIVITY // AppDelegate func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool { let mainController = (window!.rootViewController! as! UINavigationController).viewControllers.first mainController?.restoreUserActivityState(userActivity) return true }
  • 68. RESTORE ACTIVITY // GelatoListTableViewController override func restoreUserActivityState(_ activity: NSUserActivity) { if let index = activity.userInfo?["index"] as? Int { searchedGelatoIdentifier = index performSegue(withIdentifier: "showGelato", sender: self) } }
  • 69. RESTORE ACTIVITY // GelatoListTableViewController override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if let gelatoDetailVC = segue.destination as? GelatoDetailViewController { let index = tableView.indexPathForSelectedRow?.row ?? searchedGelatoIdentifier ?? 0 gelatoDetailVC.gelato = gelatoFlavors[index] gelatoDetailVC.gelatoIndex = index } }
  • 71. NSUSERACTIVITY ▸ Handoff ▸ Universal Links ▸ Search ▸ Location ▸ Contextual Reminders ▸ Contact Interactions
  • 73. ▸ Frameworks all the things! ▸ Decouple sign In ▸ NSUserActivity FTW