Commit c6a0ceef by Brent Gulanowski

Merge pull request #203 from Shopify/task/advanced-sample

Update Advanced Sample to use new API
parents 6f4e73db 402cbea2
......@@ -24,22 +24,22 @@
// THE SOFTWARE.
//
#import <Buy/Buy.h>
@import Buy;
#import "CheckoutViewController.h"
#import "GetCompletionStatusOperation.h"
#import "SummaryItemsTableViewCell.h"
#import "UIButton+PaymentButton.h"
NSString * const CheckoutCallbackNotification = @"CheckoutCallbackNotification";
NSString * const MerchantId = @"";
@interface CheckoutViewController () <GetCompletionStatusOperationDelegate, SFSafariViewControllerDelegate, PKPaymentAuthorizationViewControllerDelegate>
@interface CheckoutViewController () <SFSafariViewControllerDelegate, PKPaymentAuthorizationViewControllerDelegate>
@property (nonatomic, strong) BUYCheckout *checkout;
@property (nonatomic, strong) BUYClient *client;
@property (nonatomic, strong) BUYShop *shop;
@property (nonatomic, strong) NSArray *summaryItems;
@property (nonatomic, strong) BUYApplePayHelpers *applePayHelper;
@property (nonatomic, strong) BUYApplePayAuthorizationDelegate *applePayHelper;
@end
......@@ -139,22 +139,15 @@ NSString * const MerchantId = @"";
- (void)addCreditCardToCheckout:(void (^)(BOOL success, id<BUYPaymentToken> token))callback
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[self.client storeCreditCard:[self creditCard] checkout:self.checkout completion:^(BUYCheckout *checkout, id<BUYPaymentToken> token, NSError *error) {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
if (error == nil && checkout) {
[self.client storeCreditCard:[self creditCard] checkout:self.checkout completion:^(id<BUYPaymentToken> token, NSError *error) {
if (error == nil && token) {
NSLog(@"Successfully added credit card to checkout");
self.checkout = checkout;
}
else {
NSLog(@"Error applying credit card: %@", error);
}
callback(error == nil && checkout, token);
callback(error == nil && token, token);
}];
}
......@@ -203,32 +196,23 @@ NSString * const MerchantId = @"";
}];
}
#pragma mark Native Checkout
#pragma mark - Native Checkout
- (void)checkoutWithCreditCard
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
__weak CheckoutViewController *welf = self;
// First, the credit card must be stored on the checkout
[self addCreditCardToCheckout:^(BOOL success, id<BUYPaymentToken> token) {
if (success) {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
// Upon successfully adding the credit card to the checkout, complete checkout must be called immediately
[welf.client completeCheckout:welf.checkout paymentToken:token completion:^(BUYCheckout *checkout, NSError *error) {
[welf.client completeCheckoutWithToken:welf.checkout.token paymentToken:token completion:^(BUYCheckout *checkout, NSError *error) {
if (error == nil && checkout) {
NSLog(@"Successfully completed checkout");
welf.checkout = checkout;
GetCompletionStatusOperation *completionOperation = [[GetCompletionStatusOperation alloc] initWithClient:welf.client withCheckout:welf.checkout];
completionOperation.delegate = welf;
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[[NSOperationQueue mainQueue] addOperation:completionOperation];
}
else {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
......@@ -239,22 +223,6 @@ NSString * const MerchantId = @"";
}];
}
- (void)operation:(GetCompletionStatusOperation *)operation didReceiveCompletionStatus:(BUYStatus)completionStatus
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
NSLog(@"Successfully got completion status: %lu", (unsigned long)completionStatus);
[self getCompletedCheckout:NULL];
}
- (void)operation:(GetCompletionStatusOperation *)operation failedToReceiveCompletionStatus:(NSError *)error
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
NSLog(@"Error getting completion status: %@", error);
}
#pragma mark - Apple Pay Checkout
- (void)checkoutWithApplePay
......@@ -263,7 +231,7 @@ NSString * const MerchantId = @"";
PKPaymentAuthorizationViewController *paymentController = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:request];
self.applePayHelper = [[BUYApplePayHelpers alloc] initWithClient:self.client checkout:self.checkout shop:self.shop];
self.applePayHelper = [[BUYApplePayAuthorizationDelegate alloc] initWithClient:self.client checkout:self.checkout shopName:self.shop.name];
paymentController.delegate = self;
/**
......@@ -404,7 +372,7 @@ NSString * const MerchantId = @"";
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[self.client getCheckout:self.checkout completion:^(BUYCheckout *checkout, NSError *error) {
[self.client getCheckoutWithToken:self.checkout.token completion:^(BUYCheckout *checkout, NSError *error) {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
......
......@@ -54,7 +54,7 @@
appId:APP_ID];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[self.client getCollections:^(NSArray *collections, NSError *error) {
[self.client getCollectionsPage:1 completion:^(NSArray *collections, NSUInteger page, BOOL reachedEnd, NSError *error) {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
if (error == nil && collections) {
......
//
// GetCompletionStatusOperation.h
// Mobile Buy SDK Advanced Sample
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import <Foundation/Foundation.h>
@import Buy;
@class GetCompletionStatusOperation;
@protocol GetCompletionStatusOperationDelegate <NSObject>
- (void)operation:(GetCompletionStatusOperation *)operation didReceiveCompletionStatus:(BUYStatus)completionStatus;
- (void)operation:(GetCompletionStatusOperation *)operation failedToReceiveCompletionStatus:(NSError *)error;
@end
@interface GetCompletionStatusOperation : NSOperation
- (instancetype)initWithClient:(BUYClient *)client withCheckout:(BUYCheckout *)checkout;
@property (nonatomic, weak) id <GetCompletionStatusOperationDelegate> delegate;
@property (nonatomic, readonly) BUYStatus completionStatus;
@end
//
// GetCompletionStatusOperation.m
// Mobile Buy SDK Advanced Sample
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import "GetCompletionStatusOperation.h"
@interface GetCompletionStatusOperation ()
@property (nonatomic, strong) BUYCheckout *checkout;
@property (nonatomic, strong) BUYClient *client;
@property (nonatomic, assign) BOOL done;
@property (nonatomic, strong) NSURLSessionDataTask *task;
@property (nonatomic) BUYStatus completionStatus;
@end
@implementation GetCompletionStatusOperation
- (instancetype)initWithClient:(BUYClient *)client withCheckout:(BUYCheckout *)checkout;
{
NSParameterAssert(client);
NSParameterAssert(checkout);
self = [super init];
if (self) {
self.checkout = checkout;
self.client = client;
}
return self;
}
- (BOOL)isFinished
{
return [super isFinished] && self.done;
}
- (void)cancel
{
[self.task cancel];
[super cancel];
}
- (void)main
{
[self pollForCompletionStatus];
}
- (void)pollForCompletionStatus
{
__block BUYStatus completionStatus = BUYStatusUnknown;
if (self.isCancelled) {
return;
}
self.task = [self.client getCompletionStatusOfCheckout:self.checkout completion:^(BUYStatus status, NSError *error) {
completionStatus = status;
if (status == BUYStatusProcessing) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self pollForCompletionStatus];
});
}
else {
self.completionStatus = status;
[self willChangeValueForKey:@"isFinished"];
self.done = YES;
[self didChangeValueForKey:@"isFinished"];
if (error) {
[self.delegate operation:self failedToReceiveCompletionStatus:error];
}
else {
[self.delegate operation:self didReceiveCompletionStatus:status];
}
}
}];
}
@end
//
// GetShippingRatesOperations.h
// Mobile Buy SDK Advanced Sample
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import <Foundation/Foundation.h>
@class BUYCheckout;
@class BUYClient;
@class GetShippingRatesOperation;
@protocol GetShippingRatesOperationDelegate <NSObject>
- (void)operation:(GetShippingRatesOperation *)operation didReceiveShippingRates:(NSArray *)shippingRates;
- (void)operation:(GetShippingRatesOperation *)operation failedToReceiveShippingRates:(NSError *)error;
@end
@interface GetShippingRatesOperation : NSOperation
- (instancetype)initWithClient:(BUYClient *)client withCheckout:(BUYCheckout *)checkout;
@property (nonatomic, weak) id <GetShippingRatesOperationDelegate> delegate;
@property (nonatomic, strong, readonly) BUYCheckout *checkout;
@property (nonatomic, strong, readonly) NSArray *shippingRates;
@end
//
// GetShippingRatesOperations.m
// Mobile Buy SDK Advanced Sample
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import "GetShippingRatesOperation.h"
@import Buy;
@interface GetShippingRatesOperation ()
@property (nonatomic, strong) BUYCheckout *checkout;
@property (nonatomic, strong) BUYClient *client;
@property (nonatomic, assign) BOOL done;
@property (nonatomic, strong) NSURLSessionDataTask *task;
@property (nonatomic, strong) NSArray *shippingRates;
@end
@implementation GetShippingRatesOperation
- (instancetype)initWithClient:(BUYClient *)client withCheckout:(BUYCheckout *)checkout;
{
NSParameterAssert(client);
NSParameterAssert(checkout);
self = [super init];
if (self) {
self.checkout = checkout;
self.client = client;
}
return self;
}
- (BOOL)isFinished
{
return [super isFinished] && self.done;
}
- (void)cancel
{
[self.task cancel];
[super cancel];
}
- (void)main
{
// We're now fetching the rates from Shopify. This will will calculate shipping rates very similarly to how our web checkout.
// We then turn our BUYShippingRate objects into PKShippingMethods for Apple to present to the user.
if ([self.checkout requiresShipping] == NO) {
[self willChangeValueForKey:@"isFinished"];
self.done = YES;
[self didChangeValueForKey:@"isFinished"];
[self.delegate operation:self didReceiveShippingRates:nil];
}
else {
[self pollForShippingRates];
}
}
- (void)pollForShippingRates
{
__block BUYStatus shippingStatus = BUYStatusUnknown;
self.task = [self.client getShippingRatesForCheckout:self.checkout completion:^(NSArray *shippingRates, BUYStatus status, NSError *error) {
shippingStatus = status;
if (error) {
[self willChangeValueForKey:@"isFinished"];
self.done = YES;
[self didChangeValueForKey:@"isFinished"];
[self.delegate operation:self failedToReceiveShippingRates:error];
}
else if (shippingStatus == BUYStatusComplete) {
self.shippingRates = shippingRates;
[self willChangeValueForKey:@"isFinished"];
self.done = YES;
[self didChangeValueForKey:@"isFinished"];
[self.delegate operation:self didReceiveShippingRates:self.shippingRates];
}
else if (shippingStatus == BUYStatusProcessing) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self pollForShippingRates];
});
}
}];
}
@end
//
// GetShopOperation.h
// Mobile Buy SDK Advanced Sample
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
@import Foundation;
@import Buy;
@class GetShopOperation;
@protocol GetShopOperationDelegate <NSObject>
- (void)operation:(GetShopOperation *)operation didReceiveShop:(BUYShop*)shop;
- (void)operation:(GetShopOperation *)operation failedToReceiveShop:(NSError *)error;
@end
@interface GetShopOperation : NSOperation
- (instancetype)initWithClient:(BUYClient *)client;
@property (nonatomic, weak) id <GetShopOperationDelegate> delegate;
@end
//
// GetShopOperation.h
// Mobile Buy SDK Advanced Sample
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import "GetShopOperation.h"
@interface GetShopOperation ()
@property (nonatomic, strong) BUYClient *client;
@property (nonatomic, assign) BOOL done;
@property (nonatomic, strong) NSURLSessionDataTask *task;
@end
@implementation GetShopOperation
- (instancetype)initWithClient:(BUYClient *)client
{
NSParameterAssert(client);
self = [super init];
if (self) {
self.client = client;
}
return self;
}
- (BOOL)isFinished
{
return [super isFinished] && self.done;
}
- (void)cancel
{
[self.task cancel];
[super cancel];
}
- (void)main
{
if (self.isCancelled) {
return;
}
self.task = [self.client getShop:^(BUYShop *shop, NSError *error) {
[self willChangeValueForKey:@"isFinished"];
self.done = YES;
[self didChangeValueForKey:@"isFinished"];
if (error) {
[self.delegate operation:self failedToReceiveShop:error];
}
else {
[self.delegate operation:self didReceiveShop:shop ];
}
}];
}
@end
......@@ -237,7 +237,7 @@ typedef NS_ENUM(NSInteger, UITableViewDiscountGiftCardSection) {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[self.client applyGiftCardWithCode:[alertController.textFields[0] text] toCheckout:self.checkout completion:^(BUYCheckout *checkout, NSError *error) {
[self.client applyGiftCardCode:[alertController.textFields[0] text] toCheckout:self.checkout completion:^(BUYCheckout *checkout, NSError *error) {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
......
......@@ -56,15 +56,6 @@
- (instancetype)initWithClient:(BUYClient *)client cart:(BUYCart *)cart;
/**
* Loads the product details
*
* @param productId the product ID for the item to display
* @param completion a block to be called on completion of the loading of the product details. Will be called on the main thread.
* Upon success, the view controller should be presented modally
*/
- (void)loadProduct:(NSString *)productId completion:(void (^)(BOOL success, NSError *error))completion;
/**
* Alternative method when setting the product (and optionally, shop) directly on the view controller
*
* @param product the product to display
......
......@@ -64,10 +64,8 @@ CGFloat const BUYMaxProductViewHeight = 640.0;
@property (nonatomic, assign) BOOL shouldEnableVariantSelection;
@property (nonatomic, assign) BOOL shouldShowDescription;
@property (nonatomic, strong) BUYProduct *product;
@property (nonatomic, assign) BOOL isLoading;
@property (nonatomic, strong) NSNumberFormatter *currencyFormatter;
@property (nonatomic, weak) BUYCart *cart;
@property (nonatomic, assign) BOOL isLoadingShop;
// views
@property (nonatomic, strong) ProductView *productView;
......@@ -154,37 +152,6 @@ CGFloat const BUYMaxProductViewHeight = 640.0;
self.view.backgroundColor = [UIColor whiteColor];
}
- (ProductView *)productView
{
if (_productView == nil && self.product != nil && self.shop != nil) {
_productView = [[ProductView alloc] initWithFrame:CGRectMake(0, 0, self.preferredContentSize.width, self.preferredContentSize.height) product:self.product shouldShowApplePaySetup:self.shouldShowApplePaySetup];
_productView.translatesAutoresizingMaskIntoConstraints = NO;
_productView.hidden = YES;
[self.view addSubview:_productView];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_productView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_productView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_productView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_productView)]];
_productView.tableView.delegate = self;
_productView.tableView.dataSource = self;
[_productView.productViewFooter setApplePayAvailable:self.shouldShowApplePayButton requiresSetup:self.shouldShowApplePaySetup];
[_productView.productViewFooter.paymentButton addTarget:self action:@selector(checkoutWithApplePay) forControlEvents:UIControlEventTouchUpInside];
if (self.cart) {
[_productView.productViewFooter.actionButton setTitle:NSLocalizedString(@"Add to Cart", nil) forState:UIControlStateNormal];
[_productView.productViewFooter.actionButton addTarget:self action:@selector(addSelectedVariantToCart) forControlEvents:UIControlEventTouchUpInside];
}
else {
[_productView.productViewFooter.actionButton addTarget:self action:@selector(checkoutWithShopify) forControlEvents:UIControlEventTouchUpInside];
}
_productView.productViewHeader.collectionView.delegate = self;
_productView.productViewHeader.collectionView.dataSource = self;
_productView.layoutMargins = UIEdgeInsetsMake(self.productView.layoutMargins.top, self.productView.layoutMargins.left, self.bottomLayoutGuide.length, self.productView.layoutMargins.right);
}
return _productView;
}
- (CGSize)preferredContentSize
{
return CGSizeMake(MIN(BUYMaxProductViewWidth, self.view.bounds.size.width),
......@@ -246,55 +213,15 @@ CGFloat const BUYMaxProductViewHeight = 640.0;
return presentationController;
}
- (void)loadProduct:(NSString *)productId completion:(void (^)(BOOL success, NSError *error))completion
- (void)loadWithProduct:(BUYProduct *)product completion:(void (^)(BOOL success, NSError *error))completion
{
if (productId == nil) {
if (completion) {
completion(NO, [NSError errorWithDomain:BUYShopifyError code:BUYShopifyError_NoProductSpecified userInfo:nil]);
}
} else {
self.isLoading = YES;
[self getShopWithCallback:^(BOOL success, NSError *error) {
if (success) {
self.productId = productId;
[self.client getProductById:productId completion:^(BUYProduct *product, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
if (completion) {
completion(NO, error);
}
} else {
self.product = product;
if (completion) {
completion(YES, nil);
}
}
});
}];
} else {
self.isLoading = NO;
if (completion) {
completion(success, error);
}
}
}];
}
}
- (void)loadWithProduct:(BUYProduct *)product completion:(void (^)(BOOL success, NSError *error))completion;
{
if (product == nil) {
completion(NO, [NSError errorWithDomain:BUYShopifyError code:BUYShopifyError_NoProductSpecified userInfo:nil]);
} else {
self.isLoading = YES;
if (self.shop == nil) {
[self getShopWithCallback:^(BOOL success, NSError *error) {
if (success) {
self.product = product;
}
if (completion) {
completion(success, error);
}
......@@ -313,37 +240,30 @@ CGFloat const BUYMaxProductViewHeight = 640.0;
- (void)getShopWithCallback:(void (^)(BOOL, NSError *))block
{
// fetch shop details for the currency and country codes
self.isLoadingShop = YES;
[self.client getShop:^(BUYShop *shop, NSError *error) {
if (error == nil) {
self.shop = shop;
[self createProductView];
}
else {
if ([self.delegate respondsToSelector:@selector(controllerFailedToStartApplePayProcess:)]) {
[self.delegate controllerFailedToStartApplePayProcess:self];
}
}
self.isLoadingShop = NO;
if (block) block((error == nil), error);
}];
}
- (void)setProduct:(BUYProduct *)product
{
self.isLoading = NO;
_product = product;
self.navigationItem.title = _product.title;
self.selectedProductVariant = [_product.variants firstObject];
self.shouldShowVariantSelector = [_product isDefaultVariant] == NO;
self.shouldEnableVariantSelection = self.shouldShowVariantSelector && [_product.variants count] > 1;
self.shouldShowDescription = ([_product.htmlDescription length] == 0) == NO;
self.productView.hidden = NO;
[self setupNavigationBarAppearance];
[self.activityIndicatorView stopAnimating];
[self setNeedsStatusBarAppearanceUpdate];
if (self.presentingViewController) {
[self.navigationController setNavigationBarHidden:NO];
......@@ -362,8 +282,39 @@ CGFloat const BUYMaxProductViewHeight = 640.0;
self.currencyFormatter = [[NSNumberFormatter alloc] init];
self.currencyFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
self.currencyFormatter.currencyCode = shop.currency;
[self.productView.productViewFooter setApplePayAvailable:self.shouldShowApplePaySetup requiresSetup:self.shouldShowApplePaySetup];
[self.productView.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
}
- (void)createProductView
{
_productView = [[ProductView alloc] initWithFrame:CGRectMake(0, 0, self.preferredContentSize.width, self.preferredContentSize.height) product:self.product shouldShowApplePaySetup:self.shouldShowApplePaySetup];
_productView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:_productView];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_productView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_productView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_productView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_productView)]];
_productView.tableView.delegate = self;
_productView.tableView.dataSource = self;
[_productView.productViewFooter setApplePayAvailable:self.shouldShowApplePayButton requiresSetup:self.shouldShowApplePaySetup];
[_productView.productViewFooter.paymentButton addTarget:self action:@selector(checkoutWithApplePay) forControlEvents:UIControlEventTouchUpInside];
if (self.cart) {
[_productView.productViewFooter.actionButton setTitle:NSLocalizedString(@"Add to Cart", nil) forState:UIControlStateNormal];
[_productView.productViewFooter.actionButton addTarget:self action:@selector(addSelectedVariantToCart) forControlEvents:UIControlEventTouchUpInside];
}
else {
[_productView.productViewFooter.actionButton setTitle:NSLocalizedString(@"Check Out", nil) forState:UIControlStateNormal];
[_productView.productViewFooter.actionButton addTarget:self action:@selector(checkoutWithShopify) forControlEvents:UIControlEventTouchUpInside];
}
_productView.productViewHeader.collectionView.delegate = self;
_productView.productViewHeader.collectionView.dataSource = self;
_productView.layoutMargins = UIEdgeInsetsMake(self.productView.layoutMargins.top, self.productView.layoutMargins.left, self.bottomLayoutGuide.length, self.productView.layoutMargins.right);
[_productView.productViewFooter setApplePayAvailable:self.shouldShowApplePaySetup requiresSetup:self.shouldShowApplePaySetup];
[_productView.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
self.navigationItem.title = _product.title;
[self setupNavigationBarAppearance];
}
#pragma mark - Table view data source
......@@ -549,9 +500,9 @@ CGFloat const BUYMaxProductViewHeight = 640.0;
- (UIStatusBarStyle)preferredStatusBarStyle
{
if (self.navigationController.navigationBar.barStyle == UIBarStyleBlack || ([self navigationBarThresholdReached] == NO && self.isLoading == NO && self.productView.productViewHeader)) {
if (self.navigationController.navigationBar.barStyle == UIBarStyleBlack || ([self navigationBarThresholdReached] == NO && self.productView.productViewHeader)) {
return UIStatusBarStyleLightContent;
} else if (self.isLoading == YES && self.navigationController.navigationBar.barStyle == UIBarStyleBlack && self.productView.productViewHeader) {
} else if (self.navigationController.navigationBar.barStyle == UIBarStyleBlack && self.productView.productViewHeader) {
return UIStatusBarStyleLightContent;
} else {
return UIStatusBarStyleDefault;
......
......@@ -70,7 +70,7 @@ static void *kObservationContext = &kObservationContext;
- (UIScrollView *)scrollView
{
UIView *view = self.topViewController.view;
UIView *view = [self.topViewController isViewLoaded] ? self.topViewController.view : nil;
if ([view isKindOfClass:[UIScrollView class]]) {
return (id)view;
} else {
......
......@@ -43,8 +43,8 @@
@property (nonatomic, strong) BUYClient *client;
@property (nonatomic, strong) BUYCollection *collection;
@property (nonatomic, strong) NSArray *products;
@property (nonatomic, strong) NSURLSessionDataTask *collectionTask;
@property (nonatomic, strong) NSURLSessionDataTask *checkoutCreationTask;
@property (nonatomic, strong) NSOperation *collectionOperation;
@property (nonatomic, strong) NSOperation *checkoutCreationOperation;
@property (nonatomic, assign) BOOL demoProductViewController;
@property (nonatomic, assign) ThemeStyle themeStyle;
......@@ -112,8 +112,8 @@
- (void)dealloc
{
[self.checkoutCreationTask cancel];
[self.collectionTask cancel];
[self.checkoutCreationOperation cancel];
[self.collectionOperation cancel];
}
- (void)presentCollectionSortOptions:(id)sender
......@@ -160,9 +160,9 @@
- (void)getCollectionWithSortOrder:(BUYCollectionSort)collectionSort
{
[self.collectionTask cancel];
[self.collectionOperation cancel];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
self.collectionTask = [self.client getProductsPage:1 inCollection:self.collection.collectionId sortOrder:collectionSort completion:^(NSArray *products, NSUInteger page, BOOL reachedEnd, NSError *error) {
self.collectionOperation = [self.client getProductsPage:1 inCollection:self.collection.identifier sortOrder:collectionSort completion:^(NSArray *products, NSUInteger page, BOOL reachedEnd, NSError *error) {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
if (error == nil && products) {
......@@ -279,8 +279,8 @@
- (void)demoNativeFlowWithProduct:(BUYProduct*)product
{
if (self.checkoutCreationTask.state == NSURLSessionTaskStateRunning) {
[self.checkoutCreationTask cancel];
if (self.checkoutCreationOperation.executing) {
[self.checkoutCreationOperation cancel];
}
BUYCart *cart = [self.client.modelManager insertCartWithJSONDictionary:nil];
......@@ -296,7 +296,7 @@
self.client.urlScheme = @"advancedsample://";
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
self.checkoutCreationTask = [self.client createCheckout:checkout completion:^(BUYCheckout *checkout, NSError *error) {
self.checkoutCreationOperation = [self.client createCheckout:checkout completion:^(BUYCheckout *checkout, NSError *error) {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
if (error == nil && checkout) {
......
......@@ -25,14 +25,12 @@
//
#import "ShippingRatesTableViewController.h"
#import "GetShippingRatesOperation.h"
#import "PreCheckoutViewController.h"
#import "GetShopOperation.h"
#import "ShippingRateTableViewCell.h"
@import Buy;
@interface ShippingRatesTableViewController () <GetShippingRatesOperationDelegate, GetShopOperationDelegate>
@interface ShippingRatesTableViewController ()
@property (nonatomic, strong) BUYCheckout *checkout;
@property (nonatomic, strong) BUYClient *client;
@property (nonatomic, strong) NSNumberFormatter *currencyFormatter;
......@@ -64,27 +62,47 @@
[self.tableView registerClass:[ShippingRateTableViewCell class] forCellReuseIdentifier:@"Cell"];
// Setup both operations to run
GetShopOperation *shopOperation = [[GetShopOperation alloc] initWithClient:self.client];
shopOperation.delegate = self;
[[NSOperationQueue mainQueue] addOperation:shopOperation];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[self getShopAndShippingRates];
}
GetShippingRatesOperation *shippingOperation = [[GetShippingRatesOperation alloc] initWithClient:self.client withCheckout:self.checkout];
shippingOperation.delegate = self;
[[NSOperationQueue mainQueue] addOperation:shippingOperation];
- (void)getShopAndShippingRates
{
[self.client getShop:^(BUYShop *shop, NSError *error) {
if (shop && !error) {
[self didGetShop:shop];
[self getShippingRates];
}
else {
NSLog(@"Failed to retrieve shop: %@", error);
}
}];
}
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
- (void)didGetShop:(BUYShop *)shop
{
self.currencyFormatter = [[NSNumberFormatter alloc] init];
self.currencyFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
self.currencyFormatter.currencyCode = shop.currency;
}
// Ensure both operations are completed before we reload the table view
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
- (void)getShippingRates
{
[self.client getShippingRatesForCheckoutWithToken:self.checkout.token completion:^(NSArray *shippingRates, BUYStatus status, NSError *error) {
if ([shippingRates count] > 0 && !error) {
[self didGetShippingRates:shippingRates];
}
else {
NSLog(@"Failed to retrieve shipping rates: %@", error);
}
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
[self.tableView reloadData];
}];
[blockOperation addDependency:shopOperation];
[blockOperation addDependency:shippingOperation];
[[NSOperationQueue mainQueue] addOperation:blockOperation];
}
self.allOperations = @[blockOperation, shopOperation, shippingOperation];
- (void)didGetShippingRates:(NSArray *)shippingRates
{
self.shippingRates = shippingRates;
[self.tableView reloadData];
}
- (void)dealloc
......@@ -128,30 +146,4 @@
}];
}
#pragma mark - Shop delegate methods
-(void)operation:(GetShopOperation *)operation didReceiveShop:(BUYShop *)shop
{
self.currencyFormatter = [[NSNumberFormatter alloc] init];
self.currencyFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
self.currencyFormatter.currencyCode = shop.currency;
}
-(void)operation:(GetShopOperation *)operation failedToReceiveShop:(NSError *)error
{
NSLog(@"Failed to retrieve shop: %@", error);
}
#pragma mark - Shipping Rates delegate methods
-(void)operation:(GetShippingRatesOperation *)operation didReceiveShippingRates:(NSArray *)shippingRates
{
self.shippingRates = shippingRates;
}
-(void)operation:(GetShippingRatesOperation *)operation failedToReceiveShippingRates:(NSError *)error
{
NSLog(@"Failed to retrieve shipping rates: %@", error);
}
@end
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:Shopify Checkout.xcodeproj">
</FileRef>
</Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BEDC7A261AE03BA800C4B4FE"
BuildableName = "Mobile Buy SDK Web Sample.app"
BlueprintName = "Mobile Buy SDK Web Sample"
ReferencedContainer = "container:Mobile Buy SDK Web Sample.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BEDC7A261AE03BA800C4B4FE"
BuildableName = "Mobile Buy SDK Web Sample.app"
BlueprintName = "Mobile Buy SDK Web Sample"
ReferencedContainer = "container:Mobile Buy SDK Web Sample.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BEDC7A261AE03BA800C4B4FE"
BuildableName = "Mobile Buy SDK Web Sample.app"
BlueprintName = "Mobile Buy SDK Web Sample"
ReferencedContainer = "container:Mobile Buy SDK Web Sample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BEDC7A261AE03BA800C4B4FE"
BuildableName = "Mobile Buy SDK Web Sample.app"
BlueprintName = "Mobile Buy SDK Web Sample"
ReferencedContainer = "container:Mobile Buy SDK Web Sample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
//
// AppDelegate.h
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
@import UIKit;
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end
//
// AppDelegate.m
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import "AppDelegate.h"
#import "ViewController.h"
@import Buy;
#warning - Enter your shop domain and API Key
#define SHOP_DOMAIN @""
#define API_KEY @""
#define CHANNEL_ID @""
#warning Optionally, to support Apple Pay, enter your merchant ID
#define MERCHANT_ID @""
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Initialize the Buy SDK
BUYClient *client = [[BUYClient alloc] initWithShopDomain:SHOP_DOMAIN
apiKey:API_KEY
channelId:CHANNEL_ID];
// Setup the views
ViewController *storeController = [[ViewController alloc] initWithClient:client];
storeController.merchantId = MERCHANT_ID;
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:storeController];
navController.toolbarHidden = NO;
navController.navigationBarHidden = NO;
navController.navigationBar.barStyle = UIStatusBarStyleLightContent;
self.window.rootViewController = navController;
[self.window makeKeyAndVisible];
return YES;
}
@end
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright (c) 2015 Shopify. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
<rect key="frame" x="20" y="439" width="441" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Mobile Buy SDK Web Sample" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
<rect key="frame" x="20" y="140" width="441" height="43"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="548" y="455"/>
</view>
</objects>
</document>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6211" systemVersion="14A298i" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6204"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
//
// CheckoutSelectionController.h
// Checkout
//
// Created by Shopify.
// Copyright (c) 2015 Shopify. All rights reserved.
//
@import UIKit;
@class CheckoutSelectionController;
typedef NS_ENUM(NSUInteger, CheckoutType) {
CheckoutTypeNormal,
CheckoutTypeApplePay
};
@protocol CheckoutSelectionControllerDelegate <NSObject>
- (void)checkoutSelectionControllerCancelled:(CheckoutSelectionController *)controller;
- (void)checkoutSelectionController:(CheckoutSelectionController *)controller selectedCheckoutType:(CheckoutType)checkoutType;
@end
@interface CheckoutSelectionController : UIViewController
@property (nonatomic, strong) NSURLRequest *checkoutRequest;
@property (nonatomic, weak) id <CheckoutSelectionControllerDelegate> delegate;
@end
//
// CheckoutSelectionController.m
// Checkout
//
// Created by Shopify.
// Copyright (c) 2015 Shopify. All rights reserved.
//
#import "CheckoutSelectionController.h"
#import "CheckoutSelectionView.h"
#define kCheckoutAnimationTime 0.3f
@interface CheckoutSelectionController () <UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning>
@end
@implementation CheckoutSelectionController {
CheckoutSelectionView *_selectionView;
}
- (instancetype)init
{
self = [super init];
if (self) {
self.modalPresentationStyle = UIModalPresentationCustom;
self.transitioningDelegate = self;
}
return self;
}
- (void)loadView
{
UIView *background = [[UIView alloc] init];
background.backgroundColor = [UIColor clearColor];
self.view = background;
PAYButton *applePayButton = [PAYButton buttonWithType:UIButtonTypeCustom];
applePayButton.paymentImageView.image = [UIImage imageNamed:@"ApplePayMark"];
applePayButton.paymentLabel.text = @"Complete with Apple Pay";
PAYButton *checkoutButton = [PAYButton buttonWithType:UIButtonTypeCustom];
checkoutButton.paymentImageView.image = [UIImage imageNamed:@"credit"];
checkoutButton.paymentLabel.text = @"Continue Checkout";
PAYButton *cancelButton = [PAYButton buttonWithType:UIButtonTypeCustom];
cancelButton.paymentImageView.image = [UIImage imageNamed:@"cancel"];
cancelButton.paymentLabel.text = @"Cancel";
_selectionView = [[CheckoutSelectionView alloc] initWithFrame:self.view.bounds buttons:@[applePayButton, checkoutButton, cancelButton]];
_selectionView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:_selectionView];
[applePayButton addTarget:self action:@selector(applePayPressed:) forControlEvents:UIControlEventTouchUpInside];
[checkoutButton addTarget:self action:@selector(checkoutPressed:) forControlEvents:UIControlEventTouchUpInside];
[cancelButton addTarget:self action:@selector(cancelPressed:) forControlEvents:UIControlEventTouchUpInside];
}
#pragma mark - Button Presses
- (void)applePayPressed:(id)sender
{
[_delegate checkoutSelectionController:self selectedCheckoutType:CheckoutTypeApplePay];
}
- (void)checkoutPressed:(id)sender
{
[_delegate checkoutSelectionController:self selectedCheckoutType:CheckoutTypeNormal];
}
- (void)cancelPressed:(id)sender
{
[_delegate checkoutSelectionControllerCancelled:self];
}
#pragma mark - Transitioning Delegate Methods
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
return self;
}
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
return self;
}
#pragma mark - Animated Transitioning Methods
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
return kCheckoutAnimationTime;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
BOOL appearing = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey] == self;
BOOL animated = [transitionContext isAnimated];
UIView *containerView = [transitionContext containerView];
self.view.frame = [containerView bounds];
self.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
if (appearing) {
[containerView addSubview:self.view];
[_selectionView showButtons:animated ? kCheckoutAnimationTime : 0.0f completion:^(BOOL complete) {
[transitionContext completeTransition:YES];
}];
}
else {
[_selectionView hideButtons:animated ? kCheckoutAnimationTime : 0.0f completion:^(BOOL complete) {
[self.view removeFromSuperview];
[transitionContext completeTransition:YES];
}];
}
}
@end
//
// CheckoutSelectionView.h
// Checkout
//
// Created by Shopify.
// Copyright (c) 2015 Shopify. All rights reserved.
//
@import UIKit;
#import "PAYButton.h"
@interface CheckoutSelectionView : UIView
- (instancetype)initWithFrame:(CGRect)frame buttons:(NSArray *)buttons;
- (void)showButtons:(CGFloat)animationDuration completion:(void (^)(BOOL complete))completion;
- (void)hideButtons:(CGFloat)animationDuration completion:(void (^)(BOOL complete))completion;
@end
//
// CheckoutSelectionView.m
// Checkout
//
// Created by Shopify.
// Copyright (c) 2015 Shopify. All rights reserved.
//
#import "CheckoutSelectionView.h"
//Views
#import "PAYButton.h"
#define kSidePadding 15.0f
#define kButtonIconPadding 8.0f
#define kPaymentButtonHeight 70.0f
#define kAnimationOffset 80.0f
#define kDelay 0.10f
@implementation CheckoutSelectionView {
UIView *_background;
NSArray *_buttons;
}
- (instancetype)initWithFrame:(CGRect)frame buttons:(NSArray *)buttons
{
self = [super initWithFrame:frame];
if (self) {
_background = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
_background.alpha = 0.0f;
_background.frame = self.bounds;
_background.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[self addSubview:_background];
_buttons = buttons;
for (UIButton *button in _buttons) {
[_background addSubview:button];
button.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
}
}
return self;
}
- (CGSize)sizeThatFits:(CGSize)size
{
NSUInteger buttonCount = [_buttons count];
return CGSizeMake(size.width, kPaymentButtonHeight * buttonCount + kSidePadding * (buttonCount + 1));
}
- (void)layoutButtonsAsHidden
{
CGRect bounds = _background.bounds;
CGFloat offsetX = bounds.origin.x + roundf(bounds.size.width / 2) + kSidePadding;
CGFloat offsetY = bounds.origin.y + bounds.size.height - [_buttons count] * (kPaymentButtonHeight + kSidePadding);
CGFloat width = bounds.size.width - 2 * kSidePadding;
for (UIButton *button in _buttons) {
button.frame = CGRectMake(offsetX, offsetY, width, kPaymentButtonHeight);
button.alpha = 0.0f;
offsetY += kPaymentButtonHeight + kSidePadding;
}
}
- (void)showButtons:(CGFloat)animationDuration completion:(void (^)(BOOL complete))completion
{
[self layoutButtonsAsHidden];
[UIView animateWithDuration:animationDuration delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction animations:^{
_background.alpha = 1.0f;
} completion:nil];
[self animateButtonsToOffset:0.0f duration:animationDuration alpha:1.0f completion:completion];
}
- (void)hideButtons:(CGFloat)animationDuration completion:(void (^)(BOOL complete))completion
{
[UIView animateWithDuration:animationDuration delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction animations:^{
_background.alpha = 0.0f;
} completion:nil];
[self animateButtonsToOffset:kAnimationOffset duration:animationDuration alpha:0.0f completion:completion];
}
- (void)animateButtonsToOffset:(CGFloat)offset duration:(CGFloat)duration alpha:(CGFloat)alpha completion:(void (^)(BOOL complete))completion
{
CGRect bounds = self.bounds;
CGSize size = [self sizeThatFits:bounds.size];
CGFloat offsetY = bounds.origin.y + bounds.size.height - size.height;
CGFloat offsetX = bounds.origin.x + kSidePadding + offset;
CGFloat delay = 0.0f;
for (UIButton *button in _buttons) {
//Animate each button independently and call the completion handler when the last button has finished animating
[UIView animateWithDuration:duration delay:delay usingSpringWithDamping:0.8f initialSpringVelocity:0.2f options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction animations:^{
button.frame = CGRectMake(offsetX, offsetY, button.frame.size.width, button.frame.size.height);
button.alpha = alpha;
} completion:button == [_buttons lastObject] ? completion : nil];
offsetY += kPaymentButtonHeight + kSidePadding;
delay += kDelay;
}
}
@end
{
"images" : [
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-Small@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-Small@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-Small-40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-Small-40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-60@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "Apple_Pay_mark_normal_@2x.png"
},
{
"idiom" : "universal",
"scale" : "3x",
"filename" : "Apple_Pay_mark_normal_@3x.png"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "back@2x.png"
},
{
"idiom" : "universal",
"scale" : "3x",
"filename" : "back@3x.png"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "forward@2x.png"
},
{
"idiom" : "universal",
"scale" : "3x",
"filename" : "forward@3x.png"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "shop.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "shop@2x.png"
},
{
"idiom" : "universal",
"scale" : "3x",
"filename" : "shop@3x.png"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Web Sample</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.2.2</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.in-app-payments</key>
<array/>
</dict>
</plist>
//
// PAYButton.h
// Checkout
//
// Created by Shopify.
// Copyright (c) 2015 Shopify. All rights reserved.
//
@import UIKit;
@interface PAYButton : UIButton
@property (nonatomic, strong, readonly) UIImageView *paymentImageView;
@property (nonatomic, strong, readonly) UILabel *paymentLabel;
@end
//
// PAYButton.m
// Checkout
//
// Created by Shopify.
// Copyright (c) 2015 Shopify. All rights reserved.
//
#import "PAYButton.h"
#define kButtonMargin 11.0f
#define kButtonSelectionInset 3.0f
@implementation PAYButton {
UIView *_highlightView;
UIView *_imageBackgroundView;
BOOL _animating;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
_imageBackgroundView = [[UIView alloc] init];
_imageBackgroundView.backgroundColor = [UIColor whiteColor];
_imageBackgroundView.userInteractionEnabled = NO;
[self addSubview:_imageBackgroundView];
_paymentImageView = [[UIImageView alloc] initWithFrame:_imageBackgroundView.bounds];
_paymentImageView.contentMode = UIViewContentModeCenter;
_paymentImageView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[_imageBackgroundView addSubview:_paymentImageView];
_paymentLabel = [[UILabel alloc] init];
[_paymentLabel setFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:20.0f]];
[_paymentLabel setTextColor:[UIColor whiteColor]];
[self addSubview:_paymentLabel];
_highlightView = [[UIView alloc] initWithFrame:frame];
_highlightView.alpha = 0.0f;
_highlightView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
_highlightView.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.2f];
_highlightView.userInteractionEnabled = NO;
[self addSubview:_highlightView];
}
return self;
}
- (CGRect)rectForHighlightedBackground
{
CGRect bounds = self.bounds;
return CGRectMake(bounds.origin.x + kButtonSelectionInset, bounds.origin.y + kButtonSelectionInset, bounds.size.height - kButtonSelectionInset * 2.0f, bounds.size.height - kButtonSelectionInset * 2.0f);
}
- (CGRect)rectForNormalBackground
{
CGRect bounds = self.bounds;
return CGRectMake(bounds.origin.x, bounds.origin.y, bounds.size.height, bounds.size.height);
}
- (void)setButtonFrame:(CGRect)frame
{
_imageBackgroundView.frame = frame;
_highlightView.frame = _imageBackgroundView.frame;
_imageBackgroundView.layer.cornerRadius = floorf(frame.size.height / 2);
_highlightView.layer.cornerRadius = _imageBackgroundView.layer.cornerRadius;
}
- (void)setHighlighted:(BOOL)highlighted
{
if (highlighted != self.isHighlighted) {
[super setHighlighted:highlighted];
CGRect rect = highlighted ? [self rectForHighlightedBackground] : [self rectForNormalBackground];
_animating = YES;
[UIView animateWithDuration:0.1f delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction animations:^{
_highlightView.alpha = highlighted ? 1.0f : 0.0f;
_paymentLabel.textColor = highlighted ? [UIColor lightGrayColor] : [UIColor whiteColor];
[self setButtonFrame:rect];
} completion:^(BOOL finished) {
_animating = NO;
}];
}
}
- (void)layoutSubviews
{
[super layoutSubviews];
CGRect bounds = self.bounds;
if (_animating == NO) {
[self setButtonFrame:[self rectForNormalBackground]];
}
CGFloat textHeight = ceilf([[_paymentLabel attributedText] size].height);
CGFloat backgroundMaxX = CGRectGetMaxX([self rectForNormalBackground]) + kButtonMargin;
_paymentLabel.frame = CGRectMake(backgroundMaxX, bounds.origin.y + roundf((bounds.size.height - textHeight) * 0.5f), bounds.size.width - backgroundMaxX - kButtonMargin, textHeight);
}
@end
//
// ViewController.h
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
@import UIKit;
#import <Buy/Buy.h>
@interface ViewController : BUYStoreViewController
@end
//
// ViewController.m
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import "ViewController.h"
#import "CheckoutSelectionController.h"
@interface ViewController () <BUYStoreViewControllerDelegate, CheckoutSelectionControllerDelegate>
@property (nonatomic, copy) BUYCheckoutTypeBlock callback;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.delegate = self;
// Add a home button to the navigation bar
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.tintColor = [UIColor whiteColor];
UIImage *buttonImage = [UIImage imageNamed:@"shop"];
[button setImage:[buttonImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateNormal];
[button setFrame:CGRectMake(0, 0, 53, 31)];
[button addTarget:self action:@selector(goHome)forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *rightButton = [[UIBarButtonItem alloc] initWithCustomView:button];
self.navigationItem.rightBarButtonItem = rightButton;
[self loadShopWithCallback:^(BOOL success, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (success) {
self.title = self.shop.name;
}
else {
NSLog(@"Error fetching shop: %@", error.localizedDescription);
}
});
}];
}
- (void)goHome
{
[self reloadHomePage];
}
#pragma mark - BUYStoreViewController delegate methods
- (void)controller:(BUYStoreViewController *)controller shouldProceedWithCheckoutType:(BUYCheckoutTypeBlock)completionHandler
{
// If ApplePay is not setup, proceed to normal checkout
if (self.isApplePayAvailable)
{
CheckoutSelectionController *selectionController = [[CheckoutSelectionController alloc] init];
selectionController.delegate = self;
self.callback = completionHandler;
[self presentViewController:selectionController animated:YES completion:nil];
}
else {
completionHandler(BUYCheckoutTypeNormal);
}
}
- (void)controller:(BUYViewController *)controller failedToCreateCheckout:(NSError *)error
{
NSLog(@"Failed to create checkout: %@", error);
}
- (void)controllerFailedToStartApplePayProcess:(BUYViewController *)controller
{
NSLog(@"Failed to start the Apple Pay process. We weren't given an error :(");
}
- (void)controller:(BUYViewController *)controller failedToUpdateCheckout:(BUYCheckout *)checkout withError:(NSError *)error
{
NSLog(@"Failed to update checkout: %@, error: %@", checkout.token, error);
}
- (void)controller:(BUYViewController *)controller failedToGetShippingRates:(BUYCheckout *)checkout withError:(NSError *)error
{
NSLog(@"Failed to get shipping rates: %@, error: %@", checkout.token, error);
}
- (void)controller:(BUYViewController *)controller failedToCompleteCheckout:(BUYCheckout *)checkout withError:(NSError *)error
{
NSLog(@"Failed to complete checkout: %@, error: %@", checkout.token, error);
}
- (void)controller:(BUYViewController *)controller didCompleteCheckout:(BUYCheckout *)checkout status:(BUYStatus)status
{
NSLog(@"Did complete checkout: %@, status: %ld", checkout.token, (unsigned long)status);
}
#pragma mark - PAYSelectionController delegate
- (void)checkoutSelectionControllerCancelled:(CheckoutSelectionController *)controller
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)checkoutSelectionController:(CheckoutSelectionController *)controller selectedCheckoutType:(CheckoutType)checkoutType
{
self.callback(checkoutType == CheckoutTypeApplePay ? BUYCheckoutTypeApplePay : BUYCheckoutTypeNormal);
[self dismissViewControllerAnimated:YES completion:nil];
}
@end
//
// main.m
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
@import UIKit;
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
# Mobile Buy SDK Sample Web App
The web sample app demonstrates how you can load your shop's website in a webview, and support Apple Pay.
### Getting started
First, add your shop domain, API key and Channel ID to the `AppDelegate.h` macros.
```objc
#define SHOP_DOMAIN @"<shop_domain>"
#define API_KEY @"<api_key>"
#define CHANNEL_ID @"<channel_id>"
```
To support Apple Pay, add your Merchant ID as well.
```objc
#define MERCHANT_ID @"<merchant_id>"
```
### Overview
The sample app instantiates a view controller which is a subclass of `BUYStoreViewController`. This displays a webview with your shop, but intercepts the checkout to support Apple Pay.
The sample app also demonstrates how to use the `BUYClient` to obtain shop details by calling `getShop:`
```objc
[self.provider getShop:^(BUYShop *shop, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error == nil && shop) {
self.title = shop.name;
}
else {
NSLog(@"Error fetching shop: %@", error.localizedDescription);
}
});
}];
```
......@@ -1217,7 +1217,6 @@
84DD12A91CC63FE600A2442D /* _BUYOrder.h in Headers */,
84DD129F1CC63FE600A2442D /* _BUYCustomer.h in Headers */,
9A0B0C671CEA703E0037D68F /* BUYClient+Routing.h in Headers */,
9A0B0CA81CED0A860037D68F /* BUYCheckoutOperation.h in Headers */,
84DD12991CC63FE600A2442D /* _BUYCart.h in Headers */,
84DD129D1CC63FE600A2442D /* _BUYCollection.h in Headers */,
9A0B0C731CEB52B90037D68F /* BUYClient+Checkout.h in Headers */,
......@@ -1286,6 +1285,7 @@
9032F2DB1BE9457A00BB9EEF /* BUYCheckoutAttribute.h in Headers */,
9019315E1BC5B9BC00D1134E /* BUYError.h in Headers */,
84980F371CB75C2900CFAB58 /* NSPropertyDescription+BUYAdditions.h in Headers */,
9A0B0CA81CED0A860037D68F /* BUYCheckoutOperation.h in Headers */,
901931611BC5B9BC00D1134E /* BUYClient.h in Headers */,
9A585C0B1CE6440B001F20F0 /* BUYOperation.h in Headers */,
9A0B0C791CEB5BBD0037D68F /* BUYAuthenticatedResponse.h in Headers */,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment