├── LICENSE ├── README.md ├── Sample App ├── WatchKit Sample App Extension │ ├── Assets.xcassets │ │ ├── Complication.complicationset │ │ │ ├── Circular.imageset │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── Extra Large.imageset │ │ │ │ └── Contents.json │ │ │ ├── Graphic Bezel.imageset │ │ │ │ └── Contents.json │ │ │ ├── Graphic Circular.imageset │ │ │ │ └── Contents.json │ │ │ ├── Graphic Corner.imageset │ │ │ │ └── Contents.json │ │ │ ├── Graphic Large Rectangular.imageset │ │ │ │ └── Contents.json │ │ │ ├── Modular.imageset │ │ │ │ └── Contents.json │ │ │ └── Utilitarian.imageset │ │ │ │ └── Contents.json │ │ └── Contents.json │ ├── ExtensionDelegate.swift │ ├── Info.plist │ └── InterfaceController.swift ├── WatchKit Sample App │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ └── Interface.storyboard │ └── Info.plist └── iOS App │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── ViewController.swift ├── WatchKitTimePicker.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcshareddata │ └── xcschemes │ │ └── WatchKitTimePicker.xcscheme └── xcuserdata │ └── cal.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── WatchKitTimePicker ├── Info.plist └── TimePickerDataSource.swift └── images ├── watchkit time picker 12hr.gif └── watchkit time picker 24hr.gif /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Cal Stephens 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WatchKitTimePicker 2 | 3 | **WatchKitTimePicker** is a time picker data source for WatchKit that... 4 | - Mirrors the behavior of UIKit's `UIDatePicker` 5 | - Automatically uses either 12-hour or 24-hour time, depending on the user's current Locale. 6 | - Supports watchOS 2.0+ 7 | 8 | ## Demo 9 | 10 |

11 | 12 |

13 | 14 |
15 | 16 | 17 | ## Installation 18 | 19 | #### Manual Installation: 20 | 21 | **WatchKitTimePicker** is just one individual .swift file: [`TimePickerDataSource.swift`](https://github.com/calda/WatchKitTimePicker/blob/master/WatchKitTimePicker/TimePickerDataSource.swift). You could install it quickly by downloading that file and dragging it in to your Watch App Extension target. 22 | 23 | #### [Carthage](https://github.com/Carthage/Carthage): 24 | 25 | Add `github "calda/WatchKitTimePicker"` to your Cartfile. 26 | 27 | ## Usage 28 | 29 | Unlike with iOS view-layer libraries, you can't just distribute and use a `UIView` or `WKInterfaceObject` subclass. Interface elements have to be set up using Interface Builder. 30 | 31 | **WatchKitTimePicker** is a data source that controls and manages a group of `WKInterfacePicker` objects. 32 | 33 | ### Interface Builder: 34 | 35 | - Create a horizontal `WKInterfaceGroup`. 36 | - Add three `WKInterfacePicker` objects to the group. 37 | - Connect and `@IBOutlet` and an `@IBAction` for each of the pickers. 38 | 39 | ### Your `WKInterfaceController` subclass: 40 | 41 | ```Swift 42 | import WatchKit 43 | import Foundation 44 | import WatchKitTimePicker 45 | 46 | class InterfaceController: WKInterfaceController { 47 | 48 | var timePickerDataSource: TimePickerDataSource! 49 | @IBOutlet weak var hourTimePicker: WKInterfacePicker! 50 | @IBOutlet weak var minuteTimePicker: WKInterfacePicker! 51 | @IBOutlet weak var amPmTimePicker: WKInterfacePicker! 52 | 53 | override func awake(withContext context: Any?) { 54 | timePickerDataSource = TimePickerDataSource( 55 | hoursPicker: hourTimePicker, 56 | minutesPicker: minuteTimePicker, 57 | amPmPicker: amPmTimePicker, 58 | interval: .fiveMinutes) // supports .minute, .fiveMinutes, .fifteenMinutes, and .halfHour 59 | 60 | timePickerDataSource.selectedTimeDidUpdate = { selectedTime in 61 | // ... 62 | } 63 | 64 | timePickerDataSource.updateDate(to: Date()) 65 | } 66 | 67 | @IBAction func hourPickerDidUpdate(_ index: Int) { 68 | timePickerDataSource.hourPickerUpdated(to: index) 69 | } 70 | 71 | @IBAction func minutePickerDidUpdate(_ index: Int) { 72 | timePickerDataSource.minutePickerUpdated(to: index) 73 | } 74 | 75 | @IBAction func amPmPickerDidUpdate(_ index: Int) { 76 | timePickerDataSource.amPmPickerUpdated(to: index) 77 | } 78 | 79 | } 80 | 81 | ``` 82 | -------------------------------------------------------------------------------- /Sample App/WatchKit Sample App Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">145" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /Sample App/WatchKit Sample App Extension/Assets.xcassets/Complication.complicationset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "assets" : [ 3 | { 4 | "idiom" : "watch", 5 | "filename" : "Circular.imageset", 6 | "role" : "circular" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "filename" : "Extra Large.imageset", 11 | "role" : "extra-large" 12 | }, 13 | { 14 | "idiom" : "watch", 15 | "filename" : "Graphic Bezel.imageset", 16 | "role" : "graphic-bezel" 17 | }, 18 | { 19 | "idiom" : "watch", 20 | "filename" : "Graphic Circular.imageset", 21 | "role" : "graphic-circular" 22 | }, 23 | { 24 | "idiom" : "watch", 25 | "filename" : "Graphic Corner.imageset", 26 | "role" : "graphic-corner" 27 | }, 28 | { 29 | "idiom" : "watch", 30 | "filename" : "Graphic Large Rectangular.imageset", 31 | "role" : "graphic-large-rectangular" 32 | }, 33 | { 34 | "idiom" : "watch", 35 | "filename" : "Modular.imageset", 36 | "role" : "modular" 37 | }, 38 | { 39 | "idiom" : "watch", 40 | "filename" : "Utilitarian.imageset", 41 | "role" : "utilitarian" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /Sample App/WatchKit Sample App Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">145" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /Sample App/WatchKit Sample App Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">145" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /Sample App/WatchKit Sample App Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">145" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /Sample App/WatchKit Sample App Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">145" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /Sample App/WatchKit Sample App Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">145" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /Sample App/WatchKit Sample App Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">145" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /Sample App/WatchKit Sample App Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "watch", 5 | "scale" : "2x", 6 | "screen-width" : "<=145" 7 | }, 8 | { 9 | "idiom" : "watch", 10 | "scale" : "2x", 11 | "screen-width" : ">145" 12 | } 13 | ], 14 | "info" : { 15 | "version" : 1, 16 | "author" : "xcode" 17 | } 18 | } -------------------------------------------------------------------------------- /Sample App/WatchKit Sample App Extension/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Sample App/WatchKit Sample App Extension/ExtensionDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExtensionDelegate.swift 3 | // WatchKit Sample App Extension 4 | // 5 | // Created by Cal Stephens on 11/5/18. 6 | // Copyright © 2018 Cal Stephens. All rights reserved. 7 | // 8 | 9 | import WatchKit 10 | 11 | class ExtensionDelegate: NSObject, WKExtensionDelegate { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /Sample App/WatchKit Sample App Extension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | WatchKit Sample App 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 | CFBundleVersion 22 | 1 23 | NSExtension 24 | 25 | NSExtensionAttributes 26 | 27 | WKAppBundleIdentifier 28 | tech.calstephens.WatchKitTimePicker.Sample-App.watchkitapp 29 | 30 | NSExtensionPointIdentifier 31 | com.apple.watchkit 32 | 33 | WKExtensionDelegateClassName 34 | $(PRODUCT_MODULE_NAME).ExtensionDelegate 35 | 36 | 37 | -------------------------------------------------------------------------------- /Sample App/WatchKit Sample App Extension/InterfaceController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InterfaceController.swift 3 | // WatchKit Sample App Extension 4 | // 5 | // Created by Cal Stephens on 11/5/18. 6 | // Copyright © 2018 Cal Stephens. All rights reserved. 7 | // 8 | 9 | import WatchKit 10 | import Foundation 11 | import WatchKitTimePicker 12 | 13 | class InterfaceController: WKInterfaceController { 14 | 15 | var timePickerDataSource: TimePickerDataSource! 16 | @IBOutlet weak var hourTimePicker: WKInterfacePicker! 17 | @IBOutlet weak var minuteTimePicker: WKInterfacePicker! 18 | @IBOutlet weak var amPmTimePicker: WKInterfacePicker! 19 | @IBOutlet weak var selectedTimeLabel: WKInterfaceLabel! 20 | 21 | override func awake(withContext context: Any?) { 22 | timePickerDataSource = TimePickerDataSource( 23 | hoursPicker: hourTimePicker, 24 | minutesPicker: minuteTimePicker, 25 | amPmPicker: amPmTimePicker, 26 | interval: .fiveMinutes) 27 | 28 | timePickerDataSource.selectedTimeDidUpdate = { [weak self] selectedTime in 29 | let timeFormatter = DateFormatter() 30 | timeFormatter.timeStyle = .short 31 | timeFormatter.dateStyle = .none 32 | 33 | self?.selectedTimeLabel.setText(timeFormatter.string(from: selectedTime)) 34 | } 35 | 36 | timePickerDataSource.updateDate(to: Date()) 37 | } 38 | 39 | @IBAction func hourPickerDidUpdate(_ index: Int) { 40 | timePickerDataSource.hourPickerUpdated(to: index) 41 | } 42 | 43 | @IBAction func minutePickerDidUpdate(_ index: Int) { 44 | timePickerDataSource.minutePickerUpdated(to: index) 45 | } 46 | 47 | @IBAction func amPmPickerDidUpdate(_ index: Int) { 48 | timePickerDataSource.amPmPickerUpdated(to: index) 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /Sample App/WatchKit Sample 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" : "appLauncher", 41 | "subtype" : "40mm" 42 | }, 43 | { 44 | "size" : "50x50", 45 | "idiom" : "watch", 46 | "scale" : "2x", 47 | "role" : "appLauncher", 48 | "subtype" : "44mm" 49 | }, 50 | { 51 | "size" : "86x86", 52 | "idiom" : "watch", 53 | "scale" : "2x", 54 | "role" : "quickLook", 55 | "subtype" : "38mm" 56 | }, 57 | { 58 | "size" : "98x98", 59 | "idiom" : "watch", 60 | "scale" : "2x", 61 | "role" : "quickLook", 62 | "subtype" : "42mm" 63 | }, 64 | { 65 | "size" : "108x108", 66 | "idiom" : "watch", 67 | "scale" : "2x", 68 | "role" : "quickLook", 69 | "subtype" : "44mm" 70 | }, 71 | { 72 | "idiom" : "watch-marketing", 73 | "size" : "1024x1024", 74 | "scale" : "1x" 75 | } 76 | ], 77 | "info" : { 78 | "version" : 1, 79 | "author" : "xcode" 80 | } 81 | } -------------------------------------------------------------------------------- /Sample App/WatchKit Sample App/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Sample App/WatchKit Sample App/Base.lproj/Interface.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Sample App/WatchKit Sample App/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | 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 | CFBundleVersion 22 | 1 23 | UISupportedInterfaceOrientations 24 | 25 | UIInterfaceOrientationPortrait 26 | UIInterfaceOrientationPortraitUpsideDown 27 | 28 | WKCompanionAppBundleIdentifier 29 | tech.calstephens.WatchKitTimePicker.Sample-App 30 | WKWatchKitApp 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Sample App/iOS App/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Sample App 4 | // 5 | // Created by Cal Stephens on 11/5/18. 6 | // Copyright © 2018 Cal Stephens. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 15 | return true 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /Sample App/iOS App/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Sample App/iOS App/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Sample App/iOS App/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Sample App/iOS App/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 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Sample App/iOS App/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | LSApplicationCategoryType 6 | 7 | CFBundleDevelopmentRegion 8 | $(DEVELOPMENT_LANGUAGE) 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 | 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 | -------------------------------------------------------------------------------- /Sample App/iOS App/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Sample App 4 | // 5 | // Created by Cal Stephens on 11/5/18. 6 | // Copyright © 2018 Cal Stephens. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /WatchKitTimePicker.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2E4540522190B878004D8CEE /* TimePickerDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E4540512190B878004D8CEE /* TimePickerDataSource.swift */; }; 11 | 2E45405A2190BC88004D8CEE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E4540592190BC88004D8CEE /* AppDelegate.swift */; }; 12 | 2E45405C2190BC88004D8CEE /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E45405B2190BC88004D8CEE /* ViewController.swift */; }; 13 | 2E45405F2190BC88004D8CEE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2E45405D2190BC88004D8CEE /* Main.storyboard */; }; 14 | 2E4540612190BC8A004D8CEE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2E4540602190BC8A004D8CEE /* Assets.xcassets */; }; 15 | 2E4540642190BC8A004D8CEE /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2E4540622190BC8A004D8CEE /* LaunchScreen.storyboard */; }; 16 | 2E4540712190BCF4004D8CEE /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2E45406F2190BCF4004D8CEE /* Interface.storyboard */; }; 17 | 2E4540732190BCF5004D8CEE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2E4540722190BCF5004D8CEE /* Assets.xcassets */; }; 18 | 2E45407A2190BCF5004D8CEE /* WatchKit Sample App Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 2E4540792190BCF5004D8CEE /* WatchKit Sample App Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 19 | 2E45407F2190BCF5004D8CEE /* InterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E45407E2190BCF5004D8CEE /* InterfaceController.swift */; }; 20 | 2E4540812190BCF5004D8CEE /* ExtensionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E4540802190BCF5004D8CEE /* ExtensionDelegate.swift */; }; 21 | 2E4540832190BCF5004D8CEE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2E4540822190BCF5004D8CEE /* Assets.xcassets */; }; 22 | 2E4540872190BCF5004D8CEE /* WatchKit Sample App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 2E45406D2190BCF4004D8CEE /* WatchKit Sample App.app */; }; 23 | 2E4540902190C475004D8CEE /* WatchKitTimePicker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2E4540462190B851004D8CEE /* WatchKitTimePicker.framework */; }; 24 | 2E4540912190C475004D8CEE /* WatchKitTimePicker.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 2E4540462190B851004D8CEE /* WatchKitTimePicker.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXContainerItemProxy section */ 28 | 2E45407B2190BCF5004D8CEE /* PBXContainerItemProxy */ = { 29 | isa = PBXContainerItemProxy; 30 | containerPortal = 2E45403D2190B851004D8CEE /* Project object */; 31 | proxyType = 1; 32 | remoteGlobalIDString = 2E4540782190BCF5004D8CEE; 33 | remoteInfo = "WatchKit Sample App Extension"; 34 | }; 35 | 2E4540852190BCF5004D8CEE /* PBXContainerItemProxy */ = { 36 | isa = PBXContainerItemProxy; 37 | containerPortal = 2E45403D2190B851004D8CEE /* Project object */; 38 | proxyType = 1; 39 | remoteGlobalIDString = 2E45406C2190BCF4004D8CEE; 40 | remoteInfo = "WatchKit Sample App"; 41 | }; 42 | 2E4540922190C475004D8CEE /* PBXContainerItemProxy */ = { 43 | isa = PBXContainerItemProxy; 44 | containerPortal = 2E45403D2190B851004D8CEE /* Project object */; 45 | proxyType = 1; 46 | remoteGlobalIDString = 2E4540452190B851004D8CEE; 47 | remoteInfo = WatchKitTimePicker; 48 | }; 49 | /* End PBXContainerItemProxy section */ 50 | 51 | /* Begin PBXCopyFilesBuildPhase section */ 52 | 2E45408B2190BCF5004D8CEE /* Embed App Extensions */ = { 53 | isa = PBXCopyFilesBuildPhase; 54 | buildActionMask = 2147483647; 55 | dstPath = ""; 56 | dstSubfolderSpec = 13; 57 | files = ( 58 | 2E45407A2190BCF5004D8CEE /* WatchKit Sample App Extension.appex in Embed App Extensions */, 59 | ); 60 | name = "Embed App Extensions"; 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | 2E45408F2190BCF5004D8CEE /* Embed Watch Content */ = { 64 | isa = PBXCopyFilesBuildPhase; 65 | buildActionMask = 2147483647; 66 | dstPath = "$(CONTENTS_FOLDER_PATH)/Watch"; 67 | dstSubfolderSpec = 16; 68 | files = ( 69 | 2E4540872190BCF5004D8CEE /* WatchKit Sample App.app in Embed Watch Content */, 70 | ); 71 | name = "Embed Watch Content"; 72 | runOnlyForDeploymentPostprocessing = 0; 73 | }; 74 | 2E4540942190C475004D8CEE /* Embed Frameworks */ = { 75 | isa = PBXCopyFilesBuildPhase; 76 | buildActionMask = 2147483647; 77 | dstPath = ""; 78 | dstSubfolderSpec = 10; 79 | files = ( 80 | 2E4540912190C475004D8CEE /* WatchKitTimePicker.framework in Embed Frameworks */, 81 | ); 82 | name = "Embed Frameworks"; 83 | runOnlyForDeploymentPostprocessing = 0; 84 | }; 85 | /* End PBXCopyFilesBuildPhase section */ 86 | 87 | /* Begin PBXFileReference section */ 88 | 2E4540462190B851004D8CEE /* WatchKitTimePicker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = WatchKitTimePicker.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 89 | 2E45404A2190B851004D8CEE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 90 | 2E4540512190B878004D8CEE /* TimePickerDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimePickerDataSource.swift; sourceTree = ""; }; 91 | 2E4540572190BC88004D8CEE /* Sample App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Sample App.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 92 | 2E4540592190BC88004D8CEE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 93 | 2E45405B2190BC88004D8CEE /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 94 | 2E45405E2190BC88004D8CEE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 95 | 2E4540602190BC8A004D8CEE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 96 | 2E4540632190BC8A004D8CEE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97 | 2E4540652190BC8A004D8CEE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 98 | 2E45406D2190BCF4004D8CEE /* WatchKit Sample App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "WatchKit Sample App.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 99 | 2E4540702190BCF4004D8CEE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Interface.storyboard; sourceTree = ""; }; 100 | 2E4540722190BCF5004D8CEE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 101 | 2E4540742190BCF5004D8CEE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 102 | 2E4540792190BCF5004D8CEE /* WatchKit Sample App Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "WatchKit Sample App Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; 103 | 2E45407E2190BCF5004D8CEE /* InterfaceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterfaceController.swift; sourceTree = ""; }; 104 | 2E4540802190BCF5004D8CEE /* ExtensionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionDelegate.swift; sourceTree = ""; }; 105 | 2E4540822190BCF5004D8CEE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 106 | 2E4540842190BCF5004D8CEE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 107 | /* End PBXFileReference section */ 108 | 109 | /* Begin PBXFrameworksBuildPhase section */ 110 | 2E4540432190B851004D8CEE /* Frameworks */ = { 111 | isa = PBXFrameworksBuildPhase; 112 | buildActionMask = 2147483647; 113 | files = ( 114 | ); 115 | runOnlyForDeploymentPostprocessing = 0; 116 | }; 117 | 2E4540542190BC88004D8CEE /* Frameworks */ = { 118 | isa = PBXFrameworksBuildPhase; 119 | buildActionMask = 2147483647; 120 | files = ( 121 | ); 122 | runOnlyForDeploymentPostprocessing = 0; 123 | }; 124 | 2E4540762190BCF5004D8CEE /* Frameworks */ = { 125 | isa = PBXFrameworksBuildPhase; 126 | buildActionMask = 2147483647; 127 | files = ( 128 | 2E4540902190C475004D8CEE /* WatchKitTimePicker.framework in Frameworks */, 129 | ); 130 | runOnlyForDeploymentPostprocessing = 0; 131 | }; 132 | /* End PBXFrameworksBuildPhase section */ 133 | 134 | /* Begin PBXGroup section */ 135 | 2E45403C2190B851004D8CEE = { 136 | isa = PBXGroup; 137 | children = ( 138 | 2E45406A2190BC9A004D8CEE /* Sample App */, 139 | 2E4540482190B851004D8CEE /* WatchKitTimePicker */, 140 | 2E4540472190B851004D8CEE /* Products */, 141 | ); 142 | sourceTree = ""; 143 | }; 144 | 2E4540472190B851004D8CEE /* Products */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | 2E4540462190B851004D8CEE /* WatchKitTimePicker.framework */, 148 | 2E4540572190BC88004D8CEE /* Sample App.app */, 149 | 2E45406D2190BCF4004D8CEE /* WatchKit Sample App.app */, 150 | 2E4540792190BCF5004D8CEE /* WatchKit Sample App Extension.appex */, 151 | ); 152 | name = Products; 153 | sourceTree = ""; 154 | }; 155 | 2E4540482190B851004D8CEE /* WatchKitTimePicker */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | 2E4540512190B878004D8CEE /* TimePickerDataSource.swift */, 159 | 2E45404A2190B851004D8CEE /* Info.plist */, 160 | ); 161 | path = WatchKitTimePicker; 162 | sourceTree = ""; 163 | }; 164 | 2E4540582190BC88004D8CEE /* iOS App */ = { 165 | isa = PBXGroup; 166 | children = ( 167 | 2E4540592190BC88004D8CEE /* AppDelegate.swift */, 168 | 2E45405B2190BC88004D8CEE /* ViewController.swift */, 169 | 2E45405D2190BC88004D8CEE /* Main.storyboard */, 170 | 2E4540602190BC8A004D8CEE /* Assets.xcassets */, 171 | 2E4540622190BC8A004D8CEE /* LaunchScreen.storyboard */, 172 | 2E4540652190BC8A004D8CEE /* Info.plist */, 173 | ); 174 | path = "iOS App"; 175 | sourceTree = ""; 176 | }; 177 | 2E45406A2190BC9A004D8CEE /* Sample App */ = { 178 | isa = PBXGroup; 179 | children = ( 180 | 2E45406E2190BCF4004D8CEE /* WatchKit Sample App */, 181 | 2E45407D2190BCF5004D8CEE /* WatchKit Sample App Extension */, 182 | 2E4540582190BC88004D8CEE /* iOS App */, 183 | ); 184 | path = "Sample App"; 185 | sourceTree = ""; 186 | }; 187 | 2E45406E2190BCF4004D8CEE /* WatchKit Sample App */ = { 188 | isa = PBXGroup; 189 | children = ( 190 | 2E45406F2190BCF4004D8CEE /* Interface.storyboard */, 191 | 2E4540722190BCF5004D8CEE /* Assets.xcassets */, 192 | 2E4540742190BCF5004D8CEE /* Info.plist */, 193 | ); 194 | path = "WatchKit Sample App"; 195 | sourceTree = ""; 196 | }; 197 | 2E45407D2190BCF5004D8CEE /* WatchKit Sample App Extension */ = { 198 | isa = PBXGroup; 199 | children = ( 200 | 2E45407E2190BCF5004D8CEE /* InterfaceController.swift */, 201 | 2E4540802190BCF5004D8CEE /* ExtensionDelegate.swift */, 202 | 2E4540822190BCF5004D8CEE /* Assets.xcassets */, 203 | 2E4540842190BCF5004D8CEE /* Info.plist */, 204 | ); 205 | path = "WatchKit Sample App Extension"; 206 | sourceTree = ""; 207 | }; 208 | /* End PBXGroup section */ 209 | 210 | /* Begin PBXHeadersBuildPhase section */ 211 | 2E4540412190B851004D8CEE /* Headers */ = { 212 | isa = PBXHeadersBuildPhase; 213 | buildActionMask = 2147483647; 214 | files = ( 215 | ); 216 | runOnlyForDeploymentPostprocessing = 0; 217 | }; 218 | /* End PBXHeadersBuildPhase section */ 219 | 220 | /* Begin PBXNativeTarget section */ 221 | 2E4540452190B851004D8CEE /* WatchKitTimePicker */ = { 222 | isa = PBXNativeTarget; 223 | buildConfigurationList = 2E45404E2190B851004D8CEE /* Build configuration list for PBXNativeTarget "WatchKitTimePicker" */; 224 | buildPhases = ( 225 | 2E4540412190B851004D8CEE /* Headers */, 226 | 2E4540422190B851004D8CEE /* Sources */, 227 | 2E4540432190B851004D8CEE /* Frameworks */, 228 | 2E4540442190B851004D8CEE /* Resources */, 229 | ); 230 | buildRules = ( 231 | ); 232 | dependencies = ( 233 | ); 234 | name = WatchKitTimePicker; 235 | productName = WatchKitTimePicker; 236 | productReference = 2E4540462190B851004D8CEE /* WatchKitTimePicker.framework */; 237 | productType = "com.apple.product-type.framework"; 238 | }; 239 | 2E4540562190BC88004D8CEE /* Sample App */ = { 240 | isa = PBXNativeTarget; 241 | buildConfigurationList = 2E4540662190BC8A004D8CEE /* Build configuration list for PBXNativeTarget "Sample App" */; 242 | buildPhases = ( 243 | 2E4540532190BC88004D8CEE /* Sources */, 244 | 2E4540542190BC88004D8CEE /* Frameworks */, 245 | 2E4540552190BC88004D8CEE /* Resources */, 246 | 2E45408F2190BCF5004D8CEE /* Embed Watch Content */, 247 | ); 248 | buildRules = ( 249 | ); 250 | dependencies = ( 251 | 2E4540862190BCF5004D8CEE /* PBXTargetDependency */, 252 | ); 253 | name = "Sample App"; 254 | productName = "Sample App"; 255 | productReference = 2E4540572190BC88004D8CEE /* Sample App.app */; 256 | productType = "com.apple.product-type.application"; 257 | }; 258 | 2E45406C2190BCF4004D8CEE /* WatchKit Sample App */ = { 259 | isa = PBXNativeTarget; 260 | buildConfigurationList = 2E45408C2190BCF5004D8CEE /* Build configuration list for PBXNativeTarget "WatchKit Sample App" */; 261 | buildPhases = ( 262 | 2E45406B2190BCF4004D8CEE /* Resources */, 263 | 2E45408B2190BCF5004D8CEE /* Embed App Extensions */, 264 | ); 265 | buildRules = ( 266 | ); 267 | dependencies = ( 268 | 2E45407C2190BCF5004D8CEE /* PBXTargetDependency */, 269 | ); 270 | name = "WatchKit Sample App"; 271 | productName = "WatchKit Sample App"; 272 | productReference = 2E45406D2190BCF4004D8CEE /* WatchKit Sample App.app */; 273 | productType = "com.apple.product-type.application.watchapp2"; 274 | }; 275 | 2E4540782190BCF5004D8CEE /* WatchKit Sample App Extension */ = { 276 | isa = PBXNativeTarget; 277 | buildConfigurationList = 2E4540882190BCF5004D8CEE /* Build configuration list for PBXNativeTarget "WatchKit Sample App Extension" */; 278 | buildPhases = ( 279 | 2E4540752190BCF5004D8CEE /* Sources */, 280 | 2E4540762190BCF5004D8CEE /* Frameworks */, 281 | 2E4540772190BCF5004D8CEE /* Resources */, 282 | 2E4540942190C475004D8CEE /* Embed Frameworks */, 283 | ); 284 | buildRules = ( 285 | ); 286 | dependencies = ( 287 | 2E4540932190C475004D8CEE /* PBXTargetDependency */, 288 | ); 289 | name = "WatchKit Sample App Extension"; 290 | productName = "WatchKit Sample App Extension"; 291 | productReference = 2E4540792190BCF5004D8CEE /* WatchKit Sample App Extension.appex */; 292 | productType = "com.apple.product-type.watchkit2-extension"; 293 | }; 294 | /* End PBXNativeTarget section */ 295 | 296 | /* Begin PBXProject section */ 297 | 2E45403D2190B851004D8CEE /* Project object */ = { 298 | isa = PBXProject; 299 | attributes = { 300 | LastSwiftUpdateCheck = 1010; 301 | LastUpgradeCheck = 1010; 302 | ORGANIZATIONNAME = "Cal Stephens"; 303 | TargetAttributes = { 304 | 2E4540452190B851004D8CEE = { 305 | CreatedOnToolsVersion = 10.1; 306 | LastSwiftMigration = 1010; 307 | }; 308 | 2E4540562190BC88004D8CEE = { 309 | CreatedOnToolsVersion = 10.1; 310 | }; 311 | 2E45406C2190BCF4004D8CEE = { 312 | CreatedOnToolsVersion = 10.1; 313 | }; 314 | 2E4540782190BCF5004D8CEE = { 315 | CreatedOnToolsVersion = 10.1; 316 | }; 317 | }; 318 | }; 319 | buildConfigurationList = 2E4540402190B851004D8CEE /* Build configuration list for PBXProject "WatchKitTimePicker" */; 320 | compatibilityVersion = "Xcode 9.3"; 321 | developmentRegion = en; 322 | hasScannedForEncodings = 0; 323 | knownRegions = ( 324 | en, 325 | Base, 326 | ); 327 | mainGroup = 2E45403C2190B851004D8CEE; 328 | productRefGroup = 2E4540472190B851004D8CEE /* Products */; 329 | projectDirPath = ""; 330 | projectRoot = ""; 331 | targets = ( 332 | 2E4540452190B851004D8CEE /* WatchKitTimePicker */, 333 | 2E4540562190BC88004D8CEE /* Sample App */, 334 | 2E45406C2190BCF4004D8CEE /* WatchKit Sample App */, 335 | 2E4540782190BCF5004D8CEE /* WatchKit Sample App Extension */, 336 | ); 337 | }; 338 | /* End PBXProject section */ 339 | 340 | /* Begin PBXResourcesBuildPhase section */ 341 | 2E4540442190B851004D8CEE /* Resources */ = { 342 | isa = PBXResourcesBuildPhase; 343 | buildActionMask = 2147483647; 344 | files = ( 345 | ); 346 | runOnlyForDeploymentPostprocessing = 0; 347 | }; 348 | 2E4540552190BC88004D8CEE /* Resources */ = { 349 | isa = PBXResourcesBuildPhase; 350 | buildActionMask = 2147483647; 351 | files = ( 352 | 2E4540642190BC8A004D8CEE /* LaunchScreen.storyboard in Resources */, 353 | 2E4540612190BC8A004D8CEE /* Assets.xcassets in Resources */, 354 | 2E45405F2190BC88004D8CEE /* Main.storyboard in Resources */, 355 | ); 356 | runOnlyForDeploymentPostprocessing = 0; 357 | }; 358 | 2E45406B2190BCF4004D8CEE /* Resources */ = { 359 | isa = PBXResourcesBuildPhase; 360 | buildActionMask = 2147483647; 361 | files = ( 362 | 2E4540732190BCF5004D8CEE /* Assets.xcassets in Resources */, 363 | 2E4540712190BCF4004D8CEE /* Interface.storyboard in Resources */, 364 | ); 365 | runOnlyForDeploymentPostprocessing = 0; 366 | }; 367 | 2E4540772190BCF5004D8CEE /* Resources */ = { 368 | isa = PBXResourcesBuildPhase; 369 | buildActionMask = 2147483647; 370 | files = ( 371 | 2E4540832190BCF5004D8CEE /* Assets.xcassets in Resources */, 372 | ); 373 | runOnlyForDeploymentPostprocessing = 0; 374 | }; 375 | /* End PBXResourcesBuildPhase section */ 376 | 377 | /* Begin PBXSourcesBuildPhase section */ 378 | 2E4540422190B851004D8CEE /* Sources */ = { 379 | isa = PBXSourcesBuildPhase; 380 | buildActionMask = 2147483647; 381 | files = ( 382 | 2E4540522190B878004D8CEE /* TimePickerDataSource.swift in Sources */, 383 | ); 384 | runOnlyForDeploymentPostprocessing = 0; 385 | }; 386 | 2E4540532190BC88004D8CEE /* Sources */ = { 387 | isa = PBXSourcesBuildPhase; 388 | buildActionMask = 2147483647; 389 | files = ( 390 | 2E45405C2190BC88004D8CEE /* ViewController.swift in Sources */, 391 | 2E45405A2190BC88004D8CEE /* AppDelegate.swift in Sources */, 392 | ); 393 | runOnlyForDeploymentPostprocessing = 0; 394 | }; 395 | 2E4540752190BCF5004D8CEE /* Sources */ = { 396 | isa = PBXSourcesBuildPhase; 397 | buildActionMask = 2147483647; 398 | files = ( 399 | 2E4540812190BCF5004D8CEE /* ExtensionDelegate.swift in Sources */, 400 | 2E45407F2190BCF5004D8CEE /* InterfaceController.swift in Sources */, 401 | ); 402 | runOnlyForDeploymentPostprocessing = 0; 403 | }; 404 | /* End PBXSourcesBuildPhase section */ 405 | 406 | /* Begin PBXTargetDependency section */ 407 | 2E45407C2190BCF5004D8CEE /* PBXTargetDependency */ = { 408 | isa = PBXTargetDependency; 409 | target = 2E4540782190BCF5004D8CEE /* WatchKit Sample App Extension */; 410 | targetProxy = 2E45407B2190BCF5004D8CEE /* PBXContainerItemProxy */; 411 | }; 412 | 2E4540862190BCF5004D8CEE /* PBXTargetDependency */ = { 413 | isa = PBXTargetDependency; 414 | target = 2E45406C2190BCF4004D8CEE /* WatchKit Sample App */; 415 | targetProxy = 2E4540852190BCF5004D8CEE /* PBXContainerItemProxy */; 416 | }; 417 | 2E4540932190C475004D8CEE /* PBXTargetDependency */ = { 418 | isa = PBXTargetDependency; 419 | target = 2E4540452190B851004D8CEE /* WatchKitTimePicker */; 420 | targetProxy = 2E4540922190C475004D8CEE /* PBXContainerItemProxy */; 421 | }; 422 | /* End PBXTargetDependency section */ 423 | 424 | /* Begin PBXVariantGroup section */ 425 | 2E45405D2190BC88004D8CEE /* Main.storyboard */ = { 426 | isa = PBXVariantGroup; 427 | children = ( 428 | 2E45405E2190BC88004D8CEE /* Base */, 429 | ); 430 | name = Main.storyboard; 431 | sourceTree = ""; 432 | }; 433 | 2E4540622190BC8A004D8CEE /* LaunchScreen.storyboard */ = { 434 | isa = PBXVariantGroup; 435 | children = ( 436 | 2E4540632190BC8A004D8CEE /* Base */, 437 | ); 438 | name = LaunchScreen.storyboard; 439 | sourceTree = ""; 440 | }; 441 | 2E45406F2190BCF4004D8CEE /* Interface.storyboard */ = { 442 | isa = PBXVariantGroup; 443 | children = ( 444 | 2E4540702190BCF4004D8CEE /* Base */, 445 | ); 446 | name = Interface.storyboard; 447 | sourceTree = ""; 448 | }; 449 | /* End PBXVariantGroup section */ 450 | 451 | /* Begin XCBuildConfiguration section */ 452 | 2E45404C2190B851004D8CEE /* Debug */ = { 453 | isa = XCBuildConfiguration; 454 | buildSettings = { 455 | ALWAYS_SEARCH_USER_PATHS = NO; 456 | CLANG_ANALYZER_NONNULL = YES; 457 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 458 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 459 | CLANG_CXX_LIBRARY = "libc++"; 460 | CLANG_ENABLE_MODULES = YES; 461 | CLANG_ENABLE_OBJC_ARC = YES; 462 | CLANG_ENABLE_OBJC_WEAK = YES; 463 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 464 | CLANG_WARN_BOOL_CONVERSION = YES; 465 | CLANG_WARN_COMMA = YES; 466 | CLANG_WARN_CONSTANT_CONVERSION = YES; 467 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 468 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 469 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 470 | CLANG_WARN_EMPTY_BODY = YES; 471 | CLANG_WARN_ENUM_CONVERSION = YES; 472 | CLANG_WARN_INFINITE_RECURSION = YES; 473 | CLANG_WARN_INT_CONVERSION = YES; 474 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 475 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 476 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 477 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 478 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 479 | CLANG_WARN_STRICT_PROTOTYPES = YES; 480 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 481 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 482 | CLANG_WARN_UNREACHABLE_CODE = YES; 483 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 484 | COPY_PHASE_STRIP = NO; 485 | CURRENT_PROJECT_VERSION = 1; 486 | DEBUG_INFORMATION_FORMAT = dwarf; 487 | ENABLE_STRICT_OBJC_MSGSEND = YES; 488 | ENABLE_TESTABILITY = YES; 489 | GCC_C_LANGUAGE_STANDARD = gnu11; 490 | GCC_DYNAMIC_NO_PIC = NO; 491 | GCC_NO_COMMON_BLOCKS = YES; 492 | GCC_OPTIMIZATION_LEVEL = 0; 493 | GCC_PREPROCESSOR_DEFINITIONS = ( 494 | "DEBUG=1", 495 | "$(inherited)", 496 | ); 497 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 498 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 499 | GCC_WARN_UNDECLARED_SELECTOR = YES; 500 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 501 | GCC_WARN_UNUSED_FUNCTION = YES; 502 | GCC_WARN_UNUSED_VARIABLE = YES; 503 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 504 | MTL_FAST_MATH = YES; 505 | ONLY_ACTIVE_ARCH = YES; 506 | SDKROOT = watchos; 507 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 508 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 509 | VERSIONING_SYSTEM = "apple-generic"; 510 | VERSION_INFO_PREFIX = ""; 511 | WATCHOS_DEPLOYMENT_TARGET = 5.1; 512 | }; 513 | name = Debug; 514 | }; 515 | 2E45404D2190B851004D8CEE /* Release */ = { 516 | isa = XCBuildConfiguration; 517 | buildSettings = { 518 | ALWAYS_SEARCH_USER_PATHS = NO; 519 | CLANG_ANALYZER_NONNULL = YES; 520 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 521 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 522 | CLANG_CXX_LIBRARY = "libc++"; 523 | CLANG_ENABLE_MODULES = YES; 524 | CLANG_ENABLE_OBJC_ARC = YES; 525 | CLANG_ENABLE_OBJC_WEAK = YES; 526 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 527 | CLANG_WARN_BOOL_CONVERSION = YES; 528 | CLANG_WARN_COMMA = YES; 529 | CLANG_WARN_CONSTANT_CONVERSION = YES; 530 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 531 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 532 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 533 | CLANG_WARN_EMPTY_BODY = YES; 534 | CLANG_WARN_ENUM_CONVERSION = YES; 535 | CLANG_WARN_INFINITE_RECURSION = YES; 536 | CLANG_WARN_INT_CONVERSION = YES; 537 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 538 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 539 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 540 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 541 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 542 | CLANG_WARN_STRICT_PROTOTYPES = YES; 543 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 544 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 545 | CLANG_WARN_UNREACHABLE_CODE = YES; 546 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 547 | COPY_PHASE_STRIP = NO; 548 | CURRENT_PROJECT_VERSION = 1; 549 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 550 | ENABLE_NS_ASSERTIONS = NO; 551 | ENABLE_STRICT_OBJC_MSGSEND = YES; 552 | GCC_C_LANGUAGE_STANDARD = gnu11; 553 | GCC_NO_COMMON_BLOCKS = YES; 554 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 555 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 556 | GCC_WARN_UNDECLARED_SELECTOR = YES; 557 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 558 | GCC_WARN_UNUSED_FUNCTION = YES; 559 | GCC_WARN_UNUSED_VARIABLE = YES; 560 | MTL_ENABLE_DEBUG_INFO = NO; 561 | MTL_FAST_MATH = YES; 562 | SDKROOT = watchos; 563 | SWIFT_COMPILATION_MODE = wholemodule; 564 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 565 | VALIDATE_PRODUCT = YES; 566 | VERSIONING_SYSTEM = "apple-generic"; 567 | VERSION_INFO_PREFIX = ""; 568 | WATCHOS_DEPLOYMENT_TARGET = 5.1; 569 | }; 570 | name = Release; 571 | }; 572 | 2E45404F2190B851004D8CEE /* Debug */ = { 573 | isa = XCBuildConfiguration; 574 | buildSettings = { 575 | APPLICATION_EXTENSION_API_ONLY = YES; 576 | CLANG_ENABLE_MODULES = YES; 577 | CODE_SIGN_IDENTITY = ""; 578 | CODE_SIGN_STYLE = Automatic; 579 | DEFINES_MODULE = YES; 580 | DYLIB_COMPATIBILITY_VERSION = 1; 581 | DYLIB_CURRENT_VERSION = 1; 582 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 583 | INFOPLIST_FILE = WatchKitTimePicker/Info.plist; 584 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 585 | LD_RUNPATH_SEARCH_PATHS = ( 586 | "$(inherited)", 587 | "@executable_path/Frameworks", 588 | "@loader_path/Frameworks", 589 | ); 590 | PRODUCT_BUNDLE_IDENTIFIER = tech.calstephens.WatchKitTimePicker; 591 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 592 | SKIP_INSTALL = YES; 593 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 594 | SWIFT_VERSION = 4.2; 595 | TARGETED_DEVICE_FAMILY = 4; 596 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 597 | }; 598 | name = Debug; 599 | }; 600 | 2E4540502190B851004D8CEE /* Release */ = { 601 | isa = XCBuildConfiguration; 602 | buildSettings = { 603 | APPLICATION_EXTENSION_API_ONLY = YES; 604 | CLANG_ENABLE_MODULES = YES; 605 | CODE_SIGN_IDENTITY = ""; 606 | CODE_SIGN_STYLE = Automatic; 607 | DEFINES_MODULE = YES; 608 | DYLIB_COMPATIBILITY_VERSION = 1; 609 | DYLIB_CURRENT_VERSION = 1; 610 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 611 | INFOPLIST_FILE = WatchKitTimePicker/Info.plist; 612 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 613 | LD_RUNPATH_SEARCH_PATHS = ( 614 | "$(inherited)", 615 | "@executable_path/Frameworks", 616 | "@loader_path/Frameworks", 617 | ); 618 | PRODUCT_BUNDLE_IDENTIFIER = tech.calstephens.WatchKitTimePicker; 619 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 620 | SKIP_INSTALL = YES; 621 | SWIFT_VERSION = 4.2; 622 | TARGETED_DEVICE_FAMILY = 4; 623 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 624 | }; 625 | name = Release; 626 | }; 627 | 2E4540672190BC8A004D8CEE /* Debug */ = { 628 | isa = XCBuildConfiguration; 629 | buildSettings = { 630 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 631 | CODE_SIGN_IDENTITY = "iPhone Developer"; 632 | CODE_SIGN_STYLE = Automatic; 633 | INFOPLIST_FILE = "$(SRCROOT)/Sample App/iOS App/Info.plist"; 634 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 635 | LD_RUNPATH_SEARCH_PATHS = ( 636 | "$(inherited)", 637 | "@executable_path/Frameworks", 638 | ); 639 | PRODUCT_BUNDLE_IDENTIFIER = "tech.calstephens.WatchKitTimePicker.Sample-App"; 640 | PRODUCT_NAME = "$(TARGET_NAME)"; 641 | SDKROOT = iphoneos; 642 | SWIFT_VERSION = 4.2; 643 | TARGETED_DEVICE_FAMILY = "1,2"; 644 | }; 645 | name = Debug; 646 | }; 647 | 2E4540682190BC8A004D8CEE /* Release */ = { 648 | isa = XCBuildConfiguration; 649 | buildSettings = { 650 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 651 | CODE_SIGN_IDENTITY = "iPhone Developer"; 652 | CODE_SIGN_STYLE = Automatic; 653 | INFOPLIST_FILE = "$(SRCROOT)/Sample App/iOS App/Info.plist"; 654 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 655 | LD_RUNPATH_SEARCH_PATHS = ( 656 | "$(inherited)", 657 | "@executable_path/Frameworks", 658 | ); 659 | PRODUCT_BUNDLE_IDENTIFIER = "tech.calstephens.WatchKitTimePicker.Sample-App"; 660 | PRODUCT_NAME = "$(TARGET_NAME)"; 661 | SDKROOT = iphoneos; 662 | SWIFT_VERSION = 4.2; 663 | TARGETED_DEVICE_FAMILY = "1,2"; 664 | }; 665 | name = Release; 666 | }; 667 | 2E4540892190BCF5004D8CEE /* Debug */ = { 668 | isa = XCBuildConfiguration; 669 | buildSettings = { 670 | ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; 671 | CODE_SIGN_STYLE = Automatic; 672 | INFOPLIST_FILE = "$(SRCROOT)/Sample App/WatchKit Sample App Extension/Info.plist"; 673 | LD_RUNPATH_SEARCH_PATHS = ( 674 | "$(inherited)", 675 | "@executable_path/Frameworks", 676 | "@executable_path/../../Frameworks", 677 | ); 678 | PRODUCT_BUNDLE_IDENTIFIER = "tech.calstephens.WatchKitTimePicker.Sample-App.watchkitapp.watchkitextension"; 679 | PRODUCT_NAME = "${TARGET_NAME}"; 680 | SKIP_INSTALL = YES; 681 | SWIFT_VERSION = 4.2; 682 | TARGETED_DEVICE_FAMILY = 4; 683 | }; 684 | name = Debug; 685 | }; 686 | 2E45408A2190BCF5004D8CEE /* Release */ = { 687 | isa = XCBuildConfiguration; 688 | buildSettings = { 689 | ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; 690 | CODE_SIGN_STYLE = Automatic; 691 | INFOPLIST_FILE = "$(SRCROOT)/Sample App/WatchKit Sample App Extension/Info.plist"; 692 | LD_RUNPATH_SEARCH_PATHS = ( 693 | "$(inherited)", 694 | "@executable_path/Frameworks", 695 | "@executable_path/../../Frameworks", 696 | ); 697 | PRODUCT_BUNDLE_IDENTIFIER = "tech.calstephens.WatchKitTimePicker.Sample-App.watchkitapp.watchkitextension"; 698 | PRODUCT_NAME = "${TARGET_NAME}"; 699 | SKIP_INSTALL = YES; 700 | SWIFT_VERSION = 4.2; 701 | TARGETED_DEVICE_FAMILY = 4; 702 | }; 703 | name = Release; 704 | }; 705 | 2E45408D2190BCF5004D8CEE /* Debug */ = { 706 | isa = XCBuildConfiguration; 707 | buildSettings = { 708 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 709 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 710 | CODE_SIGN_IDENTITY = "iPhone Developer"; 711 | CODE_SIGN_STYLE = Automatic; 712 | IBSC_MODULE = WatchKit_Sample_App_Extension; 713 | INFOPLIST_FILE = "$(SRCROOT)/Sample App/WatchKit Sample App/Info.plist"; 714 | PRODUCT_BUNDLE_IDENTIFIER = "tech.calstephens.WatchKitTimePicker.Sample-App.watchkitapp"; 715 | PRODUCT_NAME = "$(TARGET_NAME)"; 716 | SKIP_INSTALL = YES; 717 | SWIFT_VERSION = 4.2; 718 | TARGETED_DEVICE_FAMILY = 4; 719 | }; 720 | name = Debug; 721 | }; 722 | 2E45408E2190BCF5004D8CEE /* Release */ = { 723 | isa = XCBuildConfiguration; 724 | buildSettings = { 725 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 726 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 727 | CODE_SIGN_IDENTITY = "iPhone Developer"; 728 | CODE_SIGN_STYLE = Automatic; 729 | IBSC_MODULE = WatchKit_Sample_App_Extension; 730 | INFOPLIST_FILE = "$(SRCROOT)/Sample App/WatchKit Sample App/Info.plist"; 731 | PRODUCT_BUNDLE_IDENTIFIER = "tech.calstephens.WatchKitTimePicker.Sample-App.watchkitapp"; 732 | PRODUCT_NAME = "$(TARGET_NAME)"; 733 | SKIP_INSTALL = YES; 734 | SWIFT_VERSION = 4.2; 735 | TARGETED_DEVICE_FAMILY = 4; 736 | }; 737 | name = Release; 738 | }; 739 | /* End XCBuildConfiguration section */ 740 | 741 | /* Begin XCConfigurationList section */ 742 | 2E4540402190B851004D8CEE /* Build configuration list for PBXProject "WatchKitTimePicker" */ = { 743 | isa = XCConfigurationList; 744 | buildConfigurations = ( 745 | 2E45404C2190B851004D8CEE /* Debug */, 746 | 2E45404D2190B851004D8CEE /* Release */, 747 | ); 748 | defaultConfigurationIsVisible = 0; 749 | defaultConfigurationName = Release; 750 | }; 751 | 2E45404E2190B851004D8CEE /* Build configuration list for PBXNativeTarget "WatchKitTimePicker" */ = { 752 | isa = XCConfigurationList; 753 | buildConfigurations = ( 754 | 2E45404F2190B851004D8CEE /* Debug */, 755 | 2E4540502190B851004D8CEE /* Release */, 756 | ); 757 | defaultConfigurationIsVisible = 0; 758 | defaultConfigurationName = Release; 759 | }; 760 | 2E4540662190BC8A004D8CEE /* Build configuration list for PBXNativeTarget "Sample App" */ = { 761 | isa = XCConfigurationList; 762 | buildConfigurations = ( 763 | 2E4540672190BC8A004D8CEE /* Debug */, 764 | 2E4540682190BC8A004D8CEE /* Release */, 765 | ); 766 | defaultConfigurationIsVisible = 0; 767 | defaultConfigurationName = Release; 768 | }; 769 | 2E4540882190BCF5004D8CEE /* Build configuration list for PBXNativeTarget "WatchKit Sample App Extension" */ = { 770 | isa = XCConfigurationList; 771 | buildConfigurations = ( 772 | 2E4540892190BCF5004D8CEE /* Debug */, 773 | 2E45408A2190BCF5004D8CEE /* Release */, 774 | ); 775 | defaultConfigurationIsVisible = 0; 776 | defaultConfigurationName = Release; 777 | }; 778 | 2E45408C2190BCF5004D8CEE /* Build configuration list for PBXNativeTarget "WatchKit Sample App" */ = { 779 | isa = XCConfigurationList; 780 | buildConfigurations = ( 781 | 2E45408D2190BCF5004D8CEE /* Debug */, 782 | 2E45408E2190BCF5004D8CEE /* Release */, 783 | ); 784 | defaultConfigurationIsVisible = 0; 785 | defaultConfigurationName = Release; 786 | }; 787 | /* End XCConfigurationList section */ 788 | }; 789 | rootObject = 2E45403D2190B851004D8CEE /* Project object */; 790 | } 791 | -------------------------------------------------------------------------------- /WatchKitTimePicker.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WatchKitTimePicker.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /WatchKitTimePicker.xcodeproj/xcshareddata/xcschemes/WatchKitTimePicker.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /WatchKitTimePicker.xcodeproj/xcuserdata/cal.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Sample App.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 1 11 | 12 | WatchKit Sample App.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 2 16 | 17 | WatchKitTimePicker.xcscheme_^#shared#^_ 18 | 19 | orderHint 20 | 0 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /WatchKitTimePicker/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /WatchKitTimePicker/TimePickerDataSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimePickerDataSource.swift 3 | // WatchKitTimePicker 4 | // 5 | // Created by Cal Stephens on 11/5/18. 6 | // Copyright © 2018 Cal Stephens. All rights reserved. 7 | // 8 | 9 | import WatchKit 10 | 11 | public class TimePickerDataSource { 12 | 13 | private weak var hoursPicker: WKInterfacePicker? 14 | private weak var minutesPicker: WKInterfacePicker? 15 | private weak var amPmPicker: WKInterfacePicker? 16 | 17 | private let interval: SelectionInterval 18 | public var selectedTimeDidUpdate: ((Date) -> Void)? 19 | 20 | public enum SelectionInterval { 21 | case minute 22 | case fiveMinutes 23 | case fifteenMinutes 24 | case halfHour 25 | 26 | var minutesBetweenOptions: Int { 27 | switch self { 28 | case .minute: return 1 29 | case .fiveMinutes: return 5 30 | case .fifteenMinutes: return 15 31 | case .halfHour: return 30 32 | } 33 | } 34 | } 35 | 36 | public init( 37 | hoursPicker: WKInterfacePicker, 38 | minutesPicker: WKInterfacePicker, 39 | amPmPicker: WKInterfacePicker?, 40 | interval: SelectionInterval = .fiveMinutes) 41 | { 42 | self.interval = interval 43 | self.hoursPicker = hoursPicker 44 | self.minutesPicker = minutesPicker 45 | self.amPmPicker = amPmPicker 46 | 47 | setup() 48 | } 49 | 50 | 51 | // MARK: Setup 52 | 53 | private lazy var userHas24HourTimeEnabled: Bool = { 54 | let timeFormatter = DateFormatter() 55 | timeFormatter.dateStyle = .none 56 | timeFormatter.timeStyle = .short 57 | 58 | let timeString = timeFormatter.string(from: Date()) 59 | return !(timeString.contains(Locale.current.calendar.amSymbol) 60 | || timeString.contains(Locale.current.calendar.pmSymbol)) 61 | }() 62 | 63 | private lazy var hourPickerOptions: [Int] = { 64 | if userHas24HourTimeEnabled { 65 | return Array(0...23) 66 | } else { 67 | return [/* am: */ 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 68 | /* pm: */ 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] 69 | } 70 | }() 71 | 72 | /// 0-60 minute entries for all 24 hours in the day. 73 | private lazy var minutePickerOptions: [Int] = { (interval: SelectionInterval) in 74 | Array(repeating: Array(stride(from: 0, to: 60, by: interval.minutesBetweenOptions)), count: 24).flatMap { $0 } 75 | }(self.interval) 76 | 77 | private lazy var amPmPickerOptions: [String]? = { 78 | if userHas24HourTimeEnabled { 79 | return nil 80 | } else { 81 | return [ 82 | Locale.current.calendar.amSymbol, 83 | Locale.current.calendar.pmSymbol] 84 | } 85 | }() 86 | 87 | private func setup() { 88 | hoursPicker?.setItems(hourPickerOptions.map { hourValue in 89 | let pickerItem = WKPickerItem() 90 | pickerItem.title = "\(hourValue)" 91 | return pickerItem 92 | }) 93 | 94 | minutesPicker?.setItems(minutePickerOptions.map { minuteValue in 95 | let pickerItem = WKPickerItem() 96 | if "\(minuteValue)".count == 1 { 97 | pickerItem.title = "0\(minuteValue)" 98 | } else { 99 | pickerItem.title = "\(minuteValue)" 100 | } 101 | return pickerItem 102 | }) 103 | 104 | if let amPmPickerOptions = amPmPickerOptions { 105 | amPmPicker?.setItems(amPmPickerOptions.map { value in 106 | let pickerItem = WKPickerItem() 107 | pickerItem.title = value 108 | return pickerItem 109 | }) 110 | 111 | amPmPicker?.setHidden(false) 112 | hoursPicker?.setRelativeWidth(0.333, withAdjustment: 0) 113 | minutesPicker?.setRelativeWidth(0.333, withAdjustment: 0) 114 | amPmPicker?.setRelativeWidth(0.333, withAdjustment: 0) 115 | 116 | } else { 117 | amPmPicker?.setHidden(true) 118 | hoursPicker?.setRelativeWidth(0.5, withAdjustment: 0) 119 | minutesPicker?.setRelativeWidth(0.5, withAdjustment: 0) 120 | } 121 | } 122 | 123 | public func updateDate(to date: Date) { 124 | let dateComponents = Calendar.current.dateComponents(Set(arrayLiteral: .hour, .minute), from: date) 125 | let hours = dateComponents.hour! 126 | let minutes = dateComponents.minute! 127 | 128 | hoursPicker?.setSelectedItemIndex(hours) 129 | selectedHour = hourPickerOptions[hours] 130 | 131 | let displayedMinutesPerHour = minutePickerOptions.count / 24 132 | let corresponsingMinuteIndex = (minutePickerOptions.firstIndex(where: { $0 >= minutes }) ?? 0) + (displayedMinutesPerHour * hours) 133 | minutesPicker?.setSelectedItemIndex(corresponsingMinuteIndex) 134 | selectedMinute = minutePickerOptions[corresponsingMinuteIndex] 135 | 136 | if userHas24HourTimeEnabled { 137 | amPm = nil 138 | } else { 139 | if hours < 12 { 140 | amPmPicker?.setSelectedItemIndex(0) 141 | amPm = .am 142 | } else { 143 | amPmPicker?.setSelectedItemIndex(1) 144 | amPm = .pm 145 | } 146 | } 147 | } 148 | 149 | 150 | // MARK: User Interaction 151 | 152 | private enum AMPM { 153 | case am, pm 154 | } 155 | 156 | private var selectedHour: Int = 0 157 | private var selectedMinute: Int = 0 158 | private var amPm: AMPM? = .am 159 | 160 | public func hourPickerUpdated(to index: Int) { 161 | selectedHour = hourPickerOptions[index] 162 | 163 | // if using 12-hour time, switching between the first half and the last half swaps between AM and PM 164 | if !userHas24HourTimeEnabled { 165 | let selectedHourIsInTheAM = (index < 12) 166 | 167 | if selectedHourIsInTheAM, amPm == .pm { 168 | amPmPicker?.setSelectedItemIndex(0) 169 | } 170 | 171 | else if !selectedHourIsInTheAM, amPm == .am { 172 | amPmPicker?.setSelectedItemIndex(1) 173 | } 174 | } 175 | 176 | // there are minute values for each of the 24 hours in the day, 177 | // so make sure the selected minute corresponds with the selected hour 178 | let displayedMinutesPerHour = minutePickerOptions.count / 24 179 | let corresponsingMinuteIndex = (minutePickerOptions.firstIndex(of: selectedMinute) ?? 0) + (displayedMinutesPerHour * index) 180 | minutesPicker?.setSelectedItemIndex(corresponsingMinuteIndex) 181 | 182 | selectedTimeDidUpdate?(selectedTime()) 183 | } 184 | 185 | public func minutePickerUpdated(to index: Int) { 186 | selectedMinute = minutePickerOptions[index] 187 | 188 | // there are minute values for each of the 24 hours in the day, 189 | // so make sure the selected hour corresponds with the selected minute 190 | let displayedMinutesPerHour = minutePickerOptions.count / 24 191 | let expectedHourIndex = index / (displayedMinutesPerHour) 192 | let expectedHour = hourPickerOptions[expectedHourIndex] 193 | if selectedHour != expectedHour { 194 | hoursPicker?.setSelectedItemIndex(expectedHourIndex) 195 | } 196 | 197 | selectedTimeDidUpdate?(selectedTime()) 198 | } 199 | 200 | public func amPmPickerUpdated(to index: Int) { 201 | guard !userHas24HourTimeEnabled else { return } 202 | 203 | if index == 0 { amPm = .am } 204 | else { amPm = .pm } 205 | 206 | // update the hour picker to inhabit the correct half of the 24 picker options 207 | var selectedIndex = hourPickerOptions.firstIndex(of: selectedHour) ?? 0 208 | if amPm == .pm { 209 | // the PM times inhabit the second half 210 | selectedIndex += 12 211 | } 212 | 213 | hoursPicker?.setSelectedItemIndex(selectedIndex) 214 | hourPickerUpdated(to: selectedIndex) 215 | 216 | selectedTimeDidUpdate?(selectedTime()) 217 | } 218 | 219 | public func selectedTime() -> Date { 220 | var hourIn24HourTime = selectedHour 221 | 222 | // if there's an option for am/pm, the user's in 12 hour time. 223 | // Need to do the necessary corrections. 224 | if let amPm = amPm { 225 | if hourIn24HourTime == 12 && amPm == .am { 226 | hourIn24HourTime = 0 227 | } else if hourIn24HourTime == 12 && amPm == .pm { 228 | hourIn24HourTime = 12 229 | } else if amPm == .pm { 230 | hourIn24HourTime += 12 231 | } 232 | } 233 | 234 | return Calendar.current.date( 235 | bySettingHour: hourIn24HourTime, 236 | minute: selectedMinute, 237 | second: 0, 238 | of: Date(), 239 | matchingPolicy: .nextTime, 240 | repeatedTimePolicy: .first, 241 | direction: .forward)! 242 | } 243 | 244 | } 245 | -------------------------------------------------------------------------------- /images/watchkit time picker 12hr.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calda/WatchKitTimePicker/a19fe8cce2ebb9992e17780dba711eea94ad6eb8/images/watchkit time picker 12hr.gif -------------------------------------------------------------------------------- /images/watchkit time picker 24hr.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/calda/WatchKitTimePicker/a19fe8cce2ebb9992e17780dba711eea94ad6eb8/images/watchkit time picker 24hr.gif --------------------------------------------------------------------------------