增加一些支付相关代码
@ -33,6 +33,8 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/sysctl.h>
|
||||
#import <TikTokOpenSDK/TikTokOpenSDKApplicationDelegate.h>
|
||||
#import "StoreObserver.h"
|
||||
@import StoreKit;
|
||||
@import FBSDKCoreKit;
|
||||
@import FirebaseCore;
|
||||
|
||||
@ -403,6 +405,7 @@ extern "C" void UnityCleanupTrampoline()
|
||||
[[FBSDKSettings sharedSettings] setIsAdvertiserTrackingEnabled:TRUE];
|
||||
}
|
||||
[FIRApp configure];
|
||||
[[SKPaymentQueue defaultQueue] addTransactionObserver:[StoreObserver sharedInstance]];
|
||||
return YES;
|
||||
}
|
||||
|
||||
@ -565,6 +568,7 @@ extern "C" void UnityCleanupTrampoline()
|
||||
UnityCleanup();
|
||||
UnityCleanupTrampoline();
|
||||
}
|
||||
[[SKPaymentQueue defaultQueue] removeTransactionObserver: [StoreObserver sharedInstance]];
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication*)application handleEventsForBackgroundURLSession:(nonnull NSString *)identifier completionHandler:(nonnull void (^)())completionHandler
|
||||
|
16
Classes_cocos/UIViewController+Purchase.h
Normal file
@ -0,0 +1,16 @@
|
||||
//
|
||||
// UIViewController+Logger.h
|
||||
// Unity-iPhone
|
||||
//
|
||||
// Created by Hl Zhang on 2023/3/21.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
|
||||
@interface UIViewController (Purchase)
|
||||
- (void)initPurchaseEnv;
|
||||
@end
|
||||
|
116
Classes_cocos/UIViewController+Purchase.mm
Normal file
@ -0,0 +1,116 @@
|
||||
//
|
||||
// UIViewController+Logger.cpp
|
||||
// Unity-iPhone
|
||||
//
|
||||
// Created by Hl Zhang on 2023/3/21.
|
||||
//
|
||||
|
||||
#import "UIViewController+Purchase.h"
|
||||
#import "Utilities.h"
|
||||
#import "StoreManager.h"
|
||||
#import "StoreObserver.h"
|
||||
#import "AppConfiguration.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
static Utilities *utility;
|
||||
|
||||
@implementation UIViewController (Purchase)
|
||||
|
||||
-(void)initPurchaseEnv {
|
||||
utility = [[Utilities alloc] init];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(handleProductRequestNotification:)
|
||||
name:PCSProductRequestNotification
|
||||
object:[StoreManager sharedInstance]];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(handlePurchaseNotification:)
|
||||
name:PCSPurchaseNotification
|
||||
object:[StoreObserver sharedInstance]];
|
||||
}
|
||||
/// Retrieves product information from the App Store.
|
||||
-(void)fetchProductInformation {
|
||||
// First, let's check whether the user is allowed to make purchases. Proceed if they are allowed. Display an alert, otherwise.
|
||||
if ([StoreObserver sharedInstance].isAuthorizedForPayments) {
|
||||
NSArray *identifiers = utility.identifiers;
|
||||
|
||||
if (identifiers != nil) {
|
||||
if (identifiers.count > 0) {
|
||||
Section *section = [[Section alloc] initWithName:PCSProductsInvalidIdentifiers elements:identifiers];
|
||||
|
||||
// Refresh the UI with identifiers to be queried.
|
||||
// [self switchToViewController:ParentViewControllerSegmentProducts];
|
||||
// [self.products reloadWithData:[NSMutableArray arrayWithObject:section]];
|
||||
|
||||
// Fetch the product information.
|
||||
[[StoreManager sharedInstance] startProductRequestWithIdentifiers:identifiers];
|
||||
} else {
|
||||
// Warn the user that the resource file does not contain anything.
|
||||
[self alertWithTitle:PCSMessagesStatus message:[NSString stringWithFormat:@"%@.%@ %@", PCSProductIdsPlistName, PCSProductIdsPlistFileExtension, PCSMessagesEmptyResource]];
|
||||
}
|
||||
} else {
|
||||
// Warn the user that the resource file could not be found.
|
||||
[self alertWithTitle:PCSMessagesStatus message:[NSString stringWithFormat:@"%@ %@.%@.", PCSMessagesResourceNotFound, PCSProductIdsPlistName, PCSProductIdsPlistFileExtension]];
|
||||
}
|
||||
} else {
|
||||
// Warn the user that they are not allowed to make purchases.
|
||||
[self alertWithTitle:PCSMessagesStatus message:[NSString stringWithFormat:@"%@", PCSMessagesCannotMakePayments]];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Creates and displays an alert.
|
||||
-(void)alertWithTitle:(NSString *)title message:(NSString *)message {
|
||||
UIAlertController *alertController = [utility alertWithTitle:title message:message];
|
||||
[self.navigationController presentViewController:alertController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark - Handle PCSProductRequest Notification
|
||||
|
||||
/// Updates the UI according to the product request notification result.
|
||||
-(void)handleProductRequestNotification:(NSNotification *)notification {
|
||||
StoreManager *productRequestNotification = (StoreManager*)notification.object;
|
||||
PCSProductRequestStatus status = (PCSProductRequestStatus)productRequestNotification.status;
|
||||
|
||||
if (status == PCSStoreResponse) {
|
||||
// Switch to the Products view controller.
|
||||
// [self switchToViewController:ParentViewControllerSegmentProducts];
|
||||
// [self.products reloadWithData:productRequestNotification.storeResponse];
|
||||
// self.segmentedControl.selectedSegmentIndex = ParentViewControllerSegmentProducts;
|
||||
} else if (status == PCSRequestFailed) {
|
||||
[self alertWithTitle:PCSMessagesProductRequestStatus message:productRequestNotification.message];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Handle PCSPurchase Notification
|
||||
|
||||
/// Updates the UI according to the purchase request notification result.
|
||||
-(void)handlePurchaseNotification:(NSNotification *)notification {
|
||||
StoreObserver *purchasesNotification = (StoreObserver *)notification.object;
|
||||
PCSPurchaseStatus status = (PCSPurchaseStatus)purchasesNotification.status;
|
||||
|
||||
switch (status) {
|
||||
case PCSNoRestorablePurchases:
|
||||
case PCSPurchaseFailed:
|
||||
case PCSRestoreFailed: [self alertWithTitle:PCSMessagesPurchaseStatus message:purchasesNotification.message];
|
||||
break;
|
||||
// Switch to the Purchases view when receiving a successful restore notification.
|
||||
case PCSRestoreSucceeded: [self handleRestoredSucceededTransaction];
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Handle Restored Transactions
|
||||
|
||||
/// Handles succesful restored transactions. Switches to the Purchases view.
|
||||
-(void)handleRestoredSucceededTransaction {
|
||||
utility.restoreWasCalled = YES;
|
||||
|
||||
// Refresh the Purchases view with the restored purchases.
|
||||
// [self switchToViewController:ParentViewControllerSegmentPurchases];
|
||||
// [self.purchases reloadWithData:self.utility.dataSourceForPurchasesUI];
|
||||
// self.segmentedControl.selectedSegmentIndex = ParentViewControllerSegmentPurchases;
|
||||
}
|
||||
@end
|
||||
|
1
Podfile
@ -18,5 +18,4 @@ target 'Unity-iPhone' do
|
||||
pod 'FBSDKLoginKit'
|
||||
pod 'FBSDKShareKit'
|
||||
pod 'FirebaseAnalytics'
|
||||
pod 'DYFStoreKit'
|
||||
end
|
||||
|
@ -316,6 +316,12 @@
|
||||
D5575A262A5FAC16009369FC /* UnityEngine.UnityWebRequestModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D55759872A5FAC16009369FC /* UnityEngine.UnityWebRequestModule.cpp */; };
|
||||
D5575A272A5FAC16009369FC /* mscorlib4.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D55759882A5FAC16009369FC /* mscorlib4.cpp */; };
|
||||
D5575A282A5FAC16009369FC /* Generics21.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D55759892A5FAC16009369FC /* Generics21.cpp */; };
|
||||
D59AB8472A68FC2200433200 /* StoreObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = D59AB8462A68FC2200433200 /* StoreObserver.m */; };
|
||||
D59AB84C2A68FC8B00433200 /* AppConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = D59AB84B2A68FC8B00433200 /* AppConfiguration.m */; };
|
||||
D59AB84F2A68FE8E00433200 /* StoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D59AB84D2A68FE8E00433200 /* StoreManager.m */; };
|
||||
D59AB8522A68FEB700433200 /* Section.m in Sources */ = {isa = PBXBuildFile; fileRef = D59AB8502A68FEB700433200 /* Section.m */; };
|
||||
D59AB8552A690BC300433200 /* UIViewController+Purchase.mm in Sources */ = {isa = PBXBuildFile; fileRef = D59AB8542A690BC300433200 /* UIViewController+Purchase.mm */; };
|
||||
D59AB8582A690C9900433200 /* Utilities.m in Sources */ = {isa = PBXBuildFile; fileRef = D59AB8572A690C9900433200 /* Utilities.m */; };
|
||||
D5BF397629C9B79400EC6351 /* UIViewController+Logger.mm in Sources */ = {isa = PBXBuildFile; fileRef = D5BF397529C9B79400EC6351 /* UIViewController+Logger.mm */; };
|
||||
D5BF397829C9B8C000EC6351 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = D5BF397729C9B8C000EC6351 /* GoogleService-Info.plist */; };
|
||||
D5C03B702A49A808002E758D /* Data in Resources */ = {isa = PBXBuildFile; fileRef = D5C03B6F2A49A808002E758D /* Data */; };
|
||||
@ -1351,6 +1357,18 @@
|
||||
D55759872A5FAC16009369FC /* UnityEngine.UnityWebRequestModule.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UnityEngine.UnityWebRequestModule.cpp; sourceTree = "<group>"; };
|
||||
D55759882A5FAC16009369FC /* mscorlib4.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mscorlib4.cpp; sourceTree = "<group>"; };
|
||||
D55759892A5FAC16009369FC /* Generics21.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Generics21.cpp; sourceTree = "<group>"; };
|
||||
D59AB8462A68FC2200433200 /* StoreObserver.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StoreObserver.m; sourceTree = "<group>"; };
|
||||
D59AB8492A68FC5200433200 /* StoreObserver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StoreObserver.h; sourceTree = "<group>"; };
|
||||
D59AB84A2A68FC7200433200 /* AppConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppConfiguration.h; sourceTree = "<group>"; };
|
||||
D59AB84B2A68FC8B00433200 /* AppConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppConfiguration.m; sourceTree = "<group>"; };
|
||||
D59AB84D2A68FE8E00433200 /* StoreManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StoreManager.m; sourceTree = "<group>"; };
|
||||
D59AB84E2A68FE8E00433200 /* StoreManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StoreManager.h; sourceTree = "<group>"; };
|
||||
D59AB8502A68FEB700433200 /* Section.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Section.m; sourceTree = "<group>"; };
|
||||
D59AB8512A68FEB700433200 /* Section.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Section.h; sourceTree = "<group>"; };
|
||||
D59AB8532A690BB200433200 /* UIViewController+Purchase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Purchase.h"; sourceTree = "<group>"; };
|
||||
D59AB8542A690BC300433200 /* UIViewController+Purchase.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = "UIViewController+Purchase.mm"; sourceTree = "<group>"; };
|
||||
D59AB8562A690C9900433200 /* Utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Utilities.h; sourceTree = "<group>"; };
|
||||
D59AB8572A690C9900433200 /* Utilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Utilities.m; sourceTree = "<group>"; };
|
||||
D5BF397429C9B77E00EC6351 /* UIViewController+Logger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Logger.h"; sourceTree = "<group>"; };
|
||||
D5BF397529C9B79400EC6351 /* UIViewController+Logger.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = "UIViewController+Logger.mm"; sourceTree = "<group>"; };
|
||||
D5BF397729C9B8C000EC6351 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
|
||||
@ -1448,9 +1466,10 @@
|
||||
path = iOS;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
29B97314FDCFA39411CA2CEA = {
|
||||
29B97314FDCFA39411CA2CEA /* CustomTemplate */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D59AB8482A68FC2900433200 /* purchase */,
|
||||
D5C03B6F2A49A808002E758D /* Data */,
|
||||
D526FA3C299498E3002A2290 /* cocos2d_libs.xcodeproj */,
|
||||
D507D6132994DEE900CF3953 /* js */,
|
||||
@ -1604,6 +1623,8 @@
|
||||
D5DE834E29C831FE0029E408 /* UIViewController+Share.mm */,
|
||||
D5BF397429C9B77E00EC6351 /* UIViewController+Logger.h */,
|
||||
D5BF397529C9B79400EC6351 /* UIViewController+Logger.mm */,
|
||||
D59AB8532A690BB200433200 /* UIViewController+Purchase.h */,
|
||||
D59AB8542A690BC300433200 /* UIViewController+Purchase.mm */,
|
||||
D507C8BF2994A0A800CF3953 /* WalletEvent.cpp */,
|
||||
D507C8C62994A0A800CF3953 /* WalletEvent.h */,
|
||||
);
|
||||
@ -3476,6 +3497,23 @@
|
||||
path = "../../../game/wallet-test/target/ios/Classes/Native";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D59AB8482A68FC2900433200 /* purchase */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D59AB8492A68FC5200433200 /* StoreObserver.h */,
|
||||
D59AB8462A68FC2200433200 /* StoreObserver.m */,
|
||||
D59AB84A2A68FC7200433200 /* AppConfiguration.h */,
|
||||
D59AB84B2A68FC8B00433200 /* AppConfiguration.m */,
|
||||
D59AB84E2A68FE8E00433200 /* StoreManager.h */,
|
||||
D59AB84D2A68FE8E00433200 /* StoreManager.m */,
|
||||
D59AB8512A68FEB700433200 /* Section.h */,
|
||||
D59AB8502A68FEB700433200 /* Section.m */,
|
||||
D59AB8562A690C9900433200 /* Utilities.h */,
|
||||
D59AB8572A690C9900433200 /* Utilities.m */,
|
||||
);
|
||||
path = purchase;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@ -3551,7 +3589,7 @@
|
||||
English,
|
||||
en,
|
||||
);
|
||||
mainGroup = 29B97314FDCFA39411CA2CEA;
|
||||
mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */;
|
||||
productRefGroup = 19C28FACFE9D520D11CA2CBB /* Products */;
|
||||
projectDirPath = "";
|
||||
projectReferences = (
|
||||
@ -3641,8 +3679,6 @@
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Unity-iPhone/Pods-Unity-iPhone-frameworks.sh",
|
||||
"${BUILT_PRODUCTS_DIR}/AppAuth/AppAuth.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/DYFRuntimeProvider/DYFRuntimeProvider.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/DYFStoreKit/DYFStoreKit.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/FirebaseCoreInternal/FirebaseCoreInternal.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/FirebaseInstallations/FirebaseInstallations.framework",
|
||||
@ -3661,8 +3697,6 @@
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AppAuth.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DYFRuntimeProvider.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DYFStoreKit.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreInternal.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseInstallations.framework",
|
||||
@ -3695,6 +3729,7 @@
|
||||
D55759F02A5FAC16009369FC /* Il2CppReversePInvokeWrapperTable.cpp in Sources */,
|
||||
D507D5DF2994C62A00CF3953 /* UnityAdsInitializationListener.mm in Sources */,
|
||||
D507C9202994A0A800CF3953 /* ToastManager.m in Sources */,
|
||||
D59AB8522A68FEB700433200 /* Section.m in Sources */,
|
||||
D55759E42A5FAC16009369FC /* Il2CppGenericMethodDefinitions.c in Sources */,
|
||||
D507D2462994C61700CF3953 /* UnityReplayKit.mm in Sources */,
|
||||
D507D2562994C61700CF3953 /* OrientationSupport.mm in Sources */,
|
||||
@ -3740,6 +3775,7 @@
|
||||
D507C9272994A0A800CF3953 /* LBXScanVideoZoomView.m in Sources */,
|
||||
D507D5EB2994C62A00CF3953 /* UnityAdsShowListener.mm in Sources */,
|
||||
D507C9332994A0A800CF3953 /* LBXPermissionSetting.m in Sources */,
|
||||
D59AB8472A68FC2200433200 /* StoreObserver.m in Sources */,
|
||||
D55759BD2A5FAC16009369FC /* UnityEngine.UIElementsModule_CodeGen.c in Sources */,
|
||||
D507D24E2994C61700CF3953 /* DisplayManager.mm in Sources */,
|
||||
D507C9362994A0A800CF3953 /* LBXPermission.m in Sources */,
|
||||
@ -3779,6 +3815,7 @@
|
||||
D5575A192A5FAC16009369FC /* UnityEngine.InputLegacyModule_CodeGen.c in Sources */,
|
||||
D507C93C2994A0A800CF3953 /* QRPhotoAlbumButton.m in Sources */,
|
||||
D55759902A5FAC16009369FC /* Generics5.cpp in Sources */,
|
||||
D59AB84F2A68FE8E00433200 /* StoreManager.m in Sources */,
|
||||
D55759D12A5FAC16009369FC /* UnityEngine.InputLegacyModule.cpp in Sources */,
|
||||
D507C9412994A0A800CF3953 /* UIView+Toast.m in Sources */,
|
||||
D55759E12A5FAC16009369FC /* UnityEngine.TextCoreTextEngineModule.cpp in Sources */,
|
||||
@ -3840,6 +3877,7 @@
|
||||
D55759952A5FAC16009369FC /* UnityEngine.Physics2DModule_CodeGen.c in Sources */,
|
||||
D55759C62A5FAC16009369FC /* UnityEngine.TextRenderingModule_CodeGen.c in Sources */,
|
||||
D507D5DB2994C62900CF3953 /* Il2CppOptions.cpp in Sources */,
|
||||
D59AB8582A690C9900433200 /* Utilities.m in Sources */,
|
||||
D5575A062A5FAC16009369FC /* mscorlib3.cpp in Sources */,
|
||||
D507D60E2994D1E300CF3953 /* main.mm in Sources */,
|
||||
D55759D32A5FAC16009369FC /* UnityEngine.SpriteShapeModule_CodeGen.c in Sources */,
|
||||
@ -3884,6 +3922,7 @@
|
||||
D55759922A5FAC16009369FC /* GenericMethods6.cpp in Sources */,
|
||||
D5575A0A2A5FAC16009369FC /* UnityEngine.UnityAnalyticsModule_CodeGen.c in Sources */,
|
||||
D507D3092994C61700CF3953 /* RenderPluginDelegate.mm in Sources */,
|
||||
D59AB8552A690BC300433200 /* UIViewController+Purchase.mm in Sources */,
|
||||
D5DE834F29C831FE0029E408 /* UIViewController+Share.mm in Sources */,
|
||||
D507C92C2994A0A800CF3953 /* CreateBarCodeViewController.m in Sources */,
|
||||
D557598B2A5FAC16009369FC /* Generics10.cpp in Sources */,
|
||||
@ -3912,6 +3951,7 @@
|
||||
D5575A082A5FAC16009369FC /* UnityEngine.TilemapModule.cpp in Sources */,
|
||||
D55759FB2A5FAC16009369FC /* UnityEngine.GridModule_CodeGen.c in Sources */,
|
||||
D507C92E2994A0A800CF3953 /* LBXScanNetAnimation.m in Sources */,
|
||||
D59AB84C2A68FC8B00433200 /* AppConfiguration.m in Sources */,
|
||||
D55759D42A5FAC16009369FC /* UnityEngine.UIElementsModule11.cpp in Sources */,
|
||||
D507C9282994A0A800CF3953 /* LBXScanLineAnimation.m in Sources */,
|
||||
D507C9212994A0A800CF3953 /* AppDelegate.mm in Sources */,
|
||||
|
@ -30,8 +30,8 @@
|
||||
filePath = "Classes/UnityAppController.mm"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "357"
|
||||
endingLineNumber = "357"
|
||||
startingLineNumber = "359"
|
||||
endingLineNumber = "359"
|
||||
landmarkName = "-application:didFinishLaunchingWithOptions:"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
@ -190,8 +190,8 @@
|
||||
filePath = "Classes/UnityAppController.mm"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "405"
|
||||
endingLineNumber = "405"
|
||||
startingLineNumber = "407"
|
||||
endingLineNumber = "407"
|
||||
landmarkName = "-application:didFinishLaunchingWithOptions:"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
|
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/100.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/102 1.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/1024.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/108 1.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/114.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/120.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/128.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/144.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/152.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/16.png
Normal file
After Width: | Height: | Size: 652 B |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/167.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/172.png
Normal file
After Width: | Height: | Size: 7.9 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/180.png
Normal file
After Width: | Height: | Size: 8.4 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/196.png
Normal file
After Width: | Height: | Size: 9.2 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/20.png
Normal file
After Width: | Height: | Size: 820 B |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/216.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/234 1.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/256.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/258 1.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/29.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/32.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/40.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/48.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/50.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/512.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/55.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/57.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/58.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/60.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/64.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/66.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/72.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/76.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/80.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/87.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/88.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
Unity-iPhone/Images.xcassets/AppIcon.appiconset/92.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
@ -1,84 +1,346 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "40.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "60.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "29.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "1x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "58.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "87.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "80.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "120.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-iPhone-120.png",
|
||||
"filename" : "57.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "1x",
|
||||
"size" : "57x57"
|
||||
},
|
||||
{
|
||||
"filename" : "114.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "57x57"
|
||||
},
|
||||
{
|
||||
"filename" : "120.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-iPhone-180.png",
|
||||
"filename" : "180.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "20.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "40.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "29.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "58.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "40.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "80.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-iPad-76.png",
|
||||
"filename" : "50.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "50x50"
|
||||
},
|
||||
{
|
||||
"filename" : "100.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "50x50"
|
||||
},
|
||||
{
|
||||
"filename" : "72.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "72x72"
|
||||
},
|
||||
{
|
||||
"filename" : "144.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "72x72"
|
||||
},
|
||||
{
|
||||
"filename" : "76.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-iPad-152.png",
|
||||
"filename" : "152.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "Icon-iPad-167.png",
|
||||
"filename" : "167.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "83.5x83.5"
|
||||
},
|
||||
{
|
||||
"filename" : "1024.png",
|
||||
"idiom" : "ios-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"filename" : "16.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"filename" : "32.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"filename" : "32.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"filename" : "64.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"filename" : "128.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"filename" : "256.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"filename" : "256.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"filename" : "512.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"filename" : "512.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "512x512"
|
||||
},
|
||||
{
|
||||
"filename" : "1024.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "512x512"
|
||||
},
|
||||
{
|
||||
"filename" : "48.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "notificationCenter",
|
||||
"scale" : "2x",
|
||||
"size" : "24x24",
|
||||
"subtype" : "38mm"
|
||||
},
|
||||
{
|
||||
"filename" : "55.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "notificationCenter",
|
||||
"scale" : "2x",
|
||||
"size" : "27.5x27.5",
|
||||
"subtype" : "42mm"
|
||||
},
|
||||
{
|
||||
"filename" : "58.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "companionSettings",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "87.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "companionSettings",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "66.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "notificationCenter",
|
||||
"scale" : "2x",
|
||||
"size" : "33x33",
|
||||
"subtype" : "45mm"
|
||||
},
|
||||
{
|
||||
"filename" : "80.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40",
|
||||
"subtype" : "38mm"
|
||||
},
|
||||
{
|
||||
"filename" : "88.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
"scale" : "2x",
|
||||
"size" : "44x44",
|
||||
"subtype" : "40mm"
|
||||
},
|
||||
{
|
||||
"filename" : "92.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
"scale" : "2x",
|
||||
"size" : "46x46",
|
||||
"subtype" : "41mm"
|
||||
},
|
||||
{
|
||||
"filename" : "100.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
"scale" : "2x",
|
||||
"size" : "50x50",
|
||||
"subtype" : "44mm"
|
||||
},
|
||||
{
|
||||
"filename" : "102 1.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
"scale" : "2x",
|
||||
"size" : "51x51",
|
||||
"subtype" : "45mm"
|
||||
},
|
||||
{
|
||||
"filename" : "108 1.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "appLauncher",
|
||||
"scale" : "2x",
|
||||
"size" : "54x54",
|
||||
"subtype" : "49mm"
|
||||
},
|
||||
{
|
||||
"filename" : "172.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "quickLook",
|
||||
"scale" : "2x",
|
||||
"size" : "86x86",
|
||||
"subtype" : "38mm"
|
||||
},
|
||||
{
|
||||
"filename" : "196.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "quickLook",
|
||||
"scale" : "2x",
|
||||
"size" : "98x98",
|
||||
"subtype" : "42mm"
|
||||
},
|
||||
{
|
||||
"filename" : "216.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "quickLook",
|
||||
"scale" : "2x",
|
||||
"size" : "108x108",
|
||||
"subtype" : "44mm"
|
||||
},
|
||||
{
|
||||
"filename" : "234 1.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "quickLook",
|
||||
"scale" : "2x",
|
||||
"size" : "117x117",
|
||||
"subtype" : "45mm"
|
||||
},
|
||||
{
|
||||
"filename" : "258 1.png",
|
||||
"idiom" : "watch",
|
||||
"role" : "quickLook",
|
||||
"scale" : "2x",
|
||||
"size" : "129x129",
|
||||
"subtype" : "49mm"
|
||||
},
|
||||
{
|
||||
"filename" : "1024.png",
|
||||
"idiom" : "watch-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.9 KiB |
102
purchase/AppConfiguration.h
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
See LICENSE folder for this sample’s licensing information.
|
||||
|
||||
Abstract:
|
||||
Handles the application's configuration information.
|
||||
*/
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#pragma mark - Hosted Download
|
||||
|
||||
extern NSString *const PCSHostedYes;
|
||||
extern NSString *const PCSHostedNot;
|
||||
|
||||
#pragma mark - Messages
|
||||
|
||||
extern NSString *const PCSMessagesCannotMakePayments;
|
||||
extern NSString *const PCSMessagesDeferred;
|
||||
extern NSString *const PCSMessagesDeliverContent;
|
||||
extern NSString *const PCSMessagesEmptyResource;
|
||||
extern NSString *const PCSMessagesError;
|
||||
extern NSString *const PCSMessagesFailed;
|
||||
extern NSString *const PCSMessagesNoRestorablePurchases;
|
||||
extern NSString *const PCSMessagesOk;
|
||||
extern NSString *const PCSMessagesPurchaseOf;
|
||||
extern NSString *const PCSMessagesPurchaseStatus;
|
||||
extern NSString *const PCSMessagesPreviouslyBought;
|
||||
extern NSString *const PCSMessagesProductRequestStatus;
|
||||
extern NSString *const PCSMessagesRemove;
|
||||
extern NSString *const PCSMessagesResourceNotFound;
|
||||
extern NSString *const PCSMessagesRestorable;
|
||||
extern NSString *const PCSMessagesRestoreContent;
|
||||
extern NSString *const PCSMessagesStatus;
|
||||
extern NSString *const PCSMessagesNoPurchasesAvailable;
|
||||
extern NSString *const PCSMessagesUseStoreRestore;
|
||||
|
||||
#pragma mark - Notifications
|
||||
|
||||
extern NSString *const PCSProductRequestNotification;
|
||||
extern NSString *const PCSPurchaseNotification;
|
||||
extern NSString *const PCSRestoredWasCalledNotification;
|
||||
|
||||
typedef NS_ENUM(NSInteger, PCSProductRequestStatus) {
|
||||
PCSIdentifiersNotFound, // indicates that there are some invalid product identifiers.
|
||||
PCSProductsFound,// Indicates that there are some valid products.
|
||||
PCSRequestFailed, // Indicates that the product request has failed.
|
||||
PCSStoreResponse, // Indicates that there are some valid products, invalid product identifiers, or both available.
|
||||
PCSProductRequestStatusNone // The PCSProductRequest notification has not occured yet. This is the default value.
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, PCSPurchaseStatus) {
|
||||
PCSPurchaseFailed, // Indicates that the purchase was unsuccessful.
|
||||
PCSPPurchaseSucceeded, // Indicates that the purchase was successful.
|
||||
PCSRestoreFailed, // Indicates that restoring purchases was unsuccessful.
|
||||
PCSRestoreSucceeded, // Indicates that restoring purchases was successful.
|
||||
PCSNoRestorablePurchases, // Indicates that there are no restorable purchases.
|
||||
PCSPurchaseStatusNone // The PCSPurchase notification has not occured yet. This is the default value.
|
||||
};
|
||||
|
||||
#pragma mark - Payment Transaction Details Labels
|
||||
extern NSString *const PCSPaymentTransactionDetailsLabelsIdentifier;
|
||||
extern NSString *const PCSPaymentTransactionDetailsLabelsContentLength;
|
||||
extern NSString *const PCSPaymentTransactionDetailsLabelsContentVersion;
|
||||
extern NSString *const PCSPaymentTransactionDetailsLabelsTransactionDate;
|
||||
extern NSString *const PCSPaymentTransactionDetailsLabelsTransactionIdentifier;
|
||||
|
||||
#pragma mark - Payment Transaction Details Table
|
||||
|
||||
extern NSString *const PCSPaymentTransactionDetailsOriginalTransaction;
|
||||
extern NSString *const PCSPaymentTransactionDetailsProductIdentifier;
|
||||
extern NSString *const PCSPaymentTransactionDetailsTransactionDate;
|
||||
extern NSString *const PCSPaymentTransactionDetailsTransactionIdentifier;
|
||||
|
||||
#pragma mark - Products Table Header Section
|
||||
|
||||
extern NSString *const PCSProductsAvailableProducts;
|
||||
extern NSString *const PCSProductsInvalidIdentifiers;
|
||||
|
||||
#pragma mark - Purchases Table Header Section
|
||||
|
||||
extern NSString *const PCSPurchasesDownload;
|
||||
extern NSString *const PCSPurchasesPurchased;
|
||||
extern NSString *const PCSPurchasesRestored;
|
||||
|
||||
#pragma mark - Resource File
|
||||
|
||||
extern NSString *const PCSProductIdsPlistName;
|
||||
extern NSString *const PCSProductIdsPlistFileExtension;
|
||||
|
||||
#pragma mark - View Controller Identifiers
|
||||
|
||||
extern NSString *const PCSViewControllerIdentifiersAvailableProducts;
|
||||
extern NSString *const PCSViewControllerIdentifiersInvalidProductIdentifiers;
|
||||
extern NSString *const PCSViewControllerIdentifiersMessages;
|
||||
extern NSString *const PCSViewControllerIdentifiersProducts;
|
||||
extern NSString *const PCSViewControllerIdentifiersPurchases;
|
||||
|
||||
#pragma mark - View Controller Names
|
||||
|
||||
extern NSString *const PCSViewControllerNamesMessages;
|
||||
extern NSString *const PCSViewControllerNamesProducts;
|
||||
extern NSString *const PCSViewControllerNamesPurchases;
|
101
purchase/AppConfiguration.m
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
See LICENSE folder for this sample’s licensing information.
|
||||
|
||||
Abstract:
|
||||
Handles the application's configuration information.
|
||||
*/
|
||||
|
||||
#import "AppConfiguration.h"
|
||||
|
||||
#pragma mark - Hosted Download
|
||||
|
||||
NSString *const PCSHostedYes = @"Yes";
|
||||
NSString *const PCSHostedNot = @"No";
|
||||
|
||||
#pragma mark - Messages
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
NSString *const PCSMessagesCannotMakePayments = @"You are not authorized to make payments. In-App Purchases may be restricted on your device.";
|
||||
#else
|
||||
NSString *const PCSMessagesCannotMakePayments = @"In-App Purchases are not allowed.";
|
||||
#endif
|
||||
NSString *const PCSMessagesDeferred = @"Allow the user to continue using your app.";
|
||||
NSString *const PCSMessagesDeliverContent = @"Deliver content for";
|
||||
NSString *const PCSMessagesEmptyResource = @"is empty. Update it with your product identifiers to retrieve product information.";
|
||||
NSString *const PCSMessagesError = @"Error:";
|
||||
NSString *const PCSMessagesFailed = @"failed.";
|
||||
NSString *const PCSMessagesNoRestorablePurchases = @"There are no restorable purchases.";
|
||||
NSString *const PCSMessagesOk = @"OK";
|
||||
NSString *const PCSMessagesPurchaseOf = @"Purchase of";
|
||||
NSString *const PCSMessagesPurchaseStatus = @"Purchase Status";
|
||||
NSString *const PCSMessagesPreviouslyBought = @"Only previously bought non-consumable products and auto-renewable subscriptions can be restored.";
|
||||
NSString *const PCSMessagesProductRequestStatus = @"Product Request Status";
|
||||
NSString *const PCSMessagesRemove = @"was removed from the payment queue.";
|
||||
NSString *const PCSMessagesResourceNotFound = @"Could not find resource file:";
|
||||
NSString *const PCSMessagesRestorable = @"All restorable transactions have been processed by the payment queue.";
|
||||
NSString *const PCSMessagesRestoreContent = @"Restore content for";
|
||||
NSString *const PCSMessagesStatus = @"Status";
|
||||
NSString *const PCSMessagesNoPurchasesAvailable = @"No purchases available.";
|
||||
NSString *const PCSMessagesUseStoreRestore = @"Use Store > Restore to restore your non-consumable products and auto-renewable subscriptions.";
|
||||
|
||||
#pragma mark - Notifications
|
||||
|
||||
NSString *const PCSProductRequestNotification = @"ProductRequestNotification";
|
||||
NSString *const PCSPurchaseNotification = @"PurchaseNotification";
|
||||
NSString *const PCSRestoredWasCalledNotification = @"restoredWasCalledNotification";
|
||||
|
||||
#pragma mark - Payment Transaction Details Labels
|
||||
|
||||
NSString *const PCSPaymentTransactionDetailsLabelsIdentifier = @"Identifier";
|
||||
NSString *const PCSPaymentTransactionDetailsLabelsContentLength = @"Length";
|
||||
NSString *const PCSPaymentTransactionDetailsLabelsContentVersion = @"Version";
|
||||
NSString *const PCSPaymentTransactionDetailsLabelsTransactionDate = @"Transaction Date";
|
||||
NSString *const PCSPaymentTransactionDetailsLabelsTransactionIdentifier = @"Transaction ID";
|
||||
|
||||
#pragma mark - Payment Transaction Details Table
|
||||
|
||||
NSString *const PCSPaymentTransactionDetailsOriginalTransaction = @"ORIGINAL TRANSACTION";
|
||||
NSString *const PCSPaymentTransactionDetailsProductIdentifier = @"PRODUCT IDENTIFIER";
|
||||
NSString *const PCSPaymentTransactionDetailsTransactionIdentifier = @"TRANSACTION ID";
|
||||
NSString *const PCSPaymentTransactionDetailsTransactionDate = @"TRANSACTION DATE";
|
||||
|
||||
#pragma mark - Products Table Header Section
|
||||
|
||||
#if TARGET_OS_OSX
|
||||
NSString *const PCSProductsAvailableProducts = @"Available Products";
|
||||
NSString *const PCSProductsInvalidIdentifiers = @"Invalid Product Identifiers";
|
||||
#else
|
||||
NSString *const PCSProductsAvailableProducts = @"AVAILABLE PRODUCTS";
|
||||
NSString *const PCSProductsInvalidIdentifiers = @"INVALID PRODUCT IDENTIFIERS";
|
||||
#endif
|
||||
|
||||
#pragma mark - Purchases Table Header Section
|
||||
|
||||
#if TARGET_OS_OSX
|
||||
NSString *const PCSPurchasesPurchased = @"Purchased";
|
||||
NSString *const PCSPurchasesRestored = @"Restored";
|
||||
#else
|
||||
NSString *const PCSPurchasesDownload = @"DOWNLOAD";
|
||||
NSString *const PCSPurchasesPurchased = @"PURCHASED";
|
||||
NSString *const PCSPurchasesRestored = @"RESTORED";
|
||||
#endif
|
||||
|
||||
#pragma mark - Resource File
|
||||
|
||||
NSString *const PCSProductIdsPlistName = @"ProductIds";
|
||||
NSString *const PCSProductIdsPlistFileExtension = @"plist";
|
||||
|
||||
#pragma mark - View Controller Identifiers
|
||||
|
||||
NSString *const PCSViewControllerIdentifiersAvailableProducts = @"availableProducts";
|
||||
NSString *const PCSViewControllerIdentifiersInvalidProductIdentifiers = @"invalidProductIdentifiers";
|
||||
NSString *const PCSViewControllerIdentifiersMessages = @"messages";
|
||||
NSString *const PCSViewControllerIdentifiersProducts = @"products";
|
||||
NSString *const PCSViewControllerIdentifiersPurchases = @"purchases";
|
||||
|
||||
#pragma mark - View Controller Names
|
||||
|
||||
NSString *const PCSViewControllerNamesMessages = @"Messages";
|
||||
NSString *const PCSViewControllerNamesProducts = @"Products";
|
||||
NSString *const PCSViewControllerNamesPurchases = @"Purchases";
|
||||
|
19
purchase/Section.h
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
See LICENSE folder for this sample’s licensing information.
|
||||
|
||||
Abstract:
|
||||
Model class used to represent a list of products/purchases.
|
||||
*/
|
||||
|
||||
@import Foundation;
|
||||
|
||||
@interface Section : NSObject
|
||||
/// Products/Purchases are organized by category.
|
||||
@property (nonatomic, copy) NSString *name;
|
||||
|
||||
/// List of products/purchases.
|
||||
@property (strong) NSArray *elements;
|
||||
|
||||
/// Create a Section object.
|
||||
-(instancetype)initWithName:(NSString *)name elements:(NSArray *)elements NS_DESIGNATED_INITIALIZER;
|
||||
@end
|
25
purchase/Section.m
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
See LICENSE folder for this sample’s licensing information.
|
||||
|
||||
Abstract:
|
||||
Model class used to represent a list of products/purchases.
|
||||
*/
|
||||
|
||||
#import "Section.h"
|
||||
|
||||
@implementation Section
|
||||
|
||||
-(instancetype)init {
|
||||
return [self initWithName:nil elements:@[]];
|
||||
}
|
||||
|
||||
-(instancetype)initWithName:(NSString *)name elements:(NSArray *)elements {
|
||||
self = [super init];
|
||||
|
||||
if(self != nil) {
|
||||
_name = [name copy];
|
||||
_elements = elements;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@end
|
33
purchase/StoreManager.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
See LICENSE folder for this sample’s licensing information.
|
||||
|
||||
Abstract:
|
||||
Retrieves product information from the App Store using SKRequestDelegate, SKProductsRequestDelegate, SKProductsResponse, and
|
||||
SKProductsRequest. Notifies its observer with a list of products available for sale along with a list of invalid product identifiers. Logs an error
|
||||
message if the product request failed.
|
||||
*/
|
||||
|
||||
@import StoreKit;
|
||||
#import "AppConfiguration.h"
|
||||
|
||||
@interface StoreManager : NSObject
|
||||
+ (StoreManager *)sharedInstance;
|
||||
|
||||
/// Indicates the cause of the product request failure.
|
||||
@property (nonatomic, copy) NSString *message;
|
||||
|
||||
/// Provides the status of the product request.
|
||||
@property (nonatomic) PCSProductRequestStatus status;
|
||||
|
||||
/// Keeps track of all valid products (these products are available for sale in the App Store) and of all invalid product identifiers.
|
||||
@property (strong) NSMutableArray *storeResponse;
|
||||
|
||||
/// Starts the product request with the specified identifiers.
|
||||
-(void)startProductRequestWithIdentifiers:(NSArray *)identifiers;
|
||||
|
||||
/// - returns: Existing product's title matching the specified product identifier.
|
||||
-(NSString *)titleMatchingIdentifier:(NSString *)identifier;
|
||||
|
||||
/// - returns: Existing product's title associated with the specified payment transaction.
|
||||
-(NSString *)titleMatchingPaymentTransaction:(SKPaymentTransaction *)transaction;
|
||||
@end
|
132
purchase/StoreManager.m
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
See LICENSE folder for this sample’s licensing information.
|
||||
|
||||
Abstract:
|
||||
Retrieves product information from the App Store using SKRequestDelegate, SKProductsRequestDelegate, SKProductsResponse, and
|
||||
SKProductsRequest. Notifies its observer with a list of products available for sale along with a list of invalid product identifiers. Logs an error
|
||||
message if the product request failed.
|
||||
*/
|
||||
|
||||
#import "Section.h"
|
||||
#import "StoreManager.h"
|
||||
|
||||
@interface StoreManager()<SKRequestDelegate, SKProductsRequestDelegate>
|
||||
/// Keeps track of all valid products. These products are available for sale in the App Store.
|
||||
@property (strong) NSMutableArray *availableProducts;
|
||||
|
||||
/// Keeps track of all invalid product identifiers.
|
||||
@property (strong) NSMutableArray *invalidProductIdentifiers;
|
||||
|
||||
/// Keeps a strong reference to the product request.
|
||||
@property (strong) SKProductsRequest *productRequest;
|
||||
@end
|
||||
|
||||
@implementation StoreManager
|
||||
|
||||
+ (StoreManager *)sharedInstance {
|
||||
static dispatch_once_t onceToken;
|
||||
static StoreManager * storeManagerSharedInstance;
|
||||
|
||||
dispatch_once(&onceToken, ^{
|
||||
storeManagerSharedInstance = [[StoreManager alloc] init];
|
||||
});
|
||||
return storeManagerSharedInstance;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
|
||||
if (self != nil) {
|
||||
_availableProducts = [[NSMutableArray alloc] initWithCapacity:0];
|
||||
_invalidProductIdentifiers = [[NSMutableArray alloc] initWithCapacity:0];
|
||||
_storeResponse = [[NSMutableArray alloc] initWithCapacity:0];
|
||||
_status = PCSProductRequestStatusNone;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Request Information
|
||||
|
||||
/// Starts the product request with the specified identifiers.
|
||||
-(void)startProductRequestWithIdentifiers:(NSArray *)identifiers {
|
||||
[self fetchProductsMatchingIdentifiers:identifiers];
|
||||
}
|
||||
|
||||
/// Fetches information about your products from the App Store.
|
||||
-(void)fetchProductsMatchingIdentifiers:(NSArray *)identifiers {
|
||||
// Create a set for the product identifiers.
|
||||
NSSet *productIdentifiers = [NSSet setWithArray:identifiers];
|
||||
|
||||
// Initialize the product request with the above identifiers.
|
||||
self.productRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
|
||||
self.productRequest.delegate = self;
|
||||
|
||||
// Send the request to the App Store.
|
||||
[self.productRequest start];
|
||||
}
|
||||
|
||||
#pragma mark - SKProductsRequestDelegate
|
||||
|
||||
/// Used to get the App Store's response to your request and notify your observer.
|
||||
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
|
||||
if (self.storeResponse.count > 0) {
|
||||
[self.storeResponse removeAllObjects];
|
||||
}
|
||||
|
||||
// products contains products whose identifiers have been recognized by the App Store. As such, they can be purchased.
|
||||
if ((response.products).count > 0) {
|
||||
self.availableProducts = [NSMutableArray arrayWithArray:response.products];
|
||||
Section *section = [[Section alloc] initWithName:PCSProductsAvailableProducts elements:response.products];
|
||||
[self.storeResponse addObject:section];
|
||||
}
|
||||
|
||||
// invalidProductIdentifiers contains all product identifiers not recognized by the App Store.
|
||||
if ((response.invalidProductIdentifiers).count > 0) {
|
||||
self.invalidProductIdentifiers = [NSMutableArray arrayWithArray:response.invalidProductIdentifiers];
|
||||
Section *section = [[Section alloc] initWithName:PCSProductsInvalidIdentifiers elements:response.invalidProductIdentifiers];
|
||||
[self.storeResponse addObject:section];
|
||||
}
|
||||
|
||||
if (self.storeResponse.count > 0) {
|
||||
self.status = PCSStoreResponse;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:PCSProductRequestNotification object:self];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - SKRequestDelegate
|
||||
|
||||
/// Called when the product request failed.
|
||||
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
|
||||
self.status = PCSRequestFailed;
|
||||
self.message = error.localizedDescription;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:PCSProductRequestNotification object:self];
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - Helper Methods
|
||||
|
||||
/// - returns: Existing product's title matching the specified product identifier.
|
||||
-(NSString *)titleMatchingIdentifier:(NSString *)identifier {
|
||||
NSString *title = nil;
|
||||
|
||||
// Search availableProducts for a product whose productIdentifier property matches identifier. Return its localized title when found.
|
||||
for (SKProduct *product in self.availableProducts) {
|
||||
if ([product.productIdentifier isEqualToString:identifier]) {
|
||||
title = product.localizedTitle;
|
||||
}
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
/// - returns: Existing product's title associated with the specified payment transaction.
|
||||
-(NSString *)titleMatchingPaymentTransaction:(SKPaymentTransaction *) transaction {
|
||||
NSString *title = [self titleMatchingIdentifier:transaction.payment.productIdentifier];
|
||||
return (title != nil) ? title : transaction.payment.productIdentifier;
|
||||
}
|
||||
|
||||
@end
|
39
purchase/StoreObserver.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
See LICENSE folder for this sample’s licensing information.
|
||||
|
||||
Abstract:
|
||||
Implements the SKPaymentTransactionObserver protocol. Handles purchasing and restoring products using paymentQueue:updatedTransactions: .
|
||||
*/
|
||||
|
||||
@import StoreKit;
|
||||
#import "AppConfiguration.h"
|
||||
|
||||
@interface StoreObserver : NSObject <SKPaymentTransactionObserver>
|
||||
+ (StoreObserver *)sharedInstance;
|
||||
|
||||
/**
|
||||
Indicates whether the user is allowed to make payments.
|
||||
- returns: true if the user is allowed to make payments and false, otherwise. Tell StoreManager to query the App Store when the user is allowed to make payments and there are product identifiers to be
|
||||
queried.
|
||||
*/
|
||||
@property (NS_NONATOMIC_IOSONLY, readonly) BOOL isAuthorizedForPayments;
|
||||
|
||||
/// Indicates the cause of the purchase failure.
|
||||
@property (nonatomic, copy) NSString *message;
|
||||
|
||||
/// Keeps track of all purchases.
|
||||
@property (strong) NSMutableArray *productsPurchased;
|
||||
|
||||
/// Keeps track of all restored purchases.
|
||||
@property (strong) NSMutableArray *productsRestored;
|
||||
|
||||
/// Indicates the purchase status.
|
||||
@property (nonatomic) PCSPurchaseStatus status;
|
||||
|
||||
/// Implements the purchase of a product.
|
||||
-(void)buy:(SKProduct *)product;
|
||||
|
||||
/// Implements the restoration of previously completed purchases.
|
||||
-(void)restore;
|
||||
@end
|
||||
|
169
purchase/StoreObserver.m
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
See LICENSE folder for this sample’s licensing information.
|
||||
|
||||
Abstract:
|
||||
Implements the SKPaymentTransactionObserver protocol. Handles purchasing and restoring products using paymentQueue:updatedTransactions: .
|
||||
*/
|
||||
|
||||
#import "StoreObserver.h"
|
||||
|
||||
@interface StoreObserver ()
|
||||
/// Indicates whether there are restorable purchases.
|
||||
@property (nonatomic) BOOL hasRestorablePurchases;
|
||||
@end
|
||||
|
||||
@implementation StoreObserver
|
||||
+ (StoreObserver *)sharedInstance {
|
||||
static dispatch_once_t onceToken;
|
||||
static StoreObserver * storeObserverSharedInstance;
|
||||
|
||||
dispatch_once(&onceToken, ^{
|
||||
storeObserverSharedInstance = [[StoreObserver alloc] init];
|
||||
});
|
||||
return storeObserverSharedInstance;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
|
||||
if (self != nil) {
|
||||
_hasRestorablePurchases = NO;
|
||||
_productsPurchased = [[NSMutableArray alloc] initWithCapacity:0];
|
||||
_productsRestored = [[NSMutableArray alloc] initWithCapacity:0];
|
||||
_status = PCSPurchaseStatusNone;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
/**
|
||||
Indicates whether the user is allowed to make payments.
|
||||
- returns: true if the user is allowed to make payments and false, otherwise. Tell StoreManager to query the App Store when the user is allowed to make payments and there are product identifiers to be
|
||||
queried.
|
||||
*/
|
||||
-(BOOL)isAuthorizedForPayments {
|
||||
return [SKPaymentQueue canMakePayments];
|
||||
}
|
||||
|
||||
#pragma mark - Submit Payment Request
|
||||
|
||||
/// Creates and adds a payment request to the payment queue.
|
||||
-(void)buy:(SKProduct *)product {
|
||||
SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];
|
||||
[[SKPaymentQueue defaultQueue] addPayment:payment];
|
||||
}
|
||||
|
||||
#pragma mark - Restore All Restorable Purchases
|
||||
|
||||
/// Restores all previously completed purchases.
|
||||
-(void)restore {
|
||||
if (self.productsRestored.count > 0) {
|
||||
[self.productsRestored removeAllObjects];
|
||||
}
|
||||
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
|
||||
}
|
||||
|
||||
#pragma mark - SKPaymentTransactionObserver Methods
|
||||
|
||||
/// Called when there are transactions in the payment queue.
|
||||
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
|
||||
for(SKPaymentTransaction *transaction in transactions) {
|
||||
switch (transaction.transactionState) {
|
||||
case SKPaymentTransactionStatePurchasing: break;
|
||||
// Do not block your UI. Allow the user to continue using your app.
|
||||
case SKPaymentTransactionStateDeferred: NSLog(@"%@", PCSMessagesDeferred);
|
||||
break;
|
||||
// The purchase was successful.
|
||||
case SKPaymentTransactionStatePurchased: [self handlePurchasedTransaction:transaction];
|
||||
break;
|
||||
// The transaction failed.
|
||||
case SKPaymentTransactionStateFailed: [self handleFailedTransaction:transaction];
|
||||
break;
|
||||
// There are restored products.
|
||||
case SKPaymentTransactionStateRestored: [self handleRestoredTransaction:transaction];
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Logs all transactions that have been removed from the payment queue.
|
||||
- (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions {
|
||||
for(SKPaymentTransaction *transaction in transactions) {
|
||||
NSLog(@"%@ %@", transaction.payment.productIdentifier, PCSMessagesRemove);
|
||||
}
|
||||
}
|
||||
|
||||
/// Called when an error occur while restoring purchases. Notifies the user about the error.
|
||||
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error {
|
||||
if (error.code != SKErrorPaymentCancelled) {
|
||||
self.status = PCSRestoreFailed;
|
||||
self.message = error.localizedDescription;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:PCSPurchaseNotification object:self];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Called when all restorable transactions have been processed by the payment queue.
|
||||
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
|
||||
NSLog(@"%@", PCSMessagesRestorable);
|
||||
|
||||
if (!self.hasRestorablePurchases) {
|
||||
self.status = PCSNoRestorablePurchases;
|
||||
self.message = [NSString stringWithFormat:@"%@\n%@", PCSMessagesNoRestorablePurchases, PCSMessagesPreviouslyBought];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:PCSPurchaseNotification object:self];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Handle Payment Transactions
|
||||
|
||||
/// Handles successful purchase transactions.
|
||||
-(void)handlePurchasedTransaction:(SKPaymentTransaction*)transaction {
|
||||
[self.productsPurchased addObject:transaction];
|
||||
NSLog(@"%@ %@.", PCSMessagesDeliverContent, transaction.payment.productIdentifier);
|
||||
|
||||
// Finish the successful transaction.
|
||||
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
|
||||
}
|
||||
|
||||
/// Handles failed purchase transactions.
|
||||
-(void)handleFailedTransaction:(SKPaymentTransaction*)transaction {
|
||||
self.status = PCSPurchaseFailed;
|
||||
self.message = [NSString stringWithFormat:@"%@ %@ %@",PCSMessagesPurchaseOf, transaction.payment.productIdentifier, PCSMessagesFailed];
|
||||
|
||||
if (transaction.error) {
|
||||
[self.message stringByAppendingString:[NSString stringWithFormat:@"\n%@ %@", PCSMessagesError, transaction.error.localizedDescription]];
|
||||
NSLog(@"%@ %@", PCSMessagesError, transaction.error.localizedDescription);
|
||||
}
|
||||
|
||||
// Do not send any notifications when the user cancels the purchase.
|
||||
if (transaction.error.code != SKErrorPaymentCancelled) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:PCSPurchaseNotification object:self];
|
||||
});
|
||||
}
|
||||
|
||||
// Finish the failed transaction.
|
||||
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
|
||||
}
|
||||
|
||||
/// Handles restored purchase transactions.
|
||||
-(void)handleRestoredTransaction:(SKPaymentTransaction*)transaction {
|
||||
self.status = PCSRestoreSucceeded;
|
||||
self.hasRestorablePurchases = true;
|
||||
[self.productsRestored addObject:transaction];
|
||||
NSLog(@"%@ %@.", PCSMessagesRestoreContent, transaction.payment.productIdentifier);
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:PCSPurchaseNotification object:self];
|
||||
});
|
||||
|
||||
// Finish the restored transaction.
|
||||
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
|
||||
}
|
||||
|
||||
@end
|
||||
|
34
purchase/Utilities.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
See LICENSE folder for this sample’s licensing information.
|
||||
|
||||
Abstract:
|
||||
Fetches identifiers from a resource file, provides the Purchases view's data source, and creates an alert.
|
||||
*/
|
||||
|
||||
@import StoreKit;
|
||||
#import "Section.h"
|
||||
|
||||
#if TARGET_OS_OSX
|
||||
@import Cocoa;
|
||||
#else
|
||||
@import UIKit;
|
||||
#endif
|
||||
|
||||
@interface Utilities : NSObject
|
||||
/// - returns: An array that will be used to populate the Purchases view.
|
||||
@property (NS_NONATOMIC_IOSONLY, readonly, assign) NSArray *dataSourceForPurchasesUI;
|
||||
|
||||
/// - returns: An array with the product identifiers to be queried.
|
||||
@property (NS_NONATOMIC_IOSONLY, readonly, copy) NSArray *identifiers;
|
||||
|
||||
/// Indicates whether the user has initiated a restore.
|
||||
@property (nonatomic) BOOL restoreWasCalled;
|
||||
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV
|
||||
/// - returns: An alert with a given title and message.
|
||||
-(UIAlertController *)alertWithTitle:(NSString *)title message:(NSString *)message;
|
||||
#endif
|
||||
|
||||
/// - returns: A Section object matching the specified name in the data array.
|
||||
-(Section *)parse:(NSArray *)data forName:(NSString *)name;
|
||||
@end
|
74
purchase/Utilities.m
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
See LICENSE folder for this sample’s licensing information.
|
||||
|
||||
Abstract:
|
||||
Fetches identifiers from a resource file, provides the Purchases view's data source, and creates an alert.
|
||||
*/
|
||||
|
||||
@import StoreKit;
|
||||
#import "Utilities.h"
|
||||
#import "StoreObserver.h"
|
||||
#import "AppConfiguration.h"
|
||||
|
||||
@implementation Utilities
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_restoreWasCalled = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/// - returns: An array with the product identifiers to be queried.
|
||||
-(NSArray *)identifiers {
|
||||
NSURL *url = [[NSBundle mainBundle] URLForResource:PCSProductIdsPlistName withExtension:PCSProductIdsPlistFileExtension];
|
||||
return [NSArray arrayWithContentsOfURL:url];
|
||||
}
|
||||
|
||||
/// - returns: An array that will be used to populate the Purchases view.
|
||||
-(NSMutableArray *)dataSourceForPurchasesUI {
|
||||
NSMutableArray *dataSource = [[NSMutableArray alloc] initWithCapacity:0];
|
||||
NSArray *purchased = [[StoreObserver sharedInstance].productsPurchased copy];
|
||||
NSArray *restored = [[StoreObserver sharedInstance].productsRestored copy];
|
||||
|
||||
if (self.restoreWasCalled && (restored.count > 0) && (purchased.count > 0)) {
|
||||
dataSource = [[NSMutableArray alloc] initWithObjects:[[Section alloc] initWithName:PCSPurchasesPurchased elements:purchased],
|
||||
[[Section alloc] initWithName:PCSPurchasesRestored elements:restored], nil];
|
||||
|
||||
} else if (self.restoreWasCalled && (restored.count > 0)) {
|
||||
dataSource = [[NSMutableArray alloc] initWithObjects:[[Section alloc] initWithName:PCSPurchasesRestored elements:restored], nil];
|
||||
} else if (purchased.count > 0) {
|
||||
dataSource = [[NSMutableArray alloc] initWithObjects:[[Section alloc] initWithName:PCSPurchasesPurchased elements:purchased], nil];
|
||||
}
|
||||
|
||||
/*
|
||||
Only want to display restored products when the "Restore" button(iOS), "Store > Restore" (macOS), or "Restore all restorable purchases" (tvOS)
|
||||
was tapped and there are restored products.
|
||||
*/
|
||||
self.restoreWasCalled = NO;
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
#pragma mark - Create Alert
|
||||
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV
|
||||
/// - returns: An alert with a given title and message.
|
||||
-(UIAlertController *)alertWithTitle:(NSString *)title message:(NSString *)message {
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:PCSMessagesOk style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {}];
|
||||
[alert addAction:defaultAction];
|
||||
return alert;
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma mark - Parse Section of Data
|
||||
|
||||
/// - returns: A Section object matching the specified name in the data array.
|
||||
-(Section *)parse:(NSArray *)data forName:(NSString *)name {
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name == %@", name];
|
||||
return (Section *)([data filteredArrayUsingPredicate:predicate].firstObject);
|
||||
}
|
||||
|
||||
@end
|