ソースを参照

Add storyboard and view controller code for tvOS demo

Andrea Bizzotto 8 年 前
コミット
dec9bc389b

+ 0 - 3
SwiftyStoreKit-tvOS-Demo/AppDelegate.swift

@@ -13,7 +13,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
 
 
     var window: UIWindow?
     var window: UIWindow?
 
 
-
     func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
     func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
         // Override point for customization after application launch.
         // Override point for customization after application launch.
         return true
         return true
@@ -41,6 +40,4 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
         // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
         // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
     }
     }
 
 
-
 }
 }
-

+ 82 - 4
SwiftyStoreKit-tvOS-Demo/Base.lproj/Main.storyboard

@@ -1,14 +1,18 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder.AppleTV.Storyboard" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="AppleTV" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder.AppleTV.Storyboard" version="3.0" toolsVersion="11762" systemVersion="16A313a" targetRuntime="AppleTV" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
+    <device id="appleTV" orientation="landscape">
+        <adaptation id="light"/>
+    </device>
     <dependencies>
     <dependencies>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11106"/>
+        <deployment identifier="tvOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
     </dependencies>
     <scenes>
     <scenes>
         <!--View Controller-->
         <!--View Controller-->
         <scene sceneID="tne-QT-ifu">
         <scene sceneID="tne-QT-ifu">
             <objects>
             <objects>
-                <viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
+                <viewController id="BYZ-38-t0r" customClass="ViewController" customModule="SwiftyStoreKit_tvOS_Demo" customModuleProvider="target" sceneMemberID="viewController">
                     <layoutGuides>
                     <layoutGuides>
                         <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
                         <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
                         <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
                         <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
@@ -16,6 +20,80 @@
                     <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
                     <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
                         <rect key="frame" x="0.0" y="0.0" width="1920" height="1080"/>
                         <rect key="frame" x="0.0" y="0.0" width="1920" height="1080"/>
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="0V2-Mg-12F">
+                                <rect key="frame" x="876" y="106" width="170" height="86"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                <inset key="contentEdgeInsets" minX="40" minY="20" maxX="40" maxY="20"/>
+                                <state key="normal" title="info 1"/>
+                                <connections>
+                                    <action selector="getInfo1" destination="BYZ-38-t0r" eventType="primaryActionTriggered" id="nHA-8L-kz5"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Fad-e7-Q2a">
+                                <rect key="frame" x="873" y="227" width="176" height="86"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                <inset key="contentEdgeInsets" minX="40" minY="20" maxX="40" maxY="20"/>
+                                <state key="normal" title="info 2"/>
+                                <connections>
+                                    <action selector="getInfo2" destination="BYZ-38-t0r" eventType="primaryActionTriggered" id="qYx-Ai-IvG"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="lOy-18-mr9">
+                                <rect key="frame" x="828" y="337" width="266" height="86"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                <inset key="contentEdgeInsets" minX="40" minY="20" maxX="40" maxY="20"/>
+                                <state key="normal" title="purchase 1"/>
+                                <connections>
+                                    <action selector="purchase1" destination="BYZ-38-t0r" eventType="primaryActionTriggered" id="K3z-2d-B9r"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="VG3-26-V8F">
+                                <rect key="frame" x="825" y="447" width="271" height="86"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                <inset key="contentEdgeInsets" minX="40" minY="20" maxX="40" maxY="20"/>
+                                <state key="normal" title="purchase 2"/>
+                                <connections>
+                                    <action selector="purchase2" destination="BYZ-38-t0r" eventType="primaryActionTriggered" id="C85-0E-sl9"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="984-qT-rjk">
+                                <rect key="frame" x="860" y="557" width="202" height="86"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                <inset key="contentEdgeInsets" minX="40" minY="20" maxX="40" maxY="20"/>
+                                <state key="normal" title="restore"/>
+                                <connections>
+                                    <action selector="restorePurchases" destination="BYZ-38-t0r" eventType="primaryActionTriggered" id="7nH-dy-bg5"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="TE5-kT-3nP">
+                                <rect key="frame" x="864" y="667" width="193" height="86"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                <inset key="contentEdgeInsets" minX="40" minY="20" maxX="40" maxY="20"/>
+                                <state key="normal" title="verify1"/>
+                                <connections>
+                                    <action selector="verifyPurchase1" destination="BYZ-38-t0r" eventType="primaryActionTriggered" id="rJq-xw-YhW"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="iYp-Z0-czh">
+                                <rect key="frame" x="862" y="777" width="198" height="86"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                <inset key="contentEdgeInsets" minX="40" minY="20" maxX="40" maxY="20"/>
+                                <state key="normal" title="verify2"/>
+                                <connections>
+                                    <action selector="verifyPurchase2" destination="BYZ-38-t0r" eventType="primaryActionTriggered" id="d3O-JX-afR"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hdi-nV-Cky">
+                                <rect key="frame" x="860" y="887" width="200" height="86"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                <inset key="contentEdgeInsets" minX="40" minY="20" maxX="40" maxY="20"/>
+                                <state key="normal" title="receipt"/>
+                                <connections>
+                                    <action selector="verifyReceipt" destination="BYZ-38-t0r" eventType="primaryActionTriggered" id="f73-g0-uzV"/>
+                                </connections>
+                            </button>
+                        </subviews>
                         <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
                         <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
                     </view>
                     </view>
                 </viewController>
                 </viewController>

+ 264 - 6
SwiftyStoreKit-tvOS-Demo/ViewController.swift

@@ -7,19 +7,277 @@
 //
 //
 
 
 import UIKit
 import UIKit
+import StoreKit
+import SwiftyStoreKit
+
+enum RegisteredPurchase: String {
+
+    case purchase1
+    case purchase2
+    case nonConsumablePurchase
+    case consumablePurchase
+    case autoRenewablePurchase
+    case nonRenewingPurchase
+}
 
 
 class ViewController: UIViewController {
 class ViewController: UIViewController {
 
 
-    override func viewDidLoad() {
-        super.viewDidLoad()
-        // Do any additional setup after loading the view, typically from a nib.
+    let appBundleId = "com.musevisions.tvOS.SwiftyStoreKit"
+
+    let purchase1Suffix = RegisteredPurchase.purchase1
+    let purchase2Suffix = RegisteredPurchase.autoRenewablePurchase
+
+    // MARK: actions
+    @IBAction func getInfo1() {
+        getInfo(purchase1Suffix)
+    }
+    @IBAction func purchase1() {
+        purchase(purchase1Suffix)
+    }
+    @IBAction func verifyPurchase1() {
+        verifyPurchase(purchase1Suffix)
+    }
+    @IBAction func getInfo2() {
+        getInfo(purchase2Suffix)
+    }
+    @IBAction func purchase2() {
+        purchase(purchase2Suffix)
+    }
+    @IBAction func verifyPurchase2() {
+        verifyPurchase(purchase2Suffix)
+    }
+
+    func getInfo(_ purchase: RegisteredPurchase) {
+
+        SwiftyStoreKit.retrieveProductsInfo([appBundleId + "." + purchase.rawValue]) { result in
+
+            self.showAlert(self.alertForProductRetrievalInfo(result))
+        }
     }
     }
 
 
-    override func didReceiveMemoryWarning() {
-        super.didReceiveMemoryWarning()
-        // Dispose of any resources that can be recreated.
+    func purchase(_ purchase: RegisteredPurchase) {
+
+        SwiftyStoreKit.purchaseProduct(appBundleId + "." + purchase.rawValue, atomically: true) { result in
+
+            if case .success(let product) = result {
+                // Deliver content from server, then:
+                if product.needsFinishTransaction {
+                    SwiftyStoreKit.finishTransaction(product.transaction)
+                }
+            }
+            if let alert = self.alertForPurchaseResult(result) {
+                self.showAlert(alert)
+            }
+        }
     }
     }
 
 
+    @IBAction func restorePurchases() {
+
+        SwiftyStoreKit.restorePurchases(atomically: true) { results in
+
+            for product in results.restoredProducts {
+                // Deliver content from server, then:
+                if product.needsFinishTransaction {
+                    SwiftyStoreKit.finishTransaction(product.transaction)
+                }
+            }
+            self.showAlert(self.alertForRestorePurchases(results))
+        }
+    }
+
+    @IBAction func verifyReceipt() {
+
+        let appleValidator = AppleReceiptValidator(service: .production)
+        SwiftyStoreKit.verifyReceipt(using: appleValidator, password: "your-shared-secret") { result in
+
+            self.showAlert(self.alertForVerifyReceipt(result))
+
+            if case .error(let error) = result {
+                if case .noReceiptData = error {
+                    self.refreshReceipt()
+                }
+            }
+        }
+    }
+
+    func verifyPurchase(_ purchase: RegisteredPurchase) {
+
+        let appleValidator = AppleReceiptValidator(service: .production)
+        SwiftyStoreKit.verifyReceipt(using: appleValidator, password: "your-shared-secret") { result in
+
+            switch result {
+            case .success(let receipt):
+
+                let productId = self.appBundleId + "." + purchase.rawValue
+
+                switch purchase {
+                case .autoRenewablePurchase:
+                    let purchaseResult = SwiftyStoreKit.verifySubscription(
+                        type: .autoRenewable,
+                        productId: productId,
+                        inReceipt: receipt,
+                        validUntil: Date()
+                    )
+                    self.showAlert(self.alertForVerifySubscription(purchaseResult))
+                case .nonRenewingPurchase:
+                    let purchaseResult = SwiftyStoreKit.verifySubscription(
+                        type: .nonRenewing(validDuration: 60),
+                        productId: productId,
+                        inReceipt: receipt,
+                        validUntil: Date()
+                    )
+                    self.showAlert(self.alertForVerifySubscription(purchaseResult))
+                default:
+                    let purchaseResult = SwiftyStoreKit.verifyPurchase(
+                        productId: productId,
+                        inReceipt: receipt
+                    )
+                    self.showAlert(self.alertForVerifyPurchase(purchaseResult))
+                }
+
+            case .error(let error):
+                self.showAlert(self.alertForVerifyReceipt(result))
+                if case .noReceiptData = error {
+                    self.refreshReceipt()
+                }
+            }
+        }
+    }
+
+    func refreshReceipt() {
+
+        SwiftyStoreKit.refreshReceipt { result in
+
+            self.showAlert(self.alertForRefreshReceipt(result))
+        }
+    }
 
 
 }
 }
 
 
+// MARK: User facing alerts
+extension ViewController {
+
+    func alertWithTitle(_ title: String, message: String) -> UIAlertController {
+
+        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
+        alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
+        return alert
+    }
+
+    func showAlert(_ alert: UIAlertController) {
+        guard let _ = self.presentedViewController else {
+            self.present(alert, animated: true, completion: nil)
+            return
+        }
+    }
+
+    func alertForProductRetrievalInfo(_ result: RetrieveResults) -> UIAlertController {
+
+        if let product = result.retrievedProducts.first {
+            let priceString = product.localizedPrice!
+            return alertWithTitle(product.localizedTitle, message: "\(product.localizedDescription) - \(priceString)")
+        } else if let invalidProductId = result.invalidProductIDs.first {
+            return alertWithTitle("Could not retrieve product info", message: "Invalid product identifier: \(invalidProductId)")
+        } else {
+            let errorString = result.error?.localizedDescription ?? "Unknown error. Please contact support"
+            return alertWithTitle("Could not retrieve product info", message: errorString)
+        }
+    }
+
+    func alertForPurchaseResult(_ result: PurchaseResult) -> UIAlertController? {
+        switch result {
+        case .success(let product):
+            print("Purchase Success: \(product.productId)")
+            return alertWithTitle("Thank You", message: "Purchase completed")
+        case .error(let error):
+            print("Purchase Failed: \(error)")
+            switch error.code {
+            case .unknown: return alertWithTitle("Purchase failed", message: "Unknown error. Please contact support")
+            case .clientInvalid: // client is not allowed to issue the request, etc.
+                return alertWithTitle("Purchase failed", message: "Not allowed to make the payment")
+            case .paymentCancelled: // user cancelled the request, etc.
+                return nil
+            case .paymentInvalid: // purchase identifier was invalid, etc.
+                return alertWithTitle("Purchase failed", message: "The purchase identifier was invalid")
+            case .paymentNotAllowed: // this device is not allowed to make the payment
+                return alertWithTitle("Purchase failed", message: "The device is not allowed to make the payment")
+            case .storeProductNotAvailable: // Product is not available in the current storefront
+                return alertWithTitle("Purchase failed", message: "The product is not available in the current storefront")
+            case .cloudServicePermissionDenied: // user has not allowed access to cloud service information
+                return alertWithTitle("Purchase failed", message: "Access to cloud service information is not allowed")
+            case .cloudServiceNetworkConnectionFailed: // the device could not connect to the nework
+                return alertWithTitle("Purchase failed", message: "Could not connect to the network")
+            }
+        }
+    }
+
+    func alertForRestorePurchases(_ results: RestoreResults) -> UIAlertController {
+
+        if results.restoreFailedProducts.count > 0 {
+            print("Restore Failed: \(results.restoreFailedProducts)")
+            return alertWithTitle("Restore failed", message: "Unknown error. Please contact support")
+        } else if results.restoredProducts.count > 0 {
+            print("Restore Success: \(results.restoredProducts)")
+            return alertWithTitle("Purchases Restored", message: "All purchases have been restored")
+        } else {
+            print("Nothing to Restore")
+            return alertWithTitle("Nothing to restore", message: "No previous purchases were found")
+        }
+    }
+
+    func alertForVerifyReceipt(_ result: VerifyReceiptResult) -> UIAlertController {
+
+        switch result {
+        case .success(let receipt):
+            print("Verify receipt Success: \(receipt)")
+            return alertWithTitle("Receipt verified", message: "Receipt verified remotly")
+        case .error(let error):
+            print("Verify receipt Failed: \(error)")
+            switch error {
+            case .noReceiptData :
+                return alertWithTitle("Receipt verification", message: "No receipt data, application will try to get a new one. Try again.")
+            default:
+                return alertWithTitle("Receipt verification", message: "Receipt verification failed")
+            }
+        }
+    }
+
+    func alertForVerifySubscription(_ result: VerifySubscriptionResult) -> UIAlertController {
+
+        switch result {
+        case .purchased(let expiresDate):
+            print("Product is valid until \(expiresDate)")
+            return alertWithTitle("Product is purchased", message: "Product is valid until \(expiresDate)")
+        case .expired(let expiresDate):
+            print("Product is expired since \(expiresDate)")
+            return alertWithTitle("Product expired", message: "Product is expired since \(expiresDate)")
+        case .notPurchased:
+            print("This product has never been purchased")
+            return alertWithTitle("Not purchased", message: "This product has never been purchased")
+        }
+    }
+
+    func alertForVerifyPurchase(_ result: VerifyPurchaseResult) -> UIAlertController {
+
+        switch result {
+        case .purchased:
+            print("Product is purchased")
+            return alertWithTitle("Product is purchased", message: "Product will not expire")
+        case .notPurchased:
+            print("This product has never been purchased")
+            return alertWithTitle("Not purchased", message: "This product has never been purchased")
+        }
+    }
+
+    func alertForRefreshReceipt(_ result: RefreshReceiptResult) -> UIAlertController {
+        switch result {
+        case .success(let receiptData):
+            print("Receipt refresh Success: \(receiptData.base64EncodedString)")
+            return alertWithTitle("Receipt refreshed", message: "Receipt refreshed successfully")
+        case .error(let error):
+            print("Receipt refresh Failed: \(error)")
+            return alertWithTitle("Receipt refresh failed", message: "Receipt refresh failed")
+        }
+    }
+
+}

+ 41 - 8
SwiftyStoreKit.xcodeproj/project.pbxproj

@@ -34,6 +34,8 @@
 		654287F31E79F5A000F61800 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 654287F21E79F5A000F61800 /* ViewController.swift */; };
 		654287F31E79F5A000F61800 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 654287F21E79F5A000F61800 /* ViewController.swift */; };
 		654287F61E79F5A000F61800 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 654287F41E79F5A000F61800 /* Main.storyboard */; };
 		654287F61E79F5A000F61800 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 654287F41E79F5A000F61800 /* Main.storyboard */; };
 		654287F81E79F5A000F61800 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 654287F71E79F5A000F61800 /* Assets.xcassets */; };
 		654287F81E79F5A000F61800 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 654287F71E79F5A000F61800 /* Assets.xcassets */; };
+		654287FD1E79F75000F61800 /* SwiftyStoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 54C0D52C1CF7404500F90BCE /* SwiftyStoreKit.framework */; };
+		654287FE1E79F75000F61800 /* SwiftyStoreKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 54C0D52C1CF7404500F90BCE /* SwiftyStoreKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		658A08371E2EC24E0074A98F /* PaymentQueueController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658A08361E2EC24E0074A98F /* PaymentQueueController.swift */; };
 		658A08371E2EC24E0074A98F /* PaymentQueueController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658A08361E2EC24E0074A98F /* PaymentQueueController.swift */; };
 		658A08381E2EC24E0074A98F /* PaymentQueueController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658A08361E2EC24E0074A98F /* PaymentQueueController.swift */; };
 		658A08381E2EC24E0074A98F /* PaymentQueueController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658A08361E2EC24E0074A98F /* PaymentQueueController.swift */; };
 		658A08391E2EC24E0074A98F /* PaymentQueueController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658A08361E2EC24E0074A98F /* PaymentQueueController.swift */; };
 		658A08391E2EC24E0074A98F /* PaymentQueueController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658A08361E2EC24E0074A98F /* PaymentQueueController.swift */; };
@@ -79,6 +81,13 @@
 /* End PBXBuildFile section */
 /* End PBXBuildFile section */
 
 
 /* Begin PBXContainerItemProxy section */
 /* Begin PBXContainerItemProxy section */
+		654287FF1E79F75000F61800 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 6502F5F61B985833004E342D /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 54C0D52B1CF7404500F90BCE;
+			remoteInfo = SwiftyStoreKit_tvOS;
+		};
 		658A08441E2EC5120074A98F /* PBXContainerItemProxy */ = {
 		658A08441E2EC5120074A98F /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			isa = PBXContainerItemProxy;
 			containerPortal = 6502F5F61B985833004E342D /* Project object */;
 			containerPortal = 6502F5F61B985833004E342D /* Project object */;
@@ -110,6 +119,17 @@
 /* End PBXContainerItemProxy section */
 /* End PBXContainerItemProxy section */
 
 
 /* Begin PBXCopyFilesBuildPhase section */
 /* Begin PBXCopyFilesBuildPhase section */
+		654288011E79F75100F61800 /* Embed Frameworks */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 10;
+			files = (
+				654287FE1E79F75000F61800 /* SwiftyStoreKit.framework in Embed Frameworks */,
+			);
+			name = "Embed Frameworks";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		65F7DF921DCD524300835D30 /* Embed Frameworks */ = {
 		65F7DF921DCD524300835D30 /* Embed Frameworks */ = {
 			isa = PBXCopyFilesBuildPhase;
 			isa = PBXCopyFilesBuildPhase;
 			buildActionMask = 2147483647;
 			buildActionMask = 2147483647;
@@ -146,7 +166,7 @@
 		650307F71E317BCF001332A4 /* CompleteTransactionsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompleteTransactionsController.swift; sourceTree = "<group>"; };
 		650307F71E317BCF001332A4 /* CompleteTransactionsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompleteTransactionsController.swift; sourceTree = "<group>"; };
 		650307FB1E33154F001332A4 /* ProductsInfoController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProductsInfoController.swift; sourceTree = "<group>"; };
 		650307FB1E33154F001332A4 /* ProductsInfoController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProductsInfoController.swift; sourceTree = "<group>"; };
 		653722801DB8282600C8F944 /* SKProduct+LocalizedPrice.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SKProduct+LocalizedPrice.swift"; sourceTree = "<group>"; };
 		653722801DB8282600C8F944 /* SKProduct+LocalizedPrice.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SKProduct+LocalizedPrice.swift"; sourceTree = "<group>"; };
-		654287EE1E79F5A000F61800 /* SwiftyStoreKit-tvOS-Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "SwiftyStoreKit-tvOS-Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+		654287EE1E79F5A000F61800 /* SwiftyStoreKit-tvOSDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "SwiftyStoreKit-tvOSDemo.app"; sourceTree = BUILT_PRODUCTS_DIR; };
 		654287F01E79F5A000F61800 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
 		654287F01E79F5A000F61800 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
 		654287F21E79F5A000F61800 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
 		654287F21E79F5A000F61800 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
 		654287F51E79F5A000F61800 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
 		654287F51E79F5A000F61800 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
@@ -216,6 +236,7 @@
 			isa = PBXFrameworksBuildPhase;
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			buildActionMask = 2147483647;
 			files = (
 			files = (
+				654287FD1E79F75000F61800 /* SwiftyStoreKit.framework in Frameworks */,
 			);
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		};
@@ -266,7 +287,7 @@
 				C4FD3A011C2954C10035CFF3 /* SwiftyStoreKit_macOSDemo.app */,
 				C4FD3A011C2954C10035CFF3 /* SwiftyStoreKit_macOSDemo.app */,
 				54C0D52C1CF7404500F90BCE /* SwiftyStoreKit.framework */,
 				54C0D52C1CF7404500F90BCE /* SwiftyStoreKit.framework */,
 				658A083E1E2EC5120074A98F /* SwiftyStoreKitTests.xctest */,
 				658A083E1E2EC5120074A98F /* SwiftyStoreKitTests.xctest */,
-				654287EE1E79F5A000F61800 /* SwiftyStoreKit-tvOS-Demo.app */,
+				654287EE1E79F5A000F61800 /* SwiftyStoreKit-tvOSDemo.app */,
 			);
 			);
 			name = Products;
 			name = Products;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -445,21 +466,23 @@
 			productReference = 6502F62D1B985C40004E342D /* SwiftyStoreKit.framework */;
 			productReference = 6502F62D1B985C40004E342D /* SwiftyStoreKit.framework */;
 			productType = "com.apple.product-type.framework";
 			productType = "com.apple.product-type.framework";
 		};
 		};
-		654287ED1E79F5A000F61800 /* SwiftyStoreKit-tvOS-Demo */ = {
+		654287ED1E79F5A000F61800 /* SwiftyStoreKit-tvOSDemo */ = {
 			isa = PBXNativeTarget;
 			isa = PBXNativeTarget;
-			buildConfigurationList = 654287FC1E79F5A000F61800 /* Build configuration list for PBXNativeTarget "SwiftyStoreKit-tvOS-Demo" */;
+			buildConfigurationList = 654287FC1E79F5A000F61800 /* Build configuration list for PBXNativeTarget "SwiftyStoreKit-tvOSDemo" */;
 			buildPhases = (
 			buildPhases = (
 				654287EA1E79F5A000F61800 /* Sources */,
 				654287EA1E79F5A000F61800 /* Sources */,
 				654287EB1E79F5A000F61800 /* Frameworks */,
 				654287EB1E79F5A000F61800 /* Frameworks */,
 				654287EC1E79F5A000F61800 /* Resources */,
 				654287EC1E79F5A000F61800 /* Resources */,
+				654288011E79F75100F61800 /* Embed Frameworks */,
 			);
 			);
 			buildRules = (
 			buildRules = (
 			);
 			);
 			dependencies = (
 			dependencies = (
+				654288001E79F75000F61800 /* PBXTargetDependency */,
 			);
 			);
-			name = "SwiftyStoreKit-tvOS-Demo";
+			name = "SwiftyStoreKit-tvOSDemo";
 			productName = "SwiftyStoreKit-tvOS-Demo";
 			productName = "SwiftyStoreKit-tvOS-Demo";
-			productReference = 654287EE1E79F5A000F61800 /* SwiftyStoreKit-tvOS-Demo.app */;
+			productReference = 654287EE1E79F5A000F61800 /* SwiftyStoreKit-tvOSDemo.app */;
 			productType = "com.apple.product-type.application";
 			productType = "com.apple.product-type.application";
 		};
 		};
 		658A083D1E2EC5120074A98F /* SwiftyStoreKitTests */ = {
 		658A083D1E2EC5120074A98F /* SwiftyStoreKitTests */ = {
@@ -531,6 +554,7 @@
 				TargetAttributes = {
 				TargetAttributes = {
 					54C0D52B1CF7404500F90BCE = {
 					54C0D52B1CF7404500F90BCE = {
 						CreatedOnToolsVersion = 7.3.1;
 						CreatedOnToolsVersion = 7.3.1;
+						DevelopmentTeam = M54ZVB688G;
 					};
 					};
 					6502F5FD1B985833004E342D = {
 					6502F5FD1B985833004E342D = {
 						CreatedOnToolsVersion = 7.0;
 						CreatedOnToolsVersion = 7.0;
@@ -579,7 +603,7 @@
 				54C0D52B1CF7404500F90BCE /* SwiftyStoreKit_tvOS */,
 				54C0D52B1CF7404500F90BCE /* SwiftyStoreKit_tvOS */,
 				6502F5FD1B985833004E342D /* SwiftyStoreKit_iOSDemo */,
 				6502F5FD1B985833004E342D /* SwiftyStoreKit_iOSDemo */,
 				C4FD3A001C2954C10035CFF3 /* SwiftyStoreKit_macOSDemo */,
 				C4FD3A001C2954C10035CFF3 /* SwiftyStoreKit_macOSDemo */,
-				654287ED1E79F5A000F61800 /* SwiftyStoreKit-tvOS-Demo */,
+				654287ED1E79F5A000F61800 /* SwiftyStoreKit-tvOSDemo */,
 				658A083D1E2EC5120074A98F /* SwiftyStoreKitTests */,
 				658A083D1E2EC5120074A98F /* SwiftyStoreKitTests */,
 			);
 			);
 		};
 		};
@@ -782,6 +806,11 @@
 /* End PBXSourcesBuildPhase section */
 /* End PBXSourcesBuildPhase section */
 
 
 /* Begin PBXTargetDependency section */
 /* Begin PBXTargetDependency section */
+		654288001E79F75000F61800 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 54C0D52B1CF7404500F90BCE /* SwiftyStoreKit_tvOS */;
+			targetProxy = 654287FF1E79F75000F61800 /* PBXContainerItemProxy */;
+		};
 		658A08451E2EC5120074A98F /* PBXTargetDependency */ = {
 		658A08451E2EC5120074A98F /* PBXTargetDependency */ = {
 			isa = PBXTargetDependency;
 			isa = PBXTargetDependency;
 			target = 6502F62C1B985C40004E342D /* SwiftyStoreKit_iOS */;
 			target = 6502F62C1B985C40004E342D /* SwiftyStoreKit_iOS */;
@@ -847,6 +876,7 @@
 				"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
 				"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
 				CURRENT_PROJECT_VERSION = 1;
 				CURRENT_PROJECT_VERSION = 1;
 				DEFINES_MODULE = YES;
 				DEFINES_MODULE = YES;
+				DEVELOPMENT_TEAM = M54ZVB688G;
 				DYLIB_COMPATIBILITY_VERSION = 1;
 				DYLIB_COMPATIBILITY_VERSION = 1;
 				DYLIB_CURRENT_VERSION = 1;
 				DYLIB_CURRENT_VERSION = 1;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
@@ -871,6 +901,7 @@
 				"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
 				"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
 				CURRENT_PROJECT_VERSION = 1;
 				CURRENT_PROJECT_VERSION = 1;
 				DEFINES_MODULE = YES;
 				DEFINES_MODULE = YES;
+				DEVELOPMENT_TEAM = M54ZVB688G;
 				DYLIB_COMPATIBILITY_VERSION = 1;
 				DYLIB_COMPATIBILITY_VERSION = 1;
 				DYLIB_CURRENT_VERSION = 1;
 				DYLIB_CURRENT_VERSION = 1;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
@@ -1063,6 +1094,7 @@
 		654287FA1E79F5A000F61800 /* Debug */ = {
 		654287FA1E79F5A000F61800 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			isa = XCBuildConfiguration;
 			buildSettings = {
 			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
 				ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
 				ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
 				ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_ANALYZER_NONNULL = YES;
@@ -1083,6 +1115,7 @@
 		654287FB1E79F5A000F61800 /* Release */ = {
 		654287FB1E79F5A000F61800 /* Release */ = {
 			isa = XCBuildConfiguration;
 			isa = XCBuildConfiguration;
 			buildSettings = {
 			buildSettings = {
+				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
 				ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
 				ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
 				ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_ANALYZER_NONNULL = YES;
@@ -1253,7 +1286,7 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 			defaultConfigurationName = Release;
 		};
 		};
-		654287FC1E79F5A000F61800 /* Build configuration list for PBXNativeTarget "SwiftyStoreKit-tvOS-Demo" */ = {
+		654287FC1E79F5A000F61800 /* Build configuration list for PBXNativeTarget "SwiftyStoreKit-tvOSDemo" */ = {
 			isa = XCConfigurationList;
 			isa = XCConfigurationList;
 			buildConfigurations = (
 			buildConfigurations = (
 				654287FA1E79F5A000F61800 /* Debug */,
 				654287FA1E79F5A000F61800 /* Debug */,

+ 8 - 8
SwiftyStoreKit.xcodeproj/xcshareddata/xcschemes/SwiftyStoreKit-tvOS-Demo.xcscheme

@@ -15,8 +15,8 @@
             <BuildableReference
             <BuildableReference
                BuildableIdentifier = "primary"
                BuildableIdentifier = "primary"
                BlueprintIdentifier = "654287ED1E79F5A000F61800"
                BlueprintIdentifier = "654287ED1E79F5A000F61800"
-               BuildableName = "SwiftyStoreKit-tvOS-Demo.app"
-               BlueprintName = "SwiftyStoreKit-tvOS-Demo"
+               BuildableName = "SwiftyStoreKit-tvOSDemo.app"
+               BlueprintName = "SwiftyStoreKit-tvOSDemo"
                ReferencedContainer = "container:SwiftyStoreKit.xcodeproj">
                ReferencedContainer = "container:SwiftyStoreKit.xcodeproj">
             </BuildableReference>
             </BuildableReference>
          </BuildActionEntry>
          </BuildActionEntry>
@@ -33,8 +33,8 @@
          <BuildableReference
          <BuildableReference
             BuildableIdentifier = "primary"
             BuildableIdentifier = "primary"
             BlueprintIdentifier = "654287ED1E79F5A000F61800"
             BlueprintIdentifier = "654287ED1E79F5A000F61800"
-            BuildableName = "SwiftyStoreKit-tvOS-Demo.app"
-            BlueprintName = "SwiftyStoreKit-tvOS-Demo"
+            BuildableName = "SwiftyStoreKit-tvOSDemo.app"
+            BlueprintName = "SwiftyStoreKit-tvOSDemo"
             ReferencedContainer = "container:SwiftyStoreKit.xcodeproj">
             ReferencedContainer = "container:SwiftyStoreKit.xcodeproj">
          </BuildableReference>
          </BuildableReference>
       </MacroExpansion>
       </MacroExpansion>
@@ -56,8 +56,8 @@
          <BuildableReference
          <BuildableReference
             BuildableIdentifier = "primary"
             BuildableIdentifier = "primary"
             BlueprintIdentifier = "654287ED1E79F5A000F61800"
             BlueprintIdentifier = "654287ED1E79F5A000F61800"
-            BuildableName = "SwiftyStoreKit-tvOS-Demo.app"
-            BlueprintName = "SwiftyStoreKit-tvOS-Demo"
+            BuildableName = "SwiftyStoreKit-tvOSDemo.app"
+            BlueprintName = "SwiftyStoreKit-tvOSDemo"
             ReferencedContainer = "container:SwiftyStoreKit.xcodeproj">
             ReferencedContainer = "container:SwiftyStoreKit.xcodeproj">
          </BuildableReference>
          </BuildableReference>
       </BuildableProductRunnable>
       </BuildableProductRunnable>
@@ -75,8 +75,8 @@
          <BuildableReference
          <BuildableReference
             BuildableIdentifier = "primary"
             BuildableIdentifier = "primary"
             BlueprintIdentifier = "654287ED1E79F5A000F61800"
             BlueprintIdentifier = "654287ED1E79F5A000F61800"
-            BuildableName = "SwiftyStoreKit-tvOS-Demo.app"
-            BlueprintName = "SwiftyStoreKit-tvOS-Demo"
+            BuildableName = "SwiftyStoreKit-tvOSDemo.app"
+            BlueprintName = "SwiftyStoreKit-tvOSDemo"
             ReferencedContainer = "container:SwiftyStoreKit.xcodeproj">
             ReferencedContainer = "container:SwiftyStoreKit.xcodeproj">
          </BuildableReference>
          </BuildableReference>
       </BuildableProductRunnable>
       </BuildableProductRunnable>