SlideShare une entreprise Scribd logo
1  sur  32
Télécharger pour lire hors ligne
AppDelegate
разделяй и властвуй
Вадим Смаль
iOS разработчик Rambler&Co
AppDelegate
UIResponder
<UIApplicationDelegate>
• Реагирует на получение уведомлений
• Реагирует на ключевые изменения в
состоянии вашего приложения
• Реагирует на события, которые
нацелены на само приложение
• Управляет процессом сохранения и
восстановления состояния приложения
Запуск
приложения
Изменение
состояния
приложения
Восстановление
состояния
приложения
Загрузка данных
в фоне
Локальные и
удаленные
уведомления
Пользовательская
активность
WatchKit
Открытие URL’ов
HealthKit
Системные
события
Разрешения для
расширений
Геометрия
интерфейса
Window
CoreData
SharedInstance
Quick Actions
<UIApplicationDelegate>
import Shared
import Storage
import AVFoundation
import XCGLogger
import Breakpad
import MessageUI
import WebImage
import SwiftKeychainWrapper
import LocalAuthentication
private let log = Logger.browserLogger
let LatestAppVersionProfileKey = "latestAppVersion"
let AllowThirdPartyKeyboardsKey = "settings.allowThirdPartyKeyboards"
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var browserViewController: BrowserViewController!
var rootViewController: UINavigationController!
weak var profile: BrowserProfile?
var tabManager: TabManager!
var adjustIntegration: AdjustIntegration?
weak var application: UIApplication?
var launchOptions: [NSObject: AnyObject]?
let appVersion = NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleShortVersionString") as! String
var openInFirefoxURL: NSURL? = nil
func application(application: UIApplication, willFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Hold references to willFinishLaunching parameters for delayed app launch
self.application = application
self.launchOptions = launchOptions
log.debug("Configuring window…")
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window!.backgroundColor = UIConstants.AppBackgroundColor
// Short circuit the app if we want to email logs from the debug menu
if DebugSettingsBundleOptions.launchIntoEmailComposer {
self.window?.rootViewController = UIViewController()
presentEmailComposerWithLogs()
return true
} else {
return startApplication(application, withLaunchOptions: launchOptions)
}
}
Импорты
Зависимости
WINDOW
startApplication
private func startApplication(application: UIApplication, withLaunchOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
log.debug("Setting UA…")
// Set the Firefox UA for browsing.
setUserAgent()
log.debug("Starting keyboard helper…")
// Start the keyboard helper to monitor and cache keyboard state.
KeyboardHelper.defaultHelper.startObserving()
log.debug("Starting dynamic font helper…")
// Start the keyboard helper to monitor and cache keyboard state.
DynamicFontHelper.defaultHelper.startObserving()
log.debug("Setting custom menu items…")
MenuHelper.defaultHelper.setItems()
log.debug("Creating Sync log file…")
let logDate = NSDate()
// Create a new sync log file on cold app launch. Note that this doesn't roll old logs.
Logger.syncLogger.newLogWithDate(logDate)
log.debug("Creating corrupt DB logger…")
Logger.corruptLogger.newLogWithDate(logDate)
log.debug("Creating Browser log file…")
Logger.browserLogger.newLogWithDate(logDate)
log.debug("Getting profile…")
let profile = getProfile(application)
if !DebugSettingsBundleOptions.disableLocalWebServer {
log.debug("Starting web server…")
// Set up a web server that serves us static content. Do this early so that it is ready when the UI is presented.
setUpWebServer(profile)
}
log.debug("Setting AVAudioSession category…")
do {
// for aural progress bar: play even with silent switch on, and do not stop audio from other apps (like music)
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, withOptions:
AVAudioSessionCategoryOptions.MixWithOthers)
} catch _ {
log.error("Failed to assign AVAudioSession category to allow playing with silent switch on for aural progress bar")
}
Настройка
приложения
let defaultRequest = NSURLRequest(URL: UIConstants.DefaultHomePage)
let imageStore = DiskImageStore(files: profile.files, namespace: "TabManagerScreenshots", quality:
UIConstants.ScreenshotQuality)
log.debug("Configuring tabManager…")
self.tabManager = TabManager(defaultNewTabRequest: defaultRequest, prefs: profile.prefs, imageStore: imageStore)
self.tabManager.stateDelegate = self
// Add restoration class, the factory that will return the ViewController we
// will restore with.
log.debug("Initing BVC…")
browserViewController = BrowserViewController(profile: self.profile!, tabManager: self.tabManager)
browserViewController.restorationIdentifier = NSStringFromClass(BrowserViewController.self)
browserViewController.restorationClass = AppDelegate.self
browserViewController.automaticallyAdjustsScrollViewInsets = false
rootViewController = UINavigationController(rootViewController: browserViewController)
rootViewController.automaticallyAdjustsScrollViewInsets = false
rootViewController.delegate = self
rootViewController.navigationBarHidden = true
self.window!.rootViewController = rootViewController
log.debug("Configuring Breakpad…")
activeCrashReporter = BreakpadCrashReporter(breakpadInstance: BreakpadController.sharedInstance())
configureActiveCrashReporter(profile.prefs.boolForKey("crashreports.send.always"))
log.debug("Adding observers…")
NSNotificationCenter.defaultCenter().addObserverForName(FSReadingListAddReadingListItemNotification, object: nil, queue: nil)
{ (notification) -> Void in
if let userInfo = notification.userInfo, url = userInfo["URL"] as? NSURL {
let title = (userInfo["Title"] as? String) ?? ""
profile.readingList?.createRecordWithURL(url.absoluteString, title: title, addedBy: UIDevice.currentDevice().name)
}
}
// check to see if we started 'cos someone tapped on a notification.
if let localNotification = launchOptions?[UIApplicationLaunchOptionsLocalNotificationKey] as? UILocalNotification {
viewURLInNewTab(localNotification)
}
adjustIntegration = AdjustIntegration(profile: profile)
// We need to check if the app is a clean install to use for
// preventing the What's New URL from appearing.
if getProfile(application).prefs.intForKey(IntroViewControllerSeenProfileKey) == nil {
getProfile(application).prefs.setString(AppInfo.appVersion, forKey: LatestAppVersionProfileKey)
}
log.debug("Updating authentication keychain state to reflect system state")
self.updateAuthenticationInfo()
log.debug("Done with setting up the application.")
return true
}
func applicationWillTerminate(application: UIApplication) {
log.debug("Application will terminate.")
// We have only five seconds here, so let's hope this doesn't take too long.
self.profile?.shutdown()
// Allow deinitializers to close our database connections.
self.profile = nil
self.tabManager = nil
self.browserViewController = nil
self.rootViewController = nil
}
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
// Override point for customization after application launch.
var shouldPerformAdditionalDelegateHandling = true
log.debug("Did finish launching.")
log.debug("Setting up Adjust")
self.adjustIntegration?.triggerApplicationDidFinishLaunchingWithOptions(launchOptions)
log.debug("Making window key and visible…")
self.window!.makeKeyAndVisible()
// Now roll logs.
log.debug("Triggering log roll.")
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
Logger.syncLogger.deleteOldLogsDownToSizeLimit()
Logger.browserLogger.deleteOldLogsDownToSizeLimit()
}
if #available(iOS 9, *) {
// If a shortcut was launched, display its information and take the appropriate action
if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsShortcutItemKey] as? UIApplicationShortcutItem {
QuickActions.sharedInstance.launchedShortcutItem = shortcutItem
// This will block "performActionForShortcutItem:completionHandler" from being called.
shouldPerformAdditionalDelegateHandling = false
}
}
log.debug("Done with applicationDidFinishLaunching.")
return shouldPerformAdditionalDelegateHandling
}
Quick Actions
func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool {
if let components = NSURLComponents(URL: url, resolvingAgainstBaseURL: false) {
if components.scheme != "firefox" && components.scheme != "firefox-x-callback" {
return false
}
var url: String?
for item in (components.queryItems ?? []) as [NSURLQueryItem] {
switch item.name {
case "url":
url = item.value
default: ()
}
}
if let url = url, newURL = NSURL(string: url.unescape()) {
// If we are active then we can ask the BVC to open the new tab right away. Else we remember the
// URL and we open it in applicationDidBecomeActive.
if application.applicationState == .Active {
if #available(iOS 9, *) {
self.browserViewController.switchToPrivacyMode(isPrivate: false)
}
self.browserViewController.openURLInNewTab(newURL)
} else {
openInFirefoxURL = newURL
}
return true
}
}
return false
}
func application(application: UIApplication, shouldAllowExtensionPointIdentifier extensionPointIdentifier: String) -> Bool {
if let thirdPartyKeyboardSettingBool = getProfile(application).prefs.boolForKey(AllowThirdPartyKeyboardsKey) where
extensionPointIdentifier == UIApplicationKeyboardExtensionPointIdentifier {
return thirdPartyKeyboardSettingBool
}
return true
}
Открытие URL’ов
Разрешения для
расширений
func applicationDidBecomeActive(application: UIApplication) {
guard !DebugSettingsBundleOptions.launchIntoEmailComposer else {
return
}
self.profile?.syncManager.applicationDidBecomeActive()
// We could load these here, but then we have to futz with the tab counter
// and making NSURLRequests.
self.browserViewController.loadQueuedTabs()
// handle quick actions is available
if #available(iOS 9, *) {
let quickActions = QuickActions.sharedInstance
if let shortcut = quickActions.launchedShortcutItem {
// dispatch asynchronously so that BVC is all set up for handling new tabs
// when we try and open them
quickActions.handleShortCutItem(shortcut, withBrowserViewController: browserViewController)
quickActions.launchedShortcutItem = nil
}
// we've removed the Last Tab option, so we should remove any quick actions that we already have that are last tabs
// we do this after we've handled any quick actions that have been used to open the app so that we don't b0rk if
// the user has opened the app for the first time after upgrade with a Last Tab quick action
QuickActions.sharedInstance.removeDynamicApplicationShortcutItemOfType(ShortcutType.OpenLastTab, fromApplication:
application)
}
// If we have a URL waiting to open, switch to non-private mode and open the URL.
if let url = openInFirefoxURL {
openInFirefoxURL = nil
// This needs to be scheduled so that the BVC is ready.
dispatch_async(dispatch_get_main_queue()) {
if #available(iOS 9, *) {
self.browserViewController.switchToPrivacyMode(isPrivate: false)
}
self.browserViewController.switchToTabForURLOrOpen(url)
}
}
}
func applicationWillEnterForeground(application: UIApplication) {
// The reason we need to call this method here instead of `applicationDidBecomeActive`
// is that this method is only invoked whenever the application is entering the foreground where as
// `applicationDidBecomeActive` will get called whenever the Touch ID authentication overlay disappears.
self.updateAuthenticationInfo()
}
private func updateAuthenticationInfo() {
if let authInfo = KeychainWrapper.authenticationInfo() {
if !LAContext().canEvaluatePolicy(.DeviceOwnerAuthenticationWithBiometrics, error: nil) {
authInfo.useTouchID = false
KeychainWrapper.setAuthenticationInfo(authInfo)
}
}
}
Quick Actions
func applicationDidEnterBackground(application: UIApplication) {
self.profile?.syncManager.applicationDidEnterBackground()
var taskId: UIBackgroundTaskIdentifier = 0
taskId = application.beginBackgroundTaskWithExpirationHandler { _ in
log.warning("Running out of background time, but we have a profile shutdown pending.")
application.endBackgroundTask(taskId)
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
self.profile?.shutdown()
application.endBackgroundTask(taskId)
}
// Workaround for crashing in the background when <select> popovers are visible (rdar://24571325).
let jsBlurSelect = "if (document.activeElement && document.activeElement.tagName === 'SELECT')
{ document.activeElement.blur(); }"
tabManager.selectedTab?.webView?.evaluateJavaScript(jsBlurSelect, completionHandler: nil)
}
func application(application: UIApplication, handleActionWithIdentifier identifier: String?, forLocalNotification notification:
UILocalNotification, completionHandler: () -> Void) {
if let actionId = identifier {
if let action = SentTabAction(rawValue: actionId) {
viewURLInNewTab(notification)
switch(action) {
case .Bookmark:
addBookmark(notification)
break
case .ReadingList:
addToReadingList(notification)
break
default:
break
}
} else {
print("ERROR: Unknown notification action received")
}
} else {
print("ERROR: Unknown notification received")
}
}
func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification) {
viewURLInNewTab(notification)
}
Загрузка данных
в фоне
Локальные и
удаленные
уведомления
func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) ->
Void) -> Bool {
if let url = userActivity.webpageURL {
browserViewController.switchToTabForURLOrOpen(url)
return true
}
return false
}
@available(iOS 9.0, *)
func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem,
completionHandler: Bool -> Void) {
let handledShortCutItem = QuickActions.sharedInstance.handleShortCutItem(shortcutItem, withBrowserViewController:
browserViewController)
completionHandler(handledShortCutItem)
}
var activeCrashReporter: CrashReporter?
func configureActiveCrashReporter(optedIn: Bool?) {
if let reporter = activeCrashReporter {
configureCrashReporter(reporter, optedIn: optedIn)
}
}
public func configureCrashReporter(reporter: CrashReporter, optedIn: Bool?) {
let configureReporter: () -> () = {
let addUploadParameterForKey: String -> Void = { key in
if let value = NSBundle.mainBundle().objectForInfoDictionaryKey(key) as? String {
reporter.addUploadParameter(value, forKey: key)
}
}
addUploadParameterForKey("AppID")
addUploadParameterForKey("BuildID")
addUploadParameterForKey("ReleaseChannel")
addUploadParameterForKey("Vendor")
}
if let optedIn = optedIn {
// User has explicitly opted-in for sending crash reports. If this is not true, then the user has
// explicitly opted-out of crash reporting so don't bother starting breakpad or stop if it was running
if optedIn {
reporter.start(true)
configureReporter()
reporter.setUploadingEnabled(true)
} else {
reporter.stop()
}
}
// We haven't asked the user for their crash reporting preference yet. Log crashes anyways but don't send them.
else {
reporter.start(true)
configureReporter()
}
}
Пользовательска
я активность
Пользовательска
я активность
ThirdParties
// MARK: - Root View Controller Animations
extension AppDelegate: UINavigationControllerDelegate {
func navigationController(navigationController: UINavigationController,
animationControllerForOperation operation: UINavigationControllerOperation,
fromViewController fromVC: UIViewController,
toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
if operation == UINavigationControllerOperation.Push {
return BrowserToTrayAnimator()
} else if operation == UINavigationControllerOperation.Pop {
return TrayToBrowserAnimator()
} else {
return nil
}
}
}
extension AppDelegate: TabManagerStateDelegate {
func tabManagerWillStoreTabs(tabs: [Browser]) {
// It is possible that not all tabs have loaded yet, so we filter out tabs with a nil URL.
let storedTabs: [RemoteTab] = tabs.flatMap( Browser.toTab )
// Don't insert into the DB immediately. We tend to contend with more important
// work like querying for top sites.
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(ProfileRemoteTabsSyncDelay * Double(NSEC_PER_MSEC))), queue) {
self.profile?.storeTabs(storedTabs)
}
}
}
extension AppDelegate: MFMailComposeViewControllerDelegate {
func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error:
NSError?) {
// Dismiss the view controller and start the app up
controller.dismissViewControllerAnimated(true, completion: nil)
startApplication(application!, withLaunchOptions: self.launchOptions)
}
}
Стек навигации
AppDelegate:
45 методов
~1000 строк кода
~ 30 зависимостей
Принцип единственной
обязанности
Принцип разделения
интерфейса
Тестируемость
Расширяемость
<UIApplicationDelegate> Логика
StartConfigurator
ThirdPartiesConfigurator
ApplicationConfigurator
AppStateConfigurator
HandoffHandler
SpotlightIndexer
QuickActionHandler
…
…
…
AppDelegate
didFinishLaunchingWithOptions {
[self.thirdPartiesConfigurator configure]
[self.startConfigurator configure]
…
[self.applicationConfigurator configure]
[self.handoffHandler activate]
[self.spotlightIndexer activate]
…
[self.quickActionHandler activate]
…
}
@interface AppDelegate : UIResponder
<UIApplicationDelegate>
@property UIWindow *window;
@property startConfigurator;
@property thirdPartiesConfigurator;
@property applicationConfigurator;
...
@property handoffHandler;
@property quickActionHandler ;
@property NSArray *urlHandlers;
@property forceLogoutHandler;
...
@end
AppDelegate:
45 методов
~200 строк кода
~20 зависимостей
Принцип единственной
обязанности
Принцип разделения
интерфейса
Тестируемость
God object
<UIApplicationDelegate>
Зависимости
Launching
Search
RemoteNotification
QuickAction
URLHandler
Handoff
ApplicationState BackgroundData
AppDelegateProxy AppDelegate
Array<UIApplicationDelegate>
@implementation RemoteNotificationAppDelegate
- didFinishLaunchingWithOptions {
if (notification) {
[self.pushNotificationCenter process:notification];
}
return YES;
}
- didRegisterForRemoteNotificationsWithDeviceToken: {
[self.pushNotificationCenter didRegisterDeviceToken:token];
}
- didReceiveRemoteNotification {
[self.pushNotificationCenter processWithUserInfo:userInfo];
}
@end
AppDelegate:
~ 3 метода
~50 строк кода
~2 зависимостей
Принцип единственной
обязанности
Принцип разделения
интерфейса
Тестируемость
Принципип “разделяй
и властвуй”
<UIApplicationDelegate>
AppDelegateProxy
RemoteNotificationAppDelegateAppDelegateProxyPUSH NOTIFICATION
<PushNotificationCenter><StartUpConfigurator>
<NavigationStackBuilder> Present navigation stack
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc,
argv,
nil,
NSStringFromClass([RCMAppDelegateProxy class]));
}
}
@interface RCMAppDelegateProxy : NSProxy
- (void)addAppDelegate:(id<UIApplicationDelegate>)delegate;
- (void)addAppDelegates:(NSArray *)delegates;
@end
Где еще использовать?
<UITableViewDelegate>/<UITableViewDataSource>
<UICollectionViewDelegate>/
<UICollectionViewDataSource>
<UITextViewDelegate>
Спасибо!
RamblerAppDelegateProxy
https://github.com/rambler-ios/RamblerAppDelegateProxy

Contenu connexe

Tendances

Workshop 13: AngularJS Parte II
Workshop 13: AngularJS Parte IIWorkshop 13: AngularJS Parte II
Workshop 13: AngularJS Parte IIVisual Engineering
 
Workshop 27: Isomorphic web apps with ReactJS
Workshop 27: Isomorphic web apps with ReactJSWorkshop 27: Isomorphic web apps with ReactJS
Workshop 27: Isomorphic web apps with ReactJSVisual Engineering
 
Angular 1 + es6
Angular 1 + es6Angular 1 + es6
Angular 1 + es6장현 한
 
Practical Protocol-Oriented-Programming
Practical Protocol-Oriented-ProgrammingPractical Protocol-Oriented-Programming
Practical Protocol-Oriented-ProgrammingNatasha Murashev
 
State management in android applications
State management in android applicationsState management in android applications
State management in android applicationsGabor Varadi
 
Reactive state management with Jetpack Components
Reactive state management with Jetpack ComponentsReactive state management with Jetpack Components
Reactive state management with Jetpack ComponentsGabor Varadi
 
Building scalable applications with angular js
Building scalable applications with angular jsBuilding scalable applications with angular js
Building scalable applications with angular jsAndrew Alpert
 
Jetpack, with new features in 2021 GDG Georgetown IO Extended
Jetpack, with new features in 2021 GDG Georgetown IO ExtendedJetpack, with new features in 2021 GDG Georgetown IO Extended
Jetpack, with new features in 2021 GDG Georgetown IO ExtendedToru Wonyoung Choi
 
Angular performance slides
Angular performance slidesAngular performance slides
Angular performance slidesDavid Barreto
 
Angular 2 Component Communication - Talk by Rob McDiarmid
Angular 2 Component Communication - Talk by Rob McDiarmidAngular 2 Component Communication - Talk by Rob McDiarmid
Angular 2 Component Communication - Talk by Rob McDiarmidAmrita Chopra
 
MBLTDev15: Egor Tolstoy, Rambler&Co
MBLTDev15: Egor Tolstoy, Rambler&CoMBLTDev15: Egor Tolstoy, Rambler&Co
MBLTDev15: Egor Tolstoy, Rambler&Coe-Legion
 
Workshop 19: ReactJS Introduction
Workshop 19: ReactJS IntroductionWorkshop 19: ReactJS Introduction
Workshop 19: ReactJS IntroductionVisual Engineering
 
Async task, threads, pools, and executors oh my!
Async task, threads, pools, and executors oh my!Async task, threads, pools, and executors oh my!
Async task, threads, pools, and executors oh my!Stacy Devino
 
Workshop 3: JavaScript build tools
Workshop 3: JavaScript build toolsWorkshop 3: JavaScript build tools
Workshop 3: JavaScript build toolsVisual Engineering
 

Tendances (20)

Workshop 13: AngularJS Parte II
Workshop 13: AngularJS Parte IIWorkshop 13: AngularJS Parte II
Workshop 13: AngularJS Parte II
 
Workshop 27: Isomorphic web apps with ReactJS
Workshop 27: Isomorphic web apps with ReactJSWorkshop 27: Isomorphic web apps with ReactJS
Workshop 27: Isomorphic web apps with ReactJS
 
Angular 1 + es6
Angular 1 + es6Angular 1 + es6
Angular 1 + es6
 
Practical Protocol-Oriented-Programming
Practical Protocol-Oriented-ProgrammingPractical Protocol-Oriented-Programming
Practical Protocol-Oriented-Programming
 
The AngularJS way
The AngularJS wayThe AngularJS way
The AngularJS way
 
Introduction to Angularjs
Introduction to AngularjsIntroduction to Angularjs
Introduction to Angularjs
 
Wicket 6
Wicket 6Wicket 6
Wicket 6
 
State management in android applications
State management in android applicationsState management in android applications
State management in android applications
 
Angular js
Angular jsAngular js
Angular js
 
Reactive state management with Jetpack Components
Reactive state management with Jetpack ComponentsReactive state management with Jetpack Components
Reactive state management with Jetpack Components
 
Building scalable applications with angular js
Building scalable applications with angular jsBuilding scalable applications with angular js
Building scalable applications with angular js
 
Speed up your GWT coding with gQuery
Speed up your GWT coding with gQuerySpeed up your GWT coding with gQuery
Speed up your GWT coding with gQuery
 
Jetpack, with new features in 2021 GDG Georgetown IO Extended
Jetpack, with new features in 2021 GDG Georgetown IO ExtendedJetpack, with new features in 2021 GDG Georgetown IO Extended
Jetpack, with new features in 2021 GDG Georgetown IO Extended
 
Workshop 17: EmberJS parte II
Workshop 17: EmberJS parte IIWorkshop 17: EmberJS parte II
Workshop 17: EmberJS parte II
 
Angular performance slides
Angular performance slidesAngular performance slides
Angular performance slides
 
Angular 2 Component Communication - Talk by Rob McDiarmid
Angular 2 Component Communication - Talk by Rob McDiarmidAngular 2 Component Communication - Talk by Rob McDiarmid
Angular 2 Component Communication - Talk by Rob McDiarmid
 
MBLTDev15: Egor Tolstoy, Rambler&Co
MBLTDev15: Egor Tolstoy, Rambler&CoMBLTDev15: Egor Tolstoy, Rambler&Co
MBLTDev15: Egor Tolstoy, Rambler&Co
 
Workshop 19: ReactJS Introduction
Workshop 19: ReactJS IntroductionWorkshop 19: ReactJS Introduction
Workshop 19: ReactJS Introduction
 
Async task, threads, pools, and executors oh my!
Async task, threads, pools, and executors oh my!Async task, threads, pools, and executors oh my!
Async task, threads, pools, and executors oh my!
 
Workshop 3: JavaScript build tools
Workshop 3: JavaScript build toolsWorkshop 3: JavaScript build tools
Workshop 3: JavaScript build tools
 

En vedette

Rambler.iOS #7: Интернет-эквайринг 101
Rambler.iOS #7: Интернет-эквайринг 101Rambler.iOS #7: Интернет-эквайринг 101
Rambler.iOS #7: Интернет-эквайринг 101RAMBLER&Co
 
NSNotificationCenter vs. AppDelegate
NSNotificationCenter vs. AppDelegateNSNotificationCenter vs. AppDelegate
NSNotificationCenter vs. AppDelegateJohn Wilker
 
iOS Dev Moscow: Как получать заказы по рекомендациям
iOS Dev Moscow: Как получать заказы по рекомендациямiOS Dev Moscow: Как получать заказы по рекомендациям
iOS Dev Moscow: Как получать заказы по рекомендациямAlina Mikhaylova
 
Concurrent Programming in iOS
Concurrent Programming in iOSConcurrent Programming in iOS
Concurrent Programming in iOSSam Mejlumyan
 
Как пройти собеседование и получить первую работу на Swift
Как пройти собеседование и получить первую работу на SwiftКак пройти собеседование и получить первую работу на Swift
Как пройти собеседование и получить первую работу на SwiftAnton Loginov
 
Интуит. Разработка приложений для iOS. Лекция 9. Нестандартный интерфейс
Интуит. Разработка приложений для iOS. Лекция 9. Нестандартный интерфейсИнтуит. Разработка приложений для iOS. Лекция 9. Нестандартный интерфейс
Интуит. Разработка приложений для iOS. Лекция 9. Нестандартный интерфейсГлеб Тарасов
 
Роман Бусыгин "Yandex Map Kit для iOS в примерах"
Роман Бусыгин "Yandex Map Kit для iOS в примерах"Роман Бусыгин "Yandex Map Kit для iOS в примерах"
Роман Бусыгин "Yandex Map Kit для iOS в примерах"Yandex
 
Интуит. Разработка приложений для iOS. Лекция 11. Расширенные возможности уст...
Интуит. Разработка приложений для iOS. Лекция 11. Расширенные возможности уст...Интуит. Разработка приложений для iOS. Лекция 11. Расширенные возможности уст...
Интуит. Разработка приложений для iOS. Лекция 11. Расширенные возможности уст...Глеб Тарасов
 
Архитектура компилятора Swift
Архитектура компилятора SwiftАрхитектура компилятора Swift
Архитектура компилятора SwiftAndrey Volobuev
 
Преимущества и недостатки языка Swift
Преимущества и недостатки языка SwiftПреимущества и недостатки языка Swift
Преимущества и недостатки языка SwiftAndrey Volobuev
 
CS193P Lecture 5 View Animation
CS193P Lecture 5 View AnimationCS193P Lecture 5 View Animation
CS193P Lecture 5 View Animationonoaonoa
 
Denis Lebedev, Swift
Denis  Lebedev, SwiftDenis  Lebedev, Swift
Denis Lebedev, SwiftYandex
 
Мобильный веб: назад в будущее
Мобильный веб: назад в будущееМобильный веб: назад в будущее
Мобильный веб: назад в будущееBadoo Development
 
Технологии vs коммуникации: что важнее?
Технологии vs коммуникации: что важнее?Технологии vs коммуникации: что важнее?
Технологии vs коммуникации: что важнее?Badoo Development
 
Багфиксинг процесса разработки в iOS: взгляд с двух сторон
Багфиксинг процесса разработки в iOS: взгляд с двух сторонБагфиксинг процесса разработки в iOS: взгляд с двух сторон
Багфиксинг процесса разработки в iOS: взгляд с двух сторонBadoo Development
 
Как автотесты ускоряют релизы в OK.ru
Как автотесты ускоряют релизы в OK.ruКак автотесты ускоряют релизы в OK.ru
Как автотесты ускоряют релизы в OK.ruBadoo Development
 
Кортунов Никита. Как ускорить разработку приложений или есть ли жизнь после P...
Кортунов Никита. Как ускорить разработку приложений или есть ли жизнь после P...Кортунов Никита. Как ускорить разработку приложений или есть ли жизнь после P...
Кортунов Никита. Как ускорить разработку приложений или есть ли жизнь после P...AvitoTech
 
Вадим Дробинин. Защищаем себя и пользователей: руководство по безопасности
Вадим Дробинин. Защищаем себя и пользователей: руководство по безопасностиВадим Дробинин. Защищаем себя и пользователей: руководство по безопасности
Вадим Дробинин. Защищаем себя и пользователей: руководство по безопасностиAvitoTech
 
Юзабилити и функциональность ДБО2017
Юзабилити и функциональность ДБО2017Юзабилити и функциональность ДБО2017
Юзабилити и функциональность ДБО2017Дмитрий Силаев
 

En vedette (20)

Rambler.iOS #7: Интернет-эквайринг 101
Rambler.iOS #7: Интернет-эквайринг 101Rambler.iOS #7: Интернет-эквайринг 101
Rambler.iOS #7: Интернет-эквайринг 101
 
NSNotificationCenter vs. AppDelegate
NSNotificationCenter vs. AppDelegateNSNotificationCenter vs. AppDelegate
NSNotificationCenter vs. AppDelegate
 
iOS Dev Moscow: Как получать заказы по рекомендациям
iOS Dev Moscow: Как получать заказы по рекомендациямiOS Dev Moscow: Как получать заказы по рекомендациям
iOS Dev Moscow: Как получать заказы по рекомендациям
 
Concurrent Programming in iOS
Concurrent Programming in iOSConcurrent Programming in iOS
Concurrent Programming in iOS
 
Как пройти собеседование и получить первую работу на Swift
Как пройти собеседование и получить первую работу на SwiftКак пройти собеседование и получить первую работу на Swift
Как пройти собеседование и получить первую работу на Swift
 
Интуит. Разработка приложений для iOS. Лекция 9. Нестандартный интерфейс
Интуит. Разработка приложений для iOS. Лекция 9. Нестандартный интерфейсИнтуит. Разработка приложений для iOS. Лекция 9. Нестандартный интерфейс
Интуит. Разработка приложений для iOS. Лекция 9. Нестандартный интерфейс
 
Роман Бусыгин "Yandex Map Kit для iOS в примерах"
Роман Бусыгин "Yandex Map Kit для iOS в примерах"Роман Бусыгин "Yandex Map Kit для iOS в примерах"
Роман Бусыгин "Yandex Map Kit для iOS в примерах"
 
Интуит. Разработка приложений для iOS. Лекция 11. Расширенные возможности уст...
Интуит. Разработка приложений для iOS. Лекция 11. Расширенные возможности уст...Интуит. Разработка приложений для iOS. Лекция 11. Расширенные возможности уст...
Интуит. Разработка приложений для iOS. Лекция 11. Расширенные возможности уст...
 
Архитектура компилятора Swift
Архитектура компилятора SwiftАрхитектура компилятора Swift
Архитектура компилятора Swift
 
Преимущества и недостатки языка Swift
Преимущества и недостатки языка SwiftПреимущества и недостатки языка Swift
Преимущества и недостатки языка Swift
 
CS193P Lecture 5 View Animation
CS193P Lecture 5 View AnimationCS193P Lecture 5 View Animation
CS193P Lecture 5 View Animation
 
Denis Lebedev, Swift
Denis  Lebedev, SwiftDenis  Lebedev, Swift
Denis Lebedev, Swift
 
Мобильный веб: назад в будущее
Мобильный веб: назад в будущееМобильный веб: назад в будущее
Мобильный веб: назад в будущее
 
Технологии vs коммуникации: что важнее?
Технологии vs коммуникации: что важнее?Технологии vs коммуникации: что важнее?
Технологии vs коммуникации: что важнее?
 
Багфиксинг процесса разработки в iOS: взгляд с двух сторон
Багфиксинг процесса разработки в iOS: взгляд с двух сторонБагфиксинг процесса разработки в iOS: взгляд с двух сторон
Багфиксинг процесса разработки в iOS: взгляд с двух сторон
 
Как автотесты ускоряют релизы в OK.ru
Как автотесты ускоряют релизы в OK.ruКак автотесты ускоряют релизы в OK.ru
Как автотесты ускоряют релизы в OK.ru
 
Кортунов Никита. Как ускорить разработку приложений или есть ли жизнь после P...
Кортунов Никита. Как ускорить разработку приложений или есть ли жизнь после P...Кортунов Никита. Как ускорить разработку приложений или есть ли жизнь после P...
Кортунов Никита. Как ускорить разработку приложений или есть ли жизнь после P...
 
Вадим Дробинин. Защищаем себя и пользователей: руководство по безопасности
Вадим Дробинин. Защищаем себя и пользователей: руководство по безопасностиВадим Дробинин. Защищаем себя и пользователей: руководство по безопасности
Вадим Дробинин. Защищаем себя и пользователей: руководство по безопасности
 
How to Lean
How to LeanHow to Lean
How to Lean
 
Юзабилити и функциональность ДБО2017
Юзабилити и функциональность ДБО2017Юзабилити и функциональность ДБО2017
Юзабилити и функциональность ДБО2017
 

Similaire à Rambler.iOS #6: App delegate - разделяй и властвуй

What's new in Android P @ I/O Extended Bangkok 2018
What's new in Android P @ I/O Extended Bangkok 2018What's new in Android P @ I/O Extended Bangkok 2018
What's new in Android P @ I/O Extended Bangkok 2018Somkiat Khitwongwattana
 
Quick Intro to Android Development
Quick Intro to Android DevelopmentQuick Intro to Android Development
Quick Intro to Android DevelopmentJussi Pohjolainen
 
Android app development basics
Android app development basicsAndroid app development basics
Android app development basicsAnton Narusberg
 
Bringing the open web and APIs to mobile devices with Firefox OS - Whisky W...
 	Bringing the open web and APIs to mobile devices with Firefox OS - Whisky W... 	Bringing the open web and APIs to mobile devices with Firefox OS - Whisky W...
Bringing the open web and APIs to mobile devices with Firefox OS - Whisky W...Robert Nyman
 
Apache Cordova In Action
Apache Cordova In ActionApache Cordova In Action
Apache Cordova In ActionHazem Saleh
 
Single page webapps & javascript-testing
Single page webapps & javascript-testingSingle page webapps & javascript-testing
Single page webapps & javascript-testingsmontanari
 
Introduction to Palm's Mojo SDK
Introduction to Palm's Mojo SDKIntroduction to Palm's Mojo SDK
Introduction to Palm's Mojo SDKBrendan Lim
 
Adopting 3D Touch in your apps
Adopting 3D Touch in your appsAdopting 3D Touch in your apps
Adopting 3D Touch in your appsJuan C Catalan
 
Android & iOS Automation Using Appium
Android & iOS Automation Using AppiumAndroid & iOS Automation Using Appium
Android & iOS Automation Using AppiumMindfire Solutions
 
Building an app with Google's new suites
Building an app with Google's new suitesBuilding an app with Google's new suites
Building an app with Google's new suitesToru Wonyoung Choi
 
Director x Backbone = :)
Director x Backbone = :)Director x Backbone = :)
Director x Backbone = :)Janessa Det
 
[JMaghreb 2014] Developing JavaScript Mobile Apps Using Apache Cordova
[JMaghreb 2014] Developing JavaScript Mobile Apps Using Apache Cordova[JMaghreb 2014] Developing JavaScript Mobile Apps Using Apache Cordova
[JMaghreb 2014] Developing JavaScript Mobile Apps Using Apache CordovaHazem Saleh
 
Android development with Scala and SBT
Android development with Scala and SBTAndroid development with Scala and SBT
Android development with Scala and SBTAnton Yalyshev
 
Adaptive UI - 解像度の異なるデバイスや画面の向きに対応する 最適なレイアウトへ -
Adaptive UI  - 解像度の異なるデバイスや画面の向きに対応する 最適なレイアウトへ - Adaptive UI  - 解像度の異なるデバイスや画面の向きに対応する 最適なレイアウトへ -
Adaptive UI - 解像度の異なるデバイスや画面の向きに対応する 最適なレイアウトへ - Yuji Hato
 
Writing Maintainable JavaScript
Writing Maintainable JavaScriptWriting Maintainable JavaScript
Writing Maintainable JavaScriptAndrew Dupont
 
Android Best Practices
Android Best PracticesAndroid Best Practices
Android Best PracticesYekmer Simsek
 

Similaire à Rambler.iOS #6: App delegate - разделяй и властвуй (20)

What's new in Android P @ I/O Extended Bangkok 2018
What's new in Android P @ I/O Extended Bangkok 2018What's new in Android P @ I/O Extended Bangkok 2018
What's new in Android P @ I/O Extended Bangkok 2018
 
Quick Intro to Android Development
Quick Intro to Android DevelopmentQuick Intro to Android Development
Quick Intro to Android Development
 
Mini curso Android
Mini curso AndroidMini curso Android
Mini curso Android
 
Android app development basics
Android app development basicsAndroid app development basics
Android app development basics
 
Android 3
Android 3Android 3
Android 3
 
Bringing the open web and APIs to mobile devices with Firefox OS - Whisky W...
 	Bringing the open web and APIs to mobile devices with Firefox OS - Whisky W... 	Bringing the open web and APIs to mobile devices with Firefox OS - Whisky W...
Bringing the open web and APIs to mobile devices with Firefox OS - Whisky W...
 
Apache Cordova In Action
Apache Cordova In ActionApache Cordova In Action
Apache Cordova In Action
 
Single page webapps & javascript-testing
Single page webapps & javascript-testingSingle page webapps & javascript-testing
Single page webapps & javascript-testing
 
Introduction to Palm's Mojo SDK
Introduction to Palm's Mojo SDKIntroduction to Palm's Mojo SDK
Introduction to Palm's Mojo SDK
 
Adopting 3D Touch in your apps
Adopting 3D Touch in your appsAdopting 3D Touch in your apps
Adopting 3D Touch in your apps
 
Android & iOS Automation Using Appium
Android & iOS Automation Using AppiumAndroid & iOS Automation Using Appium
Android & iOS Automation Using Appium
 
Building an app with Google's new suites
Building an app with Google's new suitesBuilding an app with Google's new suites
Building an app with Google's new suites
 
Director x Backbone = :)
Director x Backbone = :)Director x Backbone = :)
Director x Backbone = :)
 
[JMaghreb 2014] Developing JavaScript Mobile Apps Using Apache Cordova
[JMaghreb 2014] Developing JavaScript Mobile Apps Using Apache Cordova[JMaghreb 2014] Developing JavaScript Mobile Apps Using Apache Cordova
[JMaghreb 2014] Developing JavaScript Mobile Apps Using Apache Cordova
 
Lightning Talk - Xamarin
Lightning Talk - Xamarin Lightning Talk - Xamarin
Lightning Talk - Xamarin
 
Android development with Scala and SBT
Android development with Scala and SBTAndroid development with Scala and SBT
Android development with Scala and SBT
 
Adaptive UI - 解像度の異なるデバイスや画面の向きに対応する 最適なレイアウトへ -
Adaptive UI  - 解像度の異なるデバイスや画面の向きに対応する 最適なレイアウトへ - Adaptive UI  - 解像度の異なるデバイスや画面の向きに対応する 最適なレイアウトへ -
Adaptive UI - 解像度の異なるデバイスや画面の向きに対応する 最適なレイアウトへ -
 
Writing Maintainable JavaScript
Writing Maintainable JavaScriptWriting Maintainable JavaScript
Writing Maintainable JavaScript
 
Backbone.js
Backbone.jsBackbone.js
Backbone.js
 
Android Best Practices
Android Best PracticesAndroid Best Practices
Android Best Practices
 

Plus de RAMBLER&Co

RDSDataSource: Основы LLVM
RDSDataSource: Основы LLVMRDSDataSource: Основы LLVM
RDSDataSource: Основы LLVMRAMBLER&Co
 
Rambler.iOS #9: Анализируй это!
Rambler.iOS #9: Анализируй это!Rambler.iOS #9: Анализируй это!
Rambler.iOS #9: Анализируй это!RAMBLER&Co
 
Rambler.iOS #9: Нужны ли бэкенд-разработчики, когда есть Swift?
Rambler.iOS #9: Нужны ли бэкенд-разработчики, когда есть Swift?Rambler.iOS #9: Нужны ли бэкенд-разработчики, когда есть Swift?
Rambler.iOS #9: Нужны ли бэкенд-разработчики, когда есть Swift?RAMBLER&Co
 
Rambler.iOS #9: Life with out of memory
Rambler.iOS #9: Life with out of memoryRambler.iOS #9: Life with out of memory
Rambler.iOS #9: Life with out of memoryRAMBLER&Co
 
RDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на SwiftRDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на SwiftRAMBLER&Co
 
RDSDataSource: OCLint
RDSDataSource: OCLintRDSDataSource: OCLint
RDSDataSource: OCLintRAMBLER&Co
 
RDSDataSource: Построение UML диаграмм
RDSDataSource: Построение UML диаграммRDSDataSource: Построение UML диаграмм
RDSDataSource: Построение UML диаграммRAMBLER&Co
 
RDSDataSource: App Thinning
RDSDataSource: App ThinningRDSDataSource: App Thinning
RDSDataSource: App ThinningRAMBLER&Co
 
RDSDataSource: Мастер-класс по Dip
RDSDataSource: Мастер-класс по DipRDSDataSource: Мастер-класс по Dip
RDSDataSource: Мастер-класс по DipRAMBLER&Co
 
RDSDataSource: YapDatabase
RDSDataSource: YapDatabaseRDSDataSource: YapDatabase
RDSDataSource: YapDatabaseRAMBLER&Co
 
Rambler.iOS #8: Чистые unit-тесты
Rambler.iOS #8: Чистые unit-тестыRambler.iOS #8: Чистые unit-тесты
Rambler.iOS #8: Чистые unit-тестыRAMBLER&Co
 
Rambler.iOS #8: Сервис-ориентированная архитектура
Rambler.iOS #8: Сервис-ориентированная архитектураRambler.iOS #8: Сервис-ориентированная архитектура
Rambler.iOS #8: Сервис-ориентированная архитектураRAMBLER&Co
 
Rambler.iOS #8: Make your app extensible with JavaScriptCore
Rambler.iOS #8: Make your app extensible with JavaScriptCoreRambler.iOS #8: Make your app extensible with JavaScriptCore
Rambler.iOS #8: Make your app extensible with JavaScriptCoreRAMBLER&Co
 
Rambler.iOS #8: Как не стать жертвой бэкендеров
Rambler.iOS #8: Как не стать жертвой бэкендеровRambler.iOS #8: Как не стать жертвой бэкендеров
Rambler.iOS #8: Как не стать жертвой бэкендеровRAMBLER&Co
 
RDSDataSource: iOS Reverse Engineering for inexperienced
RDSDataSource: iOS Reverse Engineering for inexperiencedRDSDataSource: iOS Reverse Engineering for inexperienced
RDSDataSource: iOS Reverse Engineering for inexperiencedRAMBLER&Co
 
RDSDataSource: Автогенерация документации для SDK
RDSDataSource: Автогенерация документации для SDKRDSDataSource: Автогенерация документации для SDK
RDSDataSource: Автогенерация документации для SDKRAMBLER&Co
 
RDSDataSource: Плюрализация в iOS
RDSDataSource: Плюрализация в iOSRDSDataSource: Плюрализация в iOS
RDSDataSource: Плюрализация в iOSRAMBLER&Co
 
RDSDataSource: Promises
RDSDataSource: PromisesRDSDataSource: Promises
RDSDataSource: PromisesRAMBLER&Co
 
RDSDataSource: Flux, Redux, ReSwift
RDSDataSource: Flux, Redux, ReSwiftRDSDataSource: Flux, Redux, ReSwift
RDSDataSource: Flux, Redux, ReSwiftRAMBLER&Co
 
Rambler.iOS #7: Построение сложного табличного интерфейса
Rambler.iOS #7: Построение сложного табличного интерфейсаRambler.iOS #7: Построение сложного табличного интерфейса
Rambler.iOS #7: Построение сложного табличного интерфейсаRAMBLER&Co
 

Plus de RAMBLER&Co (20)

RDSDataSource: Основы LLVM
RDSDataSource: Основы LLVMRDSDataSource: Основы LLVM
RDSDataSource: Основы LLVM
 
Rambler.iOS #9: Анализируй это!
Rambler.iOS #9: Анализируй это!Rambler.iOS #9: Анализируй это!
Rambler.iOS #9: Анализируй это!
 
Rambler.iOS #9: Нужны ли бэкенд-разработчики, когда есть Swift?
Rambler.iOS #9: Нужны ли бэкенд-разработчики, когда есть Swift?Rambler.iOS #9: Нужны ли бэкенд-разработчики, когда есть Swift?
Rambler.iOS #9: Нужны ли бэкенд-разработчики, когда есть Swift?
 
Rambler.iOS #9: Life with out of memory
Rambler.iOS #9: Life with out of memoryRambler.iOS #9: Life with out of memory
Rambler.iOS #9: Life with out of memory
 
RDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на SwiftRDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на Swift
 
RDSDataSource: OCLint
RDSDataSource: OCLintRDSDataSource: OCLint
RDSDataSource: OCLint
 
RDSDataSource: Построение UML диаграмм
RDSDataSource: Построение UML диаграммRDSDataSource: Построение UML диаграмм
RDSDataSource: Построение UML диаграмм
 
RDSDataSource: App Thinning
RDSDataSource: App ThinningRDSDataSource: App Thinning
RDSDataSource: App Thinning
 
RDSDataSource: Мастер-класс по Dip
RDSDataSource: Мастер-класс по DipRDSDataSource: Мастер-класс по Dip
RDSDataSource: Мастер-класс по Dip
 
RDSDataSource: YapDatabase
RDSDataSource: YapDatabaseRDSDataSource: YapDatabase
RDSDataSource: YapDatabase
 
Rambler.iOS #8: Чистые unit-тесты
Rambler.iOS #8: Чистые unit-тестыRambler.iOS #8: Чистые unit-тесты
Rambler.iOS #8: Чистые unit-тесты
 
Rambler.iOS #8: Сервис-ориентированная архитектура
Rambler.iOS #8: Сервис-ориентированная архитектураRambler.iOS #8: Сервис-ориентированная архитектура
Rambler.iOS #8: Сервис-ориентированная архитектура
 
Rambler.iOS #8: Make your app extensible with JavaScriptCore
Rambler.iOS #8: Make your app extensible with JavaScriptCoreRambler.iOS #8: Make your app extensible with JavaScriptCore
Rambler.iOS #8: Make your app extensible with JavaScriptCore
 
Rambler.iOS #8: Как не стать жертвой бэкендеров
Rambler.iOS #8: Как не стать жертвой бэкендеровRambler.iOS #8: Как не стать жертвой бэкендеров
Rambler.iOS #8: Как не стать жертвой бэкендеров
 
RDSDataSource: iOS Reverse Engineering for inexperienced
RDSDataSource: iOS Reverse Engineering for inexperiencedRDSDataSource: iOS Reverse Engineering for inexperienced
RDSDataSource: iOS Reverse Engineering for inexperienced
 
RDSDataSource: Автогенерация документации для SDK
RDSDataSource: Автогенерация документации для SDKRDSDataSource: Автогенерация документации для SDK
RDSDataSource: Автогенерация документации для SDK
 
RDSDataSource: Плюрализация в iOS
RDSDataSource: Плюрализация в iOSRDSDataSource: Плюрализация в iOS
RDSDataSource: Плюрализация в iOS
 
RDSDataSource: Promises
RDSDataSource: PromisesRDSDataSource: Promises
RDSDataSource: Promises
 
RDSDataSource: Flux, Redux, ReSwift
RDSDataSource: Flux, Redux, ReSwiftRDSDataSource: Flux, Redux, ReSwift
RDSDataSource: Flux, Redux, ReSwift
 
Rambler.iOS #7: Построение сложного табличного интерфейса
Rambler.iOS #7: Построение сложного табличного интерфейсаRambler.iOS #7: Построение сложного табличного интерфейса
Rambler.iOS #7: Построение сложного табличного интерфейса
 

Dernier

9892124323 | Book Call Girls in Juhu and escort services 24x7
9892124323 | Book Call Girls in Juhu and escort services 24x79892124323 | Book Call Girls in Juhu and escort services 24x7
9892124323 | Book Call Girls in Juhu and escort services 24x7Pooja Nehwal
 
FULL ENJOY - 9999218229 Call Girls in {Mahipalpur}| Delhi NCR
FULL ENJOY - 9999218229 Call Girls in {Mahipalpur}| Delhi NCRFULL ENJOY - 9999218229 Call Girls in {Mahipalpur}| Delhi NCR
FULL ENJOY - 9999218229 Call Girls in {Mahipalpur}| Delhi NCRnishacall1
 
Chandigarh Call Girls Service ❤️🍑 9115573837 👄🫦Independent Escort Service Cha...
Chandigarh Call Girls Service ❤️🍑 9115573837 👄🫦Independent Escort Service Cha...Chandigarh Call Girls Service ❤️🍑 9115573837 👄🫦Independent Escort Service Cha...
Chandigarh Call Girls Service ❤️🍑 9115573837 👄🫦Independent Escort Service Cha...Niamh verma
 
Powerful Love Spells in Arkansas, AR (310) 882-6330 Bring Back Lost Lover
Powerful Love Spells in Arkansas, AR (310) 882-6330 Bring Back Lost LoverPowerful Love Spells in Arkansas, AR (310) 882-6330 Bring Back Lost Lover
Powerful Love Spells in Arkansas, AR (310) 882-6330 Bring Back Lost LoverPsychicRuben LoveSpells
 
CALL ON ➥8923113531 🔝Call Girls Saharaganj Lucknow best sexual service
CALL ON ➥8923113531 🔝Call Girls Saharaganj Lucknow best sexual serviceCALL ON ➥8923113531 🔝Call Girls Saharaganj Lucknow best sexual service
CALL ON ➥8923113531 🔝Call Girls Saharaganj Lucknow best sexual serviceanilsa9823
 
哪里有卖的《俄亥俄大学学历证书+俄亥俄大学文凭证书+俄亥俄大学学位证书》Q微信741003700《俄亥俄大学学位证书复制》办理俄亥俄大学毕业证成绩单|购买...
哪里有卖的《俄亥俄大学学历证书+俄亥俄大学文凭证书+俄亥俄大学学位证书》Q微信741003700《俄亥俄大学学位证书复制》办理俄亥俄大学毕业证成绩单|购买...哪里有卖的《俄亥俄大学学历证书+俄亥俄大学文凭证书+俄亥俄大学学位证书》Q微信741003700《俄亥俄大学学位证书复制》办理俄亥俄大学毕业证成绩单|购买...
哪里有卖的《俄亥俄大学学历证书+俄亥俄大学文凭证书+俄亥俄大学学位证书》Q微信741003700《俄亥俄大学学位证书复制》办理俄亥俄大学毕业证成绩单|购买...wyqazy
 
Call US Pooja 9892124323 ✓Call Girls In Mira Road ( Mumbai ) secure service,
Call US Pooja 9892124323 ✓Call Girls In Mira Road ( Mumbai ) secure service,Call US Pooja 9892124323 ✓Call Girls In Mira Road ( Mumbai ) secure service,
Call US Pooja 9892124323 ✓Call Girls In Mira Road ( Mumbai ) secure service,Pooja Nehwal
 
CALL ON ➥8923113531 🔝Call Girls Gomti Nagar Lucknow best Night Fun service
CALL ON ➥8923113531 🔝Call Girls Gomti Nagar Lucknow best Night Fun serviceCALL ON ➥8923113531 🔝Call Girls Gomti Nagar Lucknow best Night Fun service
CALL ON ➥8923113531 🔝Call Girls Gomti Nagar Lucknow best Night Fun serviceanilsa9823
 
BDSM⚡Call Girls in Sector 71 Noida Escorts >༒8448380779 Escort Service
BDSM⚡Call Girls in Sector 71 Noida Escorts >༒8448380779 Escort ServiceBDSM⚡Call Girls in Sector 71 Noida Escorts >༒8448380779 Escort Service
BDSM⚡Call Girls in Sector 71 Noida Escorts >༒8448380779 Escort ServiceDelhi Call girls
 

Dernier (9)

9892124323 | Book Call Girls in Juhu and escort services 24x7
9892124323 | Book Call Girls in Juhu and escort services 24x79892124323 | Book Call Girls in Juhu and escort services 24x7
9892124323 | Book Call Girls in Juhu and escort services 24x7
 
FULL ENJOY - 9999218229 Call Girls in {Mahipalpur}| Delhi NCR
FULL ENJOY - 9999218229 Call Girls in {Mahipalpur}| Delhi NCRFULL ENJOY - 9999218229 Call Girls in {Mahipalpur}| Delhi NCR
FULL ENJOY - 9999218229 Call Girls in {Mahipalpur}| Delhi NCR
 
Chandigarh Call Girls Service ❤️🍑 9115573837 👄🫦Independent Escort Service Cha...
Chandigarh Call Girls Service ❤️🍑 9115573837 👄🫦Independent Escort Service Cha...Chandigarh Call Girls Service ❤️🍑 9115573837 👄🫦Independent Escort Service Cha...
Chandigarh Call Girls Service ❤️🍑 9115573837 👄🫦Independent Escort Service Cha...
 
Powerful Love Spells in Arkansas, AR (310) 882-6330 Bring Back Lost Lover
Powerful Love Spells in Arkansas, AR (310) 882-6330 Bring Back Lost LoverPowerful Love Spells in Arkansas, AR (310) 882-6330 Bring Back Lost Lover
Powerful Love Spells in Arkansas, AR (310) 882-6330 Bring Back Lost Lover
 
CALL ON ➥8923113531 🔝Call Girls Saharaganj Lucknow best sexual service
CALL ON ➥8923113531 🔝Call Girls Saharaganj Lucknow best sexual serviceCALL ON ➥8923113531 🔝Call Girls Saharaganj Lucknow best sexual service
CALL ON ➥8923113531 🔝Call Girls Saharaganj Lucknow best sexual service
 
哪里有卖的《俄亥俄大学学历证书+俄亥俄大学文凭证书+俄亥俄大学学位证书》Q微信741003700《俄亥俄大学学位证书复制》办理俄亥俄大学毕业证成绩单|购买...
哪里有卖的《俄亥俄大学学历证书+俄亥俄大学文凭证书+俄亥俄大学学位证书》Q微信741003700《俄亥俄大学学位证书复制》办理俄亥俄大学毕业证成绩单|购买...哪里有卖的《俄亥俄大学学历证书+俄亥俄大学文凭证书+俄亥俄大学学位证书》Q微信741003700《俄亥俄大学学位证书复制》办理俄亥俄大学毕业证成绩单|购买...
哪里有卖的《俄亥俄大学学历证书+俄亥俄大学文凭证书+俄亥俄大学学位证书》Q微信741003700《俄亥俄大学学位证书复制》办理俄亥俄大学毕业证成绩单|购买...
 
Call US Pooja 9892124323 ✓Call Girls In Mira Road ( Mumbai ) secure service,
Call US Pooja 9892124323 ✓Call Girls In Mira Road ( Mumbai ) secure service,Call US Pooja 9892124323 ✓Call Girls In Mira Road ( Mumbai ) secure service,
Call US Pooja 9892124323 ✓Call Girls In Mira Road ( Mumbai ) secure service,
 
CALL ON ➥8923113531 🔝Call Girls Gomti Nagar Lucknow best Night Fun service
CALL ON ➥8923113531 🔝Call Girls Gomti Nagar Lucknow best Night Fun serviceCALL ON ➥8923113531 🔝Call Girls Gomti Nagar Lucknow best Night Fun service
CALL ON ➥8923113531 🔝Call Girls Gomti Nagar Lucknow best Night Fun service
 
BDSM⚡Call Girls in Sector 71 Noida Escorts >༒8448380779 Escort Service
BDSM⚡Call Girls in Sector 71 Noida Escorts >༒8448380779 Escort ServiceBDSM⚡Call Girls in Sector 71 Noida Escorts >༒8448380779 Escort Service
BDSM⚡Call Girls in Sector 71 Noida Escorts >༒8448380779 Escort Service
 

Rambler.iOS #6: App delegate - разделяй и властвуй

  • 1. AppDelegate разделяй и властвуй Вадим Смаль iOS разработчик Rambler&Co
  • 3. • Реагирует на получение уведомлений • Реагирует на ключевые изменения в состоянии вашего приложения • Реагирует на события, которые нацелены на само приложение • Управляет процессом сохранения и восстановления состояния приложения
  • 4. Запуск приложения Изменение состояния приложения Восстановление состояния приложения Загрузка данных в фоне Локальные и удаленные уведомления Пользовательская активность WatchKit Открытие URL’ов HealthKit Системные события Разрешения для расширений Геометрия интерфейса Window CoreData SharedInstance Quick Actions <UIApplicationDelegate>
  • 5. import Shared import Storage import AVFoundation import XCGLogger import Breakpad import MessageUI import WebImage import SwiftKeychainWrapper import LocalAuthentication private let log = Logger.browserLogger let LatestAppVersionProfileKey = "latestAppVersion" let AllowThirdPartyKeyboardsKey = "settings.allowThirdPartyKeyboards" class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? var browserViewController: BrowserViewController! var rootViewController: UINavigationController! weak var profile: BrowserProfile? var tabManager: TabManager! var adjustIntegration: AdjustIntegration? weak var application: UIApplication? var launchOptions: [NSObject: AnyObject]? let appVersion = NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleShortVersionString") as! String var openInFirefoxURL: NSURL? = nil func application(application: UIApplication, willFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { // Hold references to willFinishLaunching parameters for delayed app launch self.application = application self.launchOptions = launchOptions log.debug("Configuring window…") self.window = UIWindow(frame: UIScreen.mainScreen().bounds) self.window!.backgroundColor = UIConstants.AppBackgroundColor // Short circuit the app if we want to email logs from the debug menu if DebugSettingsBundleOptions.launchIntoEmailComposer { self.window?.rootViewController = UIViewController() presentEmailComposerWithLogs() return true } else { return startApplication(application, withLaunchOptions: launchOptions) } } Импорты Зависимости WINDOW startApplication
  • 6. private func startApplication(application: UIApplication, withLaunchOptions launchOptions: [NSObject: AnyObject]?) -> Bool { log.debug("Setting UA…") // Set the Firefox UA for browsing. setUserAgent() log.debug("Starting keyboard helper…") // Start the keyboard helper to monitor and cache keyboard state. KeyboardHelper.defaultHelper.startObserving() log.debug("Starting dynamic font helper…") // Start the keyboard helper to monitor and cache keyboard state. DynamicFontHelper.defaultHelper.startObserving() log.debug("Setting custom menu items…") MenuHelper.defaultHelper.setItems() log.debug("Creating Sync log file…") let logDate = NSDate() // Create a new sync log file on cold app launch. Note that this doesn't roll old logs. Logger.syncLogger.newLogWithDate(logDate) log.debug("Creating corrupt DB logger…") Logger.corruptLogger.newLogWithDate(logDate) log.debug("Creating Browser log file…") Logger.browserLogger.newLogWithDate(logDate) log.debug("Getting profile…") let profile = getProfile(application) if !DebugSettingsBundleOptions.disableLocalWebServer { log.debug("Starting web server…") // Set up a web server that serves us static content. Do this early so that it is ready when the UI is presented. setUpWebServer(profile) } log.debug("Setting AVAudioSession category…") do { // for aural progress bar: play even with silent switch on, and do not stop audio from other apps (like music) try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, withOptions: AVAudioSessionCategoryOptions.MixWithOthers) } catch _ { log.error("Failed to assign AVAudioSession category to allow playing with silent switch on for aural progress bar") } Настройка приложения
  • 7. let defaultRequest = NSURLRequest(URL: UIConstants.DefaultHomePage) let imageStore = DiskImageStore(files: profile.files, namespace: "TabManagerScreenshots", quality: UIConstants.ScreenshotQuality) log.debug("Configuring tabManager…") self.tabManager = TabManager(defaultNewTabRequest: defaultRequest, prefs: profile.prefs, imageStore: imageStore) self.tabManager.stateDelegate = self // Add restoration class, the factory that will return the ViewController we // will restore with. log.debug("Initing BVC…") browserViewController = BrowserViewController(profile: self.profile!, tabManager: self.tabManager) browserViewController.restorationIdentifier = NSStringFromClass(BrowserViewController.self) browserViewController.restorationClass = AppDelegate.self browserViewController.automaticallyAdjustsScrollViewInsets = false rootViewController = UINavigationController(rootViewController: browserViewController) rootViewController.automaticallyAdjustsScrollViewInsets = false rootViewController.delegate = self rootViewController.navigationBarHidden = true self.window!.rootViewController = rootViewController log.debug("Configuring Breakpad…") activeCrashReporter = BreakpadCrashReporter(breakpadInstance: BreakpadController.sharedInstance()) configureActiveCrashReporter(profile.prefs.boolForKey("crashreports.send.always")) log.debug("Adding observers…") NSNotificationCenter.defaultCenter().addObserverForName(FSReadingListAddReadingListItemNotification, object: nil, queue: nil) { (notification) -> Void in if let userInfo = notification.userInfo, url = userInfo["URL"] as? NSURL { let title = (userInfo["Title"] as? String) ?? "" profile.readingList?.createRecordWithURL(url.absoluteString, title: title, addedBy: UIDevice.currentDevice().name) } } // check to see if we started 'cos someone tapped on a notification. if let localNotification = launchOptions?[UIApplicationLaunchOptionsLocalNotificationKey] as? UILocalNotification { viewURLInNewTab(localNotification) } adjustIntegration = AdjustIntegration(profile: profile) // We need to check if the app is a clean install to use for // preventing the What's New URL from appearing. if getProfile(application).prefs.intForKey(IntroViewControllerSeenProfileKey) == nil { getProfile(application).prefs.setString(AppInfo.appVersion, forKey: LatestAppVersionProfileKey) } log.debug("Updating authentication keychain state to reflect system state") self.updateAuthenticationInfo() log.debug("Done with setting up the application.") return true }
  • 8. func applicationWillTerminate(application: UIApplication) { log.debug("Application will terminate.") // We have only five seconds here, so let's hope this doesn't take too long. self.profile?.shutdown() // Allow deinitializers to close our database connections. self.profile = nil self.tabManager = nil self.browserViewController = nil self.rootViewController = nil } func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool { // Override point for customization after application launch. var shouldPerformAdditionalDelegateHandling = true log.debug("Did finish launching.") log.debug("Setting up Adjust") self.adjustIntegration?.triggerApplicationDidFinishLaunchingWithOptions(launchOptions) log.debug("Making window key and visible…") self.window!.makeKeyAndVisible() // Now roll logs. log.debug("Triggering log roll.") dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) { Logger.syncLogger.deleteOldLogsDownToSizeLimit() Logger.browserLogger.deleteOldLogsDownToSizeLimit() } if #available(iOS 9, *) { // If a shortcut was launched, display its information and take the appropriate action if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsShortcutItemKey] as? UIApplicationShortcutItem { QuickActions.sharedInstance.launchedShortcutItem = shortcutItem // This will block "performActionForShortcutItem:completionHandler" from being called. shouldPerformAdditionalDelegateHandling = false } } log.debug("Done with applicationDidFinishLaunching.") return shouldPerformAdditionalDelegateHandling } Quick Actions
  • 9. func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool { if let components = NSURLComponents(URL: url, resolvingAgainstBaseURL: false) { if components.scheme != "firefox" && components.scheme != "firefox-x-callback" { return false } var url: String? for item in (components.queryItems ?? []) as [NSURLQueryItem] { switch item.name { case "url": url = item.value default: () } } if let url = url, newURL = NSURL(string: url.unescape()) { // If we are active then we can ask the BVC to open the new tab right away. Else we remember the // URL and we open it in applicationDidBecomeActive. if application.applicationState == .Active { if #available(iOS 9, *) { self.browserViewController.switchToPrivacyMode(isPrivate: false) } self.browserViewController.openURLInNewTab(newURL) } else { openInFirefoxURL = newURL } return true } } return false } func application(application: UIApplication, shouldAllowExtensionPointIdentifier extensionPointIdentifier: String) -> Bool { if let thirdPartyKeyboardSettingBool = getProfile(application).prefs.boolForKey(AllowThirdPartyKeyboardsKey) where extensionPointIdentifier == UIApplicationKeyboardExtensionPointIdentifier { return thirdPartyKeyboardSettingBool } return true } Открытие URL’ов Разрешения для расширений
  • 10. func applicationDidBecomeActive(application: UIApplication) { guard !DebugSettingsBundleOptions.launchIntoEmailComposer else { return } self.profile?.syncManager.applicationDidBecomeActive() // We could load these here, but then we have to futz with the tab counter // and making NSURLRequests. self.browserViewController.loadQueuedTabs() // handle quick actions is available if #available(iOS 9, *) { let quickActions = QuickActions.sharedInstance if let shortcut = quickActions.launchedShortcutItem { // dispatch asynchronously so that BVC is all set up for handling new tabs // when we try and open them quickActions.handleShortCutItem(shortcut, withBrowserViewController: browserViewController) quickActions.launchedShortcutItem = nil } // we've removed the Last Tab option, so we should remove any quick actions that we already have that are last tabs // we do this after we've handled any quick actions that have been used to open the app so that we don't b0rk if // the user has opened the app for the first time after upgrade with a Last Tab quick action QuickActions.sharedInstance.removeDynamicApplicationShortcutItemOfType(ShortcutType.OpenLastTab, fromApplication: application) } // If we have a URL waiting to open, switch to non-private mode and open the URL. if let url = openInFirefoxURL { openInFirefoxURL = nil // This needs to be scheduled so that the BVC is ready. dispatch_async(dispatch_get_main_queue()) { if #available(iOS 9, *) { self.browserViewController.switchToPrivacyMode(isPrivate: false) } self.browserViewController.switchToTabForURLOrOpen(url) } } } func applicationWillEnterForeground(application: UIApplication) { // The reason we need to call this method here instead of `applicationDidBecomeActive` // is that this method is only invoked whenever the application is entering the foreground where as // `applicationDidBecomeActive` will get called whenever the Touch ID authentication overlay disappears. self.updateAuthenticationInfo() } private func updateAuthenticationInfo() { if let authInfo = KeychainWrapper.authenticationInfo() { if !LAContext().canEvaluatePolicy(.DeviceOwnerAuthenticationWithBiometrics, error: nil) { authInfo.useTouchID = false KeychainWrapper.setAuthenticationInfo(authInfo) } } } Quick Actions
  • 11. func applicationDidEnterBackground(application: UIApplication) { self.profile?.syncManager.applicationDidEnterBackground() var taskId: UIBackgroundTaskIdentifier = 0 taskId = application.beginBackgroundTaskWithExpirationHandler { _ in log.warning("Running out of background time, but we have a profile shutdown pending.") application.endBackgroundTask(taskId) } dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) { self.profile?.shutdown() application.endBackgroundTask(taskId) } // Workaround for crashing in the background when <select> popovers are visible (rdar://24571325). let jsBlurSelect = "if (document.activeElement && document.activeElement.tagName === 'SELECT') { document.activeElement.blur(); }" tabManager.selectedTab?.webView?.evaluateJavaScript(jsBlurSelect, completionHandler: nil) } func application(application: UIApplication, handleActionWithIdentifier identifier: String?, forLocalNotification notification: UILocalNotification, completionHandler: () -> Void) { if let actionId = identifier { if let action = SentTabAction(rawValue: actionId) { viewURLInNewTab(notification) switch(action) { case .Bookmark: addBookmark(notification) break case .ReadingList: addToReadingList(notification) break default: break } } else { print("ERROR: Unknown notification action received") } } else { print("ERROR: Unknown notification received") } } func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification) { viewURLInNewTab(notification) } Загрузка данных в фоне Локальные и удаленные уведомления
  • 12. func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool { if let url = userActivity.webpageURL { browserViewController.switchToTabForURLOrOpen(url) return true } return false } @available(iOS 9.0, *) func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: Bool -> Void) { let handledShortCutItem = QuickActions.sharedInstance.handleShortCutItem(shortcutItem, withBrowserViewController: browserViewController) completionHandler(handledShortCutItem) } var activeCrashReporter: CrashReporter? func configureActiveCrashReporter(optedIn: Bool?) { if let reporter = activeCrashReporter { configureCrashReporter(reporter, optedIn: optedIn) } } public func configureCrashReporter(reporter: CrashReporter, optedIn: Bool?) { let configureReporter: () -> () = { let addUploadParameterForKey: String -> Void = { key in if let value = NSBundle.mainBundle().objectForInfoDictionaryKey(key) as? String { reporter.addUploadParameter(value, forKey: key) } } addUploadParameterForKey("AppID") addUploadParameterForKey("BuildID") addUploadParameterForKey("ReleaseChannel") addUploadParameterForKey("Vendor") } if let optedIn = optedIn { // User has explicitly opted-in for sending crash reports. If this is not true, then the user has // explicitly opted-out of crash reporting so don't bother starting breakpad or stop if it was running if optedIn { reporter.start(true) configureReporter() reporter.setUploadingEnabled(true) } else { reporter.stop() } } // We haven't asked the user for their crash reporting preference yet. Log crashes anyways but don't send them. else { reporter.start(true) configureReporter() } } Пользовательска я активность Пользовательска я активность ThirdParties
  • 13. // MARK: - Root View Controller Animations extension AppDelegate: UINavigationControllerDelegate { func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { if operation == UINavigationControllerOperation.Push { return BrowserToTrayAnimator() } else if operation == UINavigationControllerOperation.Pop { return TrayToBrowserAnimator() } else { return nil } } } extension AppDelegate: TabManagerStateDelegate { func tabManagerWillStoreTabs(tabs: [Browser]) { // It is possible that not all tabs have loaded yet, so we filter out tabs with a nil URL. let storedTabs: [RemoteTab] = tabs.flatMap( Browser.toTab ) // Don't insert into the DB immediately. We tend to contend with more important // work like querying for top sites. let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0) dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(ProfileRemoteTabsSyncDelay * Double(NSEC_PER_MSEC))), queue) { self.profile?.storeTabs(storedTabs) } } } extension AppDelegate: MFMailComposeViewControllerDelegate { func mailComposeController(controller: MFMailComposeViewController, didFinishWithResult result: MFMailComposeResult, error: NSError?) { // Dismiss the view controller and start the app up controller.dismissViewControllerAnimated(true, completion: nil) startApplication(application!, withLaunchOptions: self.launchOptions) } } Стек навигации
  • 14. AppDelegate: 45 методов ~1000 строк кода ~ 30 зависимостей
  • 18. didFinishLaunchingWithOptions { [self.thirdPartiesConfigurator configure] [self.startConfigurator configure] … [self.applicationConfigurator configure] [self.handoffHandler activate] [self.spotlightIndexer activate] … [self.quickActionHandler activate] … }
  • 19. @interface AppDelegate : UIResponder <UIApplicationDelegate> @property UIWindow *window; @property startConfigurator; @property thirdPartiesConfigurator; @property applicationConfigurator; ... @property handoffHandler; @property quickActionHandler ; @property NSArray *urlHandlers; @property forceLogoutHandler; ... @end
  • 20. AppDelegate: 45 методов ~200 строк кода ~20 зависимостей
  • 24. @implementation RemoteNotificationAppDelegate - didFinishLaunchingWithOptions { if (notification) { [self.pushNotificationCenter process:notification]; } return YES; } - didRegisterForRemoteNotificationsWithDeviceToken: { [self.pushNotificationCenter didRegisterDeviceToken:token]; } - didReceiveRemoteNotification { [self.pushNotificationCenter processWithUserInfo:userInfo]; } @end
  • 25. AppDelegate: ~ 3 метода ~50 строк кода ~2 зависимостей
  • 29. int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([RCMAppDelegateProxy class])); } }
  • 30. @interface RCMAppDelegateProxy : NSProxy - (void)addAppDelegate:(id<UIApplicationDelegate>)delegate; - (void)addAppDelegates:(NSArray *)delegates; @end