├── .gitignore ├── DemoApp ├── Shared │ ├── ItemDataSource.swift │ └── SharedConstants.swift ├── XPNCDemoApp WatchKit App │ ├── Base.lproj │ │ └── Interface.storyboard │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ └── Info.plist ├── XPNCDemoApp WatchKit Extension │ ├── Images.xcassets │ │ └── README__ignoredByTemplate__ │ ├── Info.plist │ ├── InterfaceController.swift │ ├── RowController.swift │ └── XPNCDemoApp WatchKit Extension.entitlements ├── XPNCDemoApp.xcodeproj │ └── project.pbxproj └── XPNCDemoApp │ ├── AppDelegate.swift │ ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard │ ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Info.plist │ ├── ViewController.swift │ └── XPNCDemoApp.entitlements ├── LICENSE ├── README.md ├── XPNotificationCenter.swift └── screenshots └── demo.gif /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | __MACOSX 3 | *.pbxuser 4 | !default.pbxuser 5 | *.mode1v3 6 | !default.mode1v3 7 | *.mode2v3 8 | !default.mode2v3 9 | *.perspectivev3 10 | !default.perspectivev3 11 | *.xcworkspace 12 | !default.xcworkspace 13 | xcuserdata 14 | profile 15 | *.moved-aside 16 | DerivedData 17 | .idea/ -------------------------------------------------------------------------------- /DemoApp/Shared/ItemDataSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ItemDataSource.swift 3 | // XPNCDemoApp 4 | // 5 | // Created by Guilherme Rambo on 23/08/15. 6 | // Copyright (c) 2015 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class ItemDataSource { 12 | 13 | let defaults = NSUserDefaults(suiteName: Constants.defaultsSuiteName)! 14 | var items: [String] { 15 | get { 16 | if let storedItems = defaults.objectForKey(Constants.itemsKey) as? [String] { 17 | return storedItems 18 | } else { 19 | let defaultItems = ["Item 0", "Item 1", "Item 2", "Item 3"] 20 | defaults.setObject(defaultItems, forKey: Constants.itemsKey) 21 | return defaultItems 22 | } 23 | } 24 | set { 25 | defaults.setObject(newValue as AnyObject, forKey: Constants.itemsKey) 26 | syncrhonize() 27 | } 28 | } 29 | 30 | func syncrhonize() { 31 | defaults.synchronize() 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /DemoApp/Shared/SharedConstants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SharedConstants.swift 3 | // XPNCDemoApp 4 | // 5 | // Created by Guilherme Rambo on 23/08/15. 6 | // Copyright (c) 2015 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct Constants { 12 | static let defaultsSuiteName = "group.br.com.guilhermerambo.XPNCDemoApp2" 13 | static let itemsKey = "items" 14 | } 15 | 16 | struct NotificationNames { 17 | static let itemListAdded = "br.com.guilhermerambo.XPNCDemoApp.itemListAdded" 18 | static let itemListDeleted = "br.com.guilhermerambo.XPNCDemoApp.itemListDeleted" 19 | } -------------------------------------------------------------------------------- /DemoApp/XPNCDemoApp WatchKit App/Base.lproj/Interface.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 |
28 | 29 | 30 | 31 |
32 |
33 |
34 |
35 |
36 | -------------------------------------------------------------------------------- /DemoApp/XPNCDemoApp WatchKit App/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "24x24", 5 | "idiom" : "watch", 6 | "scale" : "2x", 7 | "role" : "notificationCenter", 8 | "subtype" : "38mm" 9 | }, 10 | { 11 | "size" : "27.5x27.5", 12 | "idiom" : "watch", 13 | "scale" : "2x", 14 | "role" : "notificationCenter", 15 | "subtype" : "42mm" 16 | }, 17 | { 18 | "size" : "29x29", 19 | "idiom" : "watch", 20 | "role" : "companionSettings", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "size" : "29x29", 25 | "idiom" : "watch", 26 | "role" : "companionSettings", 27 | "scale" : "3x" 28 | }, 29 | { 30 | "size" : "40x40", 31 | "idiom" : "watch", 32 | "scale" : "2x", 33 | "role" : "appLauncher", 34 | "subtype" : "38mm" 35 | }, 36 | { 37 | "size" : "44x44", 38 | "idiom" : "watch", 39 | "scale" : "2x", 40 | "role" : "longLook", 41 | "subtype" : "42mm" 42 | }, 43 | { 44 | "size" : "86x86", 45 | "idiom" : "watch", 46 | "scale" : "2x", 47 | "role" : "quickLook", 48 | "subtype" : "38mm" 49 | }, 50 | { 51 | "size" : "98x98", 52 | "idiom" : "watch", 53 | "scale" : "2x", 54 | "role" : "quickLook", 55 | "subtype" : "42mm" 56 | } 57 | ], 58 | "info" : { 59 | "version" : 1, 60 | "author" : "xcode" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /DemoApp/XPNCDemoApp WatchKit App/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | XPNCDemoApp 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | br.com.guilhermerambo.XPNCDemoApp2.watchkitapp 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 2 25 | UISupportedInterfaceOrientations 26 | 27 | UIInterfaceOrientationPortrait 28 | UIInterfaceOrientationPortraitUpsideDown 29 | 30 | WKCompanionAppBundleIdentifier 31 | br.com.guilhermerambo.XPNCDemoApp2 32 | WKWatchKitApp 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /DemoApp/XPNCDemoApp WatchKit Extension/Images.xcassets/README__ignoredByTemplate__: -------------------------------------------------------------------------------- 1 | Did you know that git does not support storing empty directories? 2 | -------------------------------------------------------------------------------- /DemoApp/XPNCDemoApp WatchKit Extension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | XPNCDemoApp WatchKit Extension 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | br.com.guilhermerambo.XPNCDemoApp2.watchkitextension 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 2 25 | NSExtension 26 | 27 | NSExtensionAttributes 28 | 29 | WKAppBundleIdentifier 30 | br.com.guilhermerambo.XPNCDemoApp2.watchkitapp 31 | 32 | NSExtensionPointIdentifier 33 | com.apple.watchkit 34 | 35 | RemoteInterfacePrincipalClass 36 | $(PRODUCT_MODULE_NAME).InterfaceController 37 | 38 | 39 | -------------------------------------------------------------------------------- /DemoApp/XPNCDemoApp WatchKit Extension/InterfaceController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InterfaceController.swift 3 | // XPNCDemoApp WatchKit Extension 4 | // 5 | // Created by Guilherme Rambo on 23/08/15. 6 | // Copyright (c) 2015 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | import WatchKit 10 | import Foundation 11 | 12 | 13 | class InterfaceController: WKInterfaceController { 14 | 15 | @IBOutlet weak var table: WKInterfaceTable! 16 | 17 | let dataSource = ItemDataSource() 18 | 19 | var items: [String] { 20 | get { 21 | return dataSource.items 22 | } 23 | set { 24 | dataSource.items = newValue 25 | } 26 | } 27 | 28 | override func awakeWithContext(context: AnyObject?) { 29 | super.awakeWithContext(context) 30 | 31 | XPNotificationCenter.defaultCenter.addObserver(self, name: NotificationNames.itemListAdded) { note in 32 | println("WatchKit extension received notification: \(note)") 33 | 34 | self.updateTable() 35 | self.table.scrollToRowAtIndex(self.items.count-1) 36 | } 37 | XPNotificationCenter.defaultCenter.addObserver(self, name: NotificationNames.itemListDeleted) { note in 38 | println("WatchKit extension received notification: \(note)") 39 | 40 | self.updateTable() 41 | } 42 | } 43 | 44 | override func willActivate() { 45 | updateTable() 46 | 47 | super.willActivate() 48 | } 49 | 50 | override func didDeactivate() { 51 | // This method is called when watch view controller is no longer visible 52 | super.didDeactivate() 53 | } 54 | 55 | func updateTable() { 56 | dataSource.syncrhonize() 57 | 58 | table.setNumberOfRows(items.count, withRowType: "row") 59 | var index = 0 60 | for item in items { 61 | let rowController = table.rowControllerAtIndex(index) as! RowController 62 | rowController.item = item 63 | index++ 64 | } 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /DemoApp/XPNCDemoApp WatchKit Extension/RowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RowController.swift 3 | // XPNCDemoApp 4 | // 5 | // Created by Guilherme Rambo on 23/08/15. 6 | // Copyright (c) 2015 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import WatchKit 11 | 12 | class RowController: NSObject { 13 | 14 | var item: String! { 15 | didSet { 16 | titleLabel.setText(item) 17 | } 18 | } 19 | 20 | @IBOutlet weak var titleLabel: WKInterfaceLabel! 21 | } 22 | -------------------------------------------------------------------------------- /DemoApp/XPNCDemoApp WatchKit Extension/XPNCDemoApp WatchKit Extension.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.application-groups 6 | 7 | group.br.com.guilhermerambo.XPNCDemoApp2 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /DemoApp/XPNCDemoApp.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | DD75533D1B8A3F54007E4FF6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD75533C1B8A3F54007E4FF6 /* AppDelegate.swift */; }; 11 | DD75533F1B8A3F54007E4FF6 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD75533E1B8A3F54007E4FF6 /* ViewController.swift */; }; 12 | DD7553421B8A3F54007E4FF6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DD7553401B8A3F54007E4FF6 /* Main.storyboard */; }; 13 | DD7553441B8A3F54007E4FF6 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DD7553431B8A3F54007E4FF6 /* Images.xcassets */; }; 14 | DD7553471B8A3F54007E4FF6 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = DD7553451B8A3F54007E4FF6 /* LaunchScreen.xib */; }; 15 | DD7553721B8A401E007E4FF6 /* InterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7553711B8A401E007E4FF6 /* InterfaceController.swift */; }; 16 | DD7553741B8A401E007E4FF6 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DD7553731B8A401E007E4FF6 /* Images.xcassets */; }; 17 | DD7553781B8A401E007E4FF6 /* XPNCDemoApp WatchKit App.app in Resources */ = {isa = PBXBuildFile; fileRef = DD7553771B8A401E007E4FF6 /* XPNCDemoApp WatchKit App.app */; }; 18 | DD7553801B8A401E007E4FF6 /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DD75537E1B8A401E007E4FF6 /* Interface.storyboard */; }; 19 | DD7553821B8A401E007E4FF6 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DD7553811B8A401E007E4FF6 /* Images.xcassets */; }; 20 | DD7553851B8A401E007E4FF6 /* XPNCDemoApp WatchKit Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = DD75536D1B8A401E007E4FF6 /* XPNCDemoApp WatchKit Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 21 | DD7553971B8AAD03007E4FF6 /* RowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7553961B8AAD03007E4FF6 /* RowController.swift */; }; 22 | DDBF20BE1B8B653800F1280F /* ItemDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDBF20BC1B8B653800F1280F /* ItemDataSource.swift */; }; 23 | DDBF20BF1B8B653800F1280F /* ItemDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDBF20BC1B8B653800F1280F /* ItemDataSource.swift */; }; 24 | DDBF20C01B8B653800F1280F /* SharedConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDBF20BD1B8B653800F1280F /* SharedConstants.swift */; }; 25 | DDBF20C11B8B653800F1280F /* SharedConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDBF20BD1B8B653800F1280F /* SharedConstants.swift */; }; 26 | DDBF20C31B8B65D000F1280F /* XPNotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDBF20C21B8B65D000F1280F /* XPNotificationCenter.swift */; }; 27 | DDBF20C41B8B65D000F1280F /* XPNotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDBF20C21B8B65D000F1280F /* XPNotificationCenter.swift */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXContainerItemProxy section */ 31 | DD7553791B8A401E007E4FF6 /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = DD75532F1B8A3F54007E4FF6 /* Project object */; 34 | proxyType = 1; 35 | remoteGlobalIDString = DD7553761B8A401E007E4FF6; 36 | remoteInfo = "XPNCDemoApp WatchKit App"; 37 | }; 38 | DD7553831B8A401E007E4FF6 /* PBXContainerItemProxy */ = { 39 | isa = PBXContainerItemProxy; 40 | containerPortal = DD75532F1B8A3F54007E4FF6 /* Project object */; 41 | proxyType = 1; 42 | remoteGlobalIDString = DD75536C1B8A401E007E4FF6; 43 | remoteInfo = "XPNCDemoApp WatchKit Extension"; 44 | }; 45 | /* End PBXContainerItemProxy section */ 46 | 47 | /* Begin PBXCopyFilesBuildPhase section */ 48 | DD75538C1B8A401E007E4FF6 /* Embed App Extensions */ = { 49 | isa = PBXCopyFilesBuildPhase; 50 | buildActionMask = 2147483647; 51 | dstPath = ""; 52 | dstSubfolderSpec = 13; 53 | files = ( 54 | DD7553851B8A401E007E4FF6 /* XPNCDemoApp WatchKit Extension.appex in Embed App Extensions */, 55 | ); 56 | name = "Embed App Extensions"; 57 | runOnlyForDeploymentPostprocessing = 0; 58 | }; 59 | /* End PBXCopyFilesBuildPhase section */ 60 | 61 | /* Begin PBXFileReference section */ 62 | DD7553371B8A3F54007E4FF6 /* XPNCDemoApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XPNCDemoApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | DD75533B1B8A3F54007E4FF6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 64 | DD75533C1B8A3F54007E4FF6 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 65 | DD75533E1B8A3F54007E4FF6 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 66 | DD7553411B8A3F54007E4FF6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 67 | DD7553431B8A3F54007E4FF6 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 68 | DD7553461B8A3F54007E4FF6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 69 | DD7553511B8A3F54007E4FF6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 70 | DD7553521B8A3F54007E4FF6 /* XPNCDemoAppTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XPNCDemoAppTests.swift; sourceTree = ""; }; 71 | DD75536D1B8A401E007E4FF6 /* XPNCDemoApp WatchKit Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "XPNCDemoApp WatchKit Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; 72 | DD7553701B8A401E007E4FF6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 73 | DD7553711B8A401E007E4FF6 /* InterfaceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterfaceController.swift; sourceTree = ""; }; 74 | DD7553731B8A401E007E4FF6 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 75 | DD7553771B8A401E007E4FF6 /* XPNCDemoApp WatchKit App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "XPNCDemoApp WatchKit App.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 76 | DD75537D1B8A401E007E4FF6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 77 | DD75537F1B8A401E007E4FF6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Interface.storyboard; sourceTree = ""; }; 78 | DD7553811B8A401E007E4FF6 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 79 | DD7553961B8AAD03007E4FF6 /* RowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RowController.swift; sourceTree = ""; }; 80 | DD75539C1B8AAEFC007E4FF6 /* XPNCDemoApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = XPNCDemoApp.entitlements; sourceTree = ""; }; 81 | DDBF20B81B8B5EEB00F1280F /* XPNCDemoApp WatchKit Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "XPNCDemoApp WatchKit Extension.entitlements"; sourceTree = ""; }; 82 | DDBF20BC1B8B653800F1280F /* ItemDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ItemDataSource.swift; path = Shared/ItemDataSource.swift; sourceTree = ""; }; 83 | DDBF20BD1B8B653800F1280F /* SharedConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SharedConstants.swift; path = Shared/SharedConstants.swift; sourceTree = ""; }; 84 | DDBF20C21B8B65D000F1280F /* XPNotificationCenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = XPNotificationCenter.swift; path = ../XPNotificationCenter.swift; sourceTree = SOURCE_ROOT; }; 85 | /* End PBXFileReference section */ 86 | 87 | /* Begin PBXFrameworksBuildPhase section */ 88 | DD7553341B8A3F54007E4FF6 /* Frameworks */ = { 89 | isa = PBXFrameworksBuildPhase; 90 | buildActionMask = 2147483647; 91 | files = ( 92 | ); 93 | runOnlyForDeploymentPostprocessing = 0; 94 | }; 95 | DD75536A1B8A401E007E4FF6 /* Frameworks */ = { 96 | isa = PBXFrameworksBuildPhase; 97 | buildActionMask = 2147483647; 98 | files = ( 99 | ); 100 | runOnlyForDeploymentPostprocessing = 0; 101 | }; 102 | /* End PBXFrameworksBuildPhase section */ 103 | 104 | /* Begin PBXGroup section */ 105 | DD75532E1B8A3F54007E4FF6 = { 106 | isa = PBXGroup; 107 | children = ( 108 | DD7553981B8AADF8007E4FF6 /* Shared */, 109 | DD7553391B8A3F54007E4FF6 /* XPNCDemoApp */, 110 | DD75534F1B8A3F54007E4FF6 /* XPNCDemoAppTests */, 111 | DD75536E1B8A401E007E4FF6 /* XPNCDemoApp WatchKit Extension */, 112 | DD75537B1B8A401E007E4FF6 /* XPNCDemoApp WatchKit App */, 113 | DD7553381B8A3F54007E4FF6 /* Products */, 114 | ); 115 | sourceTree = ""; 116 | }; 117 | DD7553381B8A3F54007E4FF6 /* Products */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | DD7553371B8A3F54007E4FF6 /* XPNCDemoApp.app */, 121 | DD75536D1B8A401E007E4FF6 /* XPNCDemoApp WatchKit Extension.appex */, 122 | DD7553771B8A401E007E4FF6 /* XPNCDemoApp WatchKit App.app */, 123 | ); 124 | name = Products; 125 | sourceTree = ""; 126 | }; 127 | DD7553391B8A3F54007E4FF6 /* XPNCDemoApp */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | DDBF20BA1B8B64D600F1280F /* Resources */, 131 | DDBF20B91B8B64CD00F1280F /* Controllers */, 132 | DD75533A1B8A3F54007E4FF6 /* Supporting Files */, 133 | ); 134 | path = XPNCDemoApp; 135 | sourceTree = ""; 136 | }; 137 | DD75533A1B8A3F54007E4FF6 /* Supporting Files */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | DD75533C1B8A3F54007E4FF6 /* AppDelegate.swift */, 141 | DD75533B1B8A3F54007E4FF6 /* Info.plist */, 142 | ); 143 | name = "Supporting Files"; 144 | sourceTree = ""; 145 | }; 146 | DD75534F1B8A3F54007E4FF6 /* XPNCDemoAppTests */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | DD7553521B8A3F54007E4FF6 /* XPNCDemoAppTests.swift */, 150 | DD7553501B8A3F54007E4FF6 /* Supporting Files */, 151 | ); 152 | path = XPNCDemoAppTests; 153 | sourceTree = ""; 154 | }; 155 | DD7553501B8A3F54007E4FF6 /* Supporting Files */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | DD7553511B8A3F54007E4FF6 /* Info.plist */, 159 | ); 160 | name = "Supporting Files"; 161 | sourceTree = ""; 162 | }; 163 | DD7553601B8A3F78007E4FF6 /* XPNotificationCenter */ = { 164 | isa = PBXGroup; 165 | children = ( 166 | DDBF20C21B8B65D000F1280F /* XPNotificationCenter.swift */, 167 | ); 168 | name = XPNotificationCenter; 169 | path = XPNCDemoApp/XPNotificationCenter; 170 | sourceTree = ""; 171 | }; 172 | DD75536E1B8A401E007E4FF6 /* XPNCDemoApp WatchKit Extension */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | DDBF20BB1B8B64EC00F1280F /* Classes */, 176 | DD75536F1B8A401E007E4FF6 /* Supporting Files */, 177 | ); 178 | path = "XPNCDemoApp WatchKit Extension"; 179 | sourceTree = ""; 180 | }; 181 | DD75536F1B8A401E007E4FF6 /* Supporting Files */ = { 182 | isa = PBXGroup; 183 | children = ( 184 | DD7553731B8A401E007E4FF6 /* Images.xcassets */, 185 | DDBF20B81B8B5EEB00F1280F /* XPNCDemoApp WatchKit Extension.entitlements */, 186 | DD7553701B8A401E007E4FF6 /* Info.plist */, 187 | ); 188 | name = "Supporting Files"; 189 | sourceTree = ""; 190 | }; 191 | DD75537B1B8A401E007E4FF6 /* XPNCDemoApp WatchKit App */ = { 192 | isa = PBXGroup; 193 | children = ( 194 | DD75537E1B8A401E007E4FF6 /* Interface.storyboard */, 195 | DD75537C1B8A401E007E4FF6 /* Supporting Files */, 196 | ); 197 | path = "XPNCDemoApp WatchKit App"; 198 | sourceTree = ""; 199 | }; 200 | DD75537C1B8A401E007E4FF6 /* Supporting Files */ = { 201 | isa = PBXGroup; 202 | children = ( 203 | DD7553811B8A401E007E4FF6 /* Images.xcassets */, 204 | DD75537D1B8A401E007E4FF6 /* Info.plist */, 205 | ); 206 | name = "Supporting Files"; 207 | sourceTree = ""; 208 | }; 209 | DD7553981B8AADF8007E4FF6 /* Shared */ = { 210 | isa = PBXGroup; 211 | children = ( 212 | DD7553601B8A3F78007E4FF6 /* XPNotificationCenter */, 213 | DDBF20BC1B8B653800F1280F /* ItemDataSource.swift */, 214 | DDBF20BD1B8B653800F1280F /* SharedConstants.swift */, 215 | ); 216 | name = Shared; 217 | sourceTree = ""; 218 | }; 219 | DDBF20B91B8B64CD00F1280F /* Controllers */ = { 220 | isa = PBXGroup; 221 | children = ( 222 | DD75533E1B8A3F54007E4FF6 /* ViewController.swift */, 223 | ); 224 | name = Controllers; 225 | sourceTree = ""; 226 | }; 227 | DDBF20BA1B8B64D600F1280F /* Resources */ = { 228 | isa = PBXGroup; 229 | children = ( 230 | DD7553451B8A3F54007E4FF6 /* LaunchScreen.xib */, 231 | DD7553431B8A3F54007E4FF6 /* Images.xcassets */, 232 | DD7553401B8A3F54007E4FF6 /* Main.storyboard */, 233 | DD75539C1B8AAEFC007E4FF6 /* XPNCDemoApp.entitlements */, 234 | ); 235 | name = Resources; 236 | sourceTree = ""; 237 | }; 238 | DDBF20BB1B8B64EC00F1280F /* Classes */ = { 239 | isa = PBXGroup; 240 | children = ( 241 | DD7553711B8A401E007E4FF6 /* InterfaceController.swift */, 242 | DD7553961B8AAD03007E4FF6 /* RowController.swift */, 243 | ); 244 | name = Classes; 245 | sourceTree = ""; 246 | }; 247 | /* End PBXGroup section */ 248 | 249 | /* Begin PBXNativeTarget section */ 250 | DD7553361B8A3F54007E4FF6 /* XPNCDemoApp */ = { 251 | isa = PBXNativeTarget; 252 | buildConfigurationList = DD7553561B8A3F54007E4FF6 /* Build configuration list for PBXNativeTarget "XPNCDemoApp" */; 253 | buildPhases = ( 254 | DD7553331B8A3F54007E4FF6 /* Sources */, 255 | DD7553341B8A3F54007E4FF6 /* Frameworks */, 256 | DD7553351B8A3F54007E4FF6 /* Resources */, 257 | DD75538C1B8A401E007E4FF6 /* Embed App Extensions */, 258 | ); 259 | buildRules = ( 260 | ); 261 | dependencies = ( 262 | DD7553841B8A401E007E4FF6 /* PBXTargetDependency */, 263 | ); 264 | name = XPNCDemoApp; 265 | productName = XPNCDemoApp; 266 | productReference = DD7553371B8A3F54007E4FF6 /* XPNCDemoApp.app */; 267 | productType = "com.apple.product-type.application"; 268 | }; 269 | DD75536C1B8A401E007E4FF6 /* XPNCDemoApp WatchKit Extension */ = { 270 | isa = PBXNativeTarget; 271 | buildConfigurationList = DD7553891B8A401E007E4FF6 /* Build configuration list for PBXNativeTarget "XPNCDemoApp WatchKit Extension" */; 272 | buildPhases = ( 273 | DD7553691B8A401E007E4FF6 /* Sources */, 274 | DD75536A1B8A401E007E4FF6 /* Frameworks */, 275 | DD75536B1B8A401E007E4FF6 /* Resources */, 276 | ); 277 | buildRules = ( 278 | ); 279 | dependencies = ( 280 | DD75537A1B8A401E007E4FF6 /* PBXTargetDependency */, 281 | ); 282 | name = "XPNCDemoApp WatchKit Extension"; 283 | productName = "XPNCDemoApp WatchKit Extension"; 284 | productReference = DD75536D1B8A401E007E4FF6 /* XPNCDemoApp WatchKit Extension.appex */; 285 | productType = "com.apple.product-type.watchkit-extension"; 286 | }; 287 | DD7553761B8A401E007E4FF6 /* XPNCDemoApp WatchKit App */ = { 288 | isa = PBXNativeTarget; 289 | buildConfigurationList = DD7553861B8A401E007E4FF6 /* Build configuration list for PBXNativeTarget "XPNCDemoApp WatchKit App" */; 290 | buildPhases = ( 291 | DD7553751B8A401E007E4FF6 /* Resources */, 292 | ); 293 | buildRules = ( 294 | ); 295 | dependencies = ( 296 | ); 297 | name = "XPNCDemoApp WatchKit App"; 298 | productName = "XPNCDemoApp WatchKit App"; 299 | productReference = DD7553771B8A401E007E4FF6 /* XPNCDemoApp WatchKit App.app */; 300 | productType = "com.apple.product-type.application.watchapp"; 301 | }; 302 | /* End PBXNativeTarget section */ 303 | 304 | /* Begin PBXProject section */ 305 | DD75532F1B8A3F54007E4FF6 /* Project object */ = { 306 | isa = PBXProject; 307 | attributes = { 308 | LastUpgradeCheck = 0640; 309 | ORGANIZATIONNAME = "Guilherme Rambo"; 310 | TargetAttributes = { 311 | DD7553361B8A3F54007E4FF6 = { 312 | CreatedOnToolsVersion = 6.4; 313 | DevelopmentTeam = 8C7439RJLG; 314 | SystemCapabilities = { 315 | com.apple.ApplicationGroups.iOS = { 316 | enabled = 1; 317 | }; 318 | }; 319 | }; 320 | DD75536C1B8A401E007E4FF6 = { 321 | CreatedOnToolsVersion = 6.4; 322 | DevelopmentTeam = 8C7439RJLG; 323 | SystemCapabilities = { 324 | com.apple.ApplicationGroups.iOS = { 325 | enabled = 1; 326 | }; 327 | }; 328 | }; 329 | DD7553761B8A401E007E4FF6 = { 330 | CreatedOnToolsVersion = 6.4; 331 | DevelopmentTeam = 8C7439RJLG; 332 | }; 333 | }; 334 | }; 335 | buildConfigurationList = DD7553321B8A3F54007E4FF6 /* Build configuration list for PBXProject "XPNCDemoApp" */; 336 | compatibilityVersion = "Xcode 3.2"; 337 | developmentRegion = English; 338 | hasScannedForEncodings = 0; 339 | knownRegions = ( 340 | en, 341 | Base, 342 | ); 343 | mainGroup = DD75532E1B8A3F54007E4FF6; 344 | productRefGroup = DD7553381B8A3F54007E4FF6 /* Products */; 345 | projectDirPath = ""; 346 | projectRoot = ""; 347 | targets = ( 348 | DD7553361B8A3F54007E4FF6 /* XPNCDemoApp */, 349 | DD75536C1B8A401E007E4FF6 /* XPNCDemoApp WatchKit Extension */, 350 | DD7553761B8A401E007E4FF6 /* XPNCDemoApp WatchKit App */, 351 | ); 352 | }; 353 | /* End PBXProject section */ 354 | 355 | /* Begin PBXResourcesBuildPhase section */ 356 | DD7553351B8A3F54007E4FF6 /* Resources */ = { 357 | isa = PBXResourcesBuildPhase; 358 | buildActionMask = 2147483647; 359 | files = ( 360 | DD7553421B8A3F54007E4FF6 /* Main.storyboard in Resources */, 361 | DD7553471B8A3F54007E4FF6 /* LaunchScreen.xib in Resources */, 362 | DD7553441B8A3F54007E4FF6 /* Images.xcassets in Resources */, 363 | ); 364 | runOnlyForDeploymentPostprocessing = 0; 365 | }; 366 | DD75536B1B8A401E007E4FF6 /* Resources */ = { 367 | isa = PBXResourcesBuildPhase; 368 | buildActionMask = 2147483647; 369 | files = ( 370 | DD7553781B8A401E007E4FF6 /* XPNCDemoApp WatchKit App.app in Resources */, 371 | DD7553741B8A401E007E4FF6 /* Images.xcassets in Resources */, 372 | ); 373 | runOnlyForDeploymentPostprocessing = 0; 374 | }; 375 | DD7553751B8A401E007E4FF6 /* Resources */ = { 376 | isa = PBXResourcesBuildPhase; 377 | buildActionMask = 2147483647; 378 | files = ( 379 | DD7553801B8A401E007E4FF6 /* Interface.storyboard in Resources */, 380 | DD7553821B8A401E007E4FF6 /* Images.xcassets in Resources */, 381 | ); 382 | runOnlyForDeploymentPostprocessing = 0; 383 | }; 384 | /* End PBXResourcesBuildPhase section */ 385 | 386 | /* Begin PBXSourcesBuildPhase section */ 387 | DD7553331B8A3F54007E4FF6 /* Sources */ = { 388 | isa = PBXSourcesBuildPhase; 389 | buildActionMask = 2147483647; 390 | files = ( 391 | DDBF20C01B8B653800F1280F /* SharedConstants.swift in Sources */, 392 | DDBF20C31B8B65D000F1280F /* XPNotificationCenter.swift in Sources */, 393 | DDBF20BE1B8B653800F1280F /* ItemDataSource.swift in Sources */, 394 | DD75533F1B8A3F54007E4FF6 /* ViewController.swift in Sources */, 395 | DD75533D1B8A3F54007E4FF6 /* AppDelegate.swift in Sources */, 396 | ); 397 | runOnlyForDeploymentPostprocessing = 0; 398 | }; 399 | DD7553691B8A401E007E4FF6 /* Sources */ = { 400 | isa = PBXSourcesBuildPhase; 401 | buildActionMask = 2147483647; 402 | files = ( 403 | DDBF20C11B8B653800F1280F /* SharedConstants.swift in Sources */, 404 | DDBF20C41B8B65D000F1280F /* XPNotificationCenter.swift in Sources */, 405 | DDBF20BF1B8B653800F1280F /* ItemDataSource.swift in Sources */, 406 | DD7553971B8AAD03007E4FF6 /* RowController.swift in Sources */, 407 | DD7553721B8A401E007E4FF6 /* InterfaceController.swift in Sources */, 408 | ); 409 | runOnlyForDeploymentPostprocessing = 0; 410 | }; 411 | /* End PBXSourcesBuildPhase section */ 412 | 413 | /* Begin PBXTargetDependency section */ 414 | DD75537A1B8A401E007E4FF6 /* PBXTargetDependency */ = { 415 | isa = PBXTargetDependency; 416 | target = DD7553761B8A401E007E4FF6 /* XPNCDemoApp WatchKit App */; 417 | targetProxy = DD7553791B8A401E007E4FF6 /* PBXContainerItemProxy */; 418 | }; 419 | DD7553841B8A401E007E4FF6 /* PBXTargetDependency */ = { 420 | isa = PBXTargetDependency; 421 | target = DD75536C1B8A401E007E4FF6 /* XPNCDemoApp WatchKit Extension */; 422 | targetProxy = DD7553831B8A401E007E4FF6 /* PBXContainerItemProxy */; 423 | }; 424 | /* End PBXTargetDependency section */ 425 | 426 | /* Begin PBXVariantGroup section */ 427 | DD7553401B8A3F54007E4FF6 /* Main.storyboard */ = { 428 | isa = PBXVariantGroup; 429 | children = ( 430 | DD7553411B8A3F54007E4FF6 /* Base */, 431 | ); 432 | name = Main.storyboard; 433 | sourceTree = ""; 434 | }; 435 | DD7553451B8A3F54007E4FF6 /* LaunchScreen.xib */ = { 436 | isa = PBXVariantGroup; 437 | children = ( 438 | DD7553461B8A3F54007E4FF6 /* Base */, 439 | ); 440 | name = LaunchScreen.xib; 441 | sourceTree = ""; 442 | }; 443 | DD75537E1B8A401E007E4FF6 /* Interface.storyboard */ = { 444 | isa = PBXVariantGroup; 445 | children = ( 446 | DD75537F1B8A401E007E4FF6 /* Base */, 447 | ); 448 | name = Interface.storyboard; 449 | sourceTree = ""; 450 | }; 451 | /* End PBXVariantGroup section */ 452 | 453 | /* Begin XCBuildConfiguration section */ 454 | DD7553541B8A3F54007E4FF6 /* Debug */ = { 455 | isa = XCBuildConfiguration; 456 | buildSettings = { 457 | ALWAYS_SEARCH_USER_PATHS = NO; 458 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 459 | CLANG_CXX_LIBRARY = "libc++"; 460 | CLANG_ENABLE_MODULES = YES; 461 | CLANG_ENABLE_OBJC_ARC = YES; 462 | CLANG_WARN_BOOL_CONVERSION = YES; 463 | CLANG_WARN_CONSTANT_CONVERSION = YES; 464 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 465 | CLANG_WARN_EMPTY_BODY = YES; 466 | CLANG_WARN_ENUM_CONVERSION = YES; 467 | CLANG_WARN_INT_CONVERSION = YES; 468 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 469 | CLANG_WARN_UNREACHABLE_CODE = YES; 470 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 471 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 472 | COPY_PHASE_STRIP = NO; 473 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 474 | ENABLE_STRICT_OBJC_MSGSEND = YES; 475 | GCC_C_LANGUAGE_STANDARD = gnu99; 476 | GCC_DYNAMIC_NO_PIC = NO; 477 | GCC_NO_COMMON_BLOCKS = YES; 478 | GCC_OPTIMIZATION_LEVEL = 0; 479 | GCC_PREPROCESSOR_DEFINITIONS = ( 480 | "DEBUG=1", 481 | "$(inherited)", 482 | ); 483 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 484 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 485 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 486 | GCC_WARN_UNDECLARED_SELECTOR = YES; 487 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 488 | GCC_WARN_UNUSED_FUNCTION = YES; 489 | GCC_WARN_UNUSED_VARIABLE = YES; 490 | IPHONEOS_DEPLOYMENT_TARGET = 8.4; 491 | MTL_ENABLE_DEBUG_INFO = YES; 492 | ONLY_ACTIVE_ARCH = YES; 493 | SDKROOT = iphoneos; 494 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 495 | }; 496 | name = Debug; 497 | }; 498 | DD7553551B8A3F54007E4FF6 /* Release */ = { 499 | isa = XCBuildConfiguration; 500 | buildSettings = { 501 | ALWAYS_SEARCH_USER_PATHS = NO; 502 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 503 | CLANG_CXX_LIBRARY = "libc++"; 504 | CLANG_ENABLE_MODULES = YES; 505 | CLANG_ENABLE_OBJC_ARC = YES; 506 | CLANG_WARN_BOOL_CONVERSION = YES; 507 | CLANG_WARN_CONSTANT_CONVERSION = YES; 508 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 509 | CLANG_WARN_EMPTY_BODY = YES; 510 | CLANG_WARN_ENUM_CONVERSION = YES; 511 | CLANG_WARN_INT_CONVERSION = YES; 512 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 513 | CLANG_WARN_UNREACHABLE_CODE = YES; 514 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 515 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 516 | COPY_PHASE_STRIP = NO; 517 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 518 | ENABLE_NS_ASSERTIONS = NO; 519 | ENABLE_STRICT_OBJC_MSGSEND = YES; 520 | GCC_C_LANGUAGE_STANDARD = gnu99; 521 | GCC_NO_COMMON_BLOCKS = YES; 522 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 523 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 524 | GCC_WARN_UNDECLARED_SELECTOR = YES; 525 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 526 | GCC_WARN_UNUSED_FUNCTION = YES; 527 | GCC_WARN_UNUSED_VARIABLE = YES; 528 | IPHONEOS_DEPLOYMENT_TARGET = 8.4; 529 | MTL_ENABLE_DEBUG_INFO = NO; 530 | SDKROOT = iphoneos; 531 | VALIDATE_PRODUCT = YES; 532 | }; 533 | name = Release; 534 | }; 535 | DD7553571B8A3F54007E4FF6 /* Debug */ = { 536 | isa = XCBuildConfiguration; 537 | buildSettings = { 538 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 539 | CLANG_ENABLE_MODULES = YES; 540 | CODE_SIGN_ENTITLEMENTS = XPNCDemoApp/XPNCDemoApp.entitlements; 541 | CODE_SIGN_IDENTITY = "iPhone Developer"; 542 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 543 | INFOPLIST_FILE = XPNCDemoApp/Info.plist; 544 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 545 | PRODUCT_NAME = "$(TARGET_NAME)"; 546 | PROVISIONING_PROFILE = ""; 547 | SWIFT_OBJC_BRIDGING_HEADER = ""; 548 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 549 | }; 550 | name = Debug; 551 | }; 552 | DD7553581B8A3F54007E4FF6 /* Release */ = { 553 | isa = XCBuildConfiguration; 554 | buildSettings = { 555 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 556 | CLANG_ENABLE_MODULES = YES; 557 | CODE_SIGN_ENTITLEMENTS = XPNCDemoApp/XPNCDemoApp.entitlements; 558 | CODE_SIGN_IDENTITY = "iPhone Developer"; 559 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 560 | INFOPLIST_FILE = XPNCDemoApp/Info.plist; 561 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 562 | PRODUCT_NAME = "$(TARGET_NAME)"; 563 | PROVISIONING_PROFILE = ""; 564 | SWIFT_OBJC_BRIDGING_HEADER = ""; 565 | }; 566 | name = Release; 567 | }; 568 | DD7553871B8A401E007E4FF6 /* Debug */ = { 569 | isa = XCBuildConfiguration; 570 | buildSettings = { 571 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 572 | CODE_SIGN_IDENTITY = "iPhone Developer"; 573 | GCC_PREPROCESSOR_DEFINITIONS = ( 574 | "DEBUG=1", 575 | "$(inherited)", 576 | ); 577 | IBSC_MODULE = XPNCDemoApp_WatchKit_Extension; 578 | INFOPLIST_FILE = "XPNCDemoApp WatchKit App/Info.plist"; 579 | IPHONEOS_DEPLOYMENT_TARGET = 8.2; 580 | PRODUCT_NAME = "$(TARGET_NAME)"; 581 | SKIP_INSTALL = YES; 582 | TARGETED_DEVICE_FAMILY = 4; 583 | "TARGETED_DEVICE_FAMILY[sdk=iphonesimulator*]" = "1,4"; 584 | }; 585 | name = Debug; 586 | }; 587 | DD7553881B8A401E007E4FF6 /* Release */ = { 588 | isa = XCBuildConfiguration; 589 | buildSettings = { 590 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 591 | CODE_SIGN_IDENTITY = "iPhone Developer"; 592 | IBSC_MODULE = XPNCDemoApp_WatchKit_Extension; 593 | INFOPLIST_FILE = "XPNCDemoApp WatchKit App/Info.plist"; 594 | IPHONEOS_DEPLOYMENT_TARGET = 8.2; 595 | PRODUCT_NAME = "$(TARGET_NAME)"; 596 | SKIP_INSTALL = YES; 597 | TARGETED_DEVICE_FAMILY = 4; 598 | "TARGETED_DEVICE_FAMILY[sdk=iphonesimulator*]" = "1,4"; 599 | }; 600 | name = Release; 601 | }; 602 | DD75538A1B8A401E007E4FF6 /* Debug */ = { 603 | isa = XCBuildConfiguration; 604 | buildSettings = { 605 | CODE_SIGN_ENTITLEMENTS = "XPNCDemoApp WatchKit Extension/XPNCDemoApp WatchKit Extension.entitlements"; 606 | CODE_SIGN_IDENTITY = "iPhone Developer"; 607 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 608 | GCC_PREPROCESSOR_DEFINITIONS = ( 609 | "DEBUG=1", 610 | "$(inherited)", 611 | ); 612 | INFOPLIST_FILE = "XPNCDemoApp WatchKit Extension/Info.plist"; 613 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; 614 | PRODUCT_NAME = "${TARGET_NAME}"; 615 | PROVISIONING_PROFILE = ""; 616 | SKIP_INSTALL = YES; 617 | SWIFT_OBJC_BRIDGING_HEADER = ""; 618 | }; 619 | name = Debug; 620 | }; 621 | DD75538B1B8A401E007E4FF6 /* Release */ = { 622 | isa = XCBuildConfiguration; 623 | buildSettings = { 624 | CODE_SIGN_ENTITLEMENTS = "XPNCDemoApp WatchKit Extension/XPNCDemoApp WatchKit Extension.entitlements"; 625 | CODE_SIGN_IDENTITY = "iPhone Developer"; 626 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 627 | INFOPLIST_FILE = "XPNCDemoApp WatchKit Extension/Info.plist"; 628 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; 629 | PRODUCT_NAME = "${TARGET_NAME}"; 630 | PROVISIONING_PROFILE = ""; 631 | SKIP_INSTALL = YES; 632 | SWIFT_OBJC_BRIDGING_HEADER = ""; 633 | }; 634 | name = Release; 635 | }; 636 | /* End XCBuildConfiguration section */ 637 | 638 | /* Begin XCConfigurationList section */ 639 | DD7553321B8A3F54007E4FF6 /* Build configuration list for PBXProject "XPNCDemoApp" */ = { 640 | isa = XCConfigurationList; 641 | buildConfigurations = ( 642 | DD7553541B8A3F54007E4FF6 /* Debug */, 643 | DD7553551B8A3F54007E4FF6 /* Release */, 644 | ); 645 | defaultConfigurationIsVisible = 0; 646 | defaultConfigurationName = Release; 647 | }; 648 | DD7553561B8A3F54007E4FF6 /* Build configuration list for PBXNativeTarget "XPNCDemoApp" */ = { 649 | isa = XCConfigurationList; 650 | buildConfigurations = ( 651 | DD7553571B8A3F54007E4FF6 /* Debug */, 652 | DD7553581B8A3F54007E4FF6 /* Release */, 653 | ); 654 | defaultConfigurationIsVisible = 0; 655 | defaultConfigurationName = Release; 656 | }; 657 | DD7553861B8A401E007E4FF6 /* Build configuration list for PBXNativeTarget "XPNCDemoApp WatchKit App" */ = { 658 | isa = XCConfigurationList; 659 | buildConfigurations = ( 660 | DD7553871B8A401E007E4FF6 /* Debug */, 661 | DD7553881B8A401E007E4FF6 /* Release */, 662 | ); 663 | defaultConfigurationIsVisible = 0; 664 | defaultConfigurationName = Release; 665 | }; 666 | DD7553891B8A401E007E4FF6 /* Build configuration list for PBXNativeTarget "XPNCDemoApp WatchKit Extension" */ = { 667 | isa = XCConfigurationList; 668 | buildConfigurations = ( 669 | DD75538A1B8A401E007E4FF6 /* Debug */, 670 | DD75538B1B8A401E007E4FF6 /* Release */, 671 | ); 672 | defaultConfigurationIsVisible = 0; 673 | defaultConfigurationName = Release; 674 | }; 675 | /* End XCConfigurationList section */ 676 | }; 677 | rootObject = DD75532F1B8A3F54007E4FF6 /* Project object */; 678 | } 679 | -------------------------------------------------------------------------------- /DemoApp/XPNCDemoApp/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // XPNCDemoApp 4 | // 5 | // Created by Guilherme Rambo on 23/08/15. 6 | // Copyright (c) 2015 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /DemoApp/XPNCDemoApp/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /DemoApp/XPNCDemoApp/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /DemoApp/XPNCDemoApp/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /DemoApp/XPNCDemoApp/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | br.com.guilhermerambo.$(PRODUCT_NAME:rfc1034identifier)2 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 2 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /DemoApp/XPNCDemoApp/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // XPNCDemoApp 4 | // 5 | // Created by Guilherme Rambo on 23/08/15. 6 | // Copyright (c) 2015 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UITableViewController { 12 | 13 | let dataSource = ItemDataSource() 14 | 15 | var items: [String] { 16 | get { 17 | return dataSource.items 18 | } 19 | set { 20 | dataSource.items = newValue 21 | } 22 | } 23 | 24 | @IBAction func addItem(sender: AnyObject) { 25 | var mutableItems = items 26 | mutableItems.append("Item \(items.count+1)") 27 | items = mutableItems 28 | 29 | tableView.insertRowsAtIndexPaths([NSIndexPath(forRow: items.count-1, inSection: 0)], withRowAnimation: .Left) 30 | 31 | // send a notification to the watch extension so it knows the item list has changed 32 | XPNotificationCenter.defaultCenter.postNotificationName(NotificationNames.itemListAdded, sender: self) 33 | } 34 | 35 | func removeItem(index: Int) { 36 | var mutableItems = items 37 | mutableItems.removeAtIndex(index) 38 | items = mutableItems 39 | 40 | tableView.deleteRowsAtIndexPaths([NSIndexPath(forRow: index, inSection: 0)], withRowAnimation: .Right) 41 | 42 | // send a notification to the watch extension so it knows the item list has changed 43 | XPNotificationCenter.defaultCenter.postNotificationName(NotificationNames.itemListDeleted, sender: self) 44 | } 45 | 46 | override func viewDidLoad() { 47 | super.viewDidLoad() 48 | } 49 | 50 | deinit { 51 | XPNotificationCenter.defaultCenter.removeObserver(self) 52 | } 53 | 54 | override func numberOfSectionsInTableView(tableView: UITableView) -> Int { 55 | return 1 56 | } 57 | 58 | override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 59 | return items.count 60 | } 61 | 62 | override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 63 | let cell = tableView.dequeueReusableCellWithIdentifier("row") as! UITableViewCell 64 | 65 | cell.textLabel?.text = items[indexPath.row] 66 | 67 | return cell 68 | } 69 | 70 | override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { 71 | if editingStyle == .Delete { 72 | removeItem(indexPath.row) 73 | } 74 | } 75 | 76 | } 77 | 78 | -------------------------------------------------------------------------------- /DemoApp/XPNCDemoApp/XPNCDemoApp.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.application-groups 6 | 7 | group.br.com.guilhermerambo.XPNCDemoApp2 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Guilherme Rambo 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | - Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | - Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # XPNotificationCenter (Cross-process notification center) 2 | 3 | A distributed notification center designed specifically for sending commands between iOS and WatchKit apps. 4 | 5 | ## Demo 6 | 7 | See the demo app for a sample implementation. The app uses an app group to share user defaults between the iOS and WatchKit app, and XPNotificationCenter is used to send commands to the WatchKit app when the data is changed. 8 | 9 | IMPORTANT: The demo app will not work unless you configure an AppID and an app group, after you have app groups enabled you need to change the defaults suite name in `Shared/SharedConstants.swift` 10 | 11 | ![screenshot](https://raw.githubusercontent.com/insidegui/XPNotificationCenter/master/screenshots/demo.gif) 12 | 13 | ## Usage 14 | 15 | ### Posting a notification 16 | 17 | To send a notification, call `postNotificationName` with the name of the notification and the sender. 18 | 19 | The name of the notification must be unique to the system, so I recommend appending your bundle ID to It. 20 | 21 | // send a notification to the watch extension so it knows the item list has changed 22 | XPNotificationCenter.defaultCenter.postNotificationName(NotificationNames.itemListAdded, sender: self) 23 | 24 | ### Receiving notifications 25 | 26 | To receive notifications you need to register a receiver block in your WatchKit extension. 27 | 28 | XPNotificationCenter.defaultCenter.addObserver(self, name: NotificationNames.itemListAdded) { note in 29 | println("WatchKit extension received notification: \(note)") 30 | } 31 | 32 | ### Limitations 33 | 34 | The biggest limitation of this method of communication between app and extension is that you can't send any userInfo through the notification, It can only be used to pass simple commands. To share data you'll have to use NSUserDefaults, files or the network. 35 | 36 | ### Other uses 37 | 38 | Even though this was made specifically for communication between iOS apps and WatchKit extensions, XPNotificationCenter can be used to communicate between your app and other types of extensions and will also work on OS X. -------------------------------------------------------------------------------- /XPNotificationCenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XPNotificationCenter.swift 3 | // XPNCDemoApp 4 | // 5 | // Created by Guilherme Rambo on 23/08/15. 6 | // Copyright (c) 2015 Guilherme Rambo. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class XPNotification: NSObject, Printable { 12 | 13 | var identifier: String 14 | var name: String 15 | 16 | init(name: String) { 17 | self.identifier = NSUUID().UUIDString 18 | self.name = name 19 | 20 | super.init() 21 | } 22 | 23 | override var description: String { 24 | get { 25 | return "XPNotification<\(identifier)-\(name)>" 26 | } 27 | } 28 | 29 | } 30 | 31 | private let _defaultXPNotificationCenter = XPNotificationCenter() 32 | 33 | class XPNotificationCenter { 34 | 35 | class var defaultCenter: XPNotificationCenter { 36 | get { 37 | return _defaultXPNotificationCenter 38 | } 39 | } 40 | 41 | func addObserver(observer: AnyObject, name: String, callback: (note: XPNotification) -> ()) { 42 | // this block will become the notification's callback 43 | let callbackBlock: @objc_block (CFNotificationCenter!, CFString!, UnsafePointer, CFDictionary!) -> Void = { center, name, _, _ in 44 | let note = XPNotification(name: name as String) 45 | callback(note: note) 46 | } 47 | // convert the block into a function pointer which can be passed to CFNotificationCenterAddObserver 48 | let callbackImp = imp_implementationWithBlock(unsafeBitCast(callbackBlock, AnyObject.self)) 49 | let callbackPtr = unsafeBitCast(callbackImp, CFNotificationCallback.self) 50 | 51 | // convert the observer into a pointer which can be passed to CFNotificationCenterAddObserver 52 | let observerPtr: UnsafePointer = unsafeBitCast(observer, UnsafePointer.self) 53 | 54 | // register the observation 55 | CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), observerPtr, callbackPtr, name as CFString, nil, .Drop) 56 | } 57 | 58 | func postNotificationName(name: String, sender: AnyObject) { 59 | let senderPtr = unsafeBitCast(sender, UnsafePointer.self) 60 | 61 | CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), name as CFString, senderPtr, nil, UInt8(1)) 62 | } 63 | 64 | func removeObserver(observer: AnyObject, name: String?) { 65 | let theObserver = unsafeBitCast(observer, UnsafePointer.self) 66 | 67 | if name == nil { 68 | CFNotificationCenterRemoveEveryObserver(CFNotificationCenterGetDarwinNotifyCenter(), theObserver) 69 | } else { 70 | CFNotificationCenterRemoveObserver(CFNotificationCenterGetDarwinNotifyCenter(), theObserver, name as! CFString, nil) 71 | } 72 | } 73 | 74 | func removeObserver(observer: AnyObject) { 75 | removeObserver(observer, name: nil) 76 | } 77 | 78 | } -------------------------------------------------------------------------------- /screenshots/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insidegui/XPNotificationCenter/c13ab6b997980dcc4f69e71d13566fce6b25efa5/screenshots/demo.gif --------------------------------------------------------------------------------