├── .gitignore ├── FinishedTalkingDevices.gif ├── README.md ├── WatchConnectivityTutorial ├── WatchConnectivityTutorial WatchKit App │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ └── Interface.storyboard │ └── Info.plist ├── WatchConnectivityTutorial WatchKit Extension │ ├── Assets.xcassets │ │ └── README__ignoredByTemplate__ │ ├── ExtensionDelegate.swift │ ├── Info.plist │ ├── InterfaceController.swift │ ├── NotificationController.swift │ └── PushNotificationPayload.apns ├── WatchConnectivityTutorial.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata └── WatchConnectivityTutorial │ ├── AppDelegate.swift │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── ViewController.swift └── WikiImages ├── 1 Create_New_Project_iOS_App_With_WatchKit_App.jpg ├── 2 Create_Product_Name.jpg ├── 3 Buttons_and_Label_laid_out.jpg ├── 3_5_connecting_watch_buttons.jpg ├── 4_Added_labels_to_the_phone_storyboard.jpg ├── 5_Buttons_in_a_collection.jpg └── 6 BasicTalkingDevices.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | .DS_Store 21 | 22 | # CocoaPods 23 | # 24 | # We recommend against adding the Pods directory to your .gitignore. However 25 | # you should judge for yourself, the pros and cons are mentioned at: 26 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 27 | # 28 | # Pods/ 29 | 30 | # Carthage 31 | # 32 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 33 | # Carthage/Checkouts 34 | 35 | Carthage/Build 36 | -------------------------------------------------------------------------------- /FinishedTalkingDevices.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThumbWorks/WatchConnectivityTutorial/d7f789463dfe3f8f8f6fe788ed7f879c023eb803/FinishedTalkingDevices.gif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Using WatchConnectivity 2 | 3 | ## Abstract 4 | 5 | One of the most anticipated announcements of WWDC 2015 was the exposure of an SDK to allow 3rd party developers the opportunity to put real software on the newly released Apple Watch. Before WWDC 2015, developers were allowed to put a UI on the watch, with the actual controllers of the app running on the iPhone itself. While this is decent for some cases, clear disadvantages appeared nearly immediately. Apps took time to load because the iPhone app had to wake up, do some things, then send content and state to the watch, at which point the watch could actually display something. An informal poll shows that folks generally didn’t use the apps that they’d installed on their watch because they felt too cumbersome. 6 | 7 | At the keynote, Tim Cook and co discussed watchOS2. A true native experience for the Apple Watch. One where the actual code, not just the UI was running on the watch device itself. The possibilities are endless. 8 | 9 | ## Introducing WatchConnectivity 10 | In this post, I’ll be digging into a very specific enhancement that came along with watchOS2 which piqued my interest, called `WatchConnectivity`. 11 | 12 | `WatchConnectivity` describes the communication API between the Apple Watch and the iPhone. The interface is straightforward, as there is a 1 to 1 relationship between devices. As in, there is 1 iPhone paired with 1 Watch. To communicate with your paired device, you simply access the `WCSession.defaultSession()` object, activate it, ensure that you’re connected, then send data. There are several ways to send data to the paired counterpart. I’ll only go into depth on one, specifically through - sendMessage:replyHandler:errorHandler: The rest of this post will describe how to set that up. 13 | 14 | ## Our Catalyst App 15 | 16 | The toy app that we’re going to create is a single view app on both the watch and the iPhone. They each contain 4 buttons: Top Left, Top Right, Bottom Left, Bottom Right. Tapping on 1 button, say, Top Left on the watch, should send a message to the counterpart where the results will be nicely displayed. 17 | 18 | ## Starting the project 19 | So let’s start by creating a new project which will be an iOS App with WatchKit App. 20 | 21 | ![](https://raw.githubusercontent.com/ThumbWorks/WatchConnectivityTutorial/june15/WikiImages/1%20Create_New_Project_iOS_App_With_WatchKit_App.jpg) 22 | 23 | ![](https://raw.githubusercontent.com/ThumbWorks/WatchConnectivityTutorial/june15/WikiImages/2%20Create_Product_Name.jpg) 24 | 25 | Notice the 3 different groups that are created when we create this type of app. This helps us draw a clear line between the iOS app, the watch extension where our code lives and the watch views. 26 | 27 | ## Adding UI to the Watch 28 | Moving on, let’s start adding some UI. Open the `Interface.Storyboard`. This is the storyboard that describes all of our watch UI. We’re going to be focusing on the main part of the interface, not the glances or notifications. So let’s add 2 groups that each take up the full width of the screen and half of the height of the screen. We’ll then place 2 buttons into each of these groups. This should give us a quadrant looking main watch view in our storyboard. 29 | 30 | Our final UI element is a Label which describes which button was last pressed on the iPhone simulator. When a message comes from the iPhone, we'll want to update the label to display that latest activity from the phone. 31 | 32 | ![](https://raw.githubusercontent.com/ThumbWorks/WatchConnectivityTutorial/june15/WikiImages/3%20Buttons_and_Label_laid_out.jpg) 33 | 34 | Now that we’ve got the buttons and our activity label added, we’ll need to make IBOutlets so we have a reference to the UI elements, then we’ll need to make IBActions for the buttons so that we can actually do something when we press them. 35 | 36 | ![](https://raw.githubusercontent.com/ThumbWorks/WatchConnectivityTutorial/june15/WikiImages/3_5_connecting_watch_buttons.jpg) 37 | 38 | We’re going to want to kick off our network communications in these IBActions. Since we’ll more or less be doing the same action 4 times, let’s make a nice convenience method that each of these will call. To keep things simple each IBAction sends an `Int` type representing it’s position in the grid. So Top Left is 0, Top Right is 1, Bottom Left is 2, Bottom right is 3. Mine looks something like this: 39 | 40 | ```swift 41 | func buttonPressed(offset : Int) { 42 | print("Button pressed \(offset)") 43 | } 44 | 45 | @IBAction func topLeftButtonTapped() { 46 | buttonPressed(0) 47 | } 48 | 49 | @IBAction func topRightButtonTapped() { 50 | buttonPressed(1) 51 | } 52 | 53 | @IBAction func bottomLeftButtonTapped() { 54 | buttonPressed(2) 55 | } 56 | 57 | @IBAction func bottomRightButtonTapped() { 58 | buttonPressed(3) 59 | } 60 | ``` 61 | 62 | Now that we’ve got the shell of this all figured out and we’re passing an appropriate simple identifier to our convenience method, we can generate the dictionary that we’d like to send over. Again for simplicity sake, we’re going to send over a dictionary in the form `{“buttonTapped” : }`. Very simple code, we’re creating a dictionary. 63 | 64 | ## The Send API 65 | 66 | Now that we’ve got the data we’d like to send over, let’s take a look at [the docs](https://developer.apple.com/library/prerelease/watchos/documentation/WatchConnectivity/Reference/WCSession_class/index.html#//apple_ref/occ/instm/WCSession/sendMessage:replyHandler:errorHandler:) for `- sendMessage:replyHandler:errorHandler:`. 67 | 68 | The `message` is obviously the content we’re going to be sending over. The `replyHandler` closure will get called if you’re counterpart determines that they want to actually reply to what you’re saying. In an app where the watch asks questions of the iPhone, this would be how the i iPhone responds. This is optional. The error handler takes care of scenarios where either device becomes disconnected or possibly other catastrophes. For the sake of brevity, we'll not be doing anything of any significance in the reply or the error closures. We will simply send our message. 69 | 70 | ```swift 71 | func buttonPressed(offset : Int) { 72 | 73 | // verify that this device supports WCSession (iPod and iPad do not as of β1 74 | if(WCSession.isSupported()) { 75 | 76 | // create a message dictionary to send 77 | let message = ["buttonOffset" : offset] 78 | 79 | session.sendMessage(message, replyHandler: { (content:[String : AnyObject]) -> Void in 80 | print("Our counterpart sent something back. This is optional") 81 | }, errorHandler: { (error ) -> Void in 82 | print("We got an error from our paired device : " + error.domain) 83 | }) 84 | } 85 | } 86 | ``` 87 | 88 | So far this should be pretty straight forward. Assuming our `WCSession` is activated (it isn't yet) and a companion device is paired (none is), we should be able to send messages as easy as this. Again, we're not really doing much with the `replyHandler`, other than printing out that we got a reply. 89 | 90 | ## Setting up the session 91 | 92 | I'd mentioned a second ago that we need to do some `WCSession` boilerplate. This is pretty lightweight boilerplate, but it will mean the difference between this working and not. So we'll do 3 things: 93 | 94 | 1. Keep a reference to the default `WCSession` object if this device supports `WatchConnectivity` (iPods/iPads do not as of β1). 95 | 2. Set the delegate for our session and activate it. This starts us up and listens for requests from the other side through the `WCSessionDelegate` protocol. We'll need to declare that both the iPhone and the watch view controllers conform to the `WCSessionDelegate` protocol. We'll actually implement the protocol in a later section. 96 | 3. Check our paired and connected state before we send our messages. 97 | 98 | So first create a class level `let session : WCSession!`. Then override `init()` like this: 99 | 100 | ```swift 101 | override init() { 102 | if(WCSession.isSupported()) { 103 | session = WCSession.defaultSession() 104 | } else { 105 | session = nil 106 | } 107 | } 108 | ``` 109 | 110 | Then override `willActivate`: 111 | 112 | ```swift 113 | override func willActivate() { 114 | // This method is called when watch view controller is about to be visible to user 115 | super.willActivate() 116 | 117 | if(WCSession.isSupported()) { 118 | session.delegate = self 119 | session.activateSession() 120 | } 121 | self.tappedLabel.setText("") 122 | } 123 | ``` 124 | 125 | So far our code should look [something like this] (https://github.com/ThumbWorks/WatchConnectivityTutorial/blob/watch-side-networking-implemented/WatchConnectivityTutorial/WatchConnectivityTutorial%20WatchKit%20Extension/InterfaceController.swift). 126 | 127 | ## Implementing the iPhone side 128 | 129 | So let's take a look at the iPhone here. Like last time we'll set up our UI first. Let's open up Main.storyboard and add some more buttons that we can interact with. Since you're using size classes (you ARE using size classes correct?), we'll need to set some constraints on the buttons since we always want them to be visible. Let's set up 4 buttons that are constrained to the outer edges of the view. There are probably several ways of adding constraints in Interface Builder, but I like to use the ole' Ctrl+drag from the button to the edge. The details of how you constrain these are not important but it is likely safe to have the buttons 10 pixels from their closest edges. 130 | 131 | ![](https://raw.githubusercontent.com/ThumbWorks/WatchConnectivityTutorial/june15/WikiImages/4_Added_labels_to_the_phone_storyboard.jpg) 132 | 133 | Next we'll add the `IBOutlet` to the `UIViewController`. We should be able to Ctrl+drag from the "New Referencing Outlet Collection" in the connections inspector on the right side to the code in the `ViewController.swift` just below the start of the `@class InterfaceController`. Be sure to do this 1 at a time starting at the top left button and moving clockwise. We'll do this so we can determine the offset in the collection of each button. For clarification sake, we'll be adding each button to the SAME referencing outlet collections. So you should see something like this `@IBOutlet var buttons: [UIButton]!` where mousing over the little connection button next to it highlights all 4 of the buttons. 134 | 135 | ![](https://raw.githubusercontent.com/ThumbWorks/WatchConnectivityTutorial/june15/WikiImages/5_Buttons_in_a_collection.jpg) 136 | 137 | Next we can add our `IBAction`. In this case, each of the buttons can be connected to the same `IBAction` since we've got that collection of outlets as defined above, we can easily query that to determine the data we need to send over when these things are tapped. In this case, it is the `indexOf` method on that collection. We'll build our dictionary just like we did on the watch side, and send our data in a very similar fashion. So we end up with a method like this: 138 | 139 | ```swift 140 | @IBAction func tappedButton(sender: UIButton) { 141 | 142 | if let i = buttons.indexOf(sender) { 143 | let message = ["buttonOffset" : i] 144 | 145 | session.sendMessage(message, replyHandler: { (content:[String : AnyObject]) -> Void in 146 | print("Our counterpart sent something back. This is optional") 147 | }, errorHandler: { (error ) -> Void in 148 | print("We got an error from our watch device : " + error.domain) 149 | }) 150 | } 151 | } 152 | ``` 153 | 154 | To finish out the networking portion of this code, we need to set up our `WCSession` object similar to how we did it on the watch. So we'll create a `WCSession` reference in this view controller like so: `let session : WCSession!`. This should feel familiar. And in our `viewDidLoad` we'll do something like this to activate it: 155 | 156 | ```swift 157 | override func viewDidLoad() { 158 | super.viewDidLoad() 159 | 160 | if(WCSession.isSupported()) { 161 | session.delegate = self 162 | session.activateSession() 163 | } 164 | } 165 | ``` 166 | 167 | Finally, since our session is a `let` we've got to set that when we init: 168 | 169 | ```swift 170 | required init(coder aDecoder: NSCoder) { 171 | self.session = WCSession.defaultSession() 172 | super.init(coder: aDecoder) 173 | } 174 | ``` 175 | 176 | 177 | Alright, so to recap what we've done so far. We've set up our UI for the watch with 4 buttons and a label to show what's been changed on the iPhone side. We also set up a method which creates appropriate content and sends it over. We set up the UI for our iPhone similar to the watch. We created a method to send similar content to the watch. The only thing left to do is to update each UI when we get a response. We are able to do that by implementing the `WCSessionDelegate` method `- session:didReceiveMessage:replyHandler:`. So let's go ahead and do that. 178 | 179 | ## Handling the network requests 180 | 181 | So now that we've got some data being sent over, let's be sure to handle the data when we get it. You'll remember that we declared `self` as the `delegate` to the `WCSession` on both ends. This just means that we'll be the object that handles the incoming messages. So the very basic implementation is something like this: 182 | 183 | ```swift 184 | func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) { 185 | print("we just got a message: " + message.description) 186 | } 187 | 188 | ``` 189 | 190 | We'll add that to both of the view controllers. Now if we were to build and run, we'd be able to tap some buttons and actually see things happening in the debugger of the companion device. 191 | 192 | ## State of the Debugging 193 | 194 | A quick note here on the state of the tools we've got for debugging. This is the first time we've really been able to, or ever really had a need run two different simulators from the same build. There are a few little gotchas to consider when running this kind of environment. 195 | 196 | 1. We need to determine which device we're actually running when he hit build and run. More concretely, we need to determine which target is being built. To do this, we edit our scheme. Apple has done a nice job of adding some UI in the scheme editor to change the target device. In the scheme editor, take a look at the run target and look at the Executable option. You'll notice both your Watch App and your iPhone App. Keep this in mind when debugging. 197 | 1. Once we've finally started, we need to remember that the debugger is only attached to one process at a time. What that means is that only 1 device will ever show any debug statements. There is a half way workaround. You can go to Debug > Attach to Process, then select the device which is not currently being run. This will allow you to hit breakpoints, but unfortunately not debug statements. I've filed a radar (21351217) hopefully it gets duped and fixed. 198 | 199 | ## Updating the Watch UI 200 | 201 | So hopefully you're running and seeing some debug statements on at least one of your devices. As a nice finishing touch, we'll go ahead and add some text changes so we can get some better visual cues that things are happening. For the watch, we'll need to extract the appropriate mapping, which is just an integer, from the dictionary that was sent over and we'll display some text in that status label we added form earlier. Then, on the main queue, we'll change the text for the label, and after some delay, we'll set the text back to an empty string. 202 | 203 | ```swift 204 | func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) { 205 | print("we got something from the iPhone" + message.description) 206 | 207 | if let offsetValue = message["buttonOffset"] as! Int? { 208 | 209 | let labelText = ["top Left", "top Right", "bottom Left", "bottom Right"][offsetValue] 210 | 211 | dispatch_async(dispatch_get_main_queue(), { () -> Void in 212 | self.tappedLabel.setText(labelText) 213 | }) 214 | 215 | // Delay a little bit then set it back 216 | let delayTime = dispatch_time(DISPATCH_TIME_NOW, 217 | Int64(0.5 * Double(NSEC_PER_SEC))) 218 | dispatch_after(delayTime, dispatch_get_main_queue()) { 219 | self.tappedLabel.setText("") 220 | } 221 | } 222 | // optionally send some data back through the replyHandler 223 | } 224 | ``` 225 | 226 | Feel free to build and run to try it out. Make sure you're building for the watch so the changes take. Tap on the buttons on the iPhone simulator and watch the label magically update. 227 | 228 | ![](https://raw.githubusercontent.com/ThumbWorks/WatchConnectivityTutorial/june15/WikiImages/6%20BasicTalkingDevices.gif) 229 | 230 | ## Update the iPhone UI 231 | 232 | At this point we can do the same on the iPhone that we just did on the watch. We'll receive the message in the same `- session:didReceiveMessage:replyHandler:` method and update the UI appropriately. This time we can do a nice thing and change the button text. This is something we couldn't do on the watch because changing the text isn't supported as of β1. 233 | 234 | ```swift 235 | func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) { 236 | 237 | // verify that we've gotten a number at the "buttonOffset" key 238 | if let offsetValue = message["buttonOffset"] as! Int? { 239 | 240 | // Determine which watch button has been tapped as mapped to the iPhone's 241 | let tappedButton = buttons[offsetValue] 242 | 243 | // We're going to change the title, so let's store the old one so we can set it back 244 | let oldTitle = tappedButton.titleForState(UIControlState.Normal) 245 | 246 | // Change the title on the main thread 247 | dispatch_async(dispatch_get_main_queue(), { () -> Void in 248 | tappedButton.setTitle("😍", forState:UIControlState.Normal) 249 | }) 250 | 251 | // Delay a little bit then set it back 252 | let delayTime = dispatch_time(DISPATCH_TIME_NOW, 253 | Int64(1 * Double(NSEC_PER_SEC))) 254 | dispatch_after(delayTime, dispatch_get_main_queue()) { 255 | tappedButton.setTitle(oldTitle, forState:UIControlState.Normal) 256 | } 257 | // again, we can optionally call replyHandler() 258 | } 259 | } 260 | ``` 261 | 262 | Build and run and you'll now be able to tap on any of the 4 buttons on the watch or any of the 4 buttons on the iPhone. The UI on the companion is updated so we're sure that the messages are getting over. 263 | 264 | ![](https://raw.githubusercontent.com/ThumbWorks/WatchConnectivityTutorial/2871265f3e78492497d831cf11189051e43e155b/FinishedTalkingDevices.gif) 265 | 266 | ## Closing remarks 267 | 268 | So we've covered the basics of sending a message using `WCSession` from a Watch to an iPhone, having the iPhone respond appropriately to the interactions. We've also covered the inverse of an iPhone interactions being mirrored on the Watch. There are still a few APIs yet to be explored including: `- updateApplicationContext:error:`, background data sends using `- transferUserInfo:` and also updating complication content using `- transferCurrentComplicationUserInfo:`. 269 | 270 | This is some really great stuff which opens up tons of opportunities for a more interesting experience on or through the Watch. I'm curious to see what some folks end up doing with this information. 271 | 272 | If you've got any questions about the code which is posted [here](https://github.com/ThumbWorks/WatchConnectivityTutorial), or see issues, or just want to discuss Watch stuff, feel free to reach out to me on twitter [@roderic](http://twitter.com/roderic). 273 | 274 | Happy Coding! 275 | -------------------------------------------------------------------------------- /WatchConnectivityTutorial/WatchConnectivityTutorial WatchKit App/Assets.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 | -------------------------------------------------------------------------------- /WatchConnectivityTutorial/WatchConnectivityTutorial WatchKit App/Base.lproj/Interface.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 20 | 25 | 26 | 27 | 28 | 29 | 34 | 39 | 40 | 41 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /WatchConnectivityTutorial/WatchConnectivityTutorial WatchKit App/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | WatchConnectivityTutorial WatchKit App 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 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 | 1 25 | UISupportedInterfaceOrientations 26 | 27 | UIInterfaceOrientationPortrait 28 | UIInterfaceOrientationPortraitUpsideDown 29 | 30 | WKCompanionAppBundleIdentifier 31 | io.thumbworks.WatchConnectivityTutorial 32 | WKWatchKitApp 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /WatchConnectivityTutorial/WatchConnectivityTutorial WatchKit Extension/Assets.xcassets/README__ignoredByTemplate__: -------------------------------------------------------------------------------- 1 | Did you know that git does not support storing empty directories? 2 | -------------------------------------------------------------------------------- /WatchConnectivityTutorial/WatchConnectivityTutorial WatchKit Extension/ExtensionDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExtensionDelegate.swift 3 | // WatchConnectivityTutorial WatchKit Extension 4 | // 5 | // Created by Roderic on 6/15/15. 6 | // Copyright © 2015 Thumbworks. All rights reserved. 7 | // 8 | 9 | import WatchKit 10 | 11 | class ExtensionDelegate: NSObject, WKExtensionDelegate { 12 | 13 | func applicationDidFinishLaunching() { 14 | // Perform any final initialization of your application. 15 | } 16 | 17 | func applicationDidBecomeActive() { 18 | // 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. 19 | } 20 | 21 | func applicationWillResignActive() { 22 | // 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. 23 | // Use this method to pause ongoing tasks, disable timers, etc. 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /WatchConnectivityTutorial/WatchConnectivityTutorial WatchKit Extension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | WatchConnectivityTutorial WatchKit Extension 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 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 | 1 25 | NSExtension 26 | 27 | NSExtensionAttributes 28 | 29 | WKAppBundleIdentifier 30 | io.thumbworks.WatchConnectivityTutorial.watchkitapp 31 | 32 | NSExtensionPointIdentifier 33 | com.apple.watchkit 34 | 35 | RemoteInterfacePrincipalClass 36 | $(PRODUCT_MODULE_NAME).InterfaceController 37 | WKExtensionDelegateClassName 38 | $(PRODUCT_MODULE_NAME).ExtensionDelegate 39 | 40 | 41 | -------------------------------------------------------------------------------- /WatchConnectivityTutorial/WatchConnectivityTutorial WatchKit Extension/InterfaceController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InterfaceController.swift 3 | // WatchConnectivityTutorial WatchKit Extension 4 | // 5 | // Created by Roderic on 6/15/15. 6 | // Copyright © 2015 Thumbworks. All rights reserved. 7 | // 8 | 9 | import WatchKit 10 | import Foundation 11 | import WatchConnectivity 12 | 13 | class InterfaceController: WKInterfaceController, WCSessionDelegate { 14 | 15 | let session : WCSession! 16 | @IBOutlet var tappedLabel: WKInterfaceLabel! 17 | 18 | override init() { 19 | if(WCSession.isSupported()) { 20 | session = WCSession.defaultSession() 21 | } else { 22 | session = nil 23 | } 24 | } 25 | 26 | override func awakeWithContext(context: AnyObject?) { 27 | super.awakeWithContext(context) 28 | 29 | // Configure interface objects here. 30 | } 31 | 32 | override func willActivate() { 33 | // This method is called when watch view controller is about to be visible to user 34 | super.willActivate() 35 | 36 | if(WCSession.isSupported()) { 37 | session.delegate = self 38 | session.activateSession() 39 | } 40 | self.tappedLabel.setText("") 41 | 42 | } 43 | 44 | 45 | override func didDeactivate() { 46 | // This method is called when watch view controller is no longer visible 47 | super.didDeactivate() 48 | } 49 | 50 | 51 | func buttonPressed(offset : Int) { 52 | 53 | // verify that this device supports WCSession (iPod and iPad do not as of β1 54 | if(WCSession.isSupported()) { 55 | 56 | // create a message dictionary to send 57 | let message = ["buttonOffset" : offset] 58 | 59 | session.sendMessage(message, replyHandler: { (content:[String : AnyObject]) -> Void in 60 | print("Our counterpart sent something back. This is optional") 61 | }, errorHandler: { (error ) -> Void in 62 | print("We got an error from our paired device : " + error.domain) 63 | }) 64 | } 65 | } 66 | 67 | @IBAction func topLeftButtonTapped() { 68 | buttonPressed(0) 69 | } 70 | 71 | @IBAction func topRightButtonTapped() { 72 | buttonPressed(1) 73 | } 74 | 75 | @IBAction func bottomLeftButtonTapped() { 76 | buttonPressed(2) 77 | } 78 | 79 | @IBAction func bottomRightButtonTapped() { 80 | buttonPressed(3) 81 | } 82 | 83 | func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) { 84 | print("we got something from the iPhone" + message.description) 85 | 86 | if let offsetValue = message["buttonOffset"] as! Int? { 87 | 88 | let labelText = ["top Left", "top Right", "bottom Left", "bottom Right"][offsetValue] 89 | 90 | dispatch_async(dispatch_get_main_queue(), { () -> Void in 91 | self.tappedLabel.setText(labelText) 92 | }) 93 | 94 | // Delay a little bit then set it back 95 | let delayTime = dispatch_time(DISPATCH_TIME_NOW, 96 | Int64(0.5 * Double(NSEC_PER_SEC))) 97 | dispatch_after(delayTime, dispatch_get_main_queue()) { 98 | self.tappedLabel.setText("") 99 | } 100 | } 101 | // optionally send some data back through the replyHandler 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /WatchConnectivityTutorial/WatchConnectivityTutorial WatchKit Extension/NotificationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotificationController.swift 3 | // WatchConnectivityTutorial WatchKit Extension 4 | // 5 | // Created by Roderic on 6/15/15. 6 | // Copyright © 2015 Thumbworks. All rights reserved. 7 | // 8 | 9 | import WatchKit 10 | import Foundation 11 | 12 | 13 | class NotificationController: WKUserNotificationInterfaceController { 14 | 15 | override init() { 16 | // Initialize variables here. 17 | super.init() 18 | 19 | // Configure interface objects here. 20 | } 21 | 22 | override func willActivate() { 23 | // This method is called when watch view controller is about to be visible to user 24 | super.willActivate() 25 | } 26 | 27 | override func didDeactivate() { 28 | // This method is called when watch view controller is no longer visible 29 | super.didDeactivate() 30 | } 31 | 32 | /* 33 | override func didReceiveLocalNotification(localNotification: UILocalNotification, withCompletion completionHandler: ((WKUserNotificationInterfaceType) -> Void)) { 34 | // This method is called when a local notification needs to be presented. 35 | // Implement it if you use a dynamic notification interface. 36 | // Populate your dynamic notification interface as quickly as possible. 37 | // 38 | // After populating your dynamic notification interface call the completion block. 39 | completionHandler(.Custom) 40 | } 41 | */ 42 | 43 | /* 44 | override func didReceiveRemoteNotification(remoteNotification: [NSObject : AnyObject], withCompletion completionHandler: ((WKUserNotificationInterfaceType) -> Void)) { 45 | // This method is called when a remote notification needs to be presented. 46 | // Implement it if you use a dynamic notification interface. 47 | // Populate your dynamic notification interface as quickly as possible. 48 | // 49 | // After populating your dynamic notification interface call the completion block. 50 | completionHandler(.Custom) 51 | } 52 | */ 53 | } 54 | -------------------------------------------------------------------------------- /WatchConnectivityTutorial/WatchConnectivityTutorial WatchKit Extension/PushNotificationPayload.apns: -------------------------------------------------------------------------------- 1 | { 2 | "aps": { 3 | "alert": { 4 | "body": "Test message", 5 | "title": "Optional title" 6 | }, 7 | "category": "myCategory" 8 | }, 9 | 10 | "WatchKit Simulator Actions": [ 11 | { 12 | "title": "First Button", 13 | "identifier": "firstButtonAction" 14 | } 15 | ], 16 | 17 | "customKey": "Use this file to define a testing payload for your notifications. The aps dictionary specifies the category, alert text and title. The WatchKit Simulator Actions array can provide info for one or more action buttons in addition to the standard Dismiss button. Any other top level keys are custom payload. If you have multiple such JSON files in your project, you'll be able to select them when choosing to debug the notification interface of your Watch App." 18 | } 19 | -------------------------------------------------------------------------------- /WatchConnectivityTutorial/WatchConnectivityTutorial.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 9E1F17C11B2FAFE400C9EC82 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1F17C01B2FAFE400C9EC82 /* AppDelegate.swift */; }; 11 | 9E1F17C31B2FAFE400C9EC82 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1F17C21B2FAFE400C9EC82 /* ViewController.swift */; }; 12 | 9E1F17C61B2FAFE400C9EC82 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9E1F17C41B2FAFE400C9EC82 /* Main.storyboard */; }; 13 | 9E1F17C81B2FAFE400C9EC82 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9E1F17C71B2FAFE400C9EC82 /* Assets.xcassets */; }; 14 | 9E1F17CB1B2FAFE400C9EC82 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9E1F17C91B2FAFE400C9EC82 /* LaunchScreen.storyboard */; }; 15 | 9E1F17D01B2FAFE400C9EC82 /* WatchConnectivityTutorial WatchKit App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 9E1F17CF1B2FAFE400C9EC82 /* WatchConnectivityTutorial WatchKit App.app */; }; 16 | 9E1F17D61B2FAFE400C9EC82 /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9E1F17D41B2FAFE400C9EC82 /* Interface.storyboard */; }; 17 | 9E1F17D81B2FAFE400C9EC82 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9E1F17D71B2FAFE400C9EC82 /* Assets.xcassets */; }; 18 | 9E1F17DF1B2FAFE400C9EC82 /* WatchConnectivityTutorial WatchKit Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 9E1F17DE1B2FAFE400C9EC82 /* WatchConnectivityTutorial WatchKit Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 19 | 9E1F17E61B2FAFE400C9EC82 /* InterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1F17E51B2FAFE400C9EC82 /* InterfaceController.swift */; }; 20 | 9E1F17E81B2FAFE400C9EC82 /* ExtensionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1F17E71B2FAFE400C9EC82 /* ExtensionDelegate.swift */; }; 21 | 9E1F17EA1B2FAFE400C9EC82 /* NotificationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1F17E91B2FAFE400C9EC82 /* NotificationController.swift */; }; 22 | 9E1F17EC1B2FAFE400C9EC82 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9E1F17EB1B2FAFE400C9EC82 /* Assets.xcassets */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXContainerItemProxy section */ 26 | 9E1F17D11B2FAFE400C9EC82 /* PBXContainerItemProxy */ = { 27 | isa = PBXContainerItemProxy; 28 | containerPortal = 9E1F17B51B2FAFE300C9EC82 /* Project object */; 29 | proxyType = 1; 30 | remoteGlobalIDString = 9E1F17CE1B2FAFE400C9EC82; 31 | remoteInfo = "WatchConnectivityTutorial WatchKit App"; 32 | }; 33 | 9E1F17E01B2FAFE400C9EC82 /* PBXContainerItemProxy */ = { 34 | isa = PBXContainerItemProxy; 35 | containerPortal = 9E1F17B51B2FAFE300C9EC82 /* Project object */; 36 | proxyType = 1; 37 | remoteGlobalIDString = 9E1F17DD1B2FAFE400C9EC82; 38 | remoteInfo = "WatchConnectivityTutorial WatchKit Extension"; 39 | }; 40 | /* End PBXContainerItemProxy section */ 41 | 42 | /* Begin PBXCopyFilesBuildPhase section */ 43 | 9E1F17F31B2FAFE400C9EC82 /* Embed App Extensions */ = { 44 | isa = PBXCopyFilesBuildPhase; 45 | buildActionMask = 2147483647; 46 | dstPath = ""; 47 | dstSubfolderSpec = 13; 48 | files = ( 49 | 9E1F17DF1B2FAFE400C9EC82 /* WatchConnectivityTutorial WatchKit Extension.appex in Embed App Extensions */, 50 | ); 51 | name = "Embed App Extensions"; 52 | runOnlyForDeploymentPostprocessing = 0; 53 | }; 54 | 9E1F17F71B2FAFE400C9EC82 /* Embed Watch Content */ = { 55 | isa = PBXCopyFilesBuildPhase; 56 | buildActionMask = 2147483647; 57 | dstPath = "$(CONTENTS_FOLDER_PATH)/Watch"; 58 | dstSubfolderSpec = 16; 59 | files = ( 60 | 9E1F17D01B2FAFE400C9EC82 /* WatchConnectivityTutorial WatchKit App.app in Embed Watch Content */, 61 | ); 62 | name = "Embed Watch Content"; 63 | runOnlyForDeploymentPostprocessing = 0; 64 | }; 65 | /* End PBXCopyFilesBuildPhase section */ 66 | 67 | /* Begin PBXFileReference section */ 68 | 9E1F17BD1B2FAFE400C9EC82 /* WatchConnectivityTutorial.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WatchConnectivityTutorial.app; sourceTree = BUILT_PRODUCTS_DIR; }; 69 | 9E1F17C01B2FAFE400C9EC82 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 70 | 9E1F17C21B2FAFE400C9EC82 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 71 | 9E1F17C51B2FAFE400C9EC82 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 72 | 9E1F17C71B2FAFE400C9EC82 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 73 | 9E1F17CA1B2FAFE400C9EC82 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 74 | 9E1F17CC1B2FAFE400C9EC82 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 75 | 9E1F17CF1B2FAFE400C9EC82 /* WatchConnectivityTutorial WatchKit App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "WatchConnectivityTutorial WatchKit App.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 76 | 9E1F17D51B2FAFE400C9EC82 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Interface.storyboard; sourceTree = ""; }; 77 | 9E1F17D71B2FAFE400C9EC82 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 78 | 9E1F17D91B2FAFE400C9EC82 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 79 | 9E1F17DE1B2FAFE400C9EC82 /* WatchConnectivityTutorial WatchKit Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "WatchConnectivityTutorial WatchKit Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; 80 | 9E1F17E41B2FAFE400C9EC82 /* PushNotificationPayload.apns */ = {isa = PBXFileReference; lastKnownFileType = text; path = PushNotificationPayload.apns; sourceTree = ""; }; 81 | 9E1F17E51B2FAFE400C9EC82 /* InterfaceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterfaceController.swift; sourceTree = ""; }; 82 | 9E1F17E71B2FAFE400C9EC82 /* ExtensionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionDelegate.swift; sourceTree = ""; }; 83 | 9E1F17E91B2FAFE400C9EC82 /* NotificationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationController.swift; sourceTree = ""; }; 84 | 9E1F17EB1B2FAFE400C9EC82 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 85 | 9E1F17ED1B2FAFE400C9EC82 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 86 | /* End PBXFileReference section */ 87 | 88 | /* Begin PBXFrameworksBuildPhase section */ 89 | 9E1F17BA1B2FAFE300C9EC82 /* Frameworks */ = { 90 | isa = PBXFrameworksBuildPhase; 91 | buildActionMask = 2147483647; 92 | files = ( 93 | ); 94 | runOnlyForDeploymentPostprocessing = 0; 95 | }; 96 | 9E1F17DB1B2FAFE400C9EC82 /* Frameworks */ = { 97 | isa = PBXFrameworksBuildPhase; 98 | buildActionMask = 2147483647; 99 | files = ( 100 | ); 101 | runOnlyForDeploymentPostprocessing = 0; 102 | }; 103 | /* End PBXFrameworksBuildPhase section */ 104 | 105 | /* Begin PBXGroup section */ 106 | 9E1F17B41B2FAFE300C9EC82 = { 107 | isa = PBXGroup; 108 | children = ( 109 | 9E1F17BF1B2FAFE400C9EC82 /* WatchConnectivityTutorial */, 110 | 9E1F17D31B2FAFE400C9EC82 /* WatchConnectivityTutorial WatchKit App */, 111 | 9E1F17E21B2FAFE400C9EC82 /* WatchConnectivityTutorial WatchKit Extension */, 112 | 9E1F17BE1B2FAFE400C9EC82 /* Products */, 113 | ); 114 | sourceTree = ""; 115 | }; 116 | 9E1F17BE1B2FAFE400C9EC82 /* Products */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 9E1F17BD1B2FAFE400C9EC82 /* WatchConnectivityTutorial.app */, 120 | 9E1F17CF1B2FAFE400C9EC82 /* WatchConnectivityTutorial WatchKit App.app */, 121 | 9E1F17DE1B2FAFE400C9EC82 /* WatchConnectivityTutorial WatchKit Extension.appex */, 122 | ); 123 | name = Products; 124 | sourceTree = ""; 125 | }; 126 | 9E1F17BF1B2FAFE400C9EC82 /* WatchConnectivityTutorial */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 9E1F17C01B2FAFE400C9EC82 /* AppDelegate.swift */, 130 | 9E1F17C21B2FAFE400C9EC82 /* ViewController.swift */, 131 | 9E1F17C41B2FAFE400C9EC82 /* Main.storyboard */, 132 | 9E1F17C71B2FAFE400C9EC82 /* Assets.xcassets */, 133 | 9E1F17C91B2FAFE400C9EC82 /* LaunchScreen.storyboard */, 134 | 9E1F17CC1B2FAFE400C9EC82 /* Info.plist */, 135 | ); 136 | path = WatchConnectivityTutorial; 137 | sourceTree = ""; 138 | }; 139 | 9E1F17D31B2FAFE400C9EC82 /* WatchConnectivityTutorial WatchKit App */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | 9E1F17D41B2FAFE400C9EC82 /* Interface.storyboard */, 143 | 9E1F17D71B2FAFE400C9EC82 /* Assets.xcassets */, 144 | 9E1F17D91B2FAFE400C9EC82 /* Info.plist */, 145 | ); 146 | path = "WatchConnectivityTutorial WatchKit App"; 147 | sourceTree = ""; 148 | }; 149 | 9E1F17E21B2FAFE400C9EC82 /* WatchConnectivityTutorial WatchKit Extension */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | 9E1F17E51B2FAFE400C9EC82 /* InterfaceController.swift */, 153 | 9E1F17E71B2FAFE400C9EC82 /* ExtensionDelegate.swift */, 154 | 9E1F17E91B2FAFE400C9EC82 /* NotificationController.swift */, 155 | 9E1F17EB1B2FAFE400C9EC82 /* Assets.xcassets */, 156 | 9E1F17ED1B2FAFE400C9EC82 /* Info.plist */, 157 | 9E1F17E31B2FAFE400C9EC82 /* Supporting Files */, 158 | ); 159 | path = "WatchConnectivityTutorial WatchKit Extension"; 160 | sourceTree = ""; 161 | }; 162 | 9E1F17E31B2FAFE400C9EC82 /* Supporting Files */ = { 163 | isa = PBXGroup; 164 | children = ( 165 | 9E1F17E41B2FAFE400C9EC82 /* PushNotificationPayload.apns */, 166 | ); 167 | name = "Supporting Files"; 168 | sourceTree = ""; 169 | }; 170 | /* End PBXGroup section */ 171 | 172 | /* Begin PBXNativeTarget section */ 173 | 9E1F17BC1B2FAFE300C9EC82 /* WatchConnectivityTutorial */ = { 174 | isa = PBXNativeTarget; 175 | buildConfigurationList = 9E1F17F81B2FAFE400C9EC82 /* Build configuration list for PBXNativeTarget "WatchConnectivityTutorial" */; 176 | buildPhases = ( 177 | 9E1F17B91B2FAFE300C9EC82 /* Sources */, 178 | 9E1F17BA1B2FAFE300C9EC82 /* Frameworks */, 179 | 9E1F17BB1B2FAFE300C9EC82 /* Resources */, 180 | 9E1F17F71B2FAFE400C9EC82 /* Embed Watch Content */, 181 | ); 182 | buildRules = ( 183 | ); 184 | dependencies = ( 185 | 9E1F17D21B2FAFE400C9EC82 /* PBXTargetDependency */, 186 | ); 187 | name = WatchConnectivityTutorial; 188 | productName = WatchConnectivityTutorial; 189 | productReference = 9E1F17BD1B2FAFE400C9EC82 /* WatchConnectivityTutorial.app */; 190 | productType = "com.apple.product-type.application"; 191 | }; 192 | 9E1F17CE1B2FAFE400C9EC82 /* WatchConnectivityTutorial WatchKit App */ = { 193 | isa = PBXNativeTarget; 194 | buildConfigurationList = 9E1F17F41B2FAFE400C9EC82 /* Build configuration list for PBXNativeTarget "WatchConnectivityTutorial WatchKit App" */; 195 | buildPhases = ( 196 | 9E1F17CD1B2FAFE400C9EC82 /* Resources */, 197 | 9E1F17F31B2FAFE400C9EC82 /* Embed App Extensions */, 198 | ); 199 | buildRules = ( 200 | ); 201 | dependencies = ( 202 | 9E1F17E11B2FAFE400C9EC82 /* PBXTargetDependency */, 203 | ); 204 | name = "WatchConnectivityTutorial WatchKit App"; 205 | productName = "WatchConnectivityTutorial WatchKit App"; 206 | productReference = 9E1F17CF1B2FAFE400C9EC82 /* WatchConnectivityTutorial WatchKit App.app */; 207 | productType = "com.apple.product-type.application.watchapp2"; 208 | }; 209 | 9E1F17DD1B2FAFE400C9EC82 /* WatchConnectivityTutorial WatchKit Extension */ = { 210 | isa = PBXNativeTarget; 211 | buildConfigurationList = 9E1F17F01B2FAFE400C9EC82 /* Build configuration list for PBXNativeTarget "WatchConnectivityTutorial WatchKit Extension" */; 212 | buildPhases = ( 213 | 9E1F17DA1B2FAFE400C9EC82 /* Sources */, 214 | 9E1F17DB1B2FAFE400C9EC82 /* Frameworks */, 215 | 9E1F17DC1B2FAFE400C9EC82 /* Resources */, 216 | ); 217 | buildRules = ( 218 | ); 219 | dependencies = ( 220 | ); 221 | name = "WatchConnectivityTutorial WatchKit Extension"; 222 | productName = "WatchConnectivityTutorial WatchKit Extension"; 223 | productReference = 9E1F17DE1B2FAFE400C9EC82 /* WatchConnectivityTutorial WatchKit Extension.appex */; 224 | productType = "com.apple.product-type.watchkit2-extension"; 225 | }; 226 | /* End PBXNativeTarget section */ 227 | 228 | /* Begin PBXProject section */ 229 | 9E1F17B51B2FAFE300C9EC82 /* Project object */ = { 230 | isa = PBXProject; 231 | attributes = { 232 | LastUpgradeCheck = 0700; 233 | ORGANIZATIONNAME = Thumbworks; 234 | TargetAttributes = { 235 | 9E1F17BC1B2FAFE300C9EC82 = { 236 | CreatedOnToolsVersion = 7.0; 237 | }; 238 | 9E1F17CE1B2FAFE400C9EC82 = { 239 | CreatedOnToolsVersion = 7.0; 240 | }; 241 | 9E1F17DD1B2FAFE400C9EC82 = { 242 | CreatedOnToolsVersion = 7.0; 243 | }; 244 | }; 245 | }; 246 | buildConfigurationList = 9E1F17B81B2FAFE300C9EC82 /* Build configuration list for PBXProject "WatchConnectivityTutorial" */; 247 | compatibilityVersion = "Xcode 3.2"; 248 | developmentRegion = English; 249 | hasScannedForEncodings = 0; 250 | knownRegions = ( 251 | en, 252 | Base, 253 | ); 254 | mainGroup = 9E1F17B41B2FAFE300C9EC82; 255 | productRefGroup = 9E1F17BE1B2FAFE400C9EC82 /* Products */; 256 | projectDirPath = ""; 257 | projectRoot = ""; 258 | targets = ( 259 | 9E1F17BC1B2FAFE300C9EC82 /* WatchConnectivityTutorial */, 260 | 9E1F17CE1B2FAFE400C9EC82 /* WatchConnectivityTutorial WatchKit App */, 261 | 9E1F17DD1B2FAFE400C9EC82 /* WatchConnectivityTutorial WatchKit Extension */, 262 | ); 263 | }; 264 | /* End PBXProject section */ 265 | 266 | /* Begin PBXResourcesBuildPhase section */ 267 | 9E1F17BB1B2FAFE300C9EC82 /* Resources */ = { 268 | isa = PBXResourcesBuildPhase; 269 | buildActionMask = 2147483647; 270 | files = ( 271 | 9E1F17CB1B2FAFE400C9EC82 /* LaunchScreen.storyboard in Resources */, 272 | 9E1F17C81B2FAFE400C9EC82 /* Assets.xcassets in Resources */, 273 | 9E1F17C61B2FAFE400C9EC82 /* Main.storyboard in Resources */, 274 | ); 275 | runOnlyForDeploymentPostprocessing = 0; 276 | }; 277 | 9E1F17CD1B2FAFE400C9EC82 /* Resources */ = { 278 | isa = PBXResourcesBuildPhase; 279 | buildActionMask = 2147483647; 280 | files = ( 281 | 9E1F17D81B2FAFE400C9EC82 /* Assets.xcassets in Resources */, 282 | 9E1F17D61B2FAFE400C9EC82 /* Interface.storyboard in Resources */, 283 | ); 284 | runOnlyForDeploymentPostprocessing = 0; 285 | }; 286 | 9E1F17DC1B2FAFE400C9EC82 /* Resources */ = { 287 | isa = PBXResourcesBuildPhase; 288 | buildActionMask = 2147483647; 289 | files = ( 290 | 9E1F17EC1B2FAFE400C9EC82 /* Assets.xcassets in Resources */, 291 | ); 292 | runOnlyForDeploymentPostprocessing = 0; 293 | }; 294 | /* End PBXResourcesBuildPhase section */ 295 | 296 | /* Begin PBXSourcesBuildPhase section */ 297 | 9E1F17B91B2FAFE300C9EC82 /* Sources */ = { 298 | isa = PBXSourcesBuildPhase; 299 | buildActionMask = 2147483647; 300 | files = ( 301 | 9E1F17C31B2FAFE400C9EC82 /* ViewController.swift in Sources */, 302 | 9E1F17C11B2FAFE400C9EC82 /* AppDelegate.swift in Sources */, 303 | ); 304 | runOnlyForDeploymentPostprocessing = 0; 305 | }; 306 | 9E1F17DA1B2FAFE400C9EC82 /* Sources */ = { 307 | isa = PBXSourcesBuildPhase; 308 | buildActionMask = 2147483647; 309 | files = ( 310 | 9E1F17EA1B2FAFE400C9EC82 /* NotificationController.swift in Sources */, 311 | 9E1F17E81B2FAFE400C9EC82 /* ExtensionDelegate.swift in Sources */, 312 | 9E1F17E61B2FAFE400C9EC82 /* InterfaceController.swift in Sources */, 313 | ); 314 | runOnlyForDeploymentPostprocessing = 0; 315 | }; 316 | /* End PBXSourcesBuildPhase section */ 317 | 318 | /* Begin PBXTargetDependency section */ 319 | 9E1F17D21B2FAFE400C9EC82 /* PBXTargetDependency */ = { 320 | isa = PBXTargetDependency; 321 | target = 9E1F17CE1B2FAFE400C9EC82 /* WatchConnectivityTutorial WatchKit App */; 322 | targetProxy = 9E1F17D11B2FAFE400C9EC82 /* PBXContainerItemProxy */; 323 | }; 324 | 9E1F17E11B2FAFE400C9EC82 /* PBXTargetDependency */ = { 325 | isa = PBXTargetDependency; 326 | target = 9E1F17DD1B2FAFE400C9EC82 /* WatchConnectivityTutorial WatchKit Extension */; 327 | targetProxy = 9E1F17E01B2FAFE400C9EC82 /* PBXContainerItemProxy */; 328 | }; 329 | /* End PBXTargetDependency section */ 330 | 331 | /* Begin PBXVariantGroup section */ 332 | 9E1F17C41B2FAFE400C9EC82 /* Main.storyboard */ = { 333 | isa = PBXVariantGroup; 334 | children = ( 335 | 9E1F17C51B2FAFE400C9EC82 /* Base */, 336 | ); 337 | name = Main.storyboard; 338 | sourceTree = ""; 339 | }; 340 | 9E1F17C91B2FAFE400C9EC82 /* LaunchScreen.storyboard */ = { 341 | isa = PBXVariantGroup; 342 | children = ( 343 | 9E1F17CA1B2FAFE400C9EC82 /* Base */, 344 | ); 345 | name = LaunchScreen.storyboard; 346 | sourceTree = ""; 347 | }; 348 | 9E1F17D41B2FAFE400C9EC82 /* Interface.storyboard */ = { 349 | isa = PBXVariantGroup; 350 | children = ( 351 | 9E1F17D51B2FAFE400C9EC82 /* Base */, 352 | ); 353 | name = Interface.storyboard; 354 | sourceTree = ""; 355 | }; 356 | /* End PBXVariantGroup section */ 357 | 358 | /* Begin XCBuildConfiguration section */ 359 | 9E1F17EE1B2FAFE400C9EC82 /* Debug */ = { 360 | isa = XCBuildConfiguration; 361 | buildSettings = { 362 | ALWAYS_SEARCH_USER_PATHS = NO; 363 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 364 | CLANG_CXX_LIBRARY = "libc++"; 365 | CLANG_ENABLE_MODULES = YES; 366 | CLANG_ENABLE_OBJC_ARC = YES; 367 | CLANG_WARN_BOOL_CONVERSION = YES; 368 | CLANG_WARN_CONSTANT_CONVERSION = YES; 369 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 370 | CLANG_WARN_EMPTY_BODY = YES; 371 | CLANG_WARN_ENUM_CONVERSION = YES; 372 | CLANG_WARN_INT_CONVERSION = YES; 373 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 374 | CLANG_WARN_UNREACHABLE_CODE = YES; 375 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 376 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 377 | COPY_PHASE_STRIP = NO; 378 | DEBUG_INFORMATION_FORMAT = dwarf; 379 | ENABLE_STRICT_OBJC_MSGSEND = YES; 380 | ENABLE_TESTABILITY = YES; 381 | GCC_C_LANGUAGE_STANDARD = gnu99; 382 | GCC_DYNAMIC_NO_PIC = NO; 383 | GCC_NO_COMMON_BLOCKS = YES; 384 | GCC_OPTIMIZATION_LEVEL = 0; 385 | GCC_PREPROCESSOR_DEFINITIONS = ( 386 | "DEBUG=1", 387 | "$(inherited)", 388 | ); 389 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 390 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 391 | GCC_WARN_UNDECLARED_SELECTOR = YES; 392 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 393 | GCC_WARN_UNUSED_FUNCTION = YES; 394 | GCC_WARN_UNUSED_VARIABLE = YES; 395 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 396 | MTL_ENABLE_DEBUG_INFO = YES; 397 | ONLY_ACTIVE_ARCH = YES; 398 | SDKROOT = iphoneos; 399 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 400 | TARGETED_DEVICE_FAMILY = "1,2"; 401 | }; 402 | name = Debug; 403 | }; 404 | 9E1F17EF1B2FAFE400C9EC82 /* Release */ = { 405 | isa = XCBuildConfiguration; 406 | buildSettings = { 407 | ALWAYS_SEARCH_USER_PATHS = NO; 408 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 409 | CLANG_CXX_LIBRARY = "libc++"; 410 | CLANG_ENABLE_MODULES = YES; 411 | CLANG_ENABLE_OBJC_ARC = YES; 412 | CLANG_WARN_BOOL_CONVERSION = YES; 413 | CLANG_WARN_CONSTANT_CONVERSION = YES; 414 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 415 | CLANG_WARN_EMPTY_BODY = YES; 416 | CLANG_WARN_ENUM_CONVERSION = YES; 417 | CLANG_WARN_INT_CONVERSION = YES; 418 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 419 | CLANG_WARN_UNREACHABLE_CODE = YES; 420 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 421 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 422 | COPY_PHASE_STRIP = NO; 423 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 424 | ENABLE_NS_ASSERTIONS = NO; 425 | ENABLE_STRICT_OBJC_MSGSEND = YES; 426 | GCC_C_LANGUAGE_STANDARD = gnu99; 427 | GCC_NO_COMMON_BLOCKS = YES; 428 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 429 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 430 | GCC_WARN_UNDECLARED_SELECTOR = YES; 431 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 432 | GCC_WARN_UNUSED_FUNCTION = YES; 433 | GCC_WARN_UNUSED_VARIABLE = YES; 434 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 435 | MTL_ENABLE_DEBUG_INFO = NO; 436 | SDKROOT = iphoneos; 437 | TARGETED_DEVICE_FAMILY = "1,2"; 438 | VALIDATE_PRODUCT = YES; 439 | }; 440 | name = Release; 441 | }; 442 | 9E1F17F11B2FAFE400C9EC82 /* Debug */ = { 443 | isa = XCBuildConfiguration; 444 | buildSettings = { 445 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = "iPhone Developer"; 446 | INFOPLIST_FILE = "WatchConnectivityTutorial WatchKit Extension/Info.plist"; 447 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; 448 | PRODUCT_BUNDLE_IDENTIFIER = io.thumbworks.WatchConnectivityTutorial.watchkitapp.watchkitextension; 449 | PRODUCT_NAME = "${TARGET_NAME}"; 450 | SDKROOT = watchos; 451 | SKIP_INSTALL = YES; 452 | TARGETED_DEVICE_FAMILY = 4; 453 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 454 | }; 455 | name = Debug; 456 | }; 457 | 9E1F17F21B2FAFE400C9EC82 /* Release */ = { 458 | isa = XCBuildConfiguration; 459 | buildSettings = { 460 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = "iPhone Developer"; 461 | INFOPLIST_FILE = "WatchConnectivityTutorial WatchKit Extension/Info.plist"; 462 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; 463 | PRODUCT_BUNDLE_IDENTIFIER = io.thumbworks.WatchConnectivityTutorial.watchkitapp.watchkitextension; 464 | PRODUCT_NAME = "${TARGET_NAME}"; 465 | SDKROOT = watchos; 466 | SKIP_INSTALL = YES; 467 | TARGETED_DEVICE_FAMILY = 4; 468 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 469 | }; 470 | name = Release; 471 | }; 472 | 9E1F17F51B2FAFE400C9EC82 /* Debug */ = { 473 | isa = XCBuildConfiguration; 474 | buildSettings = { 475 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 476 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = "iPhone Developer"; 477 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 478 | IBSC_MODULE = WatchConnectivityTutorial_WatchKit_Extension; 479 | INFOPLIST_FILE = "WatchConnectivityTutorial WatchKit App/Info.plist"; 480 | PRODUCT_BUNDLE_IDENTIFIER = io.thumbworks.WatchConnectivityTutorial.watchkitapp; 481 | PRODUCT_NAME = "$(TARGET_NAME)"; 482 | SDKROOT = watchos; 483 | SKIP_INSTALL = YES; 484 | TARGETED_DEVICE_FAMILY = 4; 485 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 486 | }; 487 | name = Debug; 488 | }; 489 | 9E1F17F61B2FAFE400C9EC82 /* Release */ = { 490 | isa = XCBuildConfiguration; 491 | buildSettings = { 492 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 493 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = "iPhone Developer"; 494 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 495 | IBSC_MODULE = WatchConnectivityTutorial_WatchKit_Extension; 496 | INFOPLIST_FILE = "WatchConnectivityTutorial WatchKit App/Info.plist"; 497 | PRODUCT_BUNDLE_IDENTIFIER = io.thumbworks.WatchConnectivityTutorial.watchkitapp; 498 | PRODUCT_NAME = "$(TARGET_NAME)"; 499 | SDKROOT = watchos; 500 | SKIP_INSTALL = YES; 501 | TARGETED_DEVICE_FAMILY = 4; 502 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 503 | }; 504 | name = Release; 505 | }; 506 | 9E1F17F91B2FAFE400C9EC82 /* Debug */ = { 507 | isa = XCBuildConfiguration; 508 | buildSettings = { 509 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 510 | INFOPLIST_FILE = WatchConnectivityTutorial/Info.plist; 511 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 512 | PRODUCT_BUNDLE_IDENTIFIER = io.thumbworks.WatchConnectivityTutorial; 513 | PRODUCT_NAME = "$(TARGET_NAME)"; 514 | }; 515 | name = Debug; 516 | }; 517 | 9E1F17FA1B2FAFE400C9EC82 /* Release */ = { 518 | isa = XCBuildConfiguration; 519 | buildSettings = { 520 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 521 | INFOPLIST_FILE = WatchConnectivityTutorial/Info.plist; 522 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 523 | PRODUCT_BUNDLE_IDENTIFIER = io.thumbworks.WatchConnectivityTutorial; 524 | PRODUCT_NAME = "$(TARGET_NAME)"; 525 | }; 526 | name = Release; 527 | }; 528 | /* End XCBuildConfiguration section */ 529 | 530 | /* Begin XCConfigurationList section */ 531 | 9E1F17B81B2FAFE300C9EC82 /* Build configuration list for PBXProject "WatchConnectivityTutorial" */ = { 532 | isa = XCConfigurationList; 533 | buildConfigurations = ( 534 | 9E1F17EE1B2FAFE400C9EC82 /* Debug */, 535 | 9E1F17EF1B2FAFE400C9EC82 /* Release */, 536 | ); 537 | defaultConfigurationIsVisible = 0; 538 | defaultConfigurationName = Release; 539 | }; 540 | 9E1F17F01B2FAFE400C9EC82 /* Build configuration list for PBXNativeTarget "WatchConnectivityTutorial WatchKit Extension" */ = { 541 | isa = XCConfigurationList; 542 | buildConfigurations = ( 543 | 9E1F17F11B2FAFE400C9EC82 /* Debug */, 544 | 9E1F17F21B2FAFE400C9EC82 /* Release */, 545 | ); 546 | defaultConfigurationIsVisible = 0; 547 | }; 548 | 9E1F17F41B2FAFE400C9EC82 /* Build configuration list for PBXNativeTarget "WatchConnectivityTutorial WatchKit App" */ = { 549 | isa = XCConfigurationList; 550 | buildConfigurations = ( 551 | 9E1F17F51B2FAFE400C9EC82 /* Debug */, 552 | 9E1F17F61B2FAFE400C9EC82 /* Release */, 553 | ); 554 | defaultConfigurationIsVisible = 0; 555 | }; 556 | 9E1F17F81B2FAFE400C9EC82 /* Build configuration list for PBXNativeTarget "WatchConnectivityTutorial" */ = { 557 | isa = XCConfigurationList; 558 | buildConfigurations = ( 559 | 9E1F17F91B2FAFE400C9EC82 /* Debug */, 560 | 9E1F17FA1B2FAFE400C9EC82 /* Release */, 561 | ); 562 | defaultConfigurationIsVisible = 0; 563 | }; 564 | /* End XCConfigurationList section */ 565 | }; 566 | rootObject = 9E1F17B51B2FAFE300C9EC82 /* Project object */; 567 | } 568 | -------------------------------------------------------------------------------- /WatchConnectivityTutorial/WatchConnectivityTutorial.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WatchConnectivityTutorial/WatchConnectivityTutorial/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // WatchConnectivityTutorial 4 | // 5 | // Created by Roderic on 6/15/15. 6 | // Copyright © 2015 Thumbworks. 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 | -------------------------------------------------------------------------------- /WatchConnectivityTutorial/WatchConnectivityTutorial/Assets.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 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /WatchConnectivityTutorial/WatchConnectivityTutorial/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /WatchConnectivityTutorial/WatchConnectivityTutorial/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 30 | 40 | 50 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /WatchConnectivityTutorial/WatchConnectivityTutorial/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 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 | 1 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 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /WatchConnectivityTutorial/WatchConnectivityTutorial/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // WatchConnectivityTutorial 4 | // 5 | // Created by Roderic on 6/15/15. 6 | // Copyright © 2015 Thumbworks. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import WatchConnectivity 11 | 12 | class ViewController: UIViewController, WCSessionDelegate { 13 | 14 | @IBOutlet var buttons: [UIButton]! 15 | let session : WCSession! 16 | 17 | required init(coder aDecoder: NSCoder) { 18 | self.session = WCSession.defaultSession() 19 | super.init(coder: aDecoder) 20 | } 21 | 22 | @IBAction func tappedButton(sender: UIButton) { 23 | 24 | if let i = buttons.indexOf(sender) { 25 | let message = ["buttonOffset" : i] 26 | 27 | session.sendMessage(message, replyHandler: { (content:[String : AnyObject]) -> Void in 28 | print("Our counterpart sent something back. This is optional") 29 | }, errorHandler: { (error ) -> Void in 30 | print("We got an error from our watch device : " + error.domain) 31 | }) 32 | 33 | } 34 | } 35 | 36 | func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) { 37 | 38 | // verify that we've gotten a number at the "buttonOffset" key 39 | if let offsetValue = message["buttonOffset"] as! Int? { 40 | 41 | // Determine which watch button has been tapped as mapped to the iPhone's 42 | let tappedButton = buttons[offsetValue] 43 | 44 | // We're going to change the title, so let's store the old one so we can set it back 45 | let oldTitle = tappedButton.titleForState(UIControlState.Normal) 46 | 47 | // Change the title on the main thread 48 | dispatch_async(dispatch_get_main_queue(), { () -> Void in 49 | tappedButton.setTitle("😍", forState:UIControlState.Normal) 50 | }) 51 | 52 | // Delay a little bit then set it back 53 | let delayTime = dispatch_time(DISPATCH_TIME_NOW, 54 | Int64(1 * Double(NSEC_PER_SEC))) 55 | dispatch_after(delayTime, dispatch_get_main_queue()) { 56 | tappedButton.setTitle(oldTitle, forState:UIControlState.Normal) 57 | } 58 | // again, we can optionally call replyHandler() 59 | } 60 | } 61 | 62 | override func viewDidLoad() { 63 | super.viewDidLoad() 64 | 65 | if(WCSession.isSupported()) { 66 | session.delegate = self 67 | session.activateSession() 68 | } 69 | } 70 | 71 | override func didReceiveMemoryWarning() { 72 | super.didReceiveMemoryWarning() 73 | // Dispose of any resources that can be recreated. 74 | } 75 | 76 | 77 | } 78 | 79 | -------------------------------------------------------------------------------- /WikiImages/1 Create_New_Project_iOS_App_With_WatchKit_App.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThumbWorks/WatchConnectivityTutorial/d7f789463dfe3f8f8f6fe788ed7f879c023eb803/WikiImages/1 Create_New_Project_iOS_App_With_WatchKit_App.jpg -------------------------------------------------------------------------------- /WikiImages/2 Create_Product_Name.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThumbWorks/WatchConnectivityTutorial/d7f789463dfe3f8f8f6fe788ed7f879c023eb803/WikiImages/2 Create_Product_Name.jpg -------------------------------------------------------------------------------- /WikiImages/3 Buttons_and_Label_laid_out.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThumbWorks/WatchConnectivityTutorial/d7f789463dfe3f8f8f6fe788ed7f879c023eb803/WikiImages/3 Buttons_and_Label_laid_out.jpg -------------------------------------------------------------------------------- /WikiImages/3_5_connecting_watch_buttons.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThumbWorks/WatchConnectivityTutorial/d7f789463dfe3f8f8f6fe788ed7f879c023eb803/WikiImages/3_5_connecting_watch_buttons.jpg -------------------------------------------------------------------------------- /WikiImages/4_Added_labels_to_the_phone_storyboard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThumbWorks/WatchConnectivityTutorial/d7f789463dfe3f8f8f6fe788ed7f879c023eb803/WikiImages/4_Added_labels_to_the_phone_storyboard.jpg -------------------------------------------------------------------------------- /WikiImages/5_Buttons_in_a_collection.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThumbWorks/WatchConnectivityTutorial/d7f789463dfe3f8f8f6fe788ed7f879c023eb803/WikiImages/5_Buttons_in_a_collection.jpg -------------------------------------------------------------------------------- /WikiImages/6 BasicTalkingDevices.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThumbWorks/WatchConnectivityTutorial/d7f789463dfe3f8f8f6fe788ed7f879c023eb803/WikiImages/6 BasicTalkingDevices.gif --------------------------------------------------------------------------------