Commit 3ab827c2 by Brent Gulanowski

Merge branch 'integration/sdk-2.0' into task/generated-models

# Conflicts:
#	Mobile Buy SDK/Mobile Buy SDK Tests/BUYClientTest.m
#	Mobile Buy SDK/Mobile Buy SDK/Data/BUYClient.m
#	Mobile Buy SDK/Mobile Buy SDK/Data/BUYClient_Internal.h
#	Mobile Buy SDK/Mobile Buy SDK/Models/BUYObjectProtocol.h
#	Mobile Buy SDK/Mobile Buy SDK/Models/Transient/BUYAddress.m
#	Mobile Buy SDK/Mobile Buy SDK/Models/Transient/BUYCheckout.h
parents e03e9acd 93460a07
......@@ -31,6 +31,11 @@
#import "BUYTestConstants.h"
#import "BUYClientTestBase.h"
#import "NSURLComponents+BUYAdditions.h"
#import "BUYShopifyErrorCodes.h"
#import "BUYAccountCredentials.h"
#import "BUYClient+Customers.h"
NSString * const BUYFakeCustomerToken = @"dsfasdgafdg";
@interface BUYClient ()
......@@ -321,4 +326,121 @@
return [self.client.modelManager insertCartWithJSONDictionary:nil];
}
#pragma mark - Customer Tests -
- (void)testCustomerCreationURL
{
BUYAccountCredentialItem *firstName = [BUYAccountCredentialItem itemWithKey:@"first_name" value:@"michael"];
BUYAccountCredentialItem *lastName = [BUYAccountCredentialItem itemWithKey:@"last_name" value:@"scott"];
BUYAccountCredentialItem *email = [BUYAccountCredentialItem itemWithKey:@"email" value:@"fake@example.com"];
BUYAccountCredentialItem *password = [BUYAccountCredentialItem itemWithKey:@"password" value:@"password"];
BUYAccountCredentialItem *passwordConfirmation = [BUYAccountCredentialItem itemWithKey:@"password_confirmation" value:@"password"];
BUYAccountCredentials *credentials = [BUYAccountCredentials credentialsWithItems:@[firstName, lastName, email, password, passwordConfirmation]];
NSURLSessionDataTask *task = [self.client createCustomerWithCredentials:credentials callback:nil];
XCTAssertEqualObjects(task.originalRequest.URL.scheme, @"https");
XCTAssertEqualObjects(task.originalRequest.URL.path, @"/api/customers.json");
XCTAssertEqualObjects(task.originalRequest.HTTPMethod, @"POST");
NSError *error = nil;
NSDictionary *payload = [NSJSONSerialization JSONObjectWithData:task.originalRequest.HTTPBody options:0 error:&error];
XCTAssertNil(error);
NSDictionary *dict = @{@"customer": @{
@"first_name": firstName.value,
@"last_name": lastName.value,
@"email": email.value,
@"password": password.value,
@"password_confirmation": passwordConfirmation.value}};
XCTAssertEqualObjects(payload, dict);
}
- (void)testLoginCustomerURL
{
BUYAccountCredentialItem *email = [BUYAccountCredentialItem itemWithKey:@"email" value:@"fake@example.com"];
BUYAccountCredentialItem *password = [BUYAccountCredentialItem itemWithKey:@"password" value:@"password"];
BUYAccountCredentials *credentials = [BUYAccountCredentials credentialsWithItems:@[email, password]];
NSURLSessionDataTask *task = [self.client loginCustomerWithCredentials:credentials callback:nil];
XCTAssertEqualObjects(task.originalRequest.URL.scheme, @"https");
XCTAssertEqualObjects(task.originalRequest.URL.path, @"/api/customers/customer_token.json");
XCTAssertEqualObjects(task.originalRequest.HTTPMethod, @"POST");
NSError *error = nil;
NSDictionary *payload = [NSJSONSerialization JSONObjectWithData:task.originalRequest.HTTPBody options:0 error:&error];
XCTAssertNil(error);
NSDictionary *dict = @{@"customer": @{@"email": email.value, @"password": password.value}};
XCTAssertEqualObjects(payload, dict);
}
- (void)testGetCustomerURL
{
NSURLSessionDataTask *task = [self.client getCustomerWithID:nil callback:nil];
XCTAssertEqualObjects(task.originalRequest.URL.scheme, @"https");
XCTAssertEqualObjects(task.originalRequest.URL.path, @"/api/customers.json");
XCTAssertEqualObjects(task.originalRequest.HTTPMethod, @"GET");
XCTAssertEqualObjects(self.client.customerToken, task.originalRequest.allHTTPHeaderFields[BUYClientCustomerAccessToken]);
}
- (void)testGetOrdersForCustomerURL
{
NSURLSessionDataTask *task = [self.client getOrdersForCustomerWithCallback:nil];
XCTAssertEqualObjects(task.originalRequest.URL.scheme, @"https");
XCTAssertEqualObjects(task.originalRequest.URL.path, @"/api/customers/orders.json");
XCTAssertEqualObjects(task.originalRequest.HTTPMethod, @"GET");
XCTAssertEqualObjects(self.client.customerToken, task.originalRequest.allHTTPHeaderFields[BUYClientCustomerAccessToken]);
}
- (void)testCustomerRecovery
{
NSString *email = @"fake@example.com";
NSURLSessionDataTask *task = [self.client recoverPasswordForCustomer:email callback:nil];
XCTAssertEqualObjects(task.originalRequest.URL.scheme, @"https");
XCTAssertEqualObjects(task.originalRequest.URL.path, @"/api/customers/recover.json");
XCTAssertEqualObjects(task.originalRequest.HTTPMethod, @"POST");
NSError *error = nil;
NSDictionary *payload = [NSJSONSerialization JSONObjectWithData:task.originalRequest.HTTPBody options:0 error:&error];
XCTAssertNil(error);
NSDictionary *dict = @{@"email": email};
XCTAssertEqualObjects(payload, dict);
}
- (void)testTokenRenewal
{
self.client.customerToken = nil;
NSURLSessionDataTask *task = [self.client renewCustomerTokenWithID:nil callback:^(NSString *token, NSError *error) {}];
XCTAssertNil(task); // task should be nil if no customer token was set on the client
self.client.customerToken = BUYFakeCustomerToken;
task = [self.client renewCustomerTokenWithID:@"1" callback:nil];
XCTAssertEqualObjects(task.originalRequest.URL.scheme, @"https");
XCTAssertEqualObjects(task.originalRequest.URL.path, @"/api/customers/1/customer_token/renew.json");
XCTAssertEqualObjects(task.originalRequest.HTTPMethod, @"PUT");
}
- (void)testCustomerActivation
{
BUYAccountCredentialItem *passwordItem = [BUYAccountCredentialItem itemWithKey:@"password" value:@"12345"];
BUYAccountCredentialItem *passwordConfItem = [BUYAccountCredentialItem itemWithKey:@"password_confirmation" value:@"12345"];
BUYAccountCredentials *credentials = [BUYAccountCredentials credentialsWithItems:@[passwordItem, passwordConfItem]];
NSString *customerID = @"12345";
NSString *customerToken = @"12345";
NSURLSessionDataTask *task = [self.client activateCustomerWithCredentials:credentials customerID:customerID customerToken:customerToken callback:nil];
XCTAssertEqualObjects(task.originalRequest.URL.scheme, @"https");
XCTAssertEqualObjects(task.originalRequest.URL.path, @"/api/customers/12345/activate.json");
XCTAssertEqualObjects(task.originalRequest.HTTPMethod, @"PUT");
}
@end
......@@ -30,6 +30,8 @@
extern NSString * const BUYShopDomain_Placeholder;
extern NSString * const BUYAPIKey_Placeholder;
extern NSString * const BUYAppId_Placeholder;
extern NSString * const BUYChannelId_Placeholder;
extern NSString * const BUYFakeCustomerToken;
@interface BUYClientTestBase : XCTestCase
......@@ -37,6 +39,8 @@ extern NSString * const BUYAppId_Placeholder;
@property (nonatomic, strong) NSString *apiKey;
@property (nonatomic, strong) NSString *appId;
@property (nonatomic, strong) NSString *merchantId;
@property (nonatomic, strong) NSString *customerEmail;
@property (nonatomic, strong) NSString *customerPassword;
@property (nonatomic, strong) NSString *giftCardCode;
@property (nonatomic, strong) NSString *giftCardCode2;
@property (nonatomic, strong) NSString *giftCardCode3;
......@@ -45,7 +49,11 @@ extern NSString * const BUYAppId_Placeholder;
@property (nonatomic, strong) NSString *giftCardCodeInvalid;
@property (nonatomic, strong) NSString *discountCodeValid;
@property (nonatomic, strong) NSString *discountCodeExpired;
@property (nonatomic, strong) NSArray *productIds;
@property (nonatomic, strong) NSNumber *variantUntrackedId;
@property (nonatomic, strong) NSNumber *variantInventory1Id;
@property (nonatomic, strong) NSNumber *variantSoldOutId;
@property (nonatomic, strong) BUYClient *client;
......
......@@ -53,6 +53,9 @@ NSString * const BUYAppId_Placeholder = @"app_id";
self.appId = environment[kBUYTestAppId] ?: jsonConfig[kBUYTestAppId];
self.merchantId = environment[kBUYTestMerchantId] ?: jsonConfig[kBUYTestMerchantId];
self.customerEmail = environment[kBUYTestEmail] ?: jsonConfig[kBUYTestEmail];
self.customerPassword = environment[kBUYTestPassword] ?: jsonConfig[kBUYTestPassword];
NSDictionary *giftCards = jsonConfig[@"gift_cards"];
self.giftCardCode = environment[kBUYTestGiftCardCode11] ?: giftCards[@"valid11"][@"code"];
......@@ -67,6 +70,10 @@ NSString * const BUYAppId_Placeholder = @"app_id";
NSString *productIdsString = [environment[kBUYTestProductIdsCommaSeparated] stringByReplacingOccurrencesOfString:@" " withString:@""];
self.productIds = [productIdsString componentsSeparatedByString:@","];
} else {
self.variantUntrackedId = jsonConfig[@"variants"][@"variant_untracked_id"];
self.variantInventory1Id = jsonConfig[@"variants"][@"variant_inventory1_id"];
self.variantSoldOutId = jsonConfig[@"variants"][@"variant_soldout_id"];
self.productIds = jsonConfig[@"product_ids"];
}
......
//
// BUYClientTest_Customer.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 XCTest;
#import "BUYClientTestBase.h"
#import "BUYClient+Customers.h"
#import "BUYAccountCredentials.h"
#import "BUYError+BUYAdditions.h"
#import <OHHTTPStubs/OHHTTPStubs.h>
#import "OHHTTPStubsResponse+Helpers.h"
// Remove this macro entirely when test shop has customer api enabled
//#define CUSTOMER_API_AVAILABLE
@interface BUYClientTest_Customer : BUYClientTestBase
@end
@implementation BUYClientTest_Customer
- (void)tearDown
{
[super tearDown];
[OHHTTPStubs removeAllStubs];
}
- (void)testCustomerDuplicateEmail
{
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest * _Nonnull request) {
return [self shouldUseMocks];
} withStubResponse:^OHHTTPStubsResponse * _Nonnull(NSURLRequest * _Nonnull request) {
return [OHHTTPStubsResponse responseWithKey:@"testCustomerDuplicateEmail"];
}];
XCTestExpectation *expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)];
BUYAccountCredentialItem *emailItem = [BUYAccountCredentialItem itemWithKey:@"email" value:self.customerEmail];
BUYAccountCredentialItem *passwordItem = [BUYAccountCredentialItem itemWithKey:@"password" value:self.customerPassword];
BUYAccountCredentialItem *passwordConfItem = [BUYAccountCredentialItem itemWithKey:@"password_confirmation" value:self.customerPassword];
BUYAccountCredentials *credentials = [BUYAccountCredentials credentialsWithItems:@[emailItem, passwordItem, passwordConfItem]];
[self.client createCustomerWithCredentials:credentials callback:^(BUYCustomer *customer, NSString *token, NSError *error) {
XCTAssertNil(customer);
XCTAssertNotNil(error);
NSArray *errors = [BUYError errorsFromSignUpJSON:error.userInfo];
XCTAssertEqual(errors.count, 1);
BUYError *customerError = errors[0];
XCTAssertEqualObjects(customerError.code, @"taken");
XCTAssertEqualObjects(customerError.options[@"rescue_from_duplicate"], @YES);
XCTAssertEqualObjects(customerError.options[@"value"], self.customerEmail);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:10 handler:^(NSError * _Nullable error) {
XCTAssertNil(error);
}];
}
- (void)testCustomerInvalidEmailPassword
{
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest * _Nonnull request) {
return [self shouldUseMocks];
} withStubResponse:^OHHTTPStubsResponse * _Nonnull(NSURLRequest * _Nonnull request) {
return [OHHTTPStubsResponse responseWithKey:@"testCustomerInvalidEmailPassword"];
}];
XCTestExpectation *expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)];
BUYAccountCredentialItem *emailItem = [BUYAccountCredentialItem itemWithKey:@"email" value:@"a"];
BUYAccountCredentialItem *passwordItem = [BUYAccountCredentialItem itemWithKey:@"password" value:@"b"];
BUYAccountCredentialItem *passwordConfItem = [BUYAccountCredentialItem itemWithKey:@"password_confirmation" value:@"c"];
BUYAccountCredentials *credentials = [BUYAccountCredentials credentialsWithItems:@[emailItem, passwordItem, passwordConfItem]];
[self.client createCustomerWithCredentials:credentials callback:^(BUYCustomer *customer, NSString *token, NSError *error) {
XCTAssertNil(customer);
XCTAssertNotNil(error);
NSArray<BUYError *> *errors = [BUYError errorsFromSignUpJSON:error.userInfo];
XCTAssertEqual(errors.count, 3);
BUYError *emailError = errors[0];
XCTAssertEqualObjects(emailError.code, @"invalid");
BUYError *passwordConfError = errors[1];
XCTAssertEqualObjects(passwordConfError.code, @"confirmation");
XCTAssertEqualObjects(passwordConfError.options[@"attribute"], @"Password");
BUYError *passwordError = errors[2];
XCTAssertEqualObjects(passwordError.code, @"too_short");
XCTAssertEqualObjects(passwordError.options[@"count"], @5);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:10 handler:^(NSError * _Nullable error) {
XCTAssertNil(error);
}];
}
- (void)testCustomerLogin
{
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest * _Nonnull request) {
return [self shouldUseMocks];
} withStubResponse:^OHHTTPStubsResponse * _Nonnull(NSURLRequest * _Nonnull request) {
return [OHHTTPStubsResponse responseWithKey:@"testCustomerLogin"];
}];
XCTestExpectation *expectation = [self expectationWithDescription:NSStringFromSelector(_cmd)];
BUYAccountCredentialItem *emailItem = [BUYAccountCredentialItem itemWithKey:@"email" value:self.customerEmail];
BUYAccountCredentialItem *passwordItem = [BUYAccountCredentialItem itemWithKey:@"password" value:self.customerPassword];
BUYAccountCredentials *credentials = [BUYAccountCredentials credentialsWithItems:@[emailItem, passwordItem]];
[self.client loginCustomerWithCredentials:credentials callback:^(BUYCustomer *customer, NSString *token, NSError *error) {
XCTAssertNil(error);
XCTAssertNotNil(customer);
XCTAssertEqualObjects(customer.email, self.customerEmail);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:10 handler:^(NSError * _Nullable error) {
XCTAssertNil(error);
}];
}
@end
......@@ -32,6 +32,7 @@
#import "BUYClientTestBase.h"
#import <OHHTTPStubs/OHHTTPStubs.h>
#import "OHHTTPStubsResponse+Helpers.h"
#import "BUYShopifyErrorCodes.h"
@interface BUYClientTest_Storefront : BUYClientTestBase
@property (nonatomic, strong) BUYCollection *collection;
......
//
// BUYPropertyDescriptionAdditionsTests.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 <XCTest/XCTest.h>
#import <CoreData/CoreData.h>
#import "BUYFlatCollectionTransformer.h"
#import "BUYDateTransformer.h"
#import "BUYIdentityTransformer.h"
#import "NSEntityDescription+BUYAdditions.h"
#import "NSPropertyDescription+BUYAdditions.h"
#import "TestModel.h"
static NSString * const BirdEntity = @"Bird";
static NSString * const BranchEntity = @"Branch";
static NSString * const LeafEntity = @"Leaf";
static NSString * const NestEntity = @"Nest";
static NSString * const ResearchEntity = @"Researcher";
static NSString * const RootEntity = @"Root";
@interface NSIndexSet (BUYTestAdditions)
+ (instancetype)indexSetWithIndexes:(NSArray *)indexes;
@end
@interface BUYCoreDataModelAdditionsTests : XCTestCase
@property (nonatomic) NSManagedObjectModel *model;
@property (nonatomic) TestModelManager *modelManager;
@end
@implementation BUYCoreDataModelAdditionsTests
+ (void)initialize
{
if (self == [BUYCoreDataModelAdditionsTests class]) {
[NSValueTransformer setValueTransformer:[BUYFlatCollectionTransformer arrayTransformer] forName:@"Array"];
[NSValueTransformer setValueTransformer:[BUYFlatCollectionTransformer setTransformer] forName:@"Set"];
}
}
- (instancetype)initWithInvocation:(NSInvocation *)invocation
{
self = [super initWithInvocation:invocation];
if (self) {
self.modelManager = [[TestModelManager alloc] init];
self.model = self.modelManager.model;
}
return self;
}
- (NSEntityDescription *)entityForName:(NSString *)entityName
{
return self.model.entitiesByName[entityName];
}
- (NSAttributeDescription *)attributeWithName:(NSString *)attributeName forEntity:(NSString *)entityName
{
return [[self entityForName:entityName] attributesByName][attributeName];
}
- (NSRelationshipDescription *)relationshipWithName:(NSString *)propertyName forEntity:(NSString *)entityName
{
return [[self entityForName:entityName] relationshipsByName][propertyName];
}
- (void)testJSONTransformerName
{
XCTAssertEqualObjects(BUYDateTransformerName, [self attributeWithName:@"date" forEntity:LeafEntity].JSONValueTransformerName);
XCTAssertEqualObjects(BUYIdentityTransformerName, [self attributeWithName:@"identifier" forEntity:RootEntity].JSONValueTransformerName);
}
- (void)testJSONPropertyKey
{
XCTAssertEqualObjects(@"createDate", [self attributeWithName:@"date" forEntity:LeafEntity].JSONPropertyKey);
}
- (void)testJSONValueTransformer
{
XCTAssertEqualObjects([BUYFlatCollectionTransformer class], [[self attributeWithName:@"tags" forEntity:LeafEntity].JSONValueTransformer class]);
}
- (void)testNilAttribute
{
NSAttributeDescription *idAttribute = [self attributeWithName:@"identifier" forEntity:RootEntity];
XCTAssertNil([idAttribute buy_JSONForValue:nil]);
XCTAssertEqualObjects([idAttribute buy_valueForJSON:nil object:nil], [NSNull null]);
}
- (void)testNullAttribute
{
NSAttributeDescription *idAttribute = [self attributeWithName:@"identifier" forEntity:RootEntity];
XCTAssertEqualObjects([idAttribute buy_JSONForValue:[NSNull null]], [NSNull null]);
XCTAssertEqualObjects([idAttribute buy_valueForJSON:[NSNull null] object:nil], [NSNull null]);
}
- (void)testInteger
{
NSAttributeDescription *idAttribute = [self attributeWithName:@"identifier" forEntity:RootEntity];
NSNumber *identifier = @10001;
XCTAssertEqualObjects(identifier, [idAttribute buy_JSONForValue:identifier]);
XCTAssertEqualObjects(identifier, [idAttribute buy_valueForJSON:identifier object:nil]);
}
- (void)testString
{
NSAttributeDescription *stringAttribute = [self attributeWithName:@"name" forEntity:RootEntity];
NSString *name = @"MyRoot";
XCTAssertEqualObjects(name, [stringAttribute buy_JSONForValue:name]);
XCTAssertEqualObjects(name, [stringAttribute buy_valueForJSON:name object:nil]);
}
- (void)testDecimalNumber
{
NSAttributeDescription *decimalAttribute = [self attributeWithName:@"age" forEntity:RootEntity];
NSString *ageString = @"145";
NSDecimalNumber *age = [NSDecimalNumber decimalNumberWithString:ageString];
XCTAssertEqualObjects(ageString, [decimalAttribute buy_JSONForValue:age]);
XCTAssertEqualObjects(age, [decimalAttribute buy_valueForJSON:ageString object:nil]);
}
- (void)testDate
{
NSAttributeDescription *dateAttribute = [self attributeWithName:@"date" forEntity:LeafEntity];
NSString *dateString = @"1970-01-01T01:17:59+0000";
NSDate *date = [NSDate dateWithTimeIntervalSince1970:4679.0];
XCTAssertEqualObjects(dateString, [dateAttribute buy_JSONForValue:date]);
XCTAssertEqualObjects(date, [dateAttribute buy_valueForJSON:dateString object:nil]);
}
- (void)testURL
{
// the Root.url attribute declares "attributeValueClass = NSURL; JSONTransformerName = BUYURL"
NSAttributeDescription *urlAttribute = [self attributeWithName:@"url" forEntity:RootEntity];
NSString *urlString = @"https://www.example.com/api/model.json?id=100";
NSURL *url = [NSURL URLWithString:urlString];
XCTAssertEqualObjects(urlString, [urlAttribute buy_JSONForValue:url]);
XCTAssertEqualObjects(url, [urlAttribute buy_valueForJSON:urlString object:nil]);
}
- (void)testFlatArray
{
NSAttributeDescription *arrayDescription = [self attributeWithName:@"ornaments" forEntity:BranchEntity];
NSString *ornamentsString = @"one two three two one";
NSArray *ornaments = @[@"one", @"two", @"three", @"two", @"one"];
XCTAssertEqualObjects(ornamentsString, [arrayDescription buy_JSONForValue:ornaments]);
XCTAssertEqualObjects(ornaments, [arrayDescription buy_valueForJSON:ornamentsString object:nil]);
}
- (void)testFlatSet
{
NSAttributeDescription *setDescription = [self attributeWithName:@"tags" forEntity:LeafEntity];
NSString *tagsString = @"blue green red";
NSSet *tags = [NSSet setWithArray:@[@"red", @"green", @"blue"]];
XCTAssertEqualObjects(tags, [setDescription buy_valueForJSON:tagsString object:nil]);
NSString *jsonString = [setDescription buy_JSONForValue:tags];
NSSet *actual = [NSSet setWithArray:[jsonString componentsSeparatedByString:@" "]];
XCTAssertEqualObjects(actual, tags);
}
- (void)testNilRelationship
{
Branch *branch = [self.modelManager buy_objectWithEntityName:BranchEntity JSONDictionary:nil];
NSRelationshipDescription *nestRelationship = [self relationshipWithName:@"nest" forEntity:BranchEntity];
XCTAssertNil([nestRelationship buy_valueForJSON:nil object:branch]);
}
- (void)testNullRelationship
{
Branch *branch = [self.modelManager buy_objectWithEntityName:BranchEntity JSONDictionary:nil];
NSRelationshipDescription *nestRelationship = [self relationshipWithName:@"nest" forEntity:BranchEntity];
XCTAssertNil([nestRelationship buy_valueForJSON:[NSNull null] object:branch]);
}
- (void)testRelationship
{
Branch *branch = [self.modelManager buy_objectWithEntityName:BranchEntity JSONDictionary:nil];
NSRelationshipDescription *nestRelationship = [self relationshipWithName:@"nest" forEntity:BranchEntity];
NSDictionary *expected = @{ @"egg_count" : @2 };
id<BUYObject> object = [nestRelationship buy_valueForJSON:expected object:branch];
NSDictionary *actual = [nestRelationship buy_JSONForValue:object];
XCTAssertEqualObjects(actual, expected);
}
- (void)testRecursiveRelationship
{
Branch *branch = [self.modelManager buy_objectWithEntityName:BranchEntity JSONDictionary:nil];
NSRelationshipDescription *nestRelationship = [self relationshipWithName:@"nest" forEntity:BranchEntity];
NSDictionary *json = @{ @"bird_id" : @501 };
id object = [nestRelationship buy_valueForJSON:json object:branch];
XCTAssertEqualObjects(@501, [[object bird] identifier]);
id actual = [nestRelationship buy_JSONForValue:object];
XCTAssertEqualObjects(actual, json);
}
- (void)testToManyRelationship
{
Branch *branch = [self.modelManager buy_objectWithEntityName:BranchEntity JSONDictionary:nil];
NSRelationshipDescription *leafRelationship = [self relationshipWithName:@"leaves" forEntity:BranchEntity];
// Semi-random leaf objects
branch.leaves = [NSSet setWithArray:@[[self leafWithDate:[self dateWithComponents:[self november4_1605]] tags:[self tagsWithIndexes:@[@1, @5, @11]]],
[self leafWithDate:[self dateWithComponents:[self june21_1970]] tags:[self tagsWithIndexes:@[@9]]],
[self leafWithDate:[self dateWithComponents:[self jan1_2000]] tags:[self tagsWithIndexes:@[@12, @0, @8, @4]]]]];
id json = [leafRelationship buy_JSONForValue:branch.leaves];
id actual = [leafRelationship buy_valueForJSON:json object:branch];
XCTAssertEqualObjects(actual, branch.leaves);
}
- (void)testManyToManyRelationship
{
Researcher *researcher = [self.modelManager buy_objectWithEntityName:ResearchEntity JSONDictionary:nil];
NSRelationshipDescription *researchersRelationship = [self relationshipWithName:@"researchers" forEntity:BirdEntity];
XCTAssertNil([researchersRelationship buy_JSONForValue:[NSSet setWithObject:researcher]]);
}
- (void)testEntityIsPrivate
{
NSEntityDescription *forestEntity = [self entityForName:[Forest entityName]];
XCTAssertTrue([forestEntity buy_isPrivate]);
}
- (void)testFetchedProperty
{
NSFetchedPropertyDescription *fetchedProperty = [[NSFetchedPropertyDescription alloc] init];
XCTAssertNil([fetchedProperty buy_valueForJSON:nil object:nil]);
XCTAssertNil([fetchedProperty buy_JSONForValue:nil]);
}
- (Leaf *)leafWithDate:(NSDate *)date tags:(NSSet *)tags
{
Leaf *leaf = [self.modelManager buy_objectWithEntityName:[Leaf entityName] JSONDictionary:nil];
leaf.date = date;
leaf.tags = tags;
return leaf;
}
- (NSSet *)tagsWithIndexes:(NSArray *)indexes
{
static NSArray *tags;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
tags = @[@"one", @"two", @"three", @"hot", @"urgent", @"important", @"red", @"green", @"blue", @"animal", @"vegetable", @"mineral", @"fungus"];
});
return [NSSet setWithArray:[tags objectsAtIndexes:[NSIndexSet indexSetWithIndexes:indexes]]];
}
- (NSDate *)dateWithComponents:(NSDateComponents *)components
{
return [[NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian] dateFromComponents:components];
}
- (NSDateComponents *)november4_1605
{
NSDateComponents *components = [[NSDateComponents alloc] init];
components.year = 1605;
components.month = 11;
components.day = 4;
return components;
}
- (NSDateComponents *)june21_1970
{
NSDateComponents *components = [[NSDateComponents alloc] init];
components.year = 1970;
components.month = 6;
components.day = 21;
return components;
}
- (NSDateComponents *)jan1_2000
{
NSDateComponents *components = [[NSDateComponents alloc] init];
components.year = 2000;
components.month = 1;
components.day = 1;
components.second = 1;
return components;
}
@end
@implementation NSIndexSet (BUYTestAdditions)
+ (instancetype)indexSetWithIndexes:(NSArray *)indexes
{
NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
for (NSNumber *index in indexes) {
[indexSet addIndex:index.unsignedIntegerValue];
}
return indexSet;
}
@end
......@@ -78,7 +78,7 @@
- (void)testJsonDictionaryShouldShowAllProperties
{
BUYProductVariant *variant = [[BUYProductVariant alloc] initWithModelManager:[BUYModelManager modelManager] JSONDictionary:@{ @"id" : @5 }];
BUYProductVariant *variant = [[BUYProductVariant alloc] initWithModelManager:_modelManager JSONDictionary:@{ @"id" : @5 }];
_lineItem = [[BUYLineItem alloc] initWithVariant:variant];
_lineItem.quantity = [NSDecimalNumber decimalNumberWithString:@"3"];
_lineItem.price = [NSDecimalNumber decimalNumberWithString:@"5.55"];
......@@ -93,7 +93,7 @@
- (void)testUpdatingFromJsonShouldUpdateAllValues
{
BUYLineItem *lineItem = [[BUYLineItem alloc] initWithModelManager:[BUYModelManager modelManager] JSONDictionary:@{ @"id" : @"5", @"price" : @"5.99", @"quantity" : @5, @"requires_shipping" : @YES, @"title" : @"banana" }];
BUYLineItem *lineItem = [[BUYLineItem alloc] initWithModelManager:_modelManager JSONDictionary:@{ @"id" : @"5", @"price" : @"5.99", @"quantity" : @5, @"requires_shipping" : @YES, @"title" : @"banana" }];
XCTAssertEqualObjects(@"5", lineItem.identifier);
XCTAssertEqualObjects([NSDecimalNumber decimalNumberWithString:@"5.99"], lineItem.price);
XCTAssertEqualObjects([NSDecimalNumber decimalNumberWithString:@"5"], lineItem.quantity);
......
......@@ -170,7 +170,7 @@
static BUYModelManager *modelManager;
dispatch_once(&onceToken, ^{
NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:@[[NSBundle bundleForClass:self]]];
modelManager = [[BUYModelManager alloc] initWithModel:model];
modelManager = [[BUYModelManager alloc] initWithManagedObjectModel:model];
});
return modelManager;
......
......@@ -32,6 +32,8 @@
#define kBUYTestAPIKey @"api_key"
#define kBUYTestAppId @"app_id"
#define kBUYTestMerchantId @"merchant_id"
#define kBUYTestEmail @"customer_email"
#define kBUYTestPassword @"customer_password"
#define kBUYTestGiftCardCode11 @"gift_card_code_11"
#define kBUYTestGiftCardCode25 @"gift_card_code_25"
#define kBUYTestGiftCardCode50 @"gift_card_code_50"
......
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="10171" systemVersion="15E65" minimumToolsVersion="Xcode 7.0">
<entity name="Bird" representedClassName="Bird" syncable="YES">
<attribute name="colour" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="identifier" optional="YES" attributeType="Integer 64" defaultValueString="0" syncable="YES"/>
<relationship name="nests" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Nest" inverseName="bird" inverseEntity="Nest" syncable="YES">
<userInfo>
<entry key="JSONPropertyKey" value="nest_ids"/>
<entry key="key" value="value"/>
</userInfo>
</relationship>
<relationship name="researchers" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Researcher" inverseName="birds" inverseEntity="Researcher" syncable="YES"/>
</entity>
<entity name="Branch" representedClassName="Branch" syncable="YES">
<attribute name="ornaments" optional="YES" attributeType="Transformable" syncable="YES">
<userInfo>
<entry key="attributeValueClass" value="NSArray"/>
<entry key="JSONValueTransformer" value="Array"/>
</userInfo>
</attribute>
<relationship name="leaves" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="Leaf" inverseName="branch" inverseEntity="Leaf" syncable="YES"/>
<relationship name="nest" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="Nest" inverseName="branch" inverseEntity="Nest" syncable="YES"/>
<relationship name="root" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Root" inverseName="branches" inverseEntity="Root" syncable="YES"/>
</entity>
<entity name="Forest" syncable="YES">
<relationship name="trees" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Root" inverseName="forest" inverseEntity="Root" syncable="YES"/>
<userInfo>
<entry key="private" value="YES"/>
</userInfo>
</entity>
<entity name="Leaf" representedClassName="Leaf" syncable="YES">
<attribute name="date" optional="YES" attributeType="Date" syncable="YES">
<userInfo>
<entry key="JSONPropertyKey" value="createDate"/>
</userInfo>
</attribute>
<attribute name="tags" optional="YES" attributeType="Transformable" syncable="YES">
<userInfo>
<entry key="attributeValueClass" value="NSSet"/>
<entry key="JSONValueTransformer" value="Set"/>
</userInfo>
</attribute>
<relationship name="branch" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Branch" inverseName="leaves" inverseEntity="Branch" syncable="YES"/>
</entity>
<entity name="Nest" representedClassName="Nest" syncable="YES">
<attribute name="eggCount" optional="YES" attributeType="Integer 16" syncable="YES"/>
<relationship name="bird" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Bird" inverseName="nests" inverseEntity="Bird" syncable="YES">
<userInfo>
<entry key="JSONPropertyKey" value="bird_id"/>
</userInfo>
</relationship>
<relationship name="branch" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Branch" inverseName="nest" inverseEntity="Branch" syncable="YES"/>
</entity>
<entity name="Researcher" representedClassName="Researcher" syncable="YES">
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="birds" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Bird" inverseName="researchers" inverseEntity="Bird" syncable="YES"/>
</entity>
<entity name="Root" representedClassName="Root" syncable="YES">
<attribute name="age" optional="YES" attributeType="Decimal" defaultValueString="0.0" syncable="YES"/>
<attribute name="identifier" optional="YES" attributeType="Integer 64" defaultValueString="0" syncable="YES"/>
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="url" optional="YES" attributeType="Transformable" syncable="YES">
<userInfo>
<entry key="attributeValueClassName" value="NSURL"/>
<entry key="JSONTransformerName" value="BUYURL"/>
</userInfo>
</attribute>
<relationship name="branches" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="Branch" inverseName="root" inverseEntity="Branch" syncable="YES"/>
<relationship name="forest" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Forest" inverseName="trees" inverseEntity="Forest" syncable="YES"/>
</entity>
<elements>
<element name="Bird" positionX="146" positionY="111" width="128" height="103"/>
<element name="Branch" positionX="-288" positionY="-3" width="128" height="105"/>
<element name="Forest" positionX="-718" positionY="57" width="128" height="58"/>
<element name="Leaf" positionX="-72" positionY="-18" width="128" height="90"/>
<element name="Nest" positionX="-72" positionY="126" width="128" height="90"/>
<element name="Root" positionX="-504" positionY="-18" width="128" height="133"/>
<element name="Researcher" positionX="-288" positionY="81" width="128" height="75"/>
</elements>
</model>
\ No newline at end of file
//
// TestModel.h
// Mobile Buy SDK
//
// Created by Brent Gulanowski on 2016-04-27.
// Copyright © 2016 Shopify Inc. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "BUYObjectProtocol.h"
#import "BUYModelManagerProtocol.h"
@class NSManagedObjectModel;
@interface TestModelManager : NSObject<BUYModelManager>
@property (nonatomic) NSManagedObjectModel *model;
@end
@interface TestModel : NSObject<BUYObject>
@property (nonatomic, strong) TestModelManager *modelManager;
@end
@class Leaf, Nest, Researcher, Root;
@interface Bird : TestModel
@property (nonatomic) NSNumber *identifier;
@property (nonatomic) NSString *colour;
@property (nonatomic) NSSet<Researcher *> *researchers;
+ (instancetype)birdWithIdentifier:(NSNumber *)identifier;
@end
@interface Branch : TestModel
@property (nonatomic) NSArray<NSString *> *ornaments;
@property (nonatomic) NSSet<Leaf *> *leaves;
@property (nonatomic) Nest *nest;
@end
@interface Forest : TestModel
@property (nonatomic) NSSet<Root *> *trees;
@end
@interface Leaf : TestModel
@property (nonatomic) NSDate *date;
@property (nonatomic) NSSet<NSString *> *tags;
@end
@interface Nest : TestModel
@property (nonatomic) NSNumber *eggCount;
@property (nonatomic) Bird *bird;
@property (nonatomic) Branch *branch;
@end
@interface Researcher : TestModel
@property (nonatomic) NSString *name;
@property (nonatomic) NSSet<Bird *> *birds;
@end
@interface Root : TestModel
@property (nonatomic) NSNumber *identifier;
@property (nonatomic) NSDecimalNumber *age;
@property (nonatomic) NSString *name;
@property (nonatomic) NSURL *url;
@property (nonatomic) NSSet<Branch *> *branches;
@property (nonatomic) Forest *forest;
@end
//
// TestModel.m
// Mobile Buy SDK
//
// Created by Brent Gulanowski on 2016-04-27.
// Copyright © 2016 Shopify Inc. All rights reserved.
//
#import "TestModel.h"
#import "NSEntityDescription+BUYAdditions.h"
#import <CoreData/CoreData.h>
@implementation TestModelManager
- (instancetype)init
{
self = [super init];
if (self) {
self.model = [NSManagedObjectModel mergedModelFromBundles:@[[NSBundle bundleForClass:[self class]]]];
}
return self;
}
- (NSEntityDescription *)buy_entityWithName:(NSString *)entityName
{
return self.model.entitiesByName[entityName];
}
- (Class)managedModelClassForEntityName:(NSString *)entityName
{
return [[self buy_entityWithName:entityName] buy_managedObjectClass];
}
- (id<BUYObject>)buy_objectWithEntityName:(NSString *)entityName JSONDictionary:(NSDictionary *)JSON
{
return [(id)[[self managedModelClassForEntityName:entityName] alloc] initWithModelManager:self JSONDictionary:JSON];
}
- (NSArray<id<BUYObject>> *)buy_objectsWithEntityName:(NSString *)entityName JSONArray:(NSArray *)JSON {
NSMutableArray *array = [NSMutableArray array];
for (NSDictionary *dict in JSON) {
[array addObject:[self buy_objectWithEntityName:entityName JSONDictionary:dict]];
}
return array;
}
// We don't need these methods for testing
- (id<BUYObject>)buy_objectWithEntityName:(NSString *)entityName identifier:(NSNumber *)identifier {
return [entityName isEqualToString:[Bird entityName]] ? [Bird birdWithIdentifier:identifier] : nil;
}
- (NSArray<id<BUYObject>> *)buy_objectsWithEntityName:(NSString *)entityName identifiers:(NSArray *)identifiers { return nil; }
- (void)buy_refreshCacheForObject:(id<BUYObject>)object {}
- (BOOL)buy_purgeObject:(id<BUYObject>)object error:(NSError *__autoreleasing *)error { return YES; }
- (BOOL)buy_purgeObjectsWithEntityName:(NSString *)entityName matchingPredicate:(NSPredicate *)predicate { return YES; }
@end
#pragma mark -
@implementation TestModel
@synthesize modelManager=_modelManager;
- (instancetype)initWithModelManager:(id<BUYModelManager>)modelManager JSONDictionary:(NSDictionary *)dictionary
{
self = [super init];
if (self) {
self.modelManager = modelManager;
self.JSONDictionary = dictionary;
}
return self;
}
- (NSDictionary *)jsonDictionaryForCheckout
{
return self.JSONDictionary;
}
- (NSDictionary *)JSONDictionary
{
return [self.entity buy_JSONForObject:self];
}
- (void)setJSONDictionary:(NSDictionary *)JSONDictionary
{
[self.entity buy_updateObject:self withJSON:JSONDictionary];
}
- (NSDictionary *)JSONEncodedProperties
{
NSMutableDictionary *properties = [[self.entity buy_JSONEncodedProperties] mutableCopy];
for (NSString *relationshipName in self.entity.relationshipsByName) {
NSRelationshipDescription *relationship = properties[relationshipName];
if (relationship.inverseRelationship.deleteRule == NSCascadeDeleteRule) {
[properties removeObjectForKey:relationshipName];
}
}
return properties;
}
+ (NSPredicate *)fetchPredicateWithJSON:(NSDictionary *)JSONDictionary
{
return nil;
}
+ (NSString *)entityName
{
return NSStringFromClass([self class]);
}
- (NSEntityDescription *)entity
{
return [self.modelManager buy_entityWithName:[[self class] entityName]];
}
+ (BOOL)tracksDirtyProperties
{
return NO;
}
+ (BOOL)isPersistentClass
{
return NO;
}
- (BOOL)isEqual:(id)object
{
return [super isEqual:object] || [object isMemberOfClass:[self class]];
}
@end
#pragma mark - Models
@implementation Bird
+ (instancetype)birdWithIdentifier:(NSNumber *)identifier
{
Bird *bird = [[self alloc] init];
bird.identifier = identifier;
return bird;
}
- (BOOL)isEqual:(Bird *)otherModel
{
return ([super isEqual:otherModel] &&
[self.identifier isEqual:otherModel.identifier] &&
[self.colour isEqualToString:otherModel.colour]);
}
- (NSUInteger)hash
{
NSUInteger hash = self.identifier.hash;
hash = (hash << 5) ^ self.colour.hash;
return hash;
}
@end
@implementation Branch
- (BOOL)isEqual:(Branch *)otherModel
{
return ([super isEqual:otherModel] &&
[self.ornaments isEqual:otherModel.ornaments] &&
[self.leaves isEqual:otherModel.leaves]);
}
- (NSUInteger)hash
{
NSUInteger hash = self.ornaments.hash;
hash = (hash << 5) ^ self.leaves.hash;
return hash;
}
@end
@implementation Forest
- (BOOL)isEqual:(Forest *)object
{
return ([super isEqual:object] &&
[self.trees isEqual:object.trees]);
}
- (NSUInteger)hash
{
return self.trees.hash;
}
@end
@implementation Leaf
- (BOOL)isEqual:(Leaf *)object
{
return ([super isEqual:object] &&
[self.date isEqual:object.date] &&
[self.tags isEqual:object.tags]);
}
- (NSUInteger)hash
{
return (self.date.hash << 5) ^ self.tags.hash;
}
@end
@implementation Nest
- (BOOL)isEqual:(Nest *)object
{
return ([super isEqual:object] &&
[self.eggCount isEqual:object.eggCount] &&
((self.bird == nil && object.bird == nil) || [self.bird isEqual:object.bird]));
}
- (NSUInteger)hash
{
return (self.branch.hash << 5) ^ (7231UL + self.eggCount.unsignedIntegerValue);
}
@end
@implementation Researcher
- (BOOL)isEqual:(Researcher *)object
{
return ([super isEqual:object] &&
[self.name isEqual:object.name]);
}
- (NSUInteger)hash
{
return self.name.hash;
}
@end
@implementation Root
- (BOOL)isEqual:(Root *)object
{
return ([super isEqual:object] &&
[self.identifier isEqual:object.identifier] &&
[self.age isEqual:object.age] &&
[self.name isEqual:object.name] &&
[self.url isEqual:object.url] &&
[self.branches isEqual:object.branches]);
}
- (NSUInteger)hash
{
return self.identifier.hash;
}
@end
......@@ -45,6 +45,16 @@
"testCheckoutFlowUsingCreditCard_12":{"body":"","code":200,"message":"OK"},
"testCheckoutFlowUsingCreditCard_13":{"body":"{\"checkout\":{\"created_at\":\"2016-03-07T10:20:56-05:00\",\"currency\":\"CAD\",\"customer_id\":1137418883,\"email\":\"test@test.com\",\"location_id\":null,\"order_id\":2548686982,\"requires_shipping\":true,\"reservation_time\":300,\"source_name\":\"mobile_app\",\"source_identifier\":\"26915715\",\"source_url\":null,\"taxes_included\":false,\"token\":\"4bd92a3e9ab0b3adf790e0774db6c815\",\"updated_at\":\"2016-03-07T10:21:00-05:00\",\"payment_due\":\"2238.99\",\"payment_url\":\"https:\\/\\/elb.deposit.shopifycs.com\\/sessions\",\"reservation_time_left\":0,\"subtotal_price\":\"2230.99\",\"total_price\":\"2238.99\",\"total_tax\":\"0.00\",\"attributes\":[],\"note\":\"\",\"order\":{\"id\":2548686982,\"name\":\"#4269\",\"status_url\":\"https:\\/\\/checkout.shopify.com\\/9575792\\/checkouts\\/4bd92a3e9ab0b3adf790e0774db6c815\\/thank_you\"},\"order_status_url\":\"https:\\/\\/checkout.shopify.com\\/9575792\\/checkouts\\/4bd92a3e9ab0b3adf790e0774db6c815\\/thank_you\",\"privacy_policy_url\":null,\"refund_policy_url\":null,\"terms_of_service_url\":null,\"user_id\":null,\"web_url\":\"https:\\/\\/checkout.shopify.com\\/9575792\\/checkouts\\/4bd92a3e9ab0b3adf790e0774db6c815\",\"tax_lines\":[],\"line_items\":[{\"id\":\"cfdc9344bacf009e\",\"product_id\":2096063363,\"variant_id\":6030700419,\"sku\":\"\",\"vendor\":\"McCullough Group\",\"title\":\"Actinian Fur Hat\",\"variant_title\":\"Teal\",\"taxable\":false,\"requires_shipping\":true,\"price\":\"2230.99\",\"compare_at_price\":null,\"line_price\":\"2230.99\",\"properties\":{\"size\":\"large\",\"color\":\"red\"},\"quantity\":1,\"grams\":4000,\"fulfillment_service\":\"manual\",\"applied_discounts\":[]}],\"gift_cards\":[],\"shipping_rate\":{\"id\":\"shopify-Standard%20Shipping-8.00\",\"price\":\"8.00\",\"title\":\"Standard Shipping\"},\"shipping_address\":{\"id\":3479864454,\"first_name\":\"MobileBuy\",\"last_name\":\"TestBot\",\"phone\":\"1-555-555-5555\",\"company\":\"Shopify Inc.\",\"address1\":\"150 Elgin Street\",\"address2\":\"8th Floor\",\"city\":\"Ottawa\",\"province\":\"Ontario\",\"province_code\":\"ON\",\"country\":\"Canada\",\"country_code\":\"CA\",\"zip\":\"K1N5T5\"},\"credit_card\":{\"first_name\":\"John\",\"last_name\":\"Smith\",\"first_digits\":\"424242\",\"last_digits\":\"4242\",\"expiry_month\":5,\"expiry_year\":2020},\"billing_address\":{\"id\":3479864390,\"first_name\":\"MobileBuy\",\"last_name\":\"TestBot\",\"phone\":\"1-555-555-5555\",\"company\":\"Shopify Inc.\",\"address1\":\"150 Elgin Street\",\"address2\":\"8th Floor\",\"city\":\"Ottawa\",\"province\":\"Ontario\",\"province_code\":\"ON\",\"country\":\"Canada\",\"country_code\":\"CA\",\"zip\":\"K1N5T5\"},\"discount\":null}}","code":200,"message":"OK"},
"testCreateCheckoutWithExpiredDiscount_0":{"body":"{\"product_listings\":[{\"id\":2626498435,\"product_id\":2096063363,\"channel_id\":26915715,\"created_at\":\"2015-08-19T09:47:37-04:00\",\"updated_at\":\"2015-08-19T09:47:37-04:00\",\"body_html\":\"parsing the driver won't do anything, we need to calculate the online PNG program!\",\"handle\":\"actinian-fur-hat\",\"product_type\":\"enable bricks-and-clicks e-business\",\"title\":\"Actinian Fur Hat\",\"vendor\":\"McCullough Group\",\"published_at\":\"2015-08-19T09:47:37-04:00\",\"published\":true,\"available\":true,\"tags\":\"\",\"images\":[{\"id\":4277333187,\"created_at\":\"2015-08-13T14:12:44-04:00\",\"position\":1,\"updated_at\":\"2015-08-13T14:12:44-04:00\",\"product_id\":2096063363,\"src\":\"https:\\/\\/cdn.shopify.com\\/s\\/files\\/1\\/0957\\/5792\\/products\\/Kraepelin3.gif?v=1439489564\",\"variant_ids\":[]}],\"options\":[{\"id\":2524801731,\"name\":\"Color or something\",\"product_id\":2096063363,\"position\":1}],\"variants\":[{\"id\":6030700419,\"title\":\"Teal\",\"option_values\":[{\"option_id\":2524801731,\"name\":\"Color or something\",\"value\":\"Teal\"}],\"price\":\"2230.99\",\"compare_at_price\":null,\"grams\":4000,\"requires_shipping\":true,\"sku\":\"\",\"taxable\":false,\"position\":1,\"available\":true}]}]}","code":200,"message":"OK"},
"testCheckoutFlowUsingCreditCard_14": {
"body": "",
"code": 200,
"message": "OK"
},
"testCheckoutFlowUsingCreditCard_15": {
"body": "{\"checkout\":{\"created_at\":\"2015-10-08T13:34:30-04:00\",\"currency\":\"CAD\",\"customer_id\":1137418883,\"email\":\"test@test.com\",\"location_id\":null,\"order_id\":1432044675,\"requires_shipping\":true,\"reservation_time\":300,\"source_name\":\"mobile_app\",\"source_identifier\":\"26915715\",\"source_url\":null,\"taxes_included\":false,\"token\":\"89978073d2d3602c114fbd59becc5f96\",\"updated_at\":\"2015-10-08T13:34:33-04:00\",\"payment_due\":\"2238.99\",\"payment_url\":\"https:\\/\\/us-east-1-deposit.cs.shopify.com\\/sessions\",\"reservation_time_left\":0,\"subtotal_price\":\"2230.99\",\"total_price\":\"2238.99\",\"total_tax\":\"0.00\",\"order\":{\"id\":1432044675,\"name\":\"#1771\",\"status_url\":\"https:\\/\\/checkout.shopify.com\\/9575792\\/checkouts\\/89978073d2d3602c114fbd59becc5f96\\/thank_you\"},\"order_status_url\":\"https:\\/\\/checkout.shopify.com\\/9575792\\/checkouts\\/89978073d2d3602c114fbd59becc5f96\\/thank_you\",\"privacy_policy_url\":null,\"refund_policy_url\":null,\"terms_of_service_url\":null,\"user_id\":null,\"web_url\":\"https:\\/\\/checkout.shopify.com\\/9575792\\/checkouts\\/89978073d2d3602c114fbd59becc5f96\",\"tax_lines\":[],\"line_items\":[{\"id\":\"c7ec4b9bf3e6ddbd\",\"product_id\":2096063363,\"variant_id\":6030700419,\"sku\":\"\",\"vendor\":\"McCullough Group\",\"title\":\"Actinian Fur Hat\",\"variant_title\":\"Teal\",\"taxable\":false,\"requires_shipping\":true,\"price\":\"2230.99\",\"compare_at_price\":null,\"line_price\":\"2230.99\",\"properties\":{\"size\":\"large\",\"color\":\"red\"},\"quantity\":1,\"grams\":4000,\"fulfillment_service\":\"manual\"}],\"gift_cards\":[],\"shipping_rate\":{\"id\":\"shopify-Standard%20Shipping-8.00\",\"price\":\"8.00\",\"title\":\"Standard Shipping\"},\"shipping_address\":{\"first_name\":\"MobileBuy\",\"last_name\":\"TestBot\",\"phone\":\"1-555-555-5555\",\"company\":\"Shopify Inc.\",\"address1\":\"150 Elgin Street\",\"address2\":\"8th Floor\",\"city\":\"Ottawa\",\"province\":\"Ontario\",\"province_code\":\"ON\",\"country\":\"Canada\",\"country_code\":\"CA\",\"zip\":\"K1N5T5\"},\"credit_card\":{\"first_name\":\"John\",\"last_name\":\"Smith\",\"first_digits\":\"424242\",\"last_digits\":\"4242\",\"expiry_month\":5,\"expiry_year\":2020},\"billing_address\":{\"first_name\":\"MobileBuy\",\"last_name\":\"TestBot\",\"phone\":\"1-555-555-5555\",\"company\":\"Shopify Inc.\",\"address1\":\"150 Elgin Street\",\"address2\":\"8th Floor\",\"city\":\"Ottawa\",\"province\":\"Ontario\",\"province_code\":\"ON\",\"country\":\"Canada\",\"country_code\":\"CA\",\"zip\":\"K1N5T5\"},\"discount\":null}}",
"code": 200,
"message": "OK"
},
"testCreateCheckoutWithExpiredDiscount_1":{"body":"{\"errors\":{\"checkout\":{\"line_items\":[],\"source_name\":[],\"reservation_time\":[],\"discount\":{\"amount\":[{\"code\":\"greater_than\",\"message\":\"must be greater than 0\",\"options\":{\"value\":0.0,\"count\":0}}],\"code\":[{\"code\":\"discount_not_found\",\"message\":\"Unable to find a valid discount matching the code entered\",\"options\":{}}]}}}}","code":422,"message":"Unprocessable Entity"},
"testCreateCheckoutWithNonExistentDiscount_0":{"body":"{\"product_listings\":[{\"id\":2626498435,\"product_id\":2096063363,\"channel_id\":26915715,\"created_at\":\"2015-08-19T09:47:37-04:00\",\"updated_at\":\"2015-08-19T09:47:37-04:00\",\"body_html\":\"parsing the driver won't do anything, we need to calculate the online PNG program!\",\"handle\":\"actinian-fur-hat\",\"product_type\":\"enable bricks-and-clicks e-business\",\"title\":\"Actinian Fur Hat\",\"vendor\":\"McCullough Group\",\"published_at\":\"2015-08-19T09:47:37-04:00\",\"published\":true,\"available\":true,\"tags\":\"\",\"images\":[{\"id\":4277333187,\"created_at\":\"2015-08-13T14:12:44-04:00\",\"position\":1,\"updated_at\":\"2015-08-13T14:12:44-04:00\",\"product_id\":2096063363,\"src\":\"https:\\/\\/cdn.shopify.com\\/s\\/files\\/1\\/0957\\/5792\\/products\\/Kraepelin3.gif?v=1439489564\",\"variant_ids\":[]}],\"options\":[{\"id\":2524801731,\"name\":\"Color or something\",\"product_id\":2096063363,\"position\":1}],\"variants\":[{\"id\":6030700419,\"title\":\"Teal\",\"option_values\":[{\"option_id\":2524801731,\"name\":\"Color or something\",\"value\":\"Teal\"}],\"price\":\"2230.99\",\"compare_at_price\":null,\"grams\":4000,\"requires_shipping\":true,\"sku\":\"\",\"taxable\":false,\"position\":1,\"available\":true}]}]}","code":200,"message":"OK"},
"testCreateCheckoutWithNonExistentDiscount_1":{"body":"{\"errors\":{\"checkout\":{\"line_items\":[],\"source_name\":[],\"reservation_time\":[],\"discount\":{\"amount\":[{\"code\":\"greater_than\",\"message\":\"must be greater than 0\",\"options\":{\"value\":0.0,\"count\":0}}],\"code\":[{\"code\":\"discount_not_found\",\"message\":\"Unable to find a valid discount matching the code entered\",\"options\":{}}]}}}}","code":422,"message":"Unprocessable Entity"},
......@@ -109,4 +119,9 @@
"testGetProductsWithOneInvalidId_0":{"body":"{\"product_listings\":[{\"id\":2626498435,\"product_id\":2096063363,\"channel_id\":26915715,\"created_at\":\"2015-08-19T09:47:37-04:00\",\"updated_at\":\"2015-08-19T09:47:37-04:00\",\"body_html\":\"parsing the driver won't do anything, we need to calculate the online PNG program!\",\"handle\":\"actinian-fur-hat\",\"product_type\":\"enable bricks-and-clicks e-business\",\"title\":\"Actinian Fur Hat\",\"vendor\":\"McCullough Group\",\"published_at\":\"2015-08-19T09:47:37-04:00\",\"published\":true,\"available\":true,\"tags\":\"\",\"images\":[{\"id\":4277333187,\"created_at\":\"2015-08-13T14:12:44-04:00\",\"position\":1,\"updated_at\":\"2015-08-13T14:12:44-04:00\",\"product_id\":2096063363,\"src\":\"https:\\/\\/cdn.shopify.com\\/s\\/files\\/1\\/0957\\/5792\\/products\\/Kraepelin3.gif?v=1439489564\",\"variant_ids\":[]}],\"options\":[{\"id\":2524801731,\"name\":\"Color or something\",\"product_id\":2096063363,\"position\":1}],\"variants\":[{\"id\":6030700419,\"title\":\"Teal\",\"option_values\":[{\"option_id\":2524801731,\"name\":\"Color or something\",\"value\":\"Teal\"}],\"price\":\"2230.99\",\"compare_at_price\":null,\"grams\":4000,\"requires_shipping\":true,\"sku\":\"\",\"taxable\":false,\"position\":1,\"available\":true}]}]}","code":200,"message":"OK"},
"testGetShop_0":{"body":"{\"id\":9575792,\"name\":\"MobileBuySDKTestShop\",\"city\":\"Richmond Hill\",\"province\":\"Ontario\",\"country\":\"CA\",\"currency\":\"CAD\",\"domain\":\"mobilebuysdktestshop.myshopify.com\",\"url\":\"http:\\/\\/mobilebuysdktestshop.myshopify.com\",\"myshopify_domain\":\"mobilebuysdktestshop.myshopify.com\",\"description\":\"\",\"ships_to_countries\":[\"*\",\"CA\"],\"money_format\":\"${{amount}}\",\"published_collections_count\":1,\"published_products_count\":11}","code":200,"message":"OK"},
"testGetCollectionPage_0":{"body":"{\"collection_listings\":[{\"id\":171459075,\"collection_id\":109931075,\"channel_id\":26915715,\"created_at\":\"2015-08-19T09:47:00-04:00\",\"updated_at\":\"2015-08-19T15:43:26-04:00\",\"body_html\":\"\",\"handle\":\"frontpage\",\"image\":null,\"title\":\"Frontpage\",\"published_at\":\"2015-08-19T09:47:00-04:00\",\"published\":true}]}","code":200,"message":"OK"},
"testGetOutOfIndexCollectionPage_0":{"body":"{\"collection_listings\":[]}","code":200,"message":"OK"}}
\ No newline at end of file
"testGetOutOfIndexCollectionPage_0":{"body":"{\"collection_listings\":[]}","code":200,"message":"OK"},
"testOutOfStockVariant":{"body":"{\"errors\":{\"checkout\":{\"line_items\":[null,{\"quantity\":[{\"code\":\"not_enough_in_stock\",\"message\":\"Not enough items available. Only 0 left.\",\"options\":{\"remaining\":0}}]},{\"quantity\":[{\"code\":\"not_enough_in_stock\",\"message\":\"Not enough items available. Only 0 left.\",\"options\":{\"remaining\":0}}]}],\"source_name\":[],\"reservation_time\":[]}}}"},
"testCustomerDuplicateEmail":{"body":"{\"errors\":{\"customer\":{\"email\":[{\"code\":\"taken\",\"message\":\"has already been taken\",\"options\":{\"rescue_from_duplicate\":true,\"value\":\"asd@asd.com\"}}]}}}"},
"testCustomerInvalidEmailPassword":{"body":"{\"errors\":{\"customer\":{\"password\":[{\"code\":\"too_short\",\"message\":\"is too short (minimum is 5 characters)\",\"options\":{\"count\":5}}],\"password_confirmation\":[{\"code\":\"confirmation\",\"message\":\"doesn't match Password\",\"options\":{\"attribute\":\"Password\"}}],\"email\":[{\"code\":\"invalid\",\"message\":\"is invalid\",\"options\":{}}]}}}"},
"testCustomerLogin":{"body":"{\"customer\":{\"id\":2529265094,\"email\":\"asd@asd.com\",\"default_address\":{\"id\":2839567814,\"first_name\":\"Fast\",\"last_name\":\"Add\",\"company\":\"Adidas\",\"address1\":\"Sass\",\"address2\":\"12\",\"city\":\"Qsdasd\",\"province\":null,\"country\":\"Bouvet Island\",\"zip\":\"24124124\",\"phone\":\"123124124\",\"name\":\"Fast Add\",\"province_code\":null,\"country_code\":\"BV\",\"country_name\":\"Bouvet Island\",\"default\":true},\"verified_email\":true,\"accepts_marketing\":false,\"first_name\":\"Fast\",\"last_name\":\"Add\",\"orders_count\":9,\"total_spent\":\"375.00\",\"created_at\":\"2016-02-16T14:42:24-05:00\",\"updated_at\":\"2016-03-10T16:34:04-05:00\",\"state\":\"enabled\",\"last_order_id\":2566266118,\"last_order_name\":\"#1137\",\"addresses\":[{\"id\":2785988486,\"first_name\":\"AA\",\"last_name\":\"A\",\"phone\":\"\",\"company\":\"\",\"address1\":\"Assiniboine Road\",\"address2\":\"\",\"city\":\"Toronto\",\"province\":\"Ontario\",\"province_code\":\"ON\",\"country\":\"Canada\",\"country_code\":\"CA\",\"zip\":\"M3J1E1\"},{\"id\":2839567814,\"first_name\":\"Fast\",\"last_name\":\"Add\",\"phone\":\"123124124\",\"company\":\"Adidas\",\"address1\":\"Sass\",\"address2\":\"12\",\"city\":\"Qsdasd\",\"province\":null,\"province_code\":null,\"country\":\"Bouvet Island\",\"country_code\":\"BV\",\"zip\":\"24124124\"}],\"multipass_identifier\":null,\"tax_exempt\":false}}","code":200,"message":"OK"}
}
......@@ -5,10 +5,17 @@
"channel_id": "",
"app_id": "",
"merchant_id": "",
"customer_email": "",
"customer_password": "",
"product_ids": [
"",
""
],
"variants": {
"variant_untracked_id": "",
"variant_inventory1_id": "",
"variant_soldout_id": ""
},
"collection_id": "",
"gift_cards": {
"ValidGiftCard11": {
......
......@@ -80,8 +80,6 @@
8491103A1CCE718100E53B93 /* BUYExceptionAdditionsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 849110391CCE718100E53B93 /* BUYExceptionAdditionsTests.m */; };
8491103C1CCE731900E53B93 /* BUYURLAdditionsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8491103B1CCE731900E53B93 /* BUYURLAdditionsTests.m */; };
8491103E1CCE988600E53B93 /* BUYFontAdditionsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8491103D1CCE988600E53B93 /* BUYFontAdditionsTests.m */; };
849110401CCE9DFB00E53B93 /* BUYPropertyDescriptionAdditionsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8491103F1CCE9DFB00E53B93 /* BUYPropertyDescriptionAdditionsTests.m */; };
849110421CCE9E0A00E53B93 /* BUYEntityDescriptionAdditionsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 849110411CCE9E0A00E53B93 /* BUYEntityDescriptionAdditionsTests.m */; };
849110441CCE9F3F00E53B93 /* BUYTransformerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 849110431CCE9F3F00E53B93 /* BUYTransformerTests.m */; };
8491104A1CCEA85C00E53B93 /* BUYObserverTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 849110461CCEA85C00E53B93 /* BUYObserverTests.m */; };
84980F291CB75AC200CFAB58 /* BUYObjectProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 84980F281CB75AC200CFAB58 /* BUYObjectProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
......@@ -120,6 +118,28 @@
849810971CB7E07900CFAB58 /* BUYFlatCollectionTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 849810901CB7E07900CFAB58 /* BUYFlatCollectionTransformer.h */; };
849810981CB7E07900CFAB58 /* BUYFlatCollectionTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 849810911CB7E07900CFAB58 /* BUYFlatCollectionTransformer.m */; };
849810991CB7E07900CFAB58 /* BUYFlatCollectionTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 849810911CB7E07900CFAB58 /* BUYFlatCollectionTransformer.m */; };
8498DCAA1CDD1B2500BD12A8 /* BUYShopifyErrorCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = 8498DCA91CDD1B1C00BD12A8 /* BUYShopifyErrorCodes.h */; settings = {ATTRIBUTES = (Public, ); }; };
8498DCAB1CDD1B2600BD12A8 /* BUYShopifyErrorCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = 8498DCA91CDD1B1C00BD12A8 /* BUYShopifyErrorCodes.h */; settings = {ATTRIBUTES = (Public, ); }; };
8498DCAC1CDD1B2F00BD12A8 /* BUYError+BUYAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 8498DCA71CDD1B1C00BD12A8 /* BUYError+BUYAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
8498DCAD1CDD1B2F00BD12A8 /* BUYError+BUYAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 8498DCA81CDD1B1C00BD12A8 /* BUYError+BUYAdditions.m */; };
8498DCAE1CDD1B2F00BD12A8 /* BUYError+BUYAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 8498DCA71CDD1B1C00BD12A8 /* BUYError+BUYAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
8498DCAF1CDD1B2F00BD12A8 /* BUYError+BUYAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 8498DCA81CDD1B1C00BD12A8 /* BUYError+BUYAdditions.m */; };
8498DCB31CDD1B5400BD12A8 /* BUYClient_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 8498DCB01CDD1B4A00BD12A8 /* BUYClient_Internal.h */; };
8498DCB41CDD1B5400BD12A8 /* BUYClient+Customers.h in Headers */ = {isa = PBXBuildFile; fileRef = 8498DCB11CDD1B4A00BD12A8 /* BUYClient+Customers.h */; settings = {ATTRIBUTES = (Public, ); }; };
8498DCB51CDD1B5400BD12A8 /* BUYClient+Customers.m in Sources */ = {isa = PBXBuildFile; fileRef = 8498DCB21CDD1B4A00BD12A8 /* BUYClient+Customers.m */; };
8498DCB61CDD1B5400BD12A8 /* BUYClient_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 8498DCB01CDD1B4A00BD12A8 /* BUYClient_Internal.h */; };
8498DCB71CDD1B5400BD12A8 /* BUYClient+Customers.h in Headers */ = {isa = PBXBuildFile; fileRef = 8498DCB11CDD1B4A00BD12A8 /* BUYClient+Customers.h */; settings = {ATTRIBUTES = (Public, ); }; };
8498DCB81CDD1B5400BD12A8 /* BUYClient+Customers.m in Sources */ = {isa = PBXBuildFile; fileRef = 8498DCB21CDD1B4A00BD12A8 /* BUYClient+Customers.m */; };
8498DCBB1CDD1FA400BD12A8 /* BUYAccountCredentials.h in Headers */ = {isa = PBXBuildFile; fileRef = 8498DCB91CDD1FA400BD12A8 /* BUYAccountCredentials.h */; settings = {ATTRIBUTES = (Public, ); }; };
8498DCBC1CDD1FA400BD12A8 /* BUYAccountCredentials.h in Headers */ = {isa = PBXBuildFile; fileRef = 8498DCB91CDD1FA400BD12A8 /* BUYAccountCredentials.h */; settings = {ATTRIBUTES = (Public, ); }; };
8498DCBD1CDD1FA400BD12A8 /* BUYAccountCredentials.m in Sources */ = {isa = PBXBuildFile; fileRef = 8498DCBA1CDD1FA400BD12A8 /* BUYAccountCredentials.m */; };
8498DCBE1CDD1FA400BD12A8 /* BUYAccountCredentials.m in Sources */ = {isa = PBXBuildFile; fileRef = 8498DCBA1CDD1FA400BD12A8 /* BUYAccountCredentials.m */; };
8498DCC91CDD208200BD12A8 /* BUYClientTest_Customer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8498DCBF1CDD208200BD12A8 /* BUYClientTest_Customer.m */; };
8498DCCB1CDD208200BD12A8 /* BUYCoreDataModelAdditionsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8498DCC11CDD208200BD12A8 /* BUYCoreDataModelAdditionsTests.m */; };
8498DCCF1CDD208200BD12A8 /* BUYTestModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 8498DCC51CDD208200BD12A8 /* BUYTestModel.xcdatamodeld */; };
8498DCD01CDD208200BD12A8 /* TestModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 8498DCC81CDD208200BD12A8 /* TestModel.m */; };
84B0A7201CDD261100253EB0 /* BUYSerializable.m in Sources */ = {isa = PBXBuildFile; fileRef = 84B0A71F1CDD261100253EB0 /* BUYSerializable.m */; };
84B0A7211CDD261100253EB0 /* BUYSerializable.m in Sources */ = {isa = PBXBuildFile; fileRef = 84B0A71F1CDD261100253EB0 /* BUYSerializable.m */; };
84CD7C2D1CC65D5A00B6EE61 /* _BUYCheckoutAttribute.h in Headers */ = {isa = PBXBuildFile; fileRef = 84CD7C2B1CC65D5500B6EE61 /* _BUYCheckoutAttribute.h */; settings = {ATTRIBUTES = (Public, ); }; };
84CD7C2E1CC65D5A00B6EE61 /* _BUYCheckoutAttribute.h in Headers */ = {isa = PBXBuildFile; fileRef = 84CD7C2B1CC65D5500B6EE61 /* _BUYCheckoutAttribute.h */; settings = {ATTRIBUTES = (Public, ); }; };
84CD7C2F1CC65D7B00B6EE61 /* _BUYCheckoutAttribute.m in Sources */ = {isa = PBXBuildFile; fileRef = 84CD7C2C1CC65D5500B6EE61 /* _BUYCheckoutAttribute.m */; };
......@@ -214,9 +234,9 @@
84DD12CA1CC63FEE00A2442D /* _BUYShippingRate.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D915B31CC0417700D334FB /* _BUYShippingRate.m */; };
84DD12CB1CC63FEE00A2442D /* _BUYTaxLine.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D915B41CC0417700D334FB /* _BUYTaxLine.h */; settings = {ATTRIBUTES = (Public, ); }; };
84DD12CC1CC63FEE00A2442D /* _BUYTaxLine.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D915B51CC0417700D334FB /* _BUYTaxLine.m */; };
84DD12CD1CC6401400A2442D /* BUYCustomer.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D915721CC0416800D334FB /* BUYCustomer.h */; };
84DD12CD1CC6401400A2442D /* BUYCustomer.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D915721CC0416800D334FB /* BUYCustomer.h */; settings = {ATTRIBUTES = (Public, ); }; };
84DD12CE1CC6401400A2442D /* BUYCustomer.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D915731CC0416800D334FB /* BUYCustomer.m */; };
84DD12CF1CC6401400A2442D /* BUYCustomer.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D915721CC0416800D334FB /* BUYCustomer.h */; };
84DD12CF1CC6401400A2442D /* BUYCustomer.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D915721CC0416800D334FB /* BUYCustomer.h */; settings = {ATTRIBUTES = (Public, ); }; };
84DD12D01CC6401400A2442D /* BUYCustomer.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D915731CC0416800D334FB /* BUYCustomer.m */; };
9003969B1B601DF400226B73 /* BUYCartLineItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 900396991B601DF400226B73 /* BUYCartLineItem.h */; settings = {ATTRIBUTES = (Public, ); }; };
9003969C1B601DF400226B73 /* BUYCartLineItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 9003969A1B601DF400226B73 /* BUYCartLineItem.m */; };
......@@ -522,6 +542,7 @@
841ADDFC1CB6C942000004B0 /* NSURL+BUYAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSURL+BUYAdditions.m"; sourceTree = "<group>"; };
841ADDFD1CB6C942000004B0 /* NSURLComponents+BUYAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURLComponents+BUYAdditions.h"; sourceTree = "<group>"; };
841ADDFE1CB6C942000004B0 /* NSURLComponents+BUYAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSURLComponents+BUYAdditions.m"; sourceTree = "<group>"; };
8465CF441CC13CFE0010B2E6 /* Templates */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Templates; sourceTree = "<group>"; };
8491102E1CCE708900E53B93 /* BUYArrayAdditionsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYArrayAdditionsTests.m; sourceTree = "<group>"; };
8491102F1CCE708900E53B93 /* BUYRegularExpressionAdditionsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYRegularExpressionAdditionsTests.m; sourceTree = "<group>"; };
849110301CCE708900E53B93 /* BUYStringAdditionsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYStringAdditionsTests.m; sourceTree = "<group>"; };
......@@ -529,11 +550,8 @@
849110391CCE718100E53B93 /* BUYExceptionAdditionsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYExceptionAdditionsTests.m; sourceTree = "<group>"; };
8491103B1CCE731900E53B93 /* BUYURLAdditionsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYURLAdditionsTests.m; sourceTree = "<group>"; };
8491103D1CCE988600E53B93 /* BUYFontAdditionsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYFontAdditionsTests.m; sourceTree = "<group>"; };
8491103F1CCE9DFB00E53B93 /* BUYPropertyDescriptionAdditionsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYPropertyDescriptionAdditionsTests.m; sourceTree = "<group>"; };
849110411CCE9E0A00E53B93 /* BUYEntityDescriptionAdditionsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYEntityDescriptionAdditionsTests.m; sourceTree = "<group>"; };
849110431CCE9F3F00E53B93 /* BUYTransformerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYTransformerTests.m; sourceTree = "<group>"; };
849110461CCEA85C00E53B93 /* BUYObserverTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYObserverTests.m; sourceTree = "<group>"; };
8465CF441CC13CFE0010B2E6 /* Templates */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Templates; sourceTree = "<group>"; };
84980F281CB75AC200CFAB58 /* BUYObjectProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BUYObjectProtocol.h; sourceTree = "<group>"; };
84980F2B1CB75B5E00CFAB58 /* BUYModelManagerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BUYModelManagerProtocol.h; sourceTree = "<group>"; };
84980F2E1CB75C2900CFAB58 /* NSEntityDescription+BUYAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSEntityDescription+BUYAdditions.h"; sourceTree = "<group>"; };
......@@ -552,6 +570,20 @@
8498108F1CB7E07900CFAB58 /* BUYDeliveryRangeTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYDeliveryRangeTransformer.m; sourceTree = "<group>"; };
849810901CB7E07900CFAB58 /* BUYFlatCollectionTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BUYFlatCollectionTransformer.h; sourceTree = "<group>"; };
849810911CB7E07900CFAB58 /* BUYFlatCollectionTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYFlatCollectionTransformer.m; sourceTree = "<group>"; };
8498DCA71CDD1B1C00BD12A8 /* BUYError+BUYAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "BUYError+BUYAdditions.h"; sourceTree = "<group>"; };
8498DCA81CDD1B1C00BD12A8 /* BUYError+BUYAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "BUYError+BUYAdditions.m"; sourceTree = "<group>"; };
8498DCA91CDD1B1C00BD12A8 /* BUYShopifyErrorCodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BUYShopifyErrorCodes.h; sourceTree = "<group>"; };
8498DCB01CDD1B4A00BD12A8 /* BUYClient_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BUYClient_Internal.h; sourceTree = "<group>"; };
8498DCB11CDD1B4A00BD12A8 /* BUYClient+Customers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "BUYClient+Customers.h"; sourceTree = "<group>"; };
8498DCB21CDD1B4A00BD12A8 /* BUYClient+Customers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "BUYClient+Customers.m"; sourceTree = "<group>"; };
8498DCB91CDD1FA400BD12A8 /* BUYAccountCredentials.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BUYAccountCredentials.h; sourceTree = "<group>"; };
8498DCBA1CDD1FA400BD12A8 /* BUYAccountCredentials.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYAccountCredentials.m; sourceTree = "<group>"; };
8498DCBF1CDD208200BD12A8 /* BUYClientTest_Customer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYClientTest_Customer.m; sourceTree = "<group>"; };
8498DCC11CDD208200BD12A8 /* BUYCoreDataModelAdditionsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYCoreDataModelAdditionsTests.m; sourceTree = "<group>"; };
8498DCC61CDD208200BD12A8 /* BUYTestModel.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = BUYTestModel.xcdatamodel; sourceTree = "<group>"; };
8498DCC71CDD208200BD12A8 /* TestModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestModel.h; sourceTree = "<group>"; };
8498DCC81CDD208200BD12A8 /* TestModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestModel.m; sourceTree = "<group>"; };
84B0A71F1CDD261100253EB0 /* BUYSerializable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYSerializable.m; sourceTree = "<group>"; };
84CD7C2B1CC65D5500B6EE61 /* _BUYCheckoutAttribute.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _BUYCheckoutAttribute.h; sourceTree = "<group>"; };
84CD7C2C1CC65D5500B6EE61 /* _BUYCheckoutAttribute.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _BUYCheckoutAttribute.m; sourceTree = "<group>"; };
84D915411CC0359700D334FB /* BUYObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BUYObserver.h; sourceTree = "<group>"; };
......@@ -882,14 +914,14 @@
84D9156F1CC0416800D334FB /* _BUYProductVariant.m */,
84D915701CC0416800D334FB /* _BUYShop.h */,
84D915711CC0416800D334FB /* _BUYShop.m */,
84D915721CC0416800D334FB /* BUYCustomer.h */,
84D915731CC0416800D334FB /* BUYCustomer.m */,
F773744719C77A210039681C /* BUYCart.h */,
F773744819C77A210039681C /* BUYCart.m */,
900396991B601DF400226B73 /* BUYCartLineItem.h */,
9003969A1B601DF400226B73 /* BUYCartLineItem.m */,
BEB74A8E1B55A3D00005A300 /* BUYCollection.h */,
9089CC5D1BB48D06009726D6 /* BUYCollection.m */,
84D915721CC0416800D334FB /* BUYCustomer.h */,
84D915731CC0416800D334FB /* BUYCustomer.m */,
2AF52A791A700B0A0087DB2C /* BUYImageLink.h */,
2AF52A7A1A700B0A0087DB2C /* BUYImageLink.m */,
F7FDA16C19C939FF00AF4E93 /* BUYLineItem.h */,
......@@ -966,22 +998,23 @@
8491102E1CCE708900E53B93 /* BUYArrayAdditionsTests.m */,
90F592FA1B0D5F4C0026B382 /* BUYCartTest.m */,
90F592FB1B0D5F4C0026B382 /* BUYCheckoutTest.m */,
8498DCBF1CDD208200BD12A8 /* BUYClientTest_Customer.m */,
90F592FC1B0D5F4C0026B382 /* BUYClientTest_Storefront.m */,
90F592FD1B0D5F4C0026B382 /* BUYClientTest.m */,
BEB9AE7A1BA866D000575F8A /* BUYClientTestBase.h */,
BEB9AE7C1BA8685600575F8A /* BUYClientTestBase.m */,
8498DCC11CDD208200BD12A8 /* BUYCoreDataModelAdditionsTests.m */,
849110341CCE70CE00E53B93 /* BUYDictionaryAdditionsTests.m */,
849110411CCE9E0A00E53B93 /* BUYEntityDescriptionAdditionsTests.m */,
849110391CCE718100E53B93 /* BUYExceptionAdditionsTests.m */,
8491103D1CCE988600E53B93 /* BUYFontAdditionsTests.m */,
90F592F81B0D5F4C0026B382 /* BUYIntegrationTest.m */,
90F592FE1B0D5F4C0026B382 /* BUYLineItemTest.m */,
90F592FF1B0D5F4C0026B382 /* BUYObjectTests.m */,
849110461CCEA85C00E53B93 /* BUYObserverTests.m */,
8491103F1CCE9DFB00E53B93 /* BUYPropertyDescriptionAdditionsTests.m */,
8491102F1CCE708900E53B93 /* BUYRegularExpressionAdditionsTests.m */,
849110301CCE708900E53B93 /* BUYStringAdditionsTests.m */,
90F593001B0D5F4C0026B382 /* BUYTestConstants.h */,
8498DCC51CDD208200BD12A8 /* BUYTestModel.xcdatamodeld */,
849110431CCE9F3F00E53B93 /* BUYTransformerTests.m */,
8491103B1CCE731900E53B93 /* BUYURLAdditionsTests.m */,
BE6C07051BB1E46900BD9F7B /* mocked_responses.json */,
......@@ -990,6 +1023,8 @@
906CF1AE1B8B660F001F7D5B /* PKContact Test Objects */,
90F592EE1B0D5EFE0026B382 /* Supporting Files */,
BEB9AE721BA73E6C00575F8A /* test_shop_data.json */,
8498DCC71CDD208200BD12A8 /* TestModel.h */,
8498DCC81CDD208200BD12A8 /* TestModel.m */,
);
path = "Mobile Buy SDK Tests";
sourceTree = "<group>";
......@@ -1132,6 +1167,8 @@
F773744419C779C20039681C /* Models */ = {
isa = PBXGroup;
children = (
8498DCB91CDD1FA400BD12A8 /* BUYAccountCredentials.h */,
8498DCBA1CDD1FA400BD12A8 /* BUYAccountCredentials.m */,
F77374AA19C796BD0039681C /* BUYCreditCard.h */,
F77374AB19C796BD0039681C /* BUYCreditCard.m */,
BE47340D1B66C4EF00AA721A /* BUYError.h */,
......@@ -1145,6 +1182,7 @@
84D915411CC0359700D334FB /* BUYObserver.h */,
84D915421CC0359700D334FB /* BUYObserver.m */,
F76CFF1E19CB7C500079C703 /* BUYSerializable.h */,
84B0A71F1CDD261100253EB0 /* BUYSerializable.m */,
84D915561CC040C300D334FB /* Mobile Buy SDK.xcdatamodeld */,
841ADE2B1CB6F320000004B0 /* Persistent */,
84980F271CB75A7A00CFAB58 /* Protocols */,
......@@ -1161,10 +1199,13 @@
F70CE40E1A8BF1D90055BEB8 /* BUYApplePayAdditions.m */,
BE33B4ED1B15FF4D0067982B /* BUYApplePayHelpers.h */,
BE33B4EE1B15FF4D0067982B /* BUYApplePayHelpers.m */,
8498DCA71CDD1B1C00BD12A8 /* BUYError+BUYAdditions.h */,
8498DCA81CDD1B1C00BD12A8 /* BUYError+BUYAdditions.m */,
900E7C811B5DA32F006F3C81 /* BUYImageKit.h */,
900E7C821B5DA32F006F3C81 /* BUYImageKit.m */,
2AF52A731A700AC80087DB2C /* BUYRuntime.h */,
2AF52A741A700AC80087DB2C /* BUYRuntime.m */,
8498DCA91CDD1B1C00BD12A8 /* BUYShopifyErrorCodes.h */,
909944471B71B76800C40A33 /* UIFont+BUYAdditions.h */,
909944481B71B76800C40A33 /* UIFont+BUYAdditions.m */,
);
......@@ -1202,6 +1243,9 @@
F7FDA16F19C93F6100AF4E93 /* Data */ = {
isa = PBXGroup;
children = (
8498DCB01CDD1B4A00BD12A8 /* BUYClient_Internal.h */,
8498DCB11CDD1B4A00BD12A8 /* BUYClient+Customers.h */,
8498DCB21CDD1B4A00BD12A8 /* BUYClient+Customers.m */,
F7FDA17019C93F6F00AF4E93 /* BUYClient.h */,
F7FDA17119C93F6F00AF4E93 /* BUYClient.m */,
);
......@@ -1220,6 +1264,7 @@
84DD12AF1CC63FE600A2442D /* _BUYShop.h in Headers */,
84DD12C71CC63FEE00A2442D /* _BUYMaskedCreditCard.h in Headers */,
84DD12C11CC63FEE00A2442D /* _BUYCheckout.h in Headers */,
8498DCBC1CDD1FA400BD12A8 /* BUYAccountCredentials.h in Headers */,
84DD12CB1CC63FEE00A2442D /* _BUYTaxLine.h in Headers */,
84DD12C51CC63FEE00A2442D /* _BUYGiftCard.h in Headers */,
84DD12A91CC63FE600A2442D /* _BUYOrder.h in Headers */,
......@@ -1246,9 +1291,11 @@
9019312E1BC5B9BC00D1134E /* BUYProductDescriptionCell.h in Headers */,
9019312F1BC5B9BC00D1134E /* BUYLineItem.h in Headers */,
90C856B51BD6B0F300936926 /* Buy.h in Headers */,
8498DCAB1CDD1B2600BD12A8 /* BUYShopifyErrorCodes.h in Headers */,
901931311BC5B9BC00D1134E /* BUYProductViewHeaderOverlay.h in Headers */,
901931341BC5B9BC00D1134E /* BUYViewController.h in Headers */,
901931351BC5B9BC00D1134E /* BUYDiscount.h in Headers */,
8498DCB71CDD1B5400BD12A8 /* BUYClient+Customers.h in Headers */,
901931361BC5B9BC00D1134E /* BUYProductView.h in Headers */,
849810931CB7E07900CFAB58 /* BUYDeliveryRangeTransformer.h in Headers */,
901931371BC5B9BC00D1134E /* BUYTaxLine.h in Headers */,
......@@ -1265,6 +1312,7 @@
901931421BC5B9BC00D1134E /* BUYMaskedCreditCard.h in Headers */,
901931431BC5B9BC00D1134E /* BUYProductViewHeader.h in Headers */,
901931441BC5B9BC00D1134E /* BUYProductViewHeaderBackgroundImageView.h in Headers */,
8498DCB61CDD1B5400BD12A8 /* BUYClient_Internal.h in Headers */,
901931461BC5B9BC00D1134E /* BUYCheckoutButton.h in Headers */,
901931471BC5B9BC00D1134E /* BUYPaymentButton.h in Headers */,
901931481BC5B9BC00D1134E /* BUYImageView.h in Headers */,
......@@ -1311,6 +1359,7 @@
901931631BC5B9BC00D1134E /* BUYGradientView.h in Headers */,
849810971CB7E07900CFAB58 /* BUYFlatCollectionTransformer.h in Headers */,
901931641BC5B9BC00D1134E /* BUYCartLineItem.h in Headers */,
8498DCAE1CDD1B2F00BD12A8 /* BUYError+BUYAdditions.h in Headers */,
901931661BC5B9BC00D1134E /* BUYCheckout.h in Headers */,
901931671BC5B9BC00D1134E /* BUYCart.h in Headers */,
901931681BC5B9BC00D1134E /* BUYProductViewController.h in Headers */,
......@@ -1329,6 +1378,7 @@
84DD12951CC63FE600A2442D /* _BUYProductVariant.h in Headers */,
84DD12831CC63FE600A2442D /* _BUYCartLineItem.h in Headers */,
84DD12B11CC63FEE00A2442D /* _BUYAddress.h in Headers */,
8498DCBB1CDD1FA400BD12A8 /* BUYAccountCredentials.h in Headers */,
84DD12971CC63FE600A2442D /* _BUYShop.h in Headers */,
84DD12B91CC63FEE00A2442D /* _BUYMaskedCreditCard.h in Headers */,
84DD12B31CC63FEE00A2442D /* _BUYCheckout.h in Headers */,
......@@ -1355,9 +1405,11 @@
BEB74A6B1B55641B0005A300 /* BUYProductDescriptionCell.h in Headers */,
BE9A645B1B503CDC0033E558 /* BUYLineItem.h in Headers */,
907874991B7276BA0023775B /* BUYProductViewHeaderOverlay.h in Headers */,
8498DCAA1CDD1B2500BD12A8 /* BUYShopifyErrorCodes.h in Headers */,
BE9A64801B503D990033E558 /* BUYViewController.h in Headers */,
BE9A644F1B503CA90033E558 /* BUYDiscount.h in Headers */,
900396AC1B627CB900226B73 /* BUYProductView.h in Headers */,
8498DCB41CDD1B5400BD12A8 /* BUYClient+Customers.h in Headers */,
BE9A64511B503CB00033E558 /* BUYTaxLine.h in Headers */,
849810921CB7E07900CFAB58 /* BUYDeliveryRangeTransformer.h in Headers */,
BEB74A7D1B5564870005A300 /* BUYVariantSelectionViewController.h in Headers */,
......@@ -1374,6 +1426,7 @@
BEB74A731B5564350005A300 /* BUYProductViewHeader.h in Headers */,
BEB74A751B55643B0005A300 /* BUYProductViewHeaderBackgroundImageView.h in Headers */,
90C856B61BD6B0F400936926 /* Buy.h in Headers */,
8498DCB31CDD1B5400BD12A8 /* BUYClient_Internal.h in Headers */,
900396B01B67BD0A00226B73 /* BUYCheckoutButton.h in Headers */,
BE9A64821B503DAD0033E558 /* BUYPaymentButton.h in Headers */,
BEB74A771B55646D0005A300 /* BUYImageView.h in Headers */,
......@@ -1420,6 +1473,7 @@
BEB74A651B5563FF0005A300 /* BUYGradientView.h in Headers */,
849810961CB7E07900CFAB58 /* BUYFlatCollectionTransformer.h in Headers */,
9003969B1B601DF400226B73 /* BUYCartLineItem.h in Headers */,
8498DCAC1CDD1B2F00BD12A8 /* BUYError+BUYAdditions.h in Headers */,
BE9A644B1B503C9B0033E558 /* BUYCheckout.h in Headers */,
BE9A64491B503C940033E558 /* BUYCart.h in Headers */,
BEB74A2D1B554E870005A300 /* BUYProductViewController.h in Headers */,
......@@ -1640,6 +1694,7 @@
901930EC1BC5B9BC00D1134E /* BUYError.m in Sources */,
841ADE0E1CB6C942000004B0 /* NSDecimalNumber+BUYAdditions.m in Sources */,
84DD12AE1CC63FE600A2442D /* _BUYProductVariant.m in Sources */,
84B0A7211CDD261100253EB0 /* BUYSerializable.m in Sources */,
901930ED1BC5B9BC00D1134E /* BUYProductViewHeader.m in Sources */,
901930EE1BC5B9BC00D1134E /* BUYNavigationController.m in Sources */,
901930EF1BC5B9BC00D1134E /* BUYVariantSelectionViewController.m in Sources */,
......@@ -1689,6 +1744,7 @@
84DD12A21CC63FE600A2442D /* _BUYImageLink.m in Sources */,
901931101BC5B9BC00D1134E /* BUYProductHeaderCell.m in Sources */,
841ADE1A1CB6C942000004B0 /* NSRegularExpression+BUYAdditions.m in Sources */,
8498DCBE1CDD1FA400BD12A8 /* BUYAccountCredentials.m in Sources */,
9032F2DD1BE9457A00BB9EEF /* BUYCheckoutAttribute.m in Sources */,
901931111BC5B9BC00D1134E /* BUYCartLineItem.m in Sources */,
901931121BC5B9BC00D1134E /* BUYMaskedCreditCard.m in Sources */,
......@@ -1696,6 +1752,7 @@
901931141BC5B9BC00D1134E /* BUYProductViewFooter.m in Sources */,
84DD12A61CC63FE600A2442D /* _BUYOption.m in Sources */,
841ADE261CB6C942000004B0 /* NSURLComponents+BUYAdditions.m in Sources */,
8498DCAF1CDD1B2F00BD12A8 /* BUYError+BUYAdditions.m in Sources */,
901931151BC5B9BC00D1134E /* BUYPresentationControllerWithNavigationController.m in Sources */,
84DD12D01CC6401400A2442D /* BUYCustomer.m in Sources */,
84980F391CB75C2900CFAB58 /* NSPropertyDescription+BUYAdditions.m in Sources */,
......@@ -1709,6 +1766,7 @@
9019311C1BC5B9BC00D1134E /* BUYOption.m in Sources */,
9019311D1BC5B9BC00D1134E /* BUYClient.m in Sources */,
9019311E1BC5B9BC00D1134E /* UIFont+BUYAdditions.m in Sources */,
8498DCB81CDD1B5400BD12A8 /* BUYClient+Customers.m in Sources */,
9019311F1BC5B9BC00D1134E /* BUYProductView.m in Sources */,
901931201BC5B9BC00D1134E /* BUYCreditCard.m in Sources */,
901931211BC5B9BC00D1134E /* BUYProductImageCollectionViewCell.m in Sources */,
......@@ -1731,7 +1789,6 @@
files = (
BEB9AE7D1BA885E300575F8A /* BUYClientTestBase.m in Sources */,
8491103E1CCE988600E53B93 /* BUYFontAdditionsTests.m in Sources */,
849110421CCE9E0A00E53B93 /* BUYEntityDescriptionAdditionsTests.m in Sources */,
8491103A1CCE718100E53B93 /* BUYExceptionAdditionsTests.m in Sources */,
849110441CCE9F3F00E53B93 /* BUYTransformerTests.m in Sources */,
849110351CCE70CE00E53B93 /* BUYDictionaryAdditionsTests.m in Sources */,
......@@ -1739,6 +1796,7 @@
849110321CCE708900E53B93 /* BUYRegularExpressionAdditionsTests.m in Sources */,
90F593061B0D5F4C0026B382 /* BUYCartTest.m in Sources */,
90F593051B0D5F4C0026B382 /* BUYApplePayAdditionsTest.m in Sources */,
8498DCC91CDD208200BD12A8 /* BUYClientTest_Customer.m in Sources */,
90F593071B0D5F4C0026B382 /* BUYCheckoutTest.m in Sources */,
90F593091B0D5F4C0026B382 /* BUYClientTest.m in Sources */,
90F5930B1B0D5F4C0026B382 /* BUYObjectTests.m in Sources */,
......@@ -1747,12 +1805,14 @@
90BBCD731B87B6BA00FCCE51 /* BUYPKContact.m in Sources */,
849110331CCE708900E53B93 /* BUYStringAdditionsTests.m in Sources */,
906CF1B11B8B66AE001F7D5B /* BUYCNPostalAddress.m in Sources */,
8498DCD01CDD208200BD12A8 /* TestModel.m in Sources */,
8491103C1CCE731900E53B93 /* BUYURLAdditionsTests.m in Sources */,
8491104A1CCEA85C00E53B93 /* BUYObserverTests.m in Sources */,
849110401CCE9DFB00E53B93 /* BUYPropertyDescriptionAdditionsTests.m in Sources */,
8498DCCB1CDD208200BD12A8 /* BUYCoreDataModelAdditionsTests.m in Sources */,
906CF1AD1B8B5F7D001F7D5B /* BUYNSPersonNameComponents.m in Sources */,
BE98DB5C1BB1F4D000C29564 /* OHHTTPStubsResponse+Helpers.m in Sources */,
849110311CCE708900E53B93 /* BUYArrayAdditionsTests.m in Sources */,
8498DCCF1CDD208200BD12A8 /* BUYTestModel.xcdatamodeld in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......@@ -1777,6 +1837,7 @@
BE4734101B66C4EF00AA721A /* BUYError.m in Sources */,
841ADE0D1CB6C942000004B0 /* NSDecimalNumber+BUYAdditions.m in Sources */,
84DD12961CC63FE600A2442D /* _BUYProductVariant.m in Sources */,
84B0A7201CDD261100253EB0 /* BUYSerializable.m in Sources */,
BEB74A741B5564380005A300 /* BUYProductViewHeader.m in Sources */,
BEB74A681B55640F0005A300 /* BUYNavigationController.m in Sources */,
BEB74A7E1B5564890005A300 /* BUYVariantSelectionViewController.m in Sources */,
......@@ -1826,6 +1887,7 @@
84DD128A1CC63FE600A2442D /* _BUYImageLink.m in Sources */,
BEB74A6E1B5564230005A300 /* BUYProductHeaderCell.m in Sources */,
841ADE191CB6C942000004B0 /* NSRegularExpression+BUYAdditions.m in Sources */,
8498DCBD1CDD1FA400BD12A8 /* BUYAccountCredentials.m in Sources */,
9032F2DC1BE9457A00BB9EEF /* BUYCheckoutAttribute.m in Sources */,
9003969C1B601DF400226B73 /* BUYCartLineItem.m in Sources */,
BE5DC3641B71022D00B2BC1E /* BUYMaskedCreditCard.m in Sources */,
......@@ -1833,6 +1895,7 @@
BEB74A721B5564320005A300 /* BUYProductViewFooter.m in Sources */,
84DD128E1CC63FE600A2442D /* _BUYOption.m in Sources */,
841ADE251CB6C942000004B0 /* NSURLComponents+BUYAdditions.m in Sources */,
8498DCAD1CDD1B2F00BD12A8 /* BUYError+BUYAdditions.m in Sources */,
BEB74A6A1B5564190005A300 /* BUYPresentationControllerWithNavigationController.m in Sources */,
84DD12CE1CC6401400A2442D /* BUYCustomer.m in Sources */,
84980F381CB75C2900CFAB58 /* NSPropertyDescription+BUYAdditions.m in Sources */,
......@@ -1846,6 +1909,7 @@
BE9A64601B503CEC0033E558 /* BUYOption.m in Sources */,
BE9A64481B503C900033E558 /* BUYClient.m in Sources */,
9099444A1B71B76800C40A33 /* UIFont+BUYAdditions.m in Sources */,
8498DCB51CDD1B5400BD12A8 /* BUYClient+Customers.m in Sources */,
900396AD1B627CB900226B73 /* BUYProductView.m in Sources */,
BE9A64561B503CC90033E558 /* BUYCreditCard.m in Sources */,
904606B01B6BC8D700754173 /* BUYProductImageCollectionViewCell.m in Sources */,
......@@ -2232,6 +2296,16 @@
/* End XCConfigurationList section */
/* Begin XCVersionGroup section */
8498DCC51CDD208200BD12A8 /* BUYTestModel.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
8498DCC61CDD208200BD12A8 /* BUYTestModel.xcdatamodel */,
);
currentVersion = 8498DCC61CDD208200BD12A8 /* BUYTestModel.xcdatamodel */;
path = BUYTestModel.xcdatamodeld;
sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel;
};
84D915561CC040C300D334FB /* Mobile Buy SDK.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
......
......@@ -28,7 +28,26 @@
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "90F592EB1B0D5EFE0026B382"
BuildableName = "Mobile Buy SDK Tests.xctest"
BlueprintName = "Mobile Buy SDK Tests"
ReferencedContainer = "container:Mobile Buy SDK.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "901930E11BC5B9BC00D1134E"
BuildableName = "Buy.framework"
BlueprintName = "Buy"
ReferencedContainer = "container:Mobile Buy SDK.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
......
......@@ -31,14 +31,14 @@
+ (NSDateFormatter*)dateFormatterForShippingRates
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZ"];
dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSSZ";
return dateFormatter;
}
+ (NSDateFormatter*)dateFormatterForPublications
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZ"];
dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";
return dateFormatter;
}
......
......@@ -67,11 +67,11 @@ static NSString * const BUYDateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";
[NSValueTransformer setValueTransformer:[[BUYIdentityTransformer alloc] init] forName:BUYIdentityTransformerName];
// attribute type transformers
[NSValueTransformer setValueTransformer:[[BUYDateTransformer alloc] init] forName:BUYDateTransformerName];
[NSValueTransformer setValueTransformer:[[BUYDecimalNumberTransformer alloc] init] forName:BUYDecimalNumberTransformerName];
// value type transformers
[NSValueTransformer setValueTransformer:[[BUYURLTransformer alloc] init] forName:BUYURLTransformerName];
[NSValueTransformer setValueTransformer:[BUYDateTransformer dateTransformerWithFormat:BUYDateFormat] forName:BUYDateTransformerName];
});
return self.userInfo[BUYJSONValueTransformerUserInfoKey];
}
......@@ -166,21 +166,26 @@ static NSString *JSONValueTransformerNameForAttributeType(NSAttributeType type)
@implementation NSRelationshipDescription (BUYAdditions)
#pragma mark - Helpers
- (NSString *)encodesIdSuffix
{
return (self.isToMany ? @"_ids" : @"_id");
}
- (BOOL)buy_encodesIdInJSON
// array -> (ordered)set
- (id)buy_transformArray:(NSArray<id<BUYObject>> *)array
{
return [self.JSONPropertyKey hasSuffix:[self encodesIdSuffix]];
return self.isOrdered ? [NSOrderedSet orderedSetWithArray:array] : [NSSet setWithArray:array];
}
- (id)buy_transformArray:(NSArray<id<BUYObject>> *)array
// (ordered)set -> array
- (NSArray *)buy_arrayForCollection:(id)collection
{
return self.isOrdered ? [NSOrderedSet orderedSetWithArray:array] : [NSSet setWithArray:array];
return self.ordered ? [collection array] : [collection allObjects];
}
// JSON -> model
- (id)buy_objectForJSON:(id)JSON modelManager:(id<BUYModelManager>)modelManager
{
NSString *entityName = self.destinationEntity.name;
......@@ -194,7 +199,18 @@ static NSString *JSONValueTransformerNameForAttributeType(NSAttributeType type)
}
}
- (id)buy_collectionForJSON:(NSArray *)JSON object:(id<BUYObject>)object
// model -> JSON
- (id)buy_JSONForObject:(NSObject<BUYObject> *)object
{
id json = nil;
if (!self.inverseRelationship || self.inverseRelationship.allowsInverseEncoding) {
json = [self.destinationEntity buy_JSONForObject:object];
}
return json;
}
// JSON -> (ordered)set (of models)
- (id)buy_collectionForJSON:(NSArray *)JSON modelManager:(id<BUYModelManager>)modelManager
{
NSString *entityName = self.destinationEntity.name;
NSArray<id<BUYObject>> *array;
......@@ -203,16 +219,23 @@ static NSString *JSONValueTransformerNameForAttributeType(NSAttributeType type)
// Otherwise, let the object context decide how to resolve the objects and update them.
// If device caching is not provided, this will return nothing.
if (self.encodesIdInJSON) {
array = [object.modelManager buy_objectsWithEntityName:entityName identifiers:JSON];
array = [modelManager buy_objectsWithEntityName:entityName identifiers:JSON];
}
else {
array = [object.modelManager buy_objectsWithEntityName:entityName JSONArray:JSON];
array = [modelManager buy_objectsWithEntityName:entityName JSONArray:JSON];
}
// Transform the array to the correct container type (`NSSet` or `NSOrderedSet`).
return [self buy_transformArray:array];
}
// (ordered)set (of models) -> JSON
- (NSArray *)buy_JSONForCollection:(id)collection
{
return [self.destinationEntity buy_JSONForArray:[self buy_arrayForCollection:collection]];
}
#pragma mark - Property Additions Overrides
- (id)buy_valueForJSON:(id)JSON object:(id<BUYObject>)object
{
......@@ -220,41 +243,20 @@ static NSString *JSONValueTransformerNameForAttributeType(NSAttributeType type)
// The logic for decoding JSON is slightly different for to-one and to-many relationships.
// NOTE: by default, without a caching system, inverse relationships are not supported.
if (JSON && ![JSON isEqual:[NSNull null]]) {
id value = nil;
if ([JSON buy_isValidObject]) {
if (self.isToMany) {
return [self buy_collectionForJSON:JSON object:object];
value = [self buy_collectionForJSON:JSON modelManager:object.modelManager];
}
else {
return [self buy_objectForJSON:JSON modelManager:object.modelManager];
value = [self buy_objectForJSON:JSON modelManager:object.modelManager];
}
}
else {
return nil;
}
return value;
}
- (id)buy_JSONForValue:(id)value
{
return self.toMany ? [self buy_JSONForCollection:value] : [self buy_JSONForObject:value];
}
- (NSArray *)buy_JSONForCollection:(id)collection
{
if (self.manyToMany) {
return nil;
}
NSArray *array = [self buy_arrayForCollection:collection];
return self.encodesIdInJSON ? [array valueForKey:@"identifier"] : [self.destinationEntity buy_JSONForArray:array];
}
- (NSArray *)buy_arrayForCollection:(id)collection
{
return self.ordered ? [collection array] : [collection allObjects];
}
- (id)buy_JSONForObject:(NSObject<BUYObject> *)object
{
// JSON generation for a relationship depends on the rules defined in the model.
// The model can explicitly specify using an `id` encoding.
// Alternately, if the relationship is compatible, encode the entire object.
......@@ -263,15 +265,24 @@ static NSString *JSONValueTransformerNameForAttributeType(NSAttributeType type)
// 2. the inverse relationship is not an ownership relationship
// (this is inferred from the `NSCascadeDeleteRule` used by owning objects)
// 3. the relationship is to a "private" entity (not known to the API)
id json = nil;
if (self.encodesIdInJSON) {
return [[object valueForKey:self.name] identifier];
json = [value valueForKey:NSStringFromSelector(@selector(identifier))];
}
else if (!self.inverseRelationship || self.inverseRelationship.allowsInverseEncoding) {
return [self.destinationEntity buy_JSONForObject:object];
else if (!self.toMany) {
json = [self buy_JSONForObject:value];
}
else {
return nil;
else if (!self.manyToMany) {
json = [self buy_JSONForCollection:value];
}
return json;
}
#pragma mark - Properties
- (BOOL)buy_encodesIdInJSON
{
return [self.JSONPropertyKey hasSuffix:[self encodesIdSuffix]];
}
- (BOOL)buy_isManyToMany
......@@ -295,7 +306,7 @@ static NSString *JSONValueTransformerNameForAttributeType(NSAttributeType type)
return nil;
}
- (id)buy_reverseTransformedJSONValue:(id)value
- (id)buy_JSONForValue:(id)value
{
return nil;
}
......@@ -304,8 +315,6 @@ static NSString *JSONValueTransformerNameForAttributeType(NSAttributeType type)
#pragma mark -
// We could be really clever here and use an algorithm for string transformation
// but it's not worth it right now.
@implementation NSObject (BUYValueTransforming)
+ (NSString *)buy_JSONValueTransformerName
......
......@@ -32,6 +32,7 @@ FOUNDATION_EXPORT double BuyVersionNumber;
//! Project version string for Buy.
FOUNDATION_EXPORT const unsigned char BuyVersionString[];
#import <Buy/BUYAccountCredentials.h>
#import <Buy/BUYAddress.h>
#import <Buy/BUYCart.h>
#import <Buy/BUYCartLineItem.h>
......@@ -39,6 +40,7 @@ FOUNDATION_EXPORT const unsigned char BuyVersionString[];
#import <Buy/BUYCheckoutAttribute.h>
#import <Buy/BUYCollection.h>
#import <Buy/BUYCreditCard.h>
#import <Buy/BUYCustomer.h>
#import <Buy/BUYDiscount.h>
#import <Buy/BUYGiftCard.h>
#import <Buy/BUYImageLink.h>
......@@ -56,12 +58,15 @@ FOUNDATION_EXPORT const unsigned char BuyVersionString[];
#import <Buy/BUYApplePayAdditions.h>
#import <Buy/BUYApplePayHelpers.h>
#import <Buy/BUYClient.h>
#import <Buy/BUYClient+Customers.h>
#import <Buy/BUYError.h>
#import <Buy/BUYError+BUYAdditions.h>
#import <Buy/BUYManagedObject.h>
#import <Buy/BUYModelManager.h>
#import <Buy/BUYModelManagerProtocol.h>
#import <Buy/BUYObjectProtocol.h>
#import <Buy/BUYObserver.h>
#import <Buy/BUYShopifyErrorCodes.h>
#import <Buy/BUYPaymentButton.h>
#import <Buy/BUYProductViewController.h>
......
//
// BUYClient+Customers.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 "BUYClient.h"
@class BUYCustomer;
@class BUYOrder;
@class BUYAccountCredentials;
/**
* Return block containing a BUYCustomer object for an existing customer of the shop
*
* @param customer A BUYCustomer
* @param error An optional NSError
*/
typedef void (^BUYDataCustomerBlock)(BUYCustomer *customer, NSError *error);
/**
* Return block containing a customer auth token
*
* @param customer A BUYCustomer
* @param token An authentication token to retrieve the customer later. Store this token securely on the device.
* @param error An optional NSError
*/
typedef void (^BUYDataCustomerTokenBlock)(BUYCustomer *customer, NSString *token, NSError *error);
/**
* Return block containing a customer auth token
*
* @param token An authentication token to retrieve the customer later. Store this token securely on the device.
* @param error An optional NSError
*/
typedef void (^BUYDataTokenBlock)(NSString *token, NSError *error);
/**
* Return block containing an array of BUYOrders
*
* @param orders An array of BUYOrders
* @param error An optional NSError
*/
typedef void (^BUYDataOrdersBlock)(NSArray <BUYOrder*> *orders, NSError *error);
@interface BUYClient (Customers)
/**
* GET /api/customers/:customer_id
* Gets an existing customer
*
* @param customerID A customer ID retrieved from either customer creation or login
* @param block (BUYCustomer *customer, NSError *error)
*
* @return The associated NSURLSessionDataTask
*/
- (NSURLSessionDataTask *)getCustomerWithID:(NSString *)customerID callback:(BUYDataCustomerBlock)block;
/**
* POST /api/customers
* Creates a new customer
* Expects first name, last name, email, password, and password confirmation
*
* @param credentials Credentials object containing items for required fields
* @param block (BUYCustomer *customer, NSString *token, NSError *error)
*
* @return The associated NSURLSessionDataTask
*
* @discussion The customer is automatically logged in using -loginCustomerWithCredentials:callback:
*/
- (NSURLSessionDataTask *)createCustomerWithCredentials:(BUYAccountCredentials *)credentials callback:(BUYDataCustomerTokenBlock)block;
/**
* POST /api/customers/customer_token
* Logs in an existing customer
* Expects email and password
*
* @param credentials Credentials object containing items for required fields
* @param block (BUYCustomer *customer, NSString *token, NSError *error)
*
* @return The associated NSURLSessionDataTask
*/
- (NSURLSessionDataTask *)loginCustomerWithCredentials:(BUYAccountCredentials *)credentials callback:(BUYDataCustomerTokenBlock)block;
/**
* POST /api/customers/recover
* Sends email for password recovery to an existing customer
*
* @param email Email to send the password reset to
* @param block (BUYStatus status, NSError *error)
*
* @return the associated NSURLSessionDataTask
*/
- (NSURLSessionDataTask *)recoverPasswordForCustomer:(NSString *)email callback:(BUYDataCheckoutStatusBlock)block;
/**
* PUT /api/customers/:customer_id/customer_token/renew
* Renews an existing customer's token
*
* @param customerID ID of customer renewing token
* @param block (NSString *token, NSError *error)
*
* @return the associated NSURLSessionDataTask
*/
- (NSURLSessionDataTask *)renewCustomerTokenWithID:(NSString *)customerID callback:(BUYDataTokenBlock)block;
/**
* PUT /api/customers/:customer_id/activate
* Activates an unactivated customer
*
* @param credentials Credentials containing a password and password confirmation
* @param customerID ID of customer being activated
* @param customerToken Token contained in activation URL
* @param block (BUYCustomer *customer, NSString *token, NSError *error)
*
* @return The associated NSURLSessionDataTask
*/
- (NSURLSessionDataTask *)activateCustomerWithCredentials:(BUYAccountCredentials *)credentials customerID:(NSString *)customerID customerToken:(NSString *)customerToken callback:(BUYDataCustomerTokenBlock)block;
/**
* PUT /api/customers/:customer_id/reset
* Resets an existing customer's password
*
* @param credentials Credentials containing a password and password confirmation
* @param customerID ID of customer resetting password
* @param customerToken Token contained in reset URL
* @param block (BUYCustomer *customer, NSString *token, NSError *error)
*
* @return The associated NSURLSessionDataTask
*/
- (NSURLSessionDataTask *)resetPasswordWithCredentials:(BUYAccountCredentials *)credentials customerID:(NSString *)customerID customerToken:(NSString *)customerToken callback:(BUYDataCustomerTokenBlock)block;
/**
* GET /api/customers/:customer_id/orders
* Gets orders for a given customer
*
* @param token An auth token retrieved from customer creation or customer login API
* @param block (NSArray <BUYOrder*> *orders, NSError *error)
*
* @return The associated NSURLSessionDataTask
*/
- (NSURLSessionDataTask *)getOrdersForCustomerWithCallback:(BUYDataOrdersBlock)block;
@end
//
// BUYClient+Customers.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 "BUYClient+Customers.h"
#import "BUYClient_Internal.h"
#import "NSDateFormatter+BUYAdditions.h"
#import "BUYCustomer.h"
#import "BUYAccountCredentials.h"
#import "BUYOrder.h"
#import "BUYShopifyErrorCodes.h"
@interface BUYAuthenticatedResponse : NSObject
+ (BUYAuthenticatedResponse *)responseFromJSON:(NSDictionary *)json;
@property (nonatomic, copy) NSString *accessToken;
@property (nonatomic, copy) NSDate *expiry;
@property (nonatomic, copy) NSString *customerID;
@end
@implementation BUYAuthenticatedResponse
+ (BUYAuthenticatedResponse *)responseFromJSON:(NSDictionary *)json
{
BUYAuthenticatedResponse *response = [BUYAuthenticatedResponse new];
NSDictionary *access = json[@"customer_access_token"];
response.accessToken = access[@"access_token"];
NSDateFormatter *formatter = [NSDateFormatter dateFormatterForPublications];
response.expiry = [formatter dateFromString:access[@"expires_at"]];
response.customerID = [NSString stringWithFormat:@"%@", access[@"customer_id"]];
return response;
}
@end
@implementation BUYClient (Customers)
#pragma mark - Customer
- (NSURLSessionDataTask *)getCustomerWithID:(NSString *)customerID callback:(BUYDataCustomerBlock)block
{
NSURLComponents *components = [self URLComponentsForCustomerWithID:customerID];
return [self getRequestForURL:components.URL completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
BUYCustomer *customer = nil;
if (json && !error) {
customer = [self.modelManager insertCustomerWithJSONDictionary:json[@"customer"]];
}
block(customer, error);
}];
}
- (NSURLSessionDataTask *)createCustomerWithCredentials:(BUYAccountCredentials *)credentials callback:(BUYDataCustomerTokenBlock)block
{
NSURLComponents *components = [self URLComponentsForCustomers];
return [self postRequestForURL:components.URL object:credentials.JSONRepresentation completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
if (json && !error) {
[self createTokenForCustomerWithCredentials:credentials customerJSON:json callback:block];
}
else {
block(nil, nil, error);
}
}];
}
- (NSURLSessionDataTask *)createTokenForCustomerWithCredentials:(BUYAccountCredentials *)credentials customerJSON:(NSDictionary *)customerJSON callback:(BUYDataCustomerTokenBlock)block
{
NSURLComponents *components = [self URLComponentsForCustomerLogin];
return [self postRequestForURL:components.URL object:credentials.JSONRepresentation completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
if (json && !error) {
BUYAuthenticatedResponse *authenticatedResponse = [BUYAuthenticatedResponse responseFromJSON:json];
self.customerToken = authenticatedResponse.accessToken;
if (!customerJSON) {
[self getCustomerWithID:authenticatedResponse.customerID callback:^(BUYCustomer *customer, NSError *error) {
block(customer, self.customerToken, error);
}];
}
else {
BUYCustomer *customer = [self.modelManager insertCustomerWithJSONDictionary:json[@"customer"]];
block(customer, self.customerToken, error);
}
}
else {
block(nil, nil, error);
}
}];
}
- (NSURLSessionDataTask *)loginCustomerWithCredentials:(BUYAccountCredentials *)credentials callback:(BUYDataCustomerTokenBlock)block
{
return [self createTokenForCustomerWithCredentials:credentials customerJSON:nil callback:block];
}
- (NSURLSessionDataTask *)recoverPasswordForCustomer:(NSString *)email callback:(BUYDataCheckoutStatusBlock)block
{
NSURLComponents *components = [self URLComponentsForPasswordReset];
return [self postRequestForURL:components.URL object:@{@"email": email} completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
if (!error) {
error = [self extractErrorFromResponse:response json:json];
}
block(statusCode, error);
}];
}
- (NSURLSessionDataTask *)renewCustomerTokenWithID:(NSString *)customerID callback:(BUYDataTokenBlock)block
{
if (self.customerToken) {
NSURLComponents *components = [self URLComponentsForTokenRenewalWithID:customerID];
return [self putRequestForURL:components.URL body:nil completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
NSString *accessToken = nil;
if (json && !error) {
BUYAuthenticatedResponse *authenticatedResponse = [BUYAuthenticatedResponse responseFromJSON:json];
accessToken = authenticatedResponse.accessToken;
}
if (!error) {
error = [self extractErrorFromResponse:response json:json];
}
block(accessToken, error);
}];
}
else {
block(nil, [NSError errorWithDomain:kShopifyError code:BUYShopifyError_InvalidCustomerToken userInfo:nil]);
return nil;
}
}
- (NSURLSessionDataTask *)activateCustomerWithCredentials:(BUYAccountCredentials *)credentials customerID:(NSString *)customerID customerToken:(NSString *)customerToken callback:(BUYDataCustomerTokenBlock)block
{
NSURLComponents *components = [self URLComponentsForCustomerActivationWithID:customerID customerToken:customerToken];
NSData *data = [NSJSONSerialization dataWithJSONObject:credentials.JSONRepresentation options:0 error:nil];
return [self putRequestForURL:components.URL body:data completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
if (json && !error) {
BUYAccountCredentialItem *emailItem = [BUYAccountCredentialItem itemWithKey:@"email" value:json[@"customer"][@"email"]];
credentials[@"email"] = emailItem;
[self loginCustomerWithCredentials:credentials callback:block];
}
else {
block(nil, nil, error);
}
}];
}
- (NSURLSessionDataTask *)resetPasswordWithCredentials:(BUYAccountCredentials *)credentials customerID:(NSString *)customerID customerToken:(NSString *)customerToken callback:(BUYDataCustomerTokenBlock)block
{
NSURLComponents *components = [self URLComponentsForCustomerPasswordResetWithCustomerID:customerID customerToken:customerToken];
NSData *data = [NSJSONSerialization dataWithJSONObject:credentials.JSONRepresentation options:0 error:nil];
return [self putRequestForURL:components.URL body:data completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
if (json && !error) {
BUYAccountCredentialItem *emailItem = [BUYAccountCredentialItem itemWithKey:@"email" value:json[@"customer"][@"email"]];
credentials[@"email"] = emailItem;
[self loginCustomerWithCredentials:credentials callback:block];
}
else {
block(nil, nil, error);
}
}];
}
- (NSURLSessionDataTask *)getOrdersForCustomerWithCallback:(BUYDataOrdersBlock)block
{
NSURLComponents *components = [self URLComponentsForCustomerOrders];
return [self getRequestForURL:components.URL completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
NSArray *ordersJSON = json[@"orders"];
if (ordersJSON && !error) {
NSArray *orders = [self.modelManager insertOrdersWithJSONArray:ordersJSON];
block(orders, error);
} else {
block(nil, error);
}
}];
}
#pragma mark - URL Formatting
- (NSURLComponents *)URLComponentsForCustomers
{
return [self customerURLComponentsAppendingPath:nil];
}
- (NSURLComponents *)URLComponentsForCustomerWithID:(NSString *)customerID
{
return [self customerURLComponentsAppendingPath:customerID];
}
- (NSURLComponents *)URLComponentsForCustomerLogin
{
return [self customerURLComponentsAppendingPath:@"customer_token"];
}
- (NSURLComponents *)URLComponentsForCustomerActivationWithID:(NSString *)customerID customerToken:(NSString *)customerToken
{
NSDictionary *queryItems = @{ @"token": customerToken };
NSString *path = [NSString stringWithFormat:@"%@/activate", customerID];
return [self customerURLComponentsAppendingPath:path queryItems:queryItems];
}
- (NSURLComponents *)URLComponentsForCustomerPasswordResetWithCustomerID:(NSString *)customerID customerToken:(NSString *)customerToken
{
NSDictionary *queryItems = @{ @"token": customerToken };
NSString *path = [NSString stringWithFormat:@"%@/reset", customerID];
return [self customerURLComponentsAppendingPath:path queryItems:queryItems];
}
- (NSURLComponents *)URLComponentsForPasswordReset
{
return [self customerURLComponentsAppendingPath:@"recover" queryItems:nil];
}
- (NSURLComponents *)URLComponentsForTokenRenewalWithID:(NSString *)customerID
{
NSString *path = [NSString stringWithFormat:@"%@/customer_token/renew", customerID];
return [self customerURLComponentsAppendingPath:path queryItems:nil];
}
- (NSURLComponents *)URLComponentsForCustomerOrders
{
return [self customerURLComponentsAppendingPath:@"orders" queryItems:nil];
}
#pragma mark - Convenience methods
- (NSURLComponents *)customerURLComponentsAppendingPath:(NSString *)path
{
return [self customerURLComponentsAppendingPath:path queryItems:nil];
}
- (NSURLComponents *)customerURLComponentsAppendingPath:(NSString *)path queryItems:(NSDictionary *)queryItems
{
return [self URLComponentsForAPIPath:@"customers" appendingPath:path queryItems:queryItems];
}
- (NSString *)accessTokenFromHeaders:(NSDictionary *)headers
{
return [headers valueForKey:BUYClientCustomerAccessToken];
}
@end
......@@ -80,6 +80,8 @@ typedef NS_ENUM(NSUInteger, BUYCollectionSort) {
extern NSString * const BUYVersionString;
extern NSString * const BUYClientCustomerAccessToken;
/**
* A BUYStatus is associated with the completion of an enqueued job on Shopify.
* BUYStatus is equal is HTTP status codes returned from the server
......@@ -277,6 +279,12 @@ typedef void (^BUYDataGiftCardBlock)(BUYGiftCard *giftCard, NSError *error);
*/
@property (nonatomic, strong) NSString *urlScheme;
/**
* Allows the client to hold onto the customer token
*
* @param token The token received from the create and login callbacks
*/
@property (strong, nonatomic) NSString *customerToken;
#pragma mark - Storefront
......
......@@ -38,6 +38,7 @@
#import "BUYProduct.h"
#import "BUYShippingRate.h"
#import "BUYShop.h"
#import "BUYShopifyErrorCodes.h"
#import "NSDecimalNumber+BUYAdditions.h"
#import "NSDictionary+BUYAdditions.h"
#import "NSURLComponents+BUYAdditions.h"
......@@ -130,7 +131,7 @@ NSString *const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token
return [self getRequestForURL:shopComponents.URL completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
BUYShop *shop = nil;
if (json && error == nil) {
if (json && !error) {
shop = [self.modelManager insertShopWithJSONDictionary:json];
}
block(shop, error);
......@@ -146,7 +147,7 @@ NSString *const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token
return [self getRequestForURL:components.URL completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
NSArray *products = nil;
if (json && error == nil) {
if (json && !error) {
products = [self.modelManager insertProductsWithJSONArray:json[kBUYClientPathProductPublications]];
}
block(products, page, [self hasReachedEndOfPage:products] || error, error);
......@@ -156,10 +157,10 @@ NSString *const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token
- (NSURLSessionDataTask *)getProductById:(NSString *)productId completion:(BUYDataProductBlock)block;
{
return [self getProductsByIds:@[productId] completion:^(NSArray *products, NSError *error) {
if ([products count]) {
if (products.count > 0) {
block(products[0], error);
} else {
if (error == nil && [products count] == 0) {
if (!error) {
error = [NSError errorWithDomain:kShopifyError code:BUYShopifyError_InvalidProductID userInfo:@{ NSLocalizedDescriptionKey : @"Product ID is not valid. Confirm the product ID on your shop's admin and also ensure that the visibility is on for the Mobile App channel." }];
}
block(nil, error);
......@@ -175,10 +176,10 @@ NSString *const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token
return [self getRequestForURL:components.URL completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
NSArray *products = nil;
if (json && error == nil) {
if (json && !error) {
products = [self.modelManager insertProductsWithJSONArray:json[kBUYClientPathProductPublications]];
}
if (error == nil && [products count] == 0) {
if (!error && products.count == 0) {
error = [NSError errorWithDomain:kShopifyError code:BUYShopifyError_InvalidProductID userInfo:@{ NSLocalizedDescriptionKey : @"Product IDs are not valid. Confirm the product IDs on your shop's admin and also ensure that the visibility is on for the Mobile App channel." }];
}
block(products, error);
......@@ -200,7 +201,7 @@ NSString *const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token
return [self getRequestForURL:components.URL completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
NSArray *collections = nil;
if (json && error == nil) {
if (json && !error) {
collections = [self.modelManager buy_objectsWithEntityName:[BUYCollection entityName] JSONArray:json[kBUYClientPathCollectionPublications]];
}
block(collections, page, [self hasReachedEndOfPage:collections], error);
......@@ -225,7 +226,7 @@ NSString *const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token
task = [self getRequestForURL:components.URL completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
NSArray *products = nil;
if (json && error == nil) {
if (json && !error) {
products = [self.modelManager buy_objectsWithEntityName:[BUYProduct entityName] JSONArray:json[kBUYClientPathProductPublications]];
}
block(products, page, [self hasReachedEndOfPage:products] || error, error);
......@@ -297,7 +298,7 @@ NSString *const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token
- (void)handleCheckoutResponse:(NSDictionary *)json error:(NSError *)error block:(BUYDataCheckoutBlock)block
{
BUYCheckout *checkout = nil;
if (error == nil) {
if (!error) {
checkout = [self.modelManager insertCheckoutWithJSONDictionary:json[@"checkout"]];
}
block(checkout, error);
......@@ -337,7 +338,7 @@ NSString *const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token
NSError *error = nil;
NSData *data = [NSJSONSerialization dataWithJSONObject:checkoutJSON options:0 error:&error];
if (data && error == nil) {
if (data && !error) {
NSURLComponents *components = [self URLComponentsForCheckoutsAppendingPath:nil checkoutToken:nil queryItems:nil];
task = [self postRequestForURL:components.URL body:data completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
......@@ -363,7 +364,7 @@ NSString *const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token
task = [self postRequestForURL:components.URL
object:giftCard
completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
if (error == nil) {
if (!error) {
[self updateCheckout:checkout withGiftCardDictionary:json[@"gift_card"] addingGiftCard:YES];
}
block(checkout, error);
......@@ -381,7 +382,7 @@ NSString *const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token
checkoutToken:checkout.token
queryItems:nil];
task = [self deleteRequestForURL:components.URL completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
if (error == nil) {
if (!error) {
[self updateCheckout:checkout withGiftCardDictionary:json[@"gift_card"] addingGiftCard:NO];
}
block(checkout, error);
......@@ -452,7 +453,7 @@ NSString *const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token
data = [NSJSONSerialization dataWithJSONObject:paymentJson options:0 error:&error];
}
if ((data && error == nil) || (checkout.paymentDue && checkout.paymentDue.floatValue == 0)) {
if ((data && !error) || (checkout.paymentDue && checkout.paymentDue.floatValue == 0)) {
task = [self checkoutCompletionRequestWithCheckout:checkout body:data completion:block];
}
}
......@@ -472,7 +473,7 @@ NSString *const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token
if ([checkout hasToken] == NO) {
block(nil, [NSError errorWithDomain:kShopifyError code:BUYShopifyError_InvalidCheckoutObject userInfo:nil]);
}
else if (token == nil) {
else if (!token) {
block(nil, [NSError errorWithDomain:kShopifyError code:BUYShopifyError_NoApplePayTokenSpecified userInfo:nil]);
}
else {
......@@ -480,7 +481,7 @@ NSString *const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token
NSDictionary *paymentJson = @{ @"payment_token" : @{ @"payment_data" : tokenString, @"type" : @"apple_pay" }};
NSError *error = nil;
NSData *data = [NSJSONSerialization dataWithJSONObject:paymentJson options:0 error:&error];
if (data && error == nil) {
if (data && !error) {
task = [self checkoutCompletionRequestWithCheckout:checkout body:data completion:block];
}
else {
......@@ -557,7 +558,7 @@ NSString *const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token
NSURLComponents *components = [self URLComponentsForCheckoutsAppendingPath:@"shipping_rates" checkoutToken:checkout.token queryItems:@{ @"checkout" : @"" }];
task = [self getRequestForURL:components.URL completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
NSArray *shippingRates = nil;
if (error == nil && json) {
if (json && !error) {
shippingRates = [self.modelManager insertShippingRatesWithJSONArray:json[@"shipping_rates"]];
}
......@@ -580,7 +581,7 @@ NSString *const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token
if ([checkout hasToken] == NO) {
block(nil, nil, [NSError errorWithDomain:kShopifyError code:BUYShopifyError_InvalidCheckoutObject userInfo:nil]);
}
else if (creditCard == nil) {
else if (!creditCard) {
block(nil, nil, [NSError errorWithDomain:kShopifyError code:BUYShopifyError_NoCreditCardSpecified userInfo:nil]);
}
else {
......@@ -593,7 +594,7 @@ NSString *const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token
NSError *error = nil;
NSData *data = [NSJSONSerialization dataWithJSONObject:@{ @"checkout" : json } options:0 error:&error];
if (data && error == nil) {
if (data && !error) {
task = [self postPaymentRequestWithCheckout:checkout body:data completion:block];
}
else {
......@@ -638,9 +639,9 @@ NSString *const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token
return status;
}
- (BUYError *)errorFromJSON:(NSDictionary *)errorDictionary statusCode:(NSInteger)statusCode
- (NSError *)errorFromJSON:(NSDictionary *)errorDictionary statusCode:(NSInteger)statusCode
{
return [[BUYError alloc] initWithDomain:kShopifyError code:statusCode userInfo:errorDictionary];
return [[NSError alloc] initWithDomain:kShopifyError code:statusCode userInfo:errorDictionary];
}
- (NSURLSessionDataTask *)requestForURL:(NSURL *)url method:(NSString *)method object:(id <BUYSerializable>)object completionHandler:(void (^)(NSDictionary *json, NSURLResponse *response, NSError *error))completionHandler
......@@ -649,7 +650,7 @@ NSString *const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token
NSError *error = nil;
NSData *data = [NSJSONSerialization dataWithJSONObject:json options:0 error:&error];
NSURLSessionDataTask *task = nil;
if (error == nil && data) {
if (data && !error) {
task = [self requestForURL:url method:method body:data completionHandler:completionHandler];
}
return task;
......@@ -669,6 +670,10 @@ NSString *const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token
[request addValue:kJSONType forHTTPHeaderField:@"Content-Type"];
[request addValue:kJSONType forHTTPHeaderField:@"Accept"];
if (self.customerToken) {
[request addValue:self.customerToken forHTTPHeaderField:BUYClientCustomerAccessToken];
}
request.HTTPMethod = method;
NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
......@@ -681,7 +686,7 @@ NSString *const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token
}
else {
//2 is the minimum amount of data {} for a JSON Object. Just ignore anything less.
if ((error == nil || failedValidation) && [data length] > 2) {
if ((!error || failedValidation) && [data length] > 2) {
id jsonData = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
json = [jsonData isKindOfClass:[NSDictionary class]] ? jsonData : nil;
}
......@@ -702,7 +707,7 @@ NSString *const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token
{
return [self requestForURL:checkout.paymentURL method:kPOST body:body completionHandler:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
NSString *paymentSessionId = nil;
if (error == nil) {
if (!error) {
paymentSessionId = json[@"id"];
checkout.paymentSessionId = paymentSessionId;
}
......@@ -720,6 +725,11 @@ NSString *const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token
return [self requestForURL:url method:kPOST object:object completionHandler:completionHandler];
}
- (NSURLSessionDataTask *)putRequestForURL:(NSURL *)url body:(NSData *)body completionHandler:(void (^)(NSDictionary *json, NSURLResponse *response, NSError *error))completionHandler
{
return [self requestForURL:url method:kPUT body:body completionHandler:completionHandler];
}
- (NSURLSessionDataTask *)postRequestForURL:(NSURL *)url body:(NSData *)body completionHandler:(void (^)(NSDictionary *json, NSURLResponse *response, NSError *error))completionHandler
{
return [self requestForURL:url method:kPOST body:body completionHandler:completionHandler];
......
......@@ -2,8 +2,26 @@
// BUYClient_Internal.h
// Mobile Buy SDK
//
// Created by Gabriel O'Flaherty-Chan on 2016-04-04.
// Copyright © 2016 Shopify Inc. All rights reserved.
// 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 "BUYClient.h"
......@@ -13,7 +31,7 @@ extern NSString *const kShopifyError;
@interface BUYClient (Internal)
- (NSURLSessionDataTask *)postRequestForURL:(NSURL *)url object:(id<BUYSerializable>)object completionHandler:(void (^)(NSDictionary *json, NSURLResponse *response, NSError *error))completionHandler;
- (NSURLSessionDataTask *)postRequestForURL:(NSURL *)url object:(id <BUYSerializable>)object completionHandler:(void (^)(NSDictionary *json, NSURLResponse *response, NSError *error))completionHandler;
- (NSURLSessionDataTask *)putRequestForURL:(NSURL *)url body:(NSData *)body completionHandler:(void (^)(NSDictionary *json, NSURLResponse *response, NSError *error))completionHandler;
- (NSURLSessionDataTask *)getRequestForURL:(NSURL *)url completionHandler:(void (^)(NSDictionary *json, NSURLResponse *response, NSError *error))completionHandler;
......
//
// BUYAccountCredentials.h
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2016 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>
/**
* Intended for storing a collection of credential items representing individual values
*/
@class BUYAccountCredentialItem;
@interface BUYAccountCredentials : NSObject
NS_ASSUME_NONNULL_BEGIN
+ (BUYAccountCredentials *)credentialsWithItems:(NSArray<BUYAccountCredentialItem *> *)items;
+ (BUYAccountCredentials *)credentialsWithItemKeys:(NSArray<NSString *> *)keys;
@property (readonly) NSDictionary *JSONRepresentation;
@property (nonatomic, readonly, getter=isValid) BOOL valid;
- (BUYAccountCredentialItem *)objectForKeyedSubscript:(NSString *)key;
- (void)setObject:(BUYAccountCredentialItem *)obj forKeyedSubscript:(NSString *)key;
@end
/**
* Represents a key and KVC-validatable value
*/
@interface BUYAccountCredentialItem : NSObject
+ (instancetype)itemWithKey:(NSString *)key value:(NSString *)value;
@property (nonatomic, getter=isValid) BOOL valid;
@property (nonatomic, strong) NSString *key;
@property (nonatomic, strong) NSString *value;
NS_ASSUME_NONNULL_END
@end
//
// BUYAccountCredentials.m
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2016 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 "BUYAccountCredentials.h"
@class BUYAccountCredentialItem;
@interface BUYAccountCredentials()
@property (strong, nonatomic) NSMutableDictionary<NSString *, BUYAccountCredentialItem *> *items;
@end
@implementation BUYAccountCredentials
+ (BUYAccountCredentials *)credentialsWithItems:(NSArray<BUYAccountCredentialItem *> *)items
{
BUYAccountCredentials *credentials = [BUYAccountCredentials new];
NSMutableDictionary *keyedItems = [NSMutableDictionary dictionary];
for (BUYAccountCredentialItem *item in items) {
keyedItems[item.key] = item;
}
credentials.items = keyedItems;
return credentials;
}
+ (BUYAccountCredentials *)credentialsWithItemKeys:(NSArray<NSString *> *)keys
{
NSMutableArray *items = [NSMutableArray array];
for (NSString *key in keys) {
BUYAccountCredentialItem *item = [BUYAccountCredentialItem itemWithKey:key value:@""];
[items addObject:item];
}
return [BUYAccountCredentials credentialsWithItems:items];
}
- (NSDictionary *)JSONRepresentation
{
__block NSMutableDictionary *customer = [NSMutableDictionary dictionary];
[self.items enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, BUYAccountCredentialItem * _Nonnull obj, BOOL * _Nonnull stop) {
customer[key] = obj.value;
}];
return @{ @"customer": customer };
}
- (BOOL)validateValue:(inout id _Nullable __autoreleasing *)ioValue forKey:(NSString *)inKey error:(out NSError * _Nullable __autoreleasing *)outError
{
return [self.items[inKey] validateValue:ioValue forKey:inKey error:outError];
}
- (BUYAccountCredentialItem *)objectForKeyedSubscript:(NSString *)key
{
return self.items[key];
}
- (void)setObject:(BUYAccountCredentialItem *)obj forKeyedSubscript:(NSString *)key
{
self.items[key] = obj;
}
- (BOOL)validationForKey:(NSString *)key
{
return [self.items[key] isValid];
}
- (BOOL)isValid
{
__block BOOL valid = YES;
[self.items enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, BUYAccountCredentialItem * _Nonnull obj, BOOL * _Nonnull stop) {
valid = valid && [obj isValid];
}];
return valid;
}
@end
@implementation BUYAccountCredentialItem
+ (instancetype)itemWithKey:(NSString *)key value:(NSString *)value
{
BUYAccountCredentialItem *item = [BUYAccountCredentialItem new];
item.key = key;
item.value = value;
return item;
}
- (NSString *)value
{
return _value ?: @"";
}
- (BOOL)validateValue:(inout id _Nullable __autoreleasing *)ioValue forKey:(NSString *)inKey error:(out NSError * _Nullable __autoreleasing *)outError
{
self.value = *ioValue;
self.valid = self.value.length > 0;
return [self isValid];
}
@end
......@@ -26,55 +26,14 @@
#import <Foundation/Foundation.h>
extern NSString * const BUYShopifyError;
@interface BUYError : NSObject
/**
* A collection of enums for error codes specific to the SDK
*/
typedef NS_ENUM(NSUInteger, BUYCheckoutError){
/**
* An error occurred retrieving the cart for an existing web checkout with BUYStoreViewController
*/
BUYShopifyError_CartFetchError,
/**
* No shipping rates are available for the selected address
*/
BUYShopifyError_NoShippingMethodsToAddress,
/**
* No product or product ID was provided when loading a product in BUYProductViewController
*/
BUYShopifyError_NoProductSpecified,
/**
* The product ID or IDs provided were invalid for the shop. Check that the product are made visible on the Mobile App channel on /admin.
*/
BUYShopifyError_InvalidProductID,
/**
* No collection ID was provided when loading a collection
*/
BUYShopifyError_NoCollectionIdSpecified,
/**
* No gift card code was provided when applying a gift card to a checkout
*/
BUYShopifyError_NoGiftCardSpecified,
/**
* No credit card was provided when calling `storeCreditCard:completion:`
*/
BUYShopifyError_NoCreditCardSpecified,
/**
* No Apple Pay token was provided when attempting to complete a checkout using Apple Pay
*/
BUYShopifyError_NoApplePayTokenSpecified,
/**
* The checkout is invalid and does not have a checkout token. This generally means the BUYCheckout object
* has not been synced with Shopify via `createCheckout:completion:` before making subsequent calls to update
* or complete the checkout
*/
BUYShopifyError_InvalidCheckoutObject,
};
@property (nonatomic, copy) NSString *key;
/**
* BUYError overrides `description` and provides a human-readable dictionary for the error
*/
@interface BUYError : NSError
- (instancetype)initWithKey:(NSString *)key json:(NSDictionary *)json;
@property (nonatomic, copy) NSString *code;
@property (nonatomic, copy) NSString *message;
@property (nonatomic, copy) NSDictionary<NSString *, NSString *> *options;
@end
......@@ -26,13 +26,21 @@
#import "BUYError.h"
NSString * const BUYShopifyError = @"BUYShopifyError";
@implementation BUYError
- (instancetype)initWithKey:(NSString *)key json:(NSDictionary *)json
{
self = [super init];
if (self) {
self.key = key;
[self setValuesForKeysWithDictionary:json];
}
return self;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"Error code %td: %@", self.code, [self userInfo]];
return [NSString stringWithFormat:@"%@ %@", self.key, self.message];
}
@end
......@@ -40,15 +40,13 @@
*/
@property (nonatomic, strong, readonly) NSManagedObjectModel *model;
- (instancetype)init NS_UNAVAILABLE;
/**
*
* @param model The Core Data managed object model for your given model. Should be the Buy model.
*
* @return A new model manager object.
*/
- (instancetype)initWithModel:(NSManagedObjectModel *)model NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithManagedObjectModel:(NSManagedObjectModel *)model NS_DESIGNATED_INITIALIZER;
/**
* Convenience initializer. Instantiates a model using the -mergedModelFromBundles: method and the Buy.framework as the bundle.
......
......@@ -55,7 +55,12 @@ NSString * const BUYProductTagsTransformerName = @"BUYProductTags";
});
}
- (instancetype)initWithModel:(NSManagedObjectModel *)model
- (instancetype)init
{
return [self initWithManagedObjectModel:[NSManagedObjectModel mergedModelFromBundles:@[[NSBundle bundleForClass:[self class]]]]];
}
- (instancetype)initWithManagedObjectModel:(NSManagedObjectModel *)model
{
self = [super init];
if (self) {
......@@ -66,7 +71,7 @@ NSString * const BUYProductTagsTransformerName = @"BUYProductTags";
+ (instancetype)modelManager
{
return [[self alloc] initWithModel:[NSManagedObjectModel mergedModelFromBundles:@[[NSBundle bundleForClass:[BUYObject class]]]]];
return [[self alloc] init];
}
- (NSEntityDescription *)buy_entityWithName:(NSString *)entityName
......
......@@ -25,9 +25,10 @@
//
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import <Buy/BUYSerializable.h>
@class NSEntityDescription;
@protocol BUYModelManager;
/**
......
......@@ -31,3 +31,7 @@
- (NSDictionary *)jsonDictionaryForCheckout;
@end
@interface NSDictionary (BUYSerializable) <BUYSerializable>
@end
\ No newline at end of file
//
// BUYEntityDescriptionAdditionsTests.m
// BUYSerializable.m
// Mobile Buy SDK
//
// Created by Shopify.
......@@ -24,34 +24,12 @@
// THE SOFTWARE.
//
#import <XCTest/XCTest.h>
#import "BUYSerializable.h"
@interface BUYEntityDescriptionAdditionsTests : XCTestCase
@implementation NSDictionary (BUYSerializable)
@end
@implementation BUYEntityDescriptionAdditionsTests
- (void)setUp {
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testExample {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
- (void)testPerformanceExample {
// This is an example of a performance test case.
[self measureBlock:^{
// Put the code you want to measure the time of here.
}];
- (NSDictionary *)jsonDictionaryForCheckout {
return self;
}
@end
//
// _BUYAddress.m
// BUYAddress.m
// Mobile Buy SDK
//
// Created by Shopify.
......@@ -42,7 +42,7 @@
}
return NO;
}
}
- (BOOL)isValidAddressForShippingRates
{
......
//
// _BUYCheckout.h
// BUYCheckout.h
// Mobile Buy SDK
//
// Created by Shopify.
......
......@@ -48,7 +48,7 @@
@property (nonatomic, copy) NSDate *updatedAtDate;
@property (nonatomic, strong) BUYMaskedCreditCard *creditCard;
@property (nonatomic, strong) BUYOrder *order;
@property (nonatomic, copy) NSString *customerId;
@property (nonatomic, copy) NSNumber *customerId;
@property (nonatomic, strong) NSURL *privacyPolicyURL;
@property (nonatomic, strong) NSURL *refundPolicyURL;
@property (nonatomic, strong) NSURL *termsOfServiceURL;
......
......@@ -31,6 +31,7 @@
#import "BUYProduct.h"
#import "BUYProductViewController.h"
#import "BUYImageKit.h"
#import "BUYImageLink.h"
#import "BUYProductView.h"
#import "BUYProductViewFooter.h"
#import "BUYProductHeaderCell.h"
......@@ -44,7 +45,7 @@
#import "BUYVariantSelectionViewController.h"
#import "BUYError.h"
#import "BUYShop.h"
#import "BUYImageLink.h"
#import "BUYShopifyErrorCodes.h"
CGFloat const BUYMaxProductViewWidth = 414.0; // We max out to the width of the iPhone 6+
CGFloat const BUYMaxProductViewHeight = 640.0;
......
......@@ -28,13 +28,20 @@
* Umbrella header used for Cocoapods
*/
#import "BUYAccountCredentials.h"
#import "BUYApplePayAdditions.h"
#import "BUYApplePayHelpers.h"
#import "BUYAddress.h"
#import "BUYCart.h"
#import "BUYCartLineItem.h"
#import "BUYCheckout.h"
#import "BUYCheckoutAttribute.h"
#import "BUYClient+Test.h"
#import "BUYClient.h"
#import "BUYClient+Customers.h"
#import "BUYCollection.h"
#import "BUYCreditCard.h"
#import "BUYCustomer.h"
#import "BUYDiscount.h"
#import "BUYGiftCard.h"
#import "BUYImageLink.h"
......@@ -49,9 +56,6 @@
#import "BUYShop.h"
#import "BUYTaxLine.h"
#import "BUYApplePayAdditions.h"
#import "BUYApplePayHelpers.h"
#import "BUYClient.h"
#import "BUYError.h"
#import "BUYManagedObject.h"
#import "BUYModelManager.h"
......
......@@ -25,12 +25,14 @@
//
#import "BUYApplePayHelpers.h"
#import "BUYAddress.h"
#import "BUYApplePayAdditions.h"
#import "BUYClient.h"
#import "BUYCheckout.h"
#import "BUYError.h"
#import "BUYModelManager.h"
#import "BUYShop.h"
#import "BUYShopifyErrorCodes.h"
const NSTimeInterval PollDelay = 0.5;
......
//
// BUYPropertyDescriptionAdditionsTests.m
// BUYError+BUYAdditions.h
// Mobile Buy SDK
//
// Created by Shopify.
......@@ -24,34 +24,13 @@
// THE SOFTWARE.
//
#import <XCTest/XCTest.h>
@interface BUYPropertyDescriptionAdditionsTests : XCTestCase
#import "BUYError.h"
@interface BUYError (Checkout)
+ (NSArray<BUYError *> *)errorsFromCheckoutJSON:(NSDictionary *)json;
@property (readonly) NSString *quantityRemainingMessage;
@end
@implementation BUYPropertyDescriptionAdditionsTests
- (void)setUp {
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testExample {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
- (void)testPerformanceExample {
// This is an example of a performance test case.
[self measureBlock:^{
// Put the code you want to measure the time of here.
}];
}
@interface BUYError (Customer)
+ (NSArray<BUYError *> *)errorsFromSignUpJSON:(NSDictionary *)json;
@end
//
// BUYError+BUYAdditions.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 "BUYError+BUYAdditions.h"
@implementation BUYError (Checkout)
+ (NSArray<BUYError *> *)errorsFromCheckoutJSON:(NSDictionary *)json
{
NSArray *lineItems = json[@"errors"][@"checkout"][@"line_items"];
NSMutableArray *errors = [NSMutableArray array];
for (NSDictionary<NSString *, NSArray *> *lineItem in lineItems) {
if (lineItem == (id)[NSNull null]) {
[errors addObject:lineItem];
}
else {
for (NSString *key in lineItem.allKeys) {
NSDictionary *reason = [lineItem[key] firstObject];
[errors addObject:[[BUYError alloc] initWithKey:key json:reason]];
};
}
};
return errors;
}
- (NSString *)quantityRemainingMessage
{
NSNumber *remaining = (id)self.options[@"remaining"];
NSString *localizedString;
if ([remaining isEqualToNumber:@0]) {
localizedString = NSLocalizedString(@"Completely sold out", @"String describing a line item with zero stock available");
} else {
localizedString = NSLocalizedString(@"Only %1$@ left in stock, reduce the quantity and try again.", @"String describing an out of stock line item with first parameter representing amount remaining");
}
return [NSString localizedStringWithFormat:localizedString, remaining];
}
@end
@implementation BUYError (Customer)
+ (NSArray<BUYError *> *)errorsFromSignUpJSON:(NSDictionary *)json
{
NSDictionary *reasons = json[@"errors"][@"customer"];
__block NSMutableArray *errors = [NSMutableArray array];
[reasons enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSArray * _Nonnull obj, BOOL * _Nonnull stop) {
for (NSDictionary *reason in obj) {
[errors addObject:[[BUYError alloc] initWithKey:key json:reason]];
}
}];
return errors;
}
@end
//
// BUYShopifyErrorCodes.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.
//
#ifndef BUYShopifyErrorCodes_h
#define BUYShopifyErrorCodes_h
static NSString * const BUYShopifyError = @"BUYShopifyError";
/**
* A collection of enums for error codes specific to the SDK
*/
typedef NS_ENUM(NSUInteger, BUYCheckoutError){
/**
* An error occurred retrieving the cart for an existing web checkout with StoreViewController
*/
BUYShopifyError_CartFetchError,
/**
* No shipping rates are available for the selected address
*/
BUYShopifyError_NoShippingMethodsToAddress,
/**
* No product or product ID was provided when loading a product in BUYProductViewController
*/
BUYShopifyError_NoProductSpecified,
/**
* The product ID or IDs provided were invalid for the shop. Check that the product are made visible on the Mobile App channel on /admin.
*/
BUYShopifyError_InvalidProductID,
/**
* No collection ID was provided when loading a collection
*/
BUYShopifyError_NoCollectionIdSpecified,
/**
* No gift card code was provided when applying a gift card to a checkout
*/
BUYShopifyError_NoGiftCardSpecified,
/**
* No credit card was provided when calling `storeCreditCard:completion:`
*/
BUYShopifyError_NoCreditCardSpecified,
/**
* No Apple Pay token was provided when attempting to complete a checkout using Apple Pay
*/
BUYShopifyError_NoApplePayTokenSpecified,
/**
* The checkout is invalid and does not have a checkout token. This generally means the BUYCheckout object
* has not been synced with Shopify via `createCheckout:completion:` before making subsequent calls to update
* or complete the checkout
*/
BUYShopifyError_InvalidCheckoutObject,
/**
* A customer token has not been configured on the client
*/
BUYShopifyError_InvalidCustomerToken
};
#endif /* BUYShopifyErrorCodes_h */
......@@ -25,6 +25,7 @@
//
#import "BUYDateTransformer.h"
#import "NSDateFormatter+BUYAdditions.h"
NSString * const BUYDateTransformerName = @"BUYDate";
......@@ -34,6 +35,13 @@ NSString * const BUYDateTransformerName = @"BUYDate";
@implementation BUYDateTransformer
- (instancetype)init
{
NSDateFormatter *formatter = [NSDateFormatter dateFormatterForPublications];
formatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
return [self initWithDateFormatter:formatter];
}
- (instancetype)initWithDateFormatter:(NSDateFormatter *)formatter
{
self = [super init];
......
......@@ -31,6 +31,7 @@
#import "BUYStoreViewController.h"
#import "BUYError.h"
#import "BUYOrder.h"
#import "BUYShopifyErrorCodes.h"
@interface BUYStoreViewController () <WKNavigationDelegate, WKScriptMessageHandler>
@end
......
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