├── .gitignore
├── Gemfile
├── Gemfile.lock
├── LICENSE
├── Makefile
├── Phone App
├── AddressFormViewController.swift
├── AppDelegate.swift
├── Beacon.swift
├── BeaconController.swift
├── Commands.swift
├── PayPalClient.swift
├── PaymentKeychain.swift
├── Resources
│ ├── Base.lproj
│ │ ├── LaunchScreen.xib
│ │ └── Main.storyboard
│ └── Images.xcassets
│ │ └── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── appicon-120-1.png
│ │ ├── appicon-120.png
│ │ ├── appicon-180.png
│ │ ├── appicon-58.png
│ │ ├── appicon-80.png
│ │ └── appicon-87.png
├── Supporting Files
│ ├── BridgingHeader.h
│ └── Info.plist
├── ViewController.swift
└── WatchButton.entitlements
├── Podfile
├── Podfile.lock
├── README.md
├── Screenshots
├── initial_screen.png
└── order_successful.png
├── WatchButton.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── xcshareddata
│ └── xcschemes
│ ├── Glance - WatchButton WatchKit App.xcscheme
│ ├── WatchButton WatchKit App.xcscheme
│ └── WatchButton.xcscheme
├── WatchButton.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ └── WatchButton.xcscmblueprint
├── WatchKit App
├── Base.lproj
│ └── Interface.storyboard
├── Images.xcassets
│ └── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── watch-172.png
│ │ ├── watch-196.png
│ │ ├── watch-48.png
│ │ ├── watch-55.png
│ │ ├── watch-58.png
│ │ ├── watch-80.png
│ │ ├── watch-87.png
│ │ └── watch-88.png
└── Info.plist
└── WatchKit Extension
├── GlanceController.swift
├── Images.xcassets
└── README__ignoredByTemplate__
├── Info.plist
├── InterfaceController.swift
├── PaymentAlert.swift
└── WatchButton WatchKit Extension.entitlements
/.gitignore:
--------------------------------------------------------------------------------
1 | # AppCode
2 | .idea
3 |
4 | # OS X
5 | .DS_Store
6 | *.swp
7 |
8 | # Xcode
9 | build/
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata
19 | *.xccheckout
20 | profile
21 | *.moved-aside
22 | DerivedData
23 | *.hmap
24 | *.ipa
25 |
26 | # Bundler
27 | .bundle
28 |
29 | # CocoaPods
30 | Pods/
31 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gem 'cocoapods', '~> 0.39.0'
4 | gem 'cocoapods-keys'
5 | gem 'xcpretty'
6 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | RubyInline (3.12.4)
5 | ZenTest (~> 4.3)
6 | ZenTest (4.11.0)
7 | activesupport (4.2.5)
8 | i18n (~> 0.7)
9 | json (~> 1.7, >= 1.7.7)
10 | minitest (~> 5.1)
11 | thread_safe (~> 0.3, >= 0.3.4)
12 | tzinfo (~> 1.1)
13 | claide (0.9.1)
14 | cocoapods (0.39.0)
15 | activesupport (>= 4.0.2)
16 | claide (~> 0.9.1)
17 | cocoapods-core (= 0.39.0)
18 | cocoapods-downloader (~> 0.9.3)
19 | cocoapods-plugins (~> 0.4.2)
20 | cocoapods-search (~> 0.1.0)
21 | cocoapods-stats (~> 0.6.2)
22 | cocoapods-trunk (~> 0.6.4)
23 | cocoapods-try (~> 0.5.1)
24 | colored (~> 1.2)
25 | escape (~> 0.0.4)
26 | molinillo (~> 0.4.0)
27 | nap (~> 1.0)
28 | xcodeproj (~> 0.28.2)
29 | cocoapods-core (0.39.0)
30 | activesupport (>= 4.0.2)
31 | fuzzy_match (~> 2.0.4)
32 | nap (~> 1.0)
33 | cocoapods-downloader (0.9.3)
34 | cocoapods-keys (1.4.0)
35 | osx_keychain
36 | cocoapods-plugins (0.4.2)
37 | nap
38 | cocoapods-search (0.1.0)
39 | cocoapods-stats (0.6.2)
40 | cocoapods-trunk (0.6.4)
41 | nap (>= 0.8, < 2.0)
42 | netrc (= 0.7.8)
43 | cocoapods-try (0.5.1)
44 | colored (1.2)
45 | escape (0.0.4)
46 | fuzzy_match (2.0.4)
47 | i18n (0.7.0)
48 | json (1.8.3)
49 | minitest (5.8.3)
50 | molinillo (0.4.0)
51 | nap (1.0.0)
52 | netrc (0.7.8)
53 | osx_keychain (1.0.1)
54 | RubyInline (~> 3)
55 | thread_safe (0.3.5)
56 | tzinfo (1.2.2)
57 | thread_safe (~> 0.1)
58 | xcodeproj (0.28.2)
59 | activesupport (>= 3)
60 | claide (~> 0.9.1)
61 | colored (~> 1.2)
62 | xcpretty (0.1.10)
63 |
64 | PLATFORMS
65 | ruby
66 |
67 | DEPENDENCIES
68 | cocoapods (~> 0.39.0)
69 | cocoapods-keys
70 | xcpretty
71 |
72 | BUNDLED WITH
73 | 1.10.6
74 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Contentful GmbH - https://www.contentful.com
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: all pod
2 |
3 | all:
4 | xcodebuild -workspace WatchButton.xcworkspace \
5 | -scheme WatchButton -sdk iphonesimulator build CODE_SIGN_IDENTITY=-
6 |
7 | pod:
8 | bundle install
9 | bundle exec pod install --no-repo-update
10 |
--------------------------------------------------------------------------------
/Phone App/AddressFormViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AddressFormViewController.swift
3 | // WatchButton
4 | //
5 | // Created by Boris Bügling on 10/05/15.
6 | // Copyright (c) 2015 Boris Bügling. All rights reserved.
7 | //
8 |
9 | import Cube
10 | import Form
11 | import UIKit
12 |
13 | class AddressFormViewController: FORMViewController {
14 | static let DefaultAddress = Address(firstName: "YOLO Bert", lastName: "Thiefenthaler", streetName: "Ritterstr.", streetNumber: "23", postalCode: "10249", city: "Berlin", country: "DE")
15 |
16 | required init(coder aDecoder: NSCoder) {
17 | fatalError("Not supported.")
18 | }
19 |
20 | private static func group(fields: [[String:AnyObject]]) -> [String:AnyObject] {
21 | return [ "id": "group-id", "title": "Address", "sections": [ [ "id": "section-0", "fields": fields ] ] ]
22 | }
23 |
24 | private static func addressJSON() -> [[String:AnyObject]] {
25 | let keys = DefaultAddress.toDictionary().keys.sort() { $0 < $1 }
26 | let fields: [[String:AnyObject]] = keys.map { (key) in
27 | return [ "id": key, "title": key, "type": "text" ]
28 | }
29 |
30 | return [ group(fields) ]
31 | }
32 |
33 | override class func initialize() {
34 | FORMDefaultStyle.applyStyle()
35 | }
36 |
37 | init() {
38 | super.init(JSON: self.dynamicType.addressJSON(), andInitialValues: self.dynamicType.DefaultAddress.toDictionary(), disabled: false)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Phone App/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // WatchButton
4 | //
5 | // Created by Boris Bügling on 09/05/15.
6 | // Copyright (c) 2015 Boris Bügling. All rights reserved.
7 | //
8 |
9 | import Cube
10 | import Keys
11 | import MMWormhole
12 | import WatchConnectivity
13 | import UIKit
14 |
15 | // Change the used sphere.io project here
16 | let SphereIOProject = "ecomhack-demo-67"
17 |
18 | private extension Array {
19 | func randomItem() -> Element {
20 | let index = Int(arc4random_uniform(UInt32(self.count)))
21 | return self[index]
22 | }
23 | }
24 |
25 | @UIApplicationMain
26 | class AppDelegate: UIResponder, UIApplicationDelegate, WCSessionDelegate {
27 | private let beaconController = BeaconController()
28 | private let keys = WatchbuttonKeys()
29 | private var sphereClient: SphereIOClient!
30 | private let wormhole = MMWormhole(applicationGroupIdentifier: AppGroupIdentifier, optionalDirectory: DirectoryIdentifier)
31 |
32 | var selectedProduct: [String:AnyObject]?
33 | var window: UIWindow?
34 |
35 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
36 | WCSession.defaultSession().delegate = self
37 | WCSession.defaultSession().activateSession()
38 |
39 | // TODO: WCSession.defaultSession().reachable
40 |
41 | PayPalMobile.initializeWithClientIdsForEnvironments([ PayPalEnvironmentSandbox: WatchbuttonKeys().payPalSandboxClientId()])
42 |
43 | beaconController.beaconCallback = { (beacon, _) in
44 | self.wormhole.passMessageObject(true, identifier: Reply.BeaconRanged.rawValue)
45 | }
46 |
47 | beaconController.outOfRangeCallback = {
48 | self.wormhole.passMessageObject(false, identifier: Reply.BeaconRanged.rawValue)
49 | }
50 |
51 | beaconController.refresh()
52 | return true
53 | }
54 |
55 | func initializeSphereClient() {
56 | if sphereClient == nil {
57 | sphereClient = SphereIOClient(clientId: keys.sphereIOClientId(), clientSecret: keys.sphereIOClientSecret(), project: SphereIOProject)
58 | }
59 | }
60 |
61 | func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
62 | if let command = message[CommandIdentifier] as? String {
63 | handleCommand(command, replyHandler)
64 | } else {
65 | fatalError("Invalid WatchKit extension request :(")
66 | }
67 |
68 | // Keep the phone app running a bit for demonstration purposes
69 | UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler() {}
70 | }
71 |
72 | // MARK: - Helpers
73 |
74 | func fetchSelectedProduct(completion: () -> ()) {
75 | initializeSphereClient()
76 |
77 | if selectedProduct != nil {
78 | completion()
79 | return
80 | }
81 |
82 | sphereClient.fetchProductData() { (result) in
83 | if let value = result.value, results = value["results"] as? [[String:AnyObject]] {
84 | self.selectedProduct = results.randomItem()
85 | completion()
86 | } else {
87 | fatalError("Failed to retrieve products from Sphere.IO")
88 | }
89 | }
90 | }
91 |
92 | private func handleCommand(command: String, _ reply: (([String : AnyObject]) -> Void)) {
93 | initializeSphereClient()
94 |
95 | switch(Command(rawValue: command)!) {
96 | case .GetProduct:
97 | fetchSelectedProduct() {
98 | if let product = self.selectedProduct {
99 | reply([Reply.Product.rawValue: product])
100 | }
101 | return
102 | }
103 | break
104 | case .MakeOrder:
105 | fetchSelectedProduct() {
106 | self.sphereClient.quickOrder(product: self.selectedProduct!, to:retrieveShippingAddress()) { (result) in
107 | if let order = result.value {
108 | let pp = Product(self.selectedProduct!)
109 | let amount = pp.price["amount"]!
110 | let currency = pp.price["currency"]!
111 |
112 | let client = PayPalClient(clientId: self.keys.payPalSandboxClientId(), clientSecret: self.keys.payPalSandboxClientSecret(), code: retrieveRefreshToken())
113 |
114 | client.pay(retrievePaymentId(), currency, amount) { (paid) in
115 | reply([Reply.Paid.rawValue: paid])
116 |
117 | self.wormhole.passMessageObject(paid, identifier: Reply.Paid.rawValue)
118 | self.sphereClient.setPaymentState(paid ? .Paid : .Failed, forOrder: order) { (result) in
119 | //println("Payment state result: \(result)")
120 |
121 | if let order = result.value {
122 | self.sphereClient.setState(.Complete, forOrder: order) { (result) in
123 | print("Ordered successfully.")
124 | }
125 | } else {
126 | fatalError("Failed to set order to complete state.")
127 | }
128 | }
129 | }
130 | }
131 | }
132 | }
133 | break
134 | default:
135 | break
136 | }
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/Phone App/Beacon.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Beacon.swift
3 | // WatchButton
4 | //
5 | // Created by Boris Bügling on 10/05/15.
6 | // Copyright (c) 2015 Boris Bügling. All rights reserved.
7 | //
8 |
9 | struct Beacon {
10 | let identifier: String
11 | let major: NSNumber
12 | let minor: NSNumber
13 | let name: String
14 | let uuid: String
15 | }
16 |
--------------------------------------------------------------------------------
/Phone App/BeaconController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BeaconController.swift
3 | // WatchButton
4 | //
5 | // Created by Boris Bügling on 10/05/15.
6 | // Copyright (c) 2015 Boris Bügling. All rights reserved.
7 | //
8 |
9 | import CoreLocation
10 |
11 | typealias BeaconCallback = (beacon: Beacon, accuracy: CLLocationAccuracy) -> Void
12 | typealias OutOfRangeCallback = () -> Void
13 |
14 | class BeaconController: NSObject, CLLocationManagerDelegate {
15 | static let estimoteUUID = "B9407F30-F5F8-466E-AFF9-25556B57FE6D"
16 | lazy var locationManager = CLLocationManager()
17 |
18 | var beaconCallback: BeaconCallback = { (beacon, accuracy) in
19 | NSLog(String(format:"Beacon: %@, Accuracy: %.2fm", beacon.name, accuracy))
20 | }
21 | var outOfRangeCallback: OutOfRangeCallback = { }
22 |
23 | // TODO: Beacons shouldn't be hard-coded
24 | var beacons = [Beacon(identifier: estimoteUUID, major: 62556, minor: 7826, name: "TestBeacon", uuid: estimoteUUID)]
25 |
26 | var regions: [CLBeaconRegion] = [CLBeaconRegion]() {
27 | didSet {
28 | self.locationManager.delegate = self
29 | self.locationManager.requestAlwaysAuthorization()
30 |
31 | let _ = self.regions.map({ (region) -> Void in self.locationManager.startRangingBeaconsInRegion(region) })
32 | }
33 | }
34 |
35 | func refresh() {
36 | regions = beacons.map({ (beacon: Beacon) -> CLBeaconRegion in
37 | //NSLog("Will range beacon %@ with major %@, minor %@", beacon.name, beacon.major, beacon.minor)
38 | return CLBeaconRegion(proximityUUID: NSUUID(UUIDString: beacon.uuid)!, major: CLBeaconMajorValue(beacon.major.integerValue), minor: CLBeaconMinorValue(beacon.minor.integerValue), identifier: beacon.identifier)
39 | })
40 | }
41 |
42 | func stop() {
43 | let _ = regions.map({ (region) -> Void in self.locationManager.stopRangingBeaconsInRegion(region) })
44 | }
45 |
46 | // MARK: CLLocationManagerDelegate
47 |
48 | func locationManager(manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], inRegion region: CLBeaconRegion) {
49 | let beacon = self.beacons[regions.indexOf(region)!]
50 |
51 | let filteredBeacons = beacons.filter({ (beacon: CLBeacon) -> Bool in return beacon.proximity == .Immediate })
52 |
53 | //NSLog("Ranged beacon %@ as Immediate", beacon.name)
54 |
55 | if (filteredBeacons.count > 0) {
56 | let accuracy = (beacons.first)!.accuracy
57 | beaconCallback(beacon: beacon, accuracy: accuracy)
58 | } else {
59 | outOfRangeCallback()
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Phone App/Commands.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Commands.swift
3 | // WatchButton
4 | //
5 | // Created by Boris Bügling on 09/05/15.
6 | // Copyright (c) 2015 Boris Bügling. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public let AppGroupIdentifier = "group.vu0.org.WatchButton"
12 | public let CommandIdentifier = "org.vu0.command"
13 | public let DirectoryIdentifier = "WatchButton"
14 |
15 | public enum Command : String {
16 | case GetProduct = "GetProducts"
17 | case MakeOrder = "MakeOrder"
18 | case Nothing = "Nothing"
19 | }
20 |
21 | public enum Reply : String {
22 | case BeaconRanged = "BeaconRanged"
23 | case Paid = "Paid"
24 | case Product = "Product"
25 | }
26 |
--------------------------------------------------------------------------------
/Phone App/PayPalClient.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PayPalClient.swift
3 | // WatchButton
4 | //
5 | // Created by Boris Bügling on 09/05/15.
6 | // Copyright (c) 2015 Boris Bügling. All rights reserved.
7 | //
8 |
9 | import Alamofire
10 |
11 | // Somehow using `NSURLAuthenticationChallenge` didn't work against the PayPal API, either 😭
12 | private struct AuthRequest: URLRequestConvertible {
13 | private let clientId: String
14 | private let clientSecret: String
15 | private let code: String
16 | private let grantAttributeName: String
17 | private let grantType: String
18 |
19 | var URLRequest: NSMutableURLRequest {
20 | if let URL = NSURL(string: "https://api.sandbox.paypal.com/v1/oauth2/token") {
21 | let URLRequest = NSMutableURLRequest(URL: URL)
22 | URLRequest.HTTPMethod = Method.POST.rawValue
23 |
24 | let parameters = [ "grant_type": grantType, "response_type": "token", "redirect_uri": "urn:ietf:wg:oauth:2.0:oob", grantAttributeName: code ]
25 |
26 | let auth = String(format: "%@:%@", clientId, clientSecret).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
27 | let header = auth.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
28 | URLRequest.setValue("Basic \(header)", forHTTPHeaderField: "Authorization")
29 |
30 | let encoding = Alamofire.ParameterEncoding.URL
31 | return encoding.encode(URLRequest, parameters: parameters).0
32 | }
33 |
34 | fatalError("Broken Authentication URL...")
35 | }
36 | }
37 |
38 | /// Implementation of https://github.com/paypal/PayPal-iOS-SDK/blob/master/docs/future_payments_server.md because we want no server. 😎
39 | public class PayPalClient {
40 | let baseURL = "https://api.sandbox.paypal.com/v1"
41 | let clientId: String
42 | let clientSecret: String
43 | let code: String
44 | let metadataId: String
45 | var refreshToken: String?
46 | var token: String?
47 |
48 | /// For inital OAuth, pass futurePaymentCode and metadataId. When refreshing, pass refreshToken.
49 | public init(clientId: String, clientSecret: String, code: String, metadataId: String = "") {
50 | self.clientId = clientId
51 | self.clientSecret = clientSecret
52 | self.code = code
53 | self.metadataId = metadataId
54 | }
55 |
56 | private func createActualPayment(description: String, _ currency: String, _ amount: String, _ completion: (paymentId: String) -> Void) {
57 | let parameters: [String:AnyObject] = [ "intent":"authorize", "payer":["payment_method":"paypal"], "transactions": [ [ "amount": [ "currency": currency, "total": amount ], "description": description ] ]]
58 |
59 | Alamofire.request(payPalRequest("payments/payment", .POST, parameters))
60 | .responseJSON { (_, _, result) in
61 | if let JSON = result.value as? [String:AnyObject], transactions = JSON["transactions"] as? [[String:AnyObject]], relatedResources = transactions.first?["related_resources"] as? [[String:AnyObject]], authorization = relatedResources.first?["authorization"] as? [String:AnyObject], id = authorization["id"] as? String {
62 | completion(paymentId: id)
63 | } else {
64 | fatalError("Did not receive ID for payment")
65 | }
66 | }
67 | }
68 |
69 | private func payPalRequest(endpoint: String, _ method: Alamofire.Method, _ parameters: [String: AnyObject]?) -> URLRequestConvertible {
70 | assert(token != nil, "")
71 |
72 | if let token = token, URL = NSURL(string: "\(baseURL)/\(endpoint)") {
73 | let URLRequest = NSMutableURLRequest(URL: URL)
74 | URLRequest.HTTPMethod = method.rawValue
75 |
76 | URLRequest.setValue(metadataId, forHTTPHeaderField: "PayPal-Client-Metadata-Id")
77 | URLRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
78 |
79 | let encoding = Alamofire.ParameterEncoding.JSON
80 | return encoding.encode(URLRequest, parameters: parameters).0
81 | }
82 |
83 | fatalError("Could not create valid request.")
84 | }
85 |
86 | private func requestOAuthToken(completion: () -> ()) {
87 | let grantAttributeName = metadataId == "" ? "refresh_token" : "code"
88 | let grantType = metadataId == "" ? "refresh_token" : "authorization_code"
89 |
90 | Alamofire.request(AuthRequest(clientId: clientId, clientSecret: clientSecret, code: code, grantAttributeName: grantAttributeName, grantType: grantType))
91 | .responseJSON { (_, _, result) in
92 | if let JSON = result.value as? [String:AnyObject] {
93 | if let refreshToken = JSON["refresh_token"] as? String {
94 | self.refreshToken = refreshToken
95 | }
96 |
97 | if let token = JSON["access_token"] as? String {
98 | self.token = token
99 | completion()
100 | } else {
101 | fatalError("No `access_token` received: \(JSON)")
102 | }
103 | }
104 | }
105 | }
106 |
107 | public func createPayment(description: String, _ currency: String, _ amount: String, completion: (paymentId: String) -> Void) {
108 | requestOAuthToken() {
109 | self.createActualPayment(description, currency, amount, completion)
110 | }
111 | }
112 |
113 | public func pay(paymentId: String, _ currency: String, _ amount: String, completion: (paid: Bool) -> Void) {
114 | requestOAuthToken() {
115 | let URL = "payments/authorization/\(paymentId)/capture"
116 | let parameters: [String: AnyObject] = [ "amount": [ "currency": currency, "total": amount ], "is_final_capture": true]
117 |
118 | Alamofire.request(self.payPalRequest(URL, .POST, parameters))
119 | .responseJSON { (_, _, result) in
120 | //println(JSON)
121 |
122 | if let JSON = result.value as? [String:AnyObject], _ = JSON["amount"] as? [String:AnyObject] {
123 | completion(paid: true)
124 | } else {
125 | completion(paid: false)
126 | }
127 | }
128 | }
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/Phone App/PaymentKeychain.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PaymentKeychain.swift
3 | // WatchButton
4 | //
5 | // Created by Boris Bügling on 09/05/15.
6 | // Copyright (c) 2015 Boris Bügling. All rights reserved.
7 | //
8 |
9 | import Cube
10 | import KeychainAccess
11 |
12 | let WBKeychainId = "org.vu0.WatchButton.PayPal.PaymentId"
13 | let WBKeychainRefreshToken = "org.vu0.WatchButton.PayPal.RefreshToken"
14 | let WBKeychainService = "org.vu0.WatchButton"
15 |
16 | func retrieveShippingAddress() -> Address {
17 | return AddressFormViewController.DefaultAddress
18 | }
19 |
20 | func retrieveRefreshToken() -> String {
21 | let keychain = Keychain(service: WBKeychainService)
22 | return keychain[WBKeychainRefreshToken] ?? ""
23 | }
24 |
25 | func retrievePaymentId() -> String {
26 | let keychain = Keychain(service: WBKeychainService)
27 | return keychain[WBKeychainId] ?? ""
28 | }
29 |
30 | func storeRefreshToken(refreshToken: String) {
31 | let keychain = Keychain(service: WBKeychainService)
32 | keychain[WBKeychainRefreshToken] = refreshToken
33 | }
34 |
35 | func storePaymentId(paymentId: String) {
36 | let keychain = Keychain(service: WBKeychainService)
37 | keychain[WBKeychainId] = paymentId
38 | }
39 |
--------------------------------------------------------------------------------
/Phone App/Resources/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Phone App/Resources/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
39 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
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 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/Phone App/Resources/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "29x29",
5 | "idiom" : "iphone",
6 | "filename" : "appicon-58.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "29x29",
11 | "idiom" : "iphone",
12 | "filename" : "appicon-87.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "40x40",
17 | "idiom" : "iphone",
18 | "filename" : "appicon-80.png",
19 | "scale" : "2x"
20 | },
21 | {
22 | "size" : "40x40",
23 | "idiom" : "iphone",
24 | "filename" : "appicon-120.png",
25 | "scale" : "3x"
26 | },
27 | {
28 | "size" : "60x60",
29 | "idiom" : "iphone",
30 | "filename" : "appicon-120-1.png",
31 | "scale" : "2x"
32 | },
33 | {
34 | "size" : "60x60",
35 | "idiom" : "iphone",
36 | "filename" : "appicon-180.png",
37 | "scale" : "3x"
38 | }
39 | ],
40 | "info" : {
41 | "version" : 1,
42 | "author" : "xcode"
43 | }
44 | }
--------------------------------------------------------------------------------
/Phone App/Resources/Images.xcassets/AppIcon.appiconset/appicon-120-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/contentful/Wunderschnell/65d36dc26d4d5520652b202a79846b48022dd452/Phone App/Resources/Images.xcassets/AppIcon.appiconset/appicon-120-1.png
--------------------------------------------------------------------------------
/Phone App/Resources/Images.xcassets/AppIcon.appiconset/appicon-120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/contentful/Wunderschnell/65d36dc26d4d5520652b202a79846b48022dd452/Phone App/Resources/Images.xcassets/AppIcon.appiconset/appicon-120.png
--------------------------------------------------------------------------------
/Phone App/Resources/Images.xcassets/AppIcon.appiconset/appicon-180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/contentful/Wunderschnell/65d36dc26d4d5520652b202a79846b48022dd452/Phone App/Resources/Images.xcassets/AppIcon.appiconset/appicon-180.png
--------------------------------------------------------------------------------
/Phone App/Resources/Images.xcassets/AppIcon.appiconset/appicon-58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/contentful/Wunderschnell/65d36dc26d4d5520652b202a79846b48022dd452/Phone App/Resources/Images.xcassets/AppIcon.appiconset/appicon-58.png
--------------------------------------------------------------------------------
/Phone App/Resources/Images.xcassets/AppIcon.appiconset/appicon-80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/contentful/Wunderschnell/65d36dc26d4d5520652b202a79846b48022dd452/Phone App/Resources/Images.xcassets/AppIcon.appiconset/appicon-80.png
--------------------------------------------------------------------------------
/Phone App/Resources/Images.xcassets/AppIcon.appiconset/appicon-87.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/contentful/Wunderschnell/65d36dc26d4d5520652b202a79846b48022dd452/Phone App/Resources/Images.xcassets/AppIcon.appiconset/appicon-87.png
--------------------------------------------------------------------------------
/Phone App/Supporting Files/BridgingHeader.h:
--------------------------------------------------------------------------------
1 | //
2 | // BridgingHeader.h
3 | // WatchButton
4 | //
5 | // Created by Boris Bügling on 09/05/15.
6 | // Copyright (c) 2015 Boris Bügling. All rights reserved.
7 | //
8 |
9 | #import "PayPalMobile.h"
10 |
--------------------------------------------------------------------------------
/Phone App/Supporting Files/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 | ÜberSchnell
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | NSLocationAlwaysUsageDescription
26 | #yolo
27 | UILaunchStoryboardName
28 | LaunchScreen
29 | UIMainStoryboardFile
30 | Main
31 | UIRequiredDeviceCapabilities
32 |
33 | armv7
34 |
35 | UISupportedInterfaceOrientations
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationLandscapeLeft
39 | UIInterfaceOrientationLandscapeRight
40 |
41 | NSAppTransportSecurity
42 |
43 | NSAllowsArbitraryLoads
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/Phone App/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // WatchButton
4 | //
5 | // Created by Boris Bügling on 09/05/15.
6 | // Copyright (c) 2015 Boris Bügling. All rights reserved.
7 | //
8 |
9 | import Alamofire
10 | import Cube
11 | import Keys
12 | import MBProgressHUD
13 | import UIKit
14 |
15 | class ViewController: UIViewController, PayPalFuturePaymentDelegate {
16 | @IBOutlet weak var descriptionLabel: UILabel!
17 | @IBOutlet weak var nameLabel: UILabel!
18 | @IBOutlet weak var priceLabel: UILabel!
19 | @IBOutlet weak var productImageView: UIImageView!
20 |
21 | private var payPalConfiguration: PayPalConfiguration!
22 |
23 | override func viewDidLoad() {
24 | super.viewDidLoad()
25 |
26 | // Not that pretty -- but that way we localized the Sphere.IO interactions for now
27 | if let delegate = UIApplication.sharedApplication().delegate as? AppDelegate {
28 | delegate.fetchSelectedProduct() {
29 | if let productData = delegate.selectedProduct {
30 | let product = Product(productData)
31 |
32 | self.descriptionLabel.text = product.productDescription
33 | self.nameLabel.text = product.name
34 |
35 | if let amount = product.price["amount"], currency = product.price["currency"] {
36 | self.priceLabel.text = "\(amount) \(currency)"
37 | }
38 |
39 | Alamofire.request(.GET, product.imageUrl).response() { (_, _, data, error) in
40 | if let data = data {
41 | self.productImageView.image = UIImage(data: data)
42 | }
43 | }
44 | }
45 | return
46 | }
47 | }
48 | }
49 |
50 | override func viewWillAppear(animated: Bool) {
51 | super.viewWillAppear(animated)
52 |
53 | PayPalMobile.preconnectWithEnvironment(PayPalEnvironmentSandbox)
54 | }
55 |
56 | // MARK: - Actions
57 | @IBAction private func dismissForm() {
58 | dismissViewControllerAnimated(true, completion: nil)
59 | }
60 |
61 | @IBAction func setUpAddressTapped(sender: UIBarButtonItem) {
62 | let vc = AddressFormViewController()
63 | vc.navigationItem.title = "Personal"
64 | vc.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Done, target: self, action: "dismissForm")
65 |
66 | presentViewController(UINavigationController(rootViewController: vc), animated: true) { }
67 | }
68 |
69 | @IBAction func setUpPaymentTapped(sender: UIBarButtonItem) {
70 | payPalConfiguration = PayPalConfiguration()
71 |
72 | payPalConfiguration.merchantName = "Ultramagnetic Omega Supreme"
73 | payPalConfiguration.merchantPrivacyPolicyURL = NSURL(string:"https://www.omega.supreme.example/privacy")
74 | payPalConfiguration.merchantUserAgreementURL = NSURL(string:"https://www.omega.supreme.example/user_agreement")
75 |
76 | let vc = PayPalFuturePaymentViewController(configuration: payPalConfiguration, delegate: self)
77 | presentViewController(vc, animated: true, completion: nil)
78 | }
79 |
80 | // MARK: - PayPalFuturePaymentDelegate
81 |
82 | func payPalFuturePaymentDidCancel(futurePaymentViewController: PayPalFuturePaymentViewController!) {
83 | dismissViewControllerAnimated(true, completion: nil)
84 | }
85 |
86 | func payPalFuturePaymentViewController(futurePaymentViewController: PayPalFuturePaymentViewController!, didAuthorizeFuturePayment futurePaymentAuthorization: [NSObject : AnyObject]!) {
87 | dismissViewControllerAnimated(true, completion: nil)
88 | MBProgressHUD.showHUDAddedTo(view, animated: true)
89 |
90 | if let futurePaymentAuthorization = futurePaymentAuthorization {
91 | let clientMetadataId = PayPalMobile.clientMetadataID()
92 |
93 | if let response = futurePaymentAuthorization["response"] as? [String:AnyObject], code = response["code"] as? String {
94 | let keys = WatchbuttonKeys()
95 | let client = PayPalClient(clientId: keys.payPalSandboxClientId(), clientSecret: keys.payPalSandboxClientSecret(), code: code, metadataId: clientMetadataId)
96 |
97 | if let delegate = UIApplication.sharedApplication().delegate as? AppDelegate, productData = delegate.selectedProduct {
98 | let product = Product(productData)
99 |
100 | if let amount = product.price["amount"], currency = product.price["currency"] {
101 | client.createPayment(product.name, currency, amount) { (paymentId) in
102 | storePaymentId(paymentId)
103 | storeRefreshToken(client.refreshToken!)
104 | NSLog("Stored payment ID \(paymentId) in keychain.")
105 |
106 | MBProgressHUD.hideAllHUDsForView(self.view, animated: true)
107 | }
108 |
109 | return
110 | }
111 | }
112 | }
113 | }
114 |
115 | MBProgressHUD.hideAllHUDsForView(self.view, animated: true)
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/Phone App/WatchButton.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.application-groups
6 |
7 | group.vu0.org.WatchButton
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | source 'https://github.com/contentful/CocoaPodsSpecs'
2 | source 'https://github.com/CocoaPods/Specs'
3 |
4 | plugin 'cocoapods-keys', {
5 | :project => 'WatchButton',
6 | :keys => [
7 | 'PayPalSandboxClientId',
8 | 'PayPalSandboxClientSecret',
9 | 'SphereIOClientId',
10 | 'SphereIOClientSecret'
11 | ]}
12 |
13 | inhibit_all_warnings!
14 | use_frameworks!
15 |
16 | def shared_pods
17 |
18 | pod 'Cube', :git => 'https://github.com/contentful-labs/Cube.git'
19 | #pod 'Cube', :path => '../Cube'
20 | pod 'MMWormhole'
21 |
22 | end
23 |
24 | link_with 'WatchButton'
25 |
26 | shared_pods
27 |
28 | pod 'Alamofire', '~> 2.0'
29 | #pod 'ContentfulDeliveryAPI', :path => '../contentful-delivery-api'
30 | pod 'Form', :head
31 | pod 'KeychainAccess', :git => 'https://github.com/kishikawakatsumi/KeychainAccess.git'
32 | pod 'MBProgressHUD'
33 | pod 'PayPal-iOS-SDK'
34 | pod 'Result', '>= 0.6-beta.1'
35 |
36 | target 'WatchButton WatchKit Extension', :exclusive => true do
37 |
38 | platform :watchos, '2.0'
39 |
40 | shared_pods
41 |
42 | end
43 |
--------------------------------------------------------------------------------
/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Alamofire (2.0.2)
3 | - CardIO (5.2.2)
4 | - Cube (0.0.1):
5 | - Alamofire (~> 2.0)
6 | - Result (>= 0.6-beta.1)
7 | - Form (HEAD based on 3.7.2):
8 | - Hex (~> 3.0.0)
9 | - HYP8601 (~> 0.7.2)
10 | - HYPMathParser (~> 0.4.1)
11 | - HYPNorwegianAccountNumber (~> 1.2.1)
12 | - HYPNorwegianSSN (~> 1.10.2)
13 | - NSDictionary-ANDYSafeValue (~> 0.3)
14 | - NSDictionary-HYPNestedAttributes (~> 0.4.1)
15 | - NSJSONSerialization-ANDYJSONFile (~> 1.1)
16 | - NSObject-HYPTesting (~> 1.2)
17 | - NSString-HYPContainsString (~> 0.1)
18 | - NSString-HYPFormula (~> 1.6.2)
19 | - NSString-HYPRelationshipParser (~> 0.4.1)
20 | - NSString-HYPWordExtractor (~> 1.2)
21 | - NSString-ZENInflections (~> 1.2)
22 | - UIButton-ANDYHighlighted (~> 0.2.1)
23 | - UIViewController-HYPKeyboardToolbar (~> 0.1)
24 | - Hex (3.0.0)
25 | - HYP8601 (0.7.2)
26 | - HYPMathParser (0.4.1)
27 | - HYPNorwegianAccountNumber (1.2.1)
28 | - HYPNorwegianSSN (1.10.2)
29 | - KeychainAccess (2.3.2)
30 | - Keys (1.0.0)
31 | - MBProgressHUD (0.9.1)
32 | - MMWormhole (2.0.0):
33 | - MMWormhole/Core (= 2.0.0)
34 | - MMWormhole/Core (2.0.0)
35 | - NSDictionary-ANDYSafeValue (0.3.1)
36 | - NSDictionary-HYPNestedAttributes (0.4.1):
37 | - NSString-HYPRelationshipParser (~> 0.4.1)
38 | - NSJSONSerialization-ANDYJSONFile (1.1)
39 | - NSObject-HYPTesting (1.2)
40 | - NSString-HYPContainsString (0.1)
41 | - NSString-HYPFormula (1.6.2):
42 | - NSString-HYPWordExtractor
43 | - NSString-HYPRelationshipParser (0.4.1)
44 | - NSString-HYPWordExtractor (1.2)
45 | - NSString-ZENInflections (1.2)
46 | - PayPal-iOS-SDK (2.12.8):
47 | - PayPal-iOS-SDK/CardIO (= 2.12.8)
48 | - PayPal-iOS-SDK/Core (= 2.12.8)
49 | - PayPal-iOS-SDK/CardIO (2.12.8):
50 | - CardIO (~> 5.2.2)
51 | - PayPal-iOS-SDK/Core (2.12.8)
52 | - Result (0.6-beta.1)
53 | - UIButton-ANDYHighlighted (0.2.1)
54 | - UIViewController-HYPKeyboardToolbar (0.1)
55 |
56 | DEPENDENCIES:
57 | - Alamofire (~> 2.0)
58 | - Cube (from `https://github.com/contentful-labs/Cube.git`)
59 | - Form (HEAD)
60 | - KeychainAccess (from `https://github.com/kishikawakatsumi/KeychainAccess.git`)
61 | - Keys (from `Pods/CocoaPodsKeys`)
62 | - MBProgressHUD
63 | - MMWormhole
64 | - PayPal-iOS-SDK
65 | - Result (>= 0.6-beta.1)
66 |
67 | EXTERNAL SOURCES:
68 | Cube:
69 | :git: https://github.com/contentful-labs/Cube.git
70 | KeychainAccess:
71 | :git: https://github.com/kishikawakatsumi/KeychainAccess.git
72 | Keys:
73 | :path: Pods/CocoaPodsKeys
74 |
75 | CHECKOUT OPTIONS:
76 | Cube:
77 | :commit: 52b43b721811c2bd992d9fa5f10764b0b0dd24c4
78 | :git: https://github.com/contentful-labs/Cube.git
79 | KeychainAccess:
80 | :commit: 11b935868a5c3af1fe7222d02da7215ccdfc88e4
81 | :git: https://github.com/kishikawakatsumi/KeychainAccess.git
82 |
83 | SPEC CHECKSUMS:
84 | Alamofire: 8edbab00802a752bb5240cc2abe70e7e881488ad
85 | CardIO: 2460db0f4936f3441b08d51e3904b436d77ec8fc
86 | Cube: 84d69281a1432b1fd8443c26b8ca559905037830
87 | Form: 01297c7d084fcda402e95e672e322d9c0cb2bcf6
88 | Hex: ee430da9c4412d9196366ae5327f279021dc03eb
89 | HYP8601: 0118757c1332620748c75e7218e7b6b849686d06
90 | HYPMathParser: 146a4981427a0b46cc9af823b210bfefdf0a7107
91 | HYPNorwegianAccountNumber: b0062d7419d217123dff0ef04f1931ed5f1e3bbf
92 | HYPNorwegianSSN: 1eb54f1488ac24cdd7d0aee34e10d0f826c27fc1
93 | KeychainAccess: 96575483583f4640739a5cc26551803d0d34e0dd
94 | Keys: 9c35bf00f612ee1d48556f4a4b9b4551e224e90f
95 | MBProgressHUD: c47f2c166c126cf2ce36498d80f33e754d4e93ad
96 | MMWormhole: 0cd3fd35a9118b2e2d762b499f54eeaace0be791
97 | NSDictionary-ANDYSafeValue: 2d7adf339b6e302d71fec5f1d71ae00aacda993e
98 | NSDictionary-HYPNestedAttributes: 783d3adcf9f8e9a554004ee225f1bc0cdf0cfdfe
99 | NSJSONSerialization-ANDYJSONFile: 4a92ca174d5ccde13015e853e9eb62440459b478
100 | NSObject-HYPTesting: fadde70c75061b731b313c390e20777aed0a914d
101 | NSString-HYPContainsString: a8147bbb90b9aca02f69d12546b4d2938a5d01dc
102 | NSString-HYPFormula: c1d166886afd1cfd139f24ab01bd63ad8f4cc360
103 | NSString-HYPRelationshipParser: 7fa231131e408f26b3b72a41157aa6620a195b4d
104 | NSString-HYPWordExtractor: 7484da8d2d23e41b24b0bdb218293ab349f9f650
105 | NSString-ZENInflections: 35ae6d02981b1def07bd6dc8e6455e827453bc29
106 | PayPal-iOS-SDK: 66f4f6e2f0d0c31bae81642c1518bb72ac18f1a6
107 | Result: af24e3e57ab81005ec4221a53fd36dc1e7a52869
108 | UIButton-ANDYHighlighted: 3f5e9fb68fb3425bd9fddc030af452d3ac9d1613
109 | UIViewController-HYPKeyboardToolbar: d5cb85447caeaf9e0e7ed575ce3b1576c7d53dd8
110 |
111 | PODFILE CHECKSUM: 4d39f40914ea54777e672d2b2b8aa60996e50302
112 |
113 | COCOAPODS: 0.39.0
114 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WatchButton
2 |
3 | 
4 | 
5 |
6 | Software version of the [Amazon Dash Button][2] on the ᴡᴀᴛᴄʜ. Uses iBeacons
7 | to determine the room you are in and contextually offers you to buy a product.
8 | By utilizing the [commmercetools][3], PayPal and [Contentful][4] APIs on the phone, custom backend
9 | code is not needed.
10 |
11 | ## Building
12 |
13 | ```bash
14 | $ make pod
15 | $ make all
16 | ```
17 |
18 | [1]: http://www.ecomhack.io
19 | [2]: https://www.amazon.com/oc/dash-button
20 | [3]: http://www.commercetools.com/de/
21 | [4]: https://www.contentful.com
22 |
23 |
--------------------------------------------------------------------------------
/Screenshots/initial_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/contentful/Wunderschnell/65d36dc26d4d5520652b202a79846b48022dd452/Screenshots/initial_screen.png
--------------------------------------------------------------------------------
/Screenshots/order_successful.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/contentful/Wunderschnell/65d36dc26d4d5520652b202a79846b48022dd452/Screenshots/order_successful.png
--------------------------------------------------------------------------------
/WatchButton.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 4E203B455D3ECAC8B1AA8892 /* Pods_WatchButton_WatchKit_Extension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7B612E7C95BC7692FDF64C9 /* Pods_WatchButton_WatchKit_Extension.framework */; };
11 | A15AB65C1AFECD32000DEA74 /* BeaconController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A15AB65B1AFECD32000DEA74 /* BeaconController.swift */; };
12 | A15AB65E1AFECD8A000DEA74 /* Beacon.swift in Sources */ = {isa = PBXBuildFile; fileRef = A15AB65D1AFECD8A000DEA74 /* Beacon.swift */; };
13 | A19B89B31AFEEC6C00E15887 /* AddressFormViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A19B89B21AFEEC6C00E15887 /* AddressFormViewController.swift */; };
14 | A1AAA6A91AFE8950000D4227 /* PayPalClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1AAA6A71AFE86CD000D4227 /* PayPalClient.swift */; };
15 | A1AAA6AB1AFE93A0000D4227 /* PaymentKeychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1AAA6AA1AFE93A0000D4227 /* PaymentKeychain.swift */; };
16 | A1AAA6AD1AFE97C4000D4227 /* Commands.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1AAA6AC1AFE97C4000D4227 /* Commands.swift */; };
17 | A1AAA6AE1AFE97C4000D4227 /* Commands.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1AAA6AC1AFE97C4000D4227 /* Commands.swift */; };
18 | A1CF8D321AFE1325005EDE51 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1CF8D311AFE1325005EDE51 /* AppDelegate.swift */; };
19 | A1CF8D341AFE1325005EDE51 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1CF8D331AFE1325005EDE51 /* ViewController.swift */; };
20 | A1CF8D371AFE1325005EDE51 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A1CF8D351AFE1325005EDE51 /* Main.storyboard */; };
21 | A1CF8D391AFE1325005EDE51 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A1CF8D381AFE1325005EDE51 /* Images.xcassets */; };
22 | A1CF8D3C1AFE1325005EDE51 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = A1CF8D3A1AFE1325005EDE51 /* LaunchScreen.xib */; };
23 | A1CF8D5A1AFE1332005EDE51 /* InterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1CF8D591AFE1332005EDE51 /* InterfaceController.swift */; };
24 | A1CF8D5C1AFE1332005EDE51 /* GlanceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1CF8D5B1AFE1332005EDE51 /* GlanceController.swift */; };
25 | A1CF8D5E1AFE1332005EDE51 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A1CF8D5D1AFE1332005EDE51 /* Images.xcassets */; };
26 | A1CF8D6A1AFE1332005EDE51 /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A1CF8D681AFE1332005EDE51 /* Interface.storyboard */; };
27 | A1CF8D6C1AFE1332005EDE51 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A1CF8D6B1AFE1332005EDE51 /* Images.xcassets */; };
28 | A1ED03471AFEB10B00570757 /* PaymentAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1ED03461AFEB10B00570757 /* PaymentAlert.swift */; };
29 | A1F8A7F81B70BA1300D22AC4 /* WatchButton WatchKit Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = A1CF8D551AFE1332005EDE51 /* WatchButton WatchKit Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
30 | A1F8A7FB1B70BA1300D22AC4 /* WatchButton WatchKit App.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = A1CF8D611AFE1332005EDE51 /* WatchButton WatchKit App.app */; };
31 | F6312121D73E165B1EEE3D8D /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FFD8A85CEC34E4A816905356 /* Pods.framework */; };
32 | /* End PBXBuildFile section */
33 |
34 | /* Begin PBXContainerItemProxy section */
35 | A1F8A7F61B70BA1300D22AC4 /* PBXContainerItemProxy */ = {
36 | isa = PBXContainerItemProxy;
37 | containerPortal = A1CF8D241AFE1325005EDE51 /* Project object */;
38 | proxyType = 1;
39 | remoteGlobalIDString = A1CF8D541AFE1332005EDE51;
40 | remoteInfo = "WatchButton WatchKit Extension";
41 | };
42 | A1F8A7F91B70BA1300D22AC4 /* PBXContainerItemProxy */ = {
43 | isa = PBXContainerItemProxy;
44 | containerPortal = A1CF8D241AFE1325005EDE51 /* Project object */;
45 | proxyType = 1;
46 | remoteGlobalIDString = A1CF8D601AFE1332005EDE51;
47 | remoteInfo = "WatchButton WatchKit App";
48 | };
49 | /* End PBXContainerItemProxy section */
50 |
51 | /* Begin PBXCopyFilesBuildPhase section */
52 | A1F8A7FC1B70BA1300D22AC4 /* Embed App Extensions */ = {
53 | isa = PBXCopyFilesBuildPhase;
54 | buildActionMask = 2147483647;
55 | dstPath = "";
56 | dstSubfolderSpec = 13;
57 | files = (
58 | A1F8A7F81B70BA1300D22AC4 /* WatchButton WatchKit Extension.appex in Embed App Extensions */,
59 | );
60 | name = "Embed App Extensions";
61 | runOnlyForDeploymentPostprocessing = 0;
62 | };
63 | A1F8A7FD1B70BA1300D22AC4 /* Embed Watch Content */ = {
64 | isa = PBXCopyFilesBuildPhase;
65 | buildActionMask = 2147483647;
66 | dstPath = "$(CONTENTS_FOLDER_PATH)/Watch";
67 | dstSubfolderSpec = 16;
68 | files = (
69 | A1F8A7FB1B70BA1300D22AC4 /* WatchButton WatchKit App.app in Embed Watch Content */,
70 | );
71 | name = "Embed Watch Content";
72 | runOnlyForDeploymentPostprocessing = 0;
73 | };
74 | /* End PBXCopyFilesBuildPhase section */
75 |
76 | /* Begin PBXFileReference section */
77 | 4AE19A26D612E6E67E88A766 /* Pods-WatchButton WatchKit Extension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WatchButton WatchKit Extension.release.xcconfig"; path = "Pods/Target Support Files/Pods-WatchButton WatchKit Extension/Pods-WatchButton WatchKit Extension.release.xcconfig"; sourceTree = ""; };
78 | 8C79ADFDE7F13A447C77626D /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; };
79 | 8E86E131A6CC65020705ACD9 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; };
80 | A15AB65B1AFECD32000DEA74 /* BeaconController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BeaconController.swift; sourceTree = ""; };
81 | A15AB65D1AFECD8A000DEA74 /* Beacon.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Beacon.swift; sourceTree = ""; };
82 | A19B89B21AFEEC6C00E15887 /* AddressFormViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddressFormViewController.swift; sourceTree = ""; };
83 | A1AAA6A71AFE86CD000D4227 /* PayPalClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PayPalClient.swift; sourceTree = ""; };
84 | A1AAA6AA1AFE93A0000D4227 /* PaymentKeychain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaymentKeychain.swift; sourceTree = ""; };
85 | A1AAA6AC1AFE97C4000D4227 /* Commands.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Commands.swift; sourceTree = ""; };
86 | A1CF8D2C1AFE1325005EDE51 /* WatchButton.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WatchButton.app; sourceTree = BUILT_PRODUCTS_DIR; };
87 | A1CF8D301AFE1325005EDE51 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
88 | A1CF8D311AFE1325005EDE51 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
89 | A1CF8D331AFE1325005EDE51 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
90 | A1CF8D361AFE1325005EDE51 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
91 | A1CF8D381AFE1325005EDE51 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
92 | A1CF8D3B1AFE1325005EDE51 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; };
93 | A1CF8D551AFE1332005EDE51 /* WatchButton WatchKit Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "WatchButton WatchKit Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
94 | A1CF8D581AFE1332005EDE51 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
95 | A1CF8D591AFE1332005EDE51 /* InterfaceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterfaceController.swift; sourceTree = ""; };
96 | A1CF8D5B1AFE1332005EDE51 /* GlanceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlanceController.swift; sourceTree = ""; };
97 | A1CF8D5D1AFE1332005EDE51 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
98 | A1CF8D611AFE1332005EDE51 /* WatchButton WatchKit App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "WatchButton WatchKit App.app"; sourceTree = BUILT_PRODUCTS_DIR; };
99 | A1CF8D671AFE1332005EDE51 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
100 | A1CF8D691AFE1332005EDE51 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Interface.storyboard; sourceTree = ""; };
101 | A1CF8D6B1AFE1332005EDE51 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
102 | A1E9A40C1AFE742D00C28085 /* BridgingHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BridgingHeader.h; sourceTree = ""; };
103 | A1ED03461AFEB10B00570757 /* PaymentAlert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PaymentAlert.swift; sourceTree = ""; };
104 | A1ED03481AFEBE6F00570757 /* WatchButton.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = WatchButton.entitlements; path = ../WatchButton.entitlements; sourceTree = ""; };
105 | A1ED03491AFEBEA500570757 /* WatchButton WatchKit Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "WatchButton WatchKit Extension.entitlements"; sourceTree = ""; };
106 | B94957861BC3C5A0ED44131E /* Pods-WatchButton WatchKit Extension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WatchButton WatchKit Extension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-WatchButton WatchKit Extension/Pods-WatchButton WatchKit Extension.debug.xcconfig"; sourceTree = ""; };
107 | E7B612E7C95BC7692FDF64C9 /* Pods_WatchButton_WatchKit_Extension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_WatchButton_WatchKit_Extension.framework; sourceTree = BUILT_PRODUCTS_DIR; };
108 | FFD8A85CEC34E4A816905356 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; };
109 | /* End PBXFileReference section */
110 |
111 | /* Begin PBXFrameworksBuildPhase section */
112 | 083B70BE0987AE1EACD4863A /* Frameworks */ = {
113 | isa = PBXFrameworksBuildPhase;
114 | buildActionMask = 2147483647;
115 | files = (
116 | );
117 | runOnlyForDeploymentPostprocessing = 0;
118 | };
119 | A1CF8D291AFE1325005EDE51 /* Frameworks */ = {
120 | isa = PBXFrameworksBuildPhase;
121 | buildActionMask = 2147483647;
122 | files = (
123 | F6312121D73E165B1EEE3D8D /* Pods.framework in Frameworks */,
124 | );
125 | runOnlyForDeploymentPostprocessing = 0;
126 | };
127 | A1CF8D521AFE1332005EDE51 /* Frameworks */ = {
128 | isa = PBXFrameworksBuildPhase;
129 | buildActionMask = 2147483647;
130 | files = (
131 | 4E203B455D3ECAC8B1AA8892 /* Pods_WatchButton_WatchKit_Extension.framework in Frameworks */,
132 | );
133 | runOnlyForDeploymentPostprocessing = 0;
134 | };
135 | /* End PBXFrameworksBuildPhase section */
136 |
137 | /* Begin PBXGroup section */
138 | 5215D1152283133381C73C7C /* Frameworks */ = {
139 | isa = PBXGroup;
140 | children = (
141 | FFD8A85CEC34E4A816905356 /* Pods.framework */,
142 | E7B612E7C95BC7692FDF64C9 /* Pods_WatchButton_WatchKit_Extension.framework */,
143 | );
144 | name = Frameworks;
145 | sourceTree = "";
146 | };
147 | A1CF8D231AFE1325005EDE51 = {
148 | isa = PBXGroup;
149 | children = (
150 | A1CF8D2E1AFE1325005EDE51 /* Phone App */,
151 | A1CF8D651AFE1332005EDE51 /* WatchKit App */,
152 | A1CF8D561AFE1332005EDE51 /* WatchKit Extension */,
153 | 5215D1152283133381C73C7C /* Frameworks */,
154 | A1CF8D2D1AFE1325005EDE51 /* Products */,
155 | CEAF4F522F566795258DD4FE /* Pods */,
156 | );
157 | sourceTree = "";
158 | };
159 | A1CF8D2D1AFE1325005EDE51 /* Products */ = {
160 | isa = PBXGroup;
161 | children = (
162 | A1CF8D2C1AFE1325005EDE51 /* WatchButton.app */,
163 | A1CF8D551AFE1332005EDE51 /* WatchButton WatchKit Extension.appex */,
164 | A1CF8D611AFE1332005EDE51 /* WatchButton WatchKit App.app */,
165 | );
166 | name = Products;
167 | sourceTree = "";
168 | };
169 | A1CF8D2E1AFE1325005EDE51 /* Phone App */ = {
170 | isa = PBXGroup;
171 | children = (
172 | A19B89B21AFEEC6C00E15887 /* AddressFormViewController.swift */,
173 | A1CF8D311AFE1325005EDE51 /* AppDelegate.swift */,
174 | A15AB65D1AFECD8A000DEA74 /* Beacon.swift */,
175 | A15AB65B1AFECD32000DEA74 /* BeaconController.swift */,
176 | A1AAA6AC1AFE97C4000D4227 /* Commands.swift */,
177 | A1AAA6AA1AFE93A0000D4227 /* PaymentKeychain.swift */,
178 | A1AAA6A71AFE86CD000D4227 /* PayPalClient.swift */,
179 | A1CF8D331AFE1325005EDE51 /* ViewController.swift */,
180 | A1CF8D771AFE1363005EDE51 /* Resources */,
181 | A1CF8D2F1AFE1325005EDE51 /* Supporting Files */,
182 | );
183 | path = "Phone App";
184 | sourceTree = "";
185 | };
186 | A1CF8D2F1AFE1325005EDE51 /* Supporting Files */ = {
187 | isa = PBXGroup;
188 | children = (
189 | A1E9A40C1AFE742D00C28085 /* BridgingHeader.h */,
190 | A1CF8D301AFE1325005EDE51 /* Info.plist */,
191 | A1ED03481AFEBE6F00570757 /* WatchButton.entitlements */,
192 | );
193 | path = "Supporting Files";
194 | sourceTree = "";
195 | };
196 | A1CF8D561AFE1332005EDE51 /* WatchKit Extension */ = {
197 | isa = PBXGroup;
198 | children = (
199 | A1CF8D5B1AFE1332005EDE51 /* GlanceController.swift */,
200 | A1CF8D5D1AFE1332005EDE51 /* Images.xcassets */,
201 | A1CF8D581AFE1332005EDE51 /* Info.plist */,
202 | A1CF8D591AFE1332005EDE51 /* InterfaceController.swift */,
203 | A1ED03461AFEB10B00570757 /* PaymentAlert.swift */,
204 | A1ED03491AFEBEA500570757 /* WatchButton WatchKit Extension.entitlements */,
205 | );
206 | path = "WatchKit Extension";
207 | sourceTree = "";
208 | };
209 | A1CF8D651AFE1332005EDE51 /* WatchKit App */ = {
210 | isa = PBXGroup;
211 | children = (
212 | A1CF8D6B1AFE1332005EDE51 /* Images.xcassets */,
213 | A1CF8D671AFE1332005EDE51 /* Info.plist */,
214 | A1CF8D681AFE1332005EDE51 /* Interface.storyboard */,
215 | );
216 | path = "WatchKit App";
217 | sourceTree = "";
218 | };
219 | A1CF8D771AFE1363005EDE51 /* Resources */ = {
220 | isa = PBXGroup;
221 | children = (
222 | A1CF8D381AFE1325005EDE51 /* Images.xcassets */,
223 | A1CF8D3A1AFE1325005EDE51 /* LaunchScreen.xib */,
224 | A1CF8D351AFE1325005EDE51 /* Main.storyboard */,
225 | );
226 | path = Resources;
227 | sourceTree = "";
228 | };
229 | CEAF4F522F566795258DD4FE /* Pods */ = {
230 | isa = PBXGroup;
231 | children = (
232 | 8E86E131A6CC65020705ACD9 /* Pods.debug.xcconfig */,
233 | 8C79ADFDE7F13A447C77626D /* Pods.release.xcconfig */,
234 | B94957861BC3C5A0ED44131E /* Pods-WatchButton WatchKit Extension.debug.xcconfig */,
235 | 4AE19A26D612E6E67E88A766 /* Pods-WatchButton WatchKit Extension.release.xcconfig */,
236 | );
237 | name = Pods;
238 | sourceTree = "";
239 | };
240 | /* End PBXGroup section */
241 |
242 | /* Begin PBXNativeTarget section */
243 | A1CF8D2B1AFE1325005EDE51 /* WatchButton */ = {
244 | isa = PBXNativeTarget;
245 | buildConfigurationList = A1CF8D4B1AFE1325005EDE51 /* Build configuration list for PBXNativeTarget "WatchButton" */;
246 | buildPhases = (
247 | 9CDB9024CEBBA0A18D26F0FF /* Check Pods Manifest.lock */,
248 | A1CF8D281AFE1325005EDE51 /* Sources */,
249 | A1CF8D291AFE1325005EDE51 /* Frameworks */,
250 | A1CF8D2A1AFE1325005EDE51 /* Resources */,
251 | A1AAA6AF1AFE9D51000D4227 /* Show TODOs and FIXMEs */,
252 | A1F8A7FD1B70BA1300D22AC4 /* Embed Watch Content */,
253 | 1744D74E15042A95715B312F /* Embed Pods Frameworks */,
254 | A07DC42AFE7194D6716B1C0B /* Copy Pods Resources */,
255 | );
256 | buildRules = (
257 | );
258 | dependencies = (
259 | A1F8A7FA1B70BA1300D22AC4 /* PBXTargetDependency */,
260 | );
261 | name = WatchButton;
262 | productName = WatchButton;
263 | productReference = A1CF8D2C1AFE1325005EDE51 /* WatchButton.app */;
264 | productType = "com.apple.product-type.application";
265 | };
266 | A1CF8D541AFE1332005EDE51 /* WatchButton WatchKit Extension */ = {
267 | isa = PBXNativeTarget;
268 | buildConfigurationList = A1CF8D731AFE1332005EDE51 /* Build configuration list for PBXNativeTarget "WatchButton WatchKit Extension" */;
269 | buildPhases = (
270 | 851BA33E51AA8A8B8ABE8885 /* Check Pods Manifest.lock */,
271 | A1CF8D511AFE1332005EDE51 /* Sources */,
272 | A1CF8D521AFE1332005EDE51 /* Frameworks */,
273 | A1CF8D531AFE1332005EDE51 /* Resources */,
274 | FC69E518A3D0DB23661D485B /* Embed Pods Frameworks */,
275 | 1E7790BE1866CD6A69CBAB19 /* Copy Pods Resources */,
276 | );
277 | buildRules = (
278 | );
279 | dependencies = (
280 | );
281 | name = "WatchButton WatchKit Extension";
282 | productName = "WatchButton WatchKit Extension";
283 | productReference = A1CF8D551AFE1332005EDE51 /* WatchButton WatchKit Extension.appex */;
284 | productType = "com.apple.product-type.watchkit2-extension";
285 | };
286 | A1CF8D601AFE1332005EDE51 /* WatchButton WatchKit App */ = {
287 | isa = PBXNativeTarget;
288 | buildConfigurationList = A1CF8D701AFE1332005EDE51 /* Build configuration list for PBXNativeTarget "WatchButton WatchKit App" */;
289 | buildPhases = (
290 | A1CF8D5F1AFE1332005EDE51 /* Resources */,
291 | 083B70BE0987AE1EACD4863A /* Frameworks */,
292 | A1F8A7FC1B70BA1300D22AC4 /* Embed App Extensions */,
293 | );
294 | buildRules = (
295 | );
296 | dependencies = (
297 | A1F8A7F71B70BA1300D22AC4 /* PBXTargetDependency */,
298 | );
299 | name = "WatchButton WatchKit App";
300 | productName = "WatchButton WatchKit App";
301 | productReference = A1CF8D611AFE1332005EDE51 /* WatchButton WatchKit App.app */;
302 | productType = "com.apple.product-type.application.watchapp2";
303 | };
304 | /* End PBXNativeTarget section */
305 |
306 | /* Begin PBXProject section */
307 | A1CF8D241AFE1325005EDE51 /* Project object */ = {
308 | isa = PBXProject;
309 | attributes = {
310 | LastSwiftUpdateCheck = 0700;
311 | LastUpgradeCheck = 0700;
312 | ORGANIZATIONNAME = "Boris Bügling";
313 | TargetAttributes = {
314 | A1CF8D2B1AFE1325005EDE51 = {
315 | CreatedOnToolsVersion = 6.3.2;
316 | DevelopmentTeam = RWJ5E97L7R;
317 | SystemCapabilities = {
318 | com.apple.ApplicationGroups.iOS = {
319 | enabled = 1;
320 | };
321 | };
322 | };
323 | A1CF8D541AFE1332005EDE51 = {
324 | CreatedOnToolsVersion = 6.3.2;
325 | DevelopmentTeam = RWJ5E97L7R;
326 | SystemCapabilities = {
327 | com.apple.ApplicationGroups.iOS = {
328 | enabled = 1;
329 | };
330 | };
331 | };
332 | A1CF8D601AFE1332005EDE51 = {
333 | CreatedOnToolsVersion = 6.3.2;
334 | };
335 | };
336 | };
337 | buildConfigurationList = A1CF8D271AFE1325005EDE51 /* Build configuration list for PBXProject "WatchButton" */;
338 | compatibilityVersion = "Xcode 3.2";
339 | developmentRegion = English;
340 | hasScannedForEncodings = 0;
341 | knownRegions = (
342 | en,
343 | Base,
344 | );
345 | mainGroup = A1CF8D231AFE1325005EDE51;
346 | productRefGroup = A1CF8D2D1AFE1325005EDE51 /* Products */;
347 | projectDirPath = "";
348 | projectRoot = "";
349 | targets = (
350 | A1CF8D2B1AFE1325005EDE51 /* WatchButton */,
351 | A1CF8D541AFE1332005EDE51 /* WatchButton WatchKit Extension */,
352 | A1CF8D601AFE1332005EDE51 /* WatchButton WatchKit App */,
353 | );
354 | };
355 | /* End PBXProject section */
356 |
357 | /* Begin PBXResourcesBuildPhase section */
358 | A1CF8D2A1AFE1325005EDE51 /* Resources */ = {
359 | isa = PBXResourcesBuildPhase;
360 | buildActionMask = 2147483647;
361 | files = (
362 | A1CF8D371AFE1325005EDE51 /* Main.storyboard in Resources */,
363 | A1CF8D3C1AFE1325005EDE51 /* LaunchScreen.xib in Resources */,
364 | A1CF8D391AFE1325005EDE51 /* Images.xcassets in Resources */,
365 | );
366 | runOnlyForDeploymentPostprocessing = 0;
367 | };
368 | A1CF8D531AFE1332005EDE51 /* Resources */ = {
369 | isa = PBXResourcesBuildPhase;
370 | buildActionMask = 2147483647;
371 | files = (
372 | A1CF8D5E1AFE1332005EDE51 /* Images.xcassets in Resources */,
373 | );
374 | runOnlyForDeploymentPostprocessing = 0;
375 | };
376 | A1CF8D5F1AFE1332005EDE51 /* Resources */ = {
377 | isa = PBXResourcesBuildPhase;
378 | buildActionMask = 2147483647;
379 | files = (
380 | A1CF8D6A1AFE1332005EDE51 /* Interface.storyboard in Resources */,
381 | A1CF8D6C1AFE1332005EDE51 /* Images.xcassets in Resources */,
382 | );
383 | runOnlyForDeploymentPostprocessing = 0;
384 | };
385 | /* End PBXResourcesBuildPhase section */
386 |
387 | /* Begin PBXShellScriptBuildPhase section */
388 | 1744D74E15042A95715B312F /* Embed Pods Frameworks */ = {
389 | isa = PBXShellScriptBuildPhase;
390 | buildActionMask = 2147483647;
391 | files = (
392 | );
393 | inputPaths = (
394 | );
395 | name = "Embed Pods Frameworks";
396 | outputPaths = (
397 | );
398 | runOnlyForDeploymentPostprocessing = 0;
399 | shellPath = /bin/sh;
400 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
401 | showEnvVarsInLog = 0;
402 | };
403 | 1E7790BE1866CD6A69CBAB19 /* Copy Pods Resources */ = {
404 | isa = PBXShellScriptBuildPhase;
405 | buildActionMask = 2147483647;
406 | files = (
407 | );
408 | inputPaths = (
409 | );
410 | name = "Copy Pods Resources";
411 | outputPaths = (
412 | );
413 | runOnlyForDeploymentPostprocessing = 0;
414 | shellPath = /bin/sh;
415 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-WatchButton WatchKit Extension/Pods-WatchButton WatchKit Extension-resources.sh\"\n";
416 | showEnvVarsInLog = 0;
417 | };
418 | 851BA33E51AA8A8B8ABE8885 /* Check Pods Manifest.lock */ = {
419 | isa = PBXShellScriptBuildPhase;
420 | buildActionMask = 2147483647;
421 | files = (
422 | );
423 | inputPaths = (
424 | );
425 | name = "Check Pods Manifest.lock";
426 | outputPaths = (
427 | );
428 | runOnlyForDeploymentPostprocessing = 0;
429 | shellPath = /bin/sh;
430 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
431 | showEnvVarsInLog = 0;
432 | };
433 | 9CDB9024CEBBA0A18D26F0FF /* Check Pods Manifest.lock */ = {
434 | isa = PBXShellScriptBuildPhase;
435 | buildActionMask = 2147483647;
436 | files = (
437 | );
438 | inputPaths = (
439 | );
440 | name = "Check Pods Manifest.lock";
441 | outputPaths = (
442 | );
443 | runOnlyForDeploymentPostprocessing = 0;
444 | shellPath = /bin/sh;
445 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
446 | showEnvVarsInLog = 0;
447 | };
448 | A07DC42AFE7194D6716B1C0B /* Copy Pods Resources */ = {
449 | isa = PBXShellScriptBuildPhase;
450 | buildActionMask = 2147483647;
451 | files = (
452 | );
453 | inputPaths = (
454 | );
455 | name = "Copy Pods Resources";
456 | outputPaths = (
457 | );
458 | runOnlyForDeploymentPostprocessing = 0;
459 | shellPath = /bin/sh;
460 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
461 | showEnvVarsInLog = 0;
462 | };
463 | A1AAA6AF1AFE9D51000D4227 /* Show TODOs and FIXMEs */ = {
464 | isa = PBXShellScriptBuildPhase;
465 | buildActionMask = 2147483647;
466 | files = (
467 | );
468 | inputPaths = (
469 | );
470 | name = "Show TODOs and FIXMEs";
471 | outputPaths = (
472 | );
473 | runOnlyForDeploymentPostprocessing = 0;
474 | shellPath = /bin/sh;
475 | shellScript = "KEYWORDS=\"TODO:|FIXME:|\\?\\?\\?:|\\!\\!\\!:\"\nfind \"${SRCROOT}\" \\( -name \"*.h\" -or -name \"*.m\" -or -name \"*.mm\" -or -name \"*.swift\" \\) -print0 | xargs -0 egrep --with-filename --line-number --only-matching \"($KEYWORDS).*\\$\" | grep -v '/Pods/' | perl -p -e \"s/($KEYWORDS)/ warning: \\$1/\"";
476 | };
477 | FC69E518A3D0DB23661D485B /* Embed Pods Frameworks */ = {
478 | isa = PBXShellScriptBuildPhase;
479 | buildActionMask = 2147483647;
480 | files = (
481 | );
482 | inputPaths = (
483 | );
484 | name = "Embed Pods Frameworks";
485 | outputPaths = (
486 | );
487 | runOnlyForDeploymentPostprocessing = 0;
488 | shellPath = /bin/sh;
489 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-WatchButton WatchKit Extension/Pods-WatchButton WatchKit Extension-frameworks.sh\"\n";
490 | showEnvVarsInLog = 0;
491 | };
492 | /* End PBXShellScriptBuildPhase section */
493 |
494 | /* Begin PBXSourcesBuildPhase section */
495 | A1CF8D281AFE1325005EDE51 /* Sources */ = {
496 | isa = PBXSourcesBuildPhase;
497 | buildActionMask = 2147483647;
498 | files = (
499 | A1AAA6AB1AFE93A0000D4227 /* PaymentKeychain.swift in Sources */,
500 | A1AAA6AD1AFE97C4000D4227 /* Commands.swift in Sources */,
501 | A19B89B31AFEEC6C00E15887 /* AddressFormViewController.swift in Sources */,
502 | A1CF8D341AFE1325005EDE51 /* ViewController.swift in Sources */,
503 | A1CF8D321AFE1325005EDE51 /* AppDelegate.swift in Sources */,
504 | A15AB65E1AFECD8A000DEA74 /* Beacon.swift in Sources */,
505 | A15AB65C1AFECD32000DEA74 /* BeaconController.swift in Sources */,
506 | A1AAA6A91AFE8950000D4227 /* PayPalClient.swift in Sources */,
507 | );
508 | runOnlyForDeploymentPostprocessing = 0;
509 | };
510 | A1CF8D511AFE1332005EDE51 /* Sources */ = {
511 | isa = PBXSourcesBuildPhase;
512 | buildActionMask = 2147483647;
513 | files = (
514 | A1CF8D5A1AFE1332005EDE51 /* InterfaceController.swift in Sources */,
515 | A1CF8D5C1AFE1332005EDE51 /* GlanceController.swift in Sources */,
516 | A1ED03471AFEB10B00570757 /* PaymentAlert.swift in Sources */,
517 | A1AAA6AE1AFE97C4000D4227 /* Commands.swift in Sources */,
518 | );
519 | runOnlyForDeploymentPostprocessing = 0;
520 | };
521 | /* End PBXSourcesBuildPhase section */
522 |
523 | /* Begin PBXTargetDependency section */
524 | A1F8A7F71B70BA1300D22AC4 /* PBXTargetDependency */ = {
525 | isa = PBXTargetDependency;
526 | target = A1CF8D541AFE1332005EDE51 /* WatchButton WatchKit Extension */;
527 | targetProxy = A1F8A7F61B70BA1300D22AC4 /* PBXContainerItemProxy */;
528 | };
529 | A1F8A7FA1B70BA1300D22AC4 /* PBXTargetDependency */ = {
530 | isa = PBXTargetDependency;
531 | target = A1CF8D601AFE1332005EDE51 /* WatchButton WatchKit App */;
532 | targetProxy = A1F8A7F91B70BA1300D22AC4 /* PBXContainerItemProxy */;
533 | };
534 | /* End PBXTargetDependency section */
535 |
536 | /* Begin PBXVariantGroup section */
537 | A1CF8D351AFE1325005EDE51 /* Main.storyboard */ = {
538 | isa = PBXVariantGroup;
539 | children = (
540 | A1CF8D361AFE1325005EDE51 /* Base */,
541 | );
542 | name = Main.storyboard;
543 | path = .;
544 | sourceTree = "";
545 | };
546 | A1CF8D3A1AFE1325005EDE51 /* LaunchScreen.xib */ = {
547 | isa = PBXVariantGroup;
548 | children = (
549 | A1CF8D3B1AFE1325005EDE51 /* Base */,
550 | );
551 | name = LaunchScreen.xib;
552 | path = .;
553 | sourceTree = "";
554 | };
555 | A1CF8D681AFE1332005EDE51 /* Interface.storyboard */ = {
556 | isa = PBXVariantGroup;
557 | children = (
558 | A1CF8D691AFE1332005EDE51 /* Base */,
559 | );
560 | name = Interface.storyboard;
561 | path = .;
562 | sourceTree = "";
563 | };
564 | /* End PBXVariantGroup section */
565 |
566 | /* Begin XCBuildConfiguration section */
567 | A1CF8D491AFE1325005EDE51 /* Debug */ = {
568 | isa = XCBuildConfiguration;
569 | buildSettings = {
570 | ALWAYS_SEARCH_USER_PATHS = NO;
571 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
572 | CLANG_CXX_LIBRARY = "libc++";
573 | CLANG_ENABLE_MODULES = YES;
574 | CLANG_ENABLE_OBJC_ARC = YES;
575 | CLANG_WARN_BOOL_CONVERSION = YES;
576 | CLANG_WARN_CONSTANT_CONVERSION = YES;
577 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
578 | CLANG_WARN_EMPTY_BODY = YES;
579 | CLANG_WARN_ENUM_CONVERSION = YES;
580 | CLANG_WARN_INT_CONVERSION = YES;
581 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
582 | CLANG_WARN_UNREACHABLE_CODE = YES;
583 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
584 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
585 | COPY_PHASE_STRIP = NO;
586 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
587 | ENABLE_STRICT_OBJC_MSGSEND = YES;
588 | ENABLE_TESTABILITY = YES;
589 | GCC_C_LANGUAGE_STANDARD = gnu99;
590 | GCC_DYNAMIC_NO_PIC = NO;
591 | GCC_NO_COMMON_BLOCKS = YES;
592 | GCC_OPTIMIZATION_LEVEL = 0;
593 | GCC_PREPROCESSOR_DEFINITIONS = (
594 | "DEBUG=1",
595 | "$(inherited)",
596 | );
597 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
598 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
599 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
600 | GCC_WARN_UNDECLARED_SELECTOR = YES;
601 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
602 | GCC_WARN_UNUSED_FUNCTION = YES;
603 | GCC_WARN_UNUSED_VARIABLE = YES;
604 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
605 | MTL_ENABLE_DEBUG_INFO = YES;
606 | ONLY_ACTIVE_ARCH = YES;
607 | SDKROOT = iphoneos;
608 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
609 | };
610 | name = Debug;
611 | };
612 | A1CF8D4A1AFE1325005EDE51 /* Release */ = {
613 | isa = XCBuildConfiguration;
614 | buildSettings = {
615 | ALWAYS_SEARCH_USER_PATHS = NO;
616 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
617 | CLANG_CXX_LIBRARY = "libc++";
618 | CLANG_ENABLE_MODULES = YES;
619 | CLANG_ENABLE_OBJC_ARC = YES;
620 | CLANG_WARN_BOOL_CONVERSION = YES;
621 | CLANG_WARN_CONSTANT_CONVERSION = YES;
622 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
623 | CLANG_WARN_EMPTY_BODY = YES;
624 | CLANG_WARN_ENUM_CONVERSION = YES;
625 | CLANG_WARN_INT_CONVERSION = YES;
626 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
627 | CLANG_WARN_UNREACHABLE_CODE = YES;
628 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
629 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
630 | COPY_PHASE_STRIP = NO;
631 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
632 | ENABLE_NS_ASSERTIONS = NO;
633 | ENABLE_STRICT_OBJC_MSGSEND = YES;
634 | GCC_C_LANGUAGE_STANDARD = gnu99;
635 | GCC_NO_COMMON_BLOCKS = YES;
636 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
637 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
638 | GCC_WARN_UNDECLARED_SELECTOR = YES;
639 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
640 | GCC_WARN_UNUSED_FUNCTION = YES;
641 | GCC_WARN_UNUSED_VARIABLE = YES;
642 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
643 | MTL_ENABLE_DEBUG_INFO = NO;
644 | SDKROOT = iphoneos;
645 | VALIDATE_PRODUCT = YES;
646 | };
647 | name = Release;
648 | };
649 | A1CF8D4C1AFE1325005EDE51 /* Debug */ = {
650 | isa = XCBuildConfiguration;
651 | baseConfigurationReference = 8E86E131A6CC65020705ACD9 /* Pods.debug.xcconfig */;
652 | buildSettings = {
653 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
654 | CODE_SIGN_ENTITLEMENTS = "Phone App/WatchButton.entitlements";
655 | CODE_SIGN_IDENTITY = "iPhone Developer";
656 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
657 | ENABLE_BITCODE = NO;
658 | INFOPLIST_FILE = "Phone App/Supporting Files/Info.plist";
659 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
660 | PRODUCT_BUNDLE_IDENTIFIER = "org.vu0.$(PRODUCT_NAME:rfc1034identifier)";
661 | PRODUCT_NAME = "$(TARGET_NAME)";
662 | PROVISIONING_PROFILE = "";
663 | SWIFT_OBJC_BRIDGING_HEADER = "Phone App/Supporting Files/BridgingHeader.h";
664 | USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/Pods/Headers/Public/PayPal-iOS-SDK";
665 | };
666 | name = Debug;
667 | };
668 | A1CF8D4D1AFE1325005EDE51 /* Release */ = {
669 | isa = XCBuildConfiguration;
670 | baseConfigurationReference = 8C79ADFDE7F13A447C77626D /* Pods.release.xcconfig */;
671 | buildSettings = {
672 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
673 | CODE_SIGN_ENTITLEMENTS = "Phone App/WatchButton.entitlements";
674 | CODE_SIGN_IDENTITY = "iPhone Developer";
675 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
676 | ENABLE_BITCODE = NO;
677 | INFOPLIST_FILE = "Phone App/Supporting Files/Info.plist";
678 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
679 | PRODUCT_BUNDLE_IDENTIFIER = "org.vu0.$(PRODUCT_NAME:rfc1034identifier)";
680 | PRODUCT_NAME = "$(TARGET_NAME)";
681 | PROVISIONING_PROFILE = "";
682 | SWIFT_OBJC_BRIDGING_HEADER = "Phone App/Supporting Files/BridgingHeader.h";
683 | USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/Pods/Headers/Public/PayPal-iOS-SDK";
684 | };
685 | name = Release;
686 | };
687 | A1CF8D711AFE1332005EDE51 /* Debug */ = {
688 | isa = XCBuildConfiguration;
689 | buildSettings = {
690 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
691 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
692 | GCC_PREPROCESSOR_DEFINITIONS = (
693 | "DEBUG=1",
694 | "$(inherited)",
695 | );
696 | IBSC_MODULE = WatchButton_WatchKit_Extension;
697 | INFOPLIST_FILE = "WatchKit App/Info.plist";
698 | PRODUCT_BUNDLE_IDENTIFIER = org.vu0.WatchButton.watchkitapp;
699 | PRODUCT_NAME = "$(TARGET_NAME)";
700 | SDKROOT = watchos;
701 | SKIP_INSTALL = YES;
702 | TARGETED_DEVICE_FAMILY = 4;
703 | WATCHOS_DEPLOYMENT_TARGET = 2.0;
704 | };
705 | name = Debug;
706 | };
707 | A1CF8D721AFE1332005EDE51 /* Release */ = {
708 | isa = XCBuildConfiguration;
709 | buildSettings = {
710 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
711 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
712 | IBSC_MODULE = WatchButton_WatchKit_Extension;
713 | INFOPLIST_FILE = "WatchKit App/Info.plist";
714 | PRODUCT_BUNDLE_IDENTIFIER = org.vu0.WatchButton.watchkitapp;
715 | PRODUCT_NAME = "$(TARGET_NAME)";
716 | SDKROOT = watchos;
717 | SKIP_INSTALL = YES;
718 | TARGETED_DEVICE_FAMILY = 4;
719 | WATCHOS_DEPLOYMENT_TARGET = 2.0;
720 | };
721 | name = Release;
722 | };
723 | A1CF8D741AFE1332005EDE51 /* Debug */ = {
724 | isa = XCBuildConfiguration;
725 | baseConfigurationReference = B94957861BC3C5A0ED44131E /* Pods-WatchButton WatchKit Extension.debug.xcconfig */;
726 | buildSettings = {
727 | CODE_SIGN_ENTITLEMENTS = "WatchKit Extension/WatchButton WatchKit Extension.entitlements";
728 | CODE_SIGN_IDENTITY = "iPhone Developer";
729 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
730 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = "iPhone Developer";
731 | GCC_PREPROCESSOR_DEFINITIONS = (
732 | "DEBUG=1",
733 | "$(inherited)",
734 | );
735 | INFOPLIST_FILE = "WatchKit Extension/Info.plist";
736 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
737 | PRODUCT_BUNDLE_IDENTIFIER = org.vu0.WatchButton.watchkitapp.watchkitextension;
738 | PRODUCT_NAME = "${TARGET_NAME}";
739 | PROVISIONING_PROFILE = "";
740 | SDKROOT = watchos;
741 | SKIP_INSTALL = YES;
742 | TARGETED_DEVICE_FAMILY = 4;
743 | WATCHOS_DEPLOYMENT_TARGET = 2.0;
744 | };
745 | name = Debug;
746 | };
747 | A1CF8D751AFE1332005EDE51 /* Release */ = {
748 | isa = XCBuildConfiguration;
749 | baseConfigurationReference = 4AE19A26D612E6E67E88A766 /* Pods-WatchButton WatchKit Extension.release.xcconfig */;
750 | buildSettings = {
751 | CODE_SIGN_ENTITLEMENTS = "WatchKit Extension/WatchButton WatchKit Extension.entitlements";
752 | CODE_SIGN_IDENTITY = "iPhone Developer";
753 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
754 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = "iPhone Developer";
755 | INFOPLIST_FILE = "WatchKit Extension/Info.plist";
756 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
757 | PRODUCT_BUNDLE_IDENTIFIER = org.vu0.WatchButton.watchkitapp.watchkitextension;
758 | PRODUCT_NAME = "${TARGET_NAME}";
759 | PROVISIONING_PROFILE = "";
760 | SDKROOT = watchos;
761 | SKIP_INSTALL = YES;
762 | TARGETED_DEVICE_FAMILY = 4;
763 | WATCHOS_DEPLOYMENT_TARGET = 2.0;
764 | };
765 | name = Release;
766 | };
767 | /* End XCBuildConfiguration section */
768 |
769 | /* Begin XCConfigurationList section */
770 | A1CF8D271AFE1325005EDE51 /* Build configuration list for PBXProject "WatchButton" */ = {
771 | isa = XCConfigurationList;
772 | buildConfigurations = (
773 | A1CF8D491AFE1325005EDE51 /* Debug */,
774 | A1CF8D4A1AFE1325005EDE51 /* Release */,
775 | );
776 | defaultConfigurationIsVisible = 0;
777 | defaultConfigurationName = Release;
778 | };
779 | A1CF8D4B1AFE1325005EDE51 /* Build configuration list for PBXNativeTarget "WatchButton" */ = {
780 | isa = XCConfigurationList;
781 | buildConfigurations = (
782 | A1CF8D4C1AFE1325005EDE51 /* Debug */,
783 | A1CF8D4D1AFE1325005EDE51 /* Release */,
784 | );
785 | defaultConfigurationIsVisible = 0;
786 | defaultConfigurationName = Release;
787 | };
788 | A1CF8D701AFE1332005EDE51 /* Build configuration list for PBXNativeTarget "WatchButton WatchKit App" */ = {
789 | isa = XCConfigurationList;
790 | buildConfigurations = (
791 | A1CF8D711AFE1332005EDE51 /* Debug */,
792 | A1CF8D721AFE1332005EDE51 /* Release */,
793 | );
794 | defaultConfigurationIsVisible = 0;
795 | defaultConfigurationName = Release;
796 | };
797 | A1CF8D731AFE1332005EDE51 /* Build configuration list for PBXNativeTarget "WatchButton WatchKit Extension" */ = {
798 | isa = XCConfigurationList;
799 | buildConfigurations = (
800 | A1CF8D741AFE1332005EDE51 /* Debug */,
801 | A1CF8D751AFE1332005EDE51 /* Release */,
802 | );
803 | defaultConfigurationIsVisible = 0;
804 | defaultConfigurationName = Release;
805 | };
806 | /* End XCConfigurationList section */
807 | };
808 | rootObject = A1CF8D241AFE1325005EDE51 /* Project object */;
809 | }
810 |
--------------------------------------------------------------------------------
/WatchButton.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/WatchButton.xcodeproj/xcshareddata/xcschemes/Glance - WatchButton WatchKit App.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
43 |
49 |
50 |
51 |
52 |
53 |
58 |
59 |
60 |
61 |
67 |
68 |
69 |
70 |
71 |
72 |
83 |
87 |
93 |
94 |
95 |
96 |
102 |
103 |
104 |
105 |
106 |
107 |
114 |
118 |
124 |
125 |
126 |
127 |
133 |
134 |
135 |
136 |
138 |
139 |
142 |
143 |
144 |
--------------------------------------------------------------------------------
/WatchButton.xcodeproj/xcshareddata/xcschemes/WatchButton WatchKit App.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
43 |
49 |
50 |
51 |
52 |
53 |
58 |
59 |
60 |
61 |
67 |
68 |
69 |
70 |
71 |
72 |
82 |
86 |
92 |
93 |
94 |
95 |
101 |
102 |
103 |
104 |
105 |
106 |
112 |
116 |
122 |
123 |
124 |
125 |
131 |
132 |
133 |
134 |
136 |
137 |
140 |
141 |
142 |
--------------------------------------------------------------------------------
/WatchButton.xcodeproj/xcshareddata/xcschemes/WatchButton.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/WatchButton.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/WatchButton.xcworkspace/xcshareddata/WatchButton.xcscmblueprint:
--------------------------------------------------------------------------------
1 | {
2 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "1F11C153B7ACCE97334A74ED7522D4780A4D0750",
3 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
4 |
5 | },
6 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
7 | "1F11C153B7ACCE97334A74ED7522D4780A4D0750" : 0,
8 | "EB9FF6CC2185ECB3EFA62740E51DAF662F69DD0D" : 0
9 | },
10 | "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "A512FC55-DA6E-45AF-B1B7-A65024CB2B87",
11 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
12 | "1F11C153B7ACCE97334A74ED7522D4780A4D0750" : "WatchButton\/",
13 | "EB9FF6CC2185ECB3EFA62740E51DAF662F69DD0D" : "Cube\/"
14 | },
15 | "DVTSourceControlWorkspaceBlueprintNameKey" : "WatchButton",
16 | "DVTSourceControlWorkspaceBlueprintVersion" : 204,
17 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "WatchButton.xcworkspace",
18 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
19 | {
20 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:contentful-labs\/Wunderschnell.git",
21 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
22 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "1F11C153B7ACCE97334A74ED7522D4780A4D0750"
23 | },
24 | {
25 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:contentful-labs\/Cube.git",
26 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
27 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "EB9FF6CC2185ECB3EFA62740E51DAF662F69DD0D"
28 | }
29 | ]
30 | }
--------------------------------------------------------------------------------
/WatchKit App/Base.lproj/Interface.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/WatchKit App/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "24x24",
5 | "idiom" : "watch",
6 | "scale" : "2x",
7 | "filename" : "watch-48.png",
8 | "role" : "notificationCenter",
9 | "subtype" : "38mm"
10 | },
11 | {
12 | "size" : "27.5x27.5",
13 | "idiom" : "watch",
14 | "scale" : "2x",
15 | "filename" : "watch-55.png",
16 | "role" : "notificationCenter",
17 | "subtype" : "42mm"
18 | },
19 | {
20 | "size" : "29x29",
21 | "idiom" : "watch",
22 | "filename" : "watch-58.png",
23 | "role" : "companionSettings",
24 | "scale" : "2x"
25 | },
26 | {
27 | "size" : "29x29",
28 | "idiom" : "watch",
29 | "filename" : "watch-87.png",
30 | "role" : "companionSettings",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "watch",
36 | "scale" : "2x",
37 | "filename" : "watch-80.png",
38 | "role" : "appLauncher",
39 | "subtype" : "38mm"
40 | },
41 | {
42 | "size" : "44x44",
43 | "idiom" : "watch",
44 | "scale" : "2x",
45 | "filename" : "watch-88.png",
46 | "role" : "longLook",
47 | "subtype" : "42mm"
48 | },
49 | {
50 | "size" : "86x86",
51 | "idiom" : "watch",
52 | "scale" : "2x",
53 | "filename" : "watch-172.png",
54 | "role" : "quickLook",
55 | "subtype" : "38mm"
56 | },
57 | {
58 | "size" : "98x98",
59 | "idiom" : "watch",
60 | "scale" : "2x",
61 | "filename" : "watch-196.png",
62 | "role" : "quickLook",
63 | "subtype" : "42mm"
64 | }
65 | ],
66 | "info" : {
67 | "version" : 1,
68 | "author" : "xcode"
69 | }
70 | }
--------------------------------------------------------------------------------
/WatchKit App/Images.xcassets/AppIcon.appiconset/watch-172.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/contentful/Wunderschnell/65d36dc26d4d5520652b202a79846b48022dd452/WatchKit App/Images.xcassets/AppIcon.appiconset/watch-172.png
--------------------------------------------------------------------------------
/WatchKit App/Images.xcassets/AppIcon.appiconset/watch-196.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/contentful/Wunderschnell/65d36dc26d4d5520652b202a79846b48022dd452/WatchKit App/Images.xcassets/AppIcon.appiconset/watch-196.png
--------------------------------------------------------------------------------
/WatchKit App/Images.xcassets/AppIcon.appiconset/watch-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/contentful/Wunderschnell/65d36dc26d4d5520652b202a79846b48022dd452/WatchKit App/Images.xcassets/AppIcon.appiconset/watch-48.png
--------------------------------------------------------------------------------
/WatchKit App/Images.xcassets/AppIcon.appiconset/watch-55.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/contentful/Wunderschnell/65d36dc26d4d5520652b202a79846b48022dd452/WatchKit App/Images.xcassets/AppIcon.appiconset/watch-55.png
--------------------------------------------------------------------------------
/WatchKit App/Images.xcassets/AppIcon.appiconset/watch-58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/contentful/Wunderschnell/65d36dc26d4d5520652b202a79846b48022dd452/WatchKit App/Images.xcassets/AppIcon.appiconset/watch-58.png
--------------------------------------------------------------------------------
/WatchKit App/Images.xcassets/AppIcon.appiconset/watch-80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/contentful/Wunderschnell/65d36dc26d4d5520652b202a79846b48022dd452/WatchKit App/Images.xcassets/AppIcon.appiconset/watch-80.png
--------------------------------------------------------------------------------
/WatchKit App/Images.xcassets/AppIcon.appiconset/watch-87.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/contentful/Wunderschnell/65d36dc26d4d5520652b202a79846b48022dd452/WatchKit App/Images.xcassets/AppIcon.appiconset/watch-87.png
--------------------------------------------------------------------------------
/WatchKit App/Images.xcassets/AppIcon.appiconset/watch-88.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/contentful/Wunderschnell/65d36dc26d4d5520652b202a79846b48022dd452/WatchKit App/Images.xcassets/AppIcon.appiconset/watch-88.png
--------------------------------------------------------------------------------
/WatchKit App/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | ÜberSchnell
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 | org.vu0.WatchButton
32 | WKWatchKitApp
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/WatchKit Extension/GlanceController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GlanceController.swift
3 | // WatchButton WatchKit Extension
4 | //
5 | // Created by Boris Bügling on 09/05/15.
6 | // Copyright (c) 2015 Boris Bügling. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import MMWormhole
11 | import WatchConnectivity
12 | import WatchKit
13 |
14 | class GlanceController: WKInterfaceController {
15 | let wormhole = MMWormhole(applicationGroupIdentifier: AppGroupIdentifier, optionalDirectory: DirectoryIdentifier)
16 |
17 | @IBOutlet weak var infoLabel: WKInterfaceLabel!
18 |
19 | func resetLabel() {
20 | infoLabel.setText("No beacons in range :(")
21 | }
22 |
23 | override func willActivate() {
24 | super.willActivate()
25 |
26 | resetLabel()
27 | wormhole.listenForMessageWithIdentifier(Reply.BeaconRanged.rawValue) { (data) in
28 | if let ranged = data as? Bool {
29 | if ranged {
30 | self.infoLabel.setText("Beacon in range, tap to buy.")
31 | }
32 | return
33 | }
34 |
35 | self.resetLabel()
36 | }
37 |
38 | WCSession.defaultSession().sendMessage([ CommandIdentifier: Command.Nothing.rawValue ], replyHandler: { (_) in
39 | }, errorHandler: nil)
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/WatchKit Extension/Images.xcassets/README__ignoredByTemplate__:
--------------------------------------------------------------------------------
1 | Did you know that git does not support storing empty directories?
2 |
--------------------------------------------------------------------------------
/WatchKit Extension/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | ÜberSchnell
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.0
25 | NSExtension
26 |
27 | NSExtensionAttributes
28 |
29 | WKAppBundleIdentifier
30 | org.vu0.WatchButton.watchkitapp
31 |
32 | NSExtensionPointIdentifier
33 | com.apple.watchkit
34 |
35 | RemoteInterfacePrincipalClass
36 | $(PRODUCT_MODULE_NAME).InterfaceController
37 |
38 |
39 |
--------------------------------------------------------------------------------
/WatchKit Extension/InterfaceController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // InterfaceController.swift
3 | // WatchButton WatchKit Extension
4 | //
5 | // Created by Boris Bügling on 09/05/15.
6 | // Copyright (c) 2015 Boris Bügling. All rights reserved.
7 | //
8 |
9 | import Cube
10 | import Foundation
11 | import MMWormhole
12 | import WatchConnectivity
13 | import WatchKit
14 |
15 | class InterfaceController: WKInterfaceController, WCSessionDelegate {
16 | let wormhole = MMWormhole(applicationGroupIdentifier: AppGroupIdentifier, optionalDirectory: DirectoryIdentifier)
17 |
18 | @IBOutlet weak var productImage: WKInterfaceImage!
19 | @IBOutlet weak var productPrice: WKInterfaceLabel!
20 |
21 | override func willActivate() {
22 | super.willActivate()
23 |
24 | let session = WCSession.defaultSession()
25 | session.delegate = self
26 | session.activateSession()
27 |
28 | session.sendMessage([ CommandIdentifier: Command.GetProduct.rawValue ], replyHandler: { (data) in
29 | let product = Product(data[Reply.Product.rawValue] as! [String:AnyObject])
30 |
31 | if let amount = product.price["amount"], currency = product.price["currency"] {
32 | self.productPrice.setText("\(amount) \(currency)")
33 | }
34 |
35 | NSURLSession.sharedSession().dataTaskWithRequest(NSURLRequest(URL: NSURL(string: product.imageUrl)!)) { (data, _, _) in
36 | if let data = data {
37 | self.productImage.setImage(UIImage(data: data))
38 | }
39 | }
40 | }) { (error) in
41 | print(error)
42 | }
43 | }
44 |
45 | @IBAction func tapped() {
46 | wormhole.listenForMessageWithIdentifier(Reply.Paid.rawValue) { (data) in
47 | if let paid = data as? Bool {
48 | self.presentControllerWithName("PaymentAlert", context: paid)
49 | } else {
50 | self.presentControllerWithName("PaymentAlert", context: false)
51 | }
52 | }
53 |
54 | WCSession.defaultSession().sendMessage([ CommandIdentifier: Command.MakeOrder.rawValue ], replyHandler: { (_) in
55 | // Seems like openParentApplication() has a too short timeout for our orders.
56 | }, errorHandler: nil)
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/WatchKit Extension/PaymentAlert.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PaymentAlert.swift
3 | // WatchButton
4 | //
5 | // Created by Boris Bügling on 09/05/15.
6 | // Copyright (c) 2015 Boris Bügling. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import WatchKit
11 |
12 | class PaymentAlert: WKInterfaceController {
13 | @IBOutlet weak var infoLabel: WKInterfaceLabel!
14 |
15 | override func awakeWithContext(context: AnyObject?) {
16 | if let paid = context as? Bool {
17 | if paid {
18 | infoLabel.setText("Ordered successfully! 💸")
19 | return
20 | }
21 | }
22 |
23 | infoLabel.setText("Order failed... 😭")
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/WatchKit Extension/WatchButton WatchKit Extension.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.application-groups
6 |
7 | group.vu0.org.WatchButton
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------