diff --git a/Mobile Buy SDK/Mobile Buy SDK.xcodeproj/project.pbxproj b/Mobile Buy SDK/Mobile Buy SDK.xcodeproj/project.pbxproj index f8213c4..720a207 100644 --- a/Mobile Buy SDK/Mobile Buy SDK.xcodeproj/project.pbxproj +++ b/Mobile Buy SDK/Mobile Buy SDK.xcodeproj/project.pbxproj @@ -248,6 +248,20 @@ 9A3B2DCA1CD27D5B00BFF49C /* BUYCustomer.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A3B2DC71CD27D5B00BFF49C /* BUYCustomer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9A3B2DCB1CD27D5B00BFF49C /* BUYCustomer.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A3B2DC81CD27D5B00BFF49C /* BUYCustomer.m */; }; 9A3B2DCC1CD27D5B00BFF49C /* BUYCustomer.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A3B2DC81CD27D5B00BFF49C /* BUYCustomer.m */; }; + 9A3B2DCF1CD2822F00BFF49C /* BUYClient+Customers.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A3B2DCD1CD2822F00BFF49C /* BUYClient+Customers.h */; }; + 9A3B2DD01CD2822F00BFF49C /* BUYClient+Customers.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A3B2DCD1CD2822F00BFF49C /* BUYClient+Customers.h */; }; + 9A3B2DD11CD2822F00BFF49C /* BUYClient+Customers.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A3B2DCE1CD2822F00BFF49C /* BUYClient+Customers.m */; }; + 9A3B2DD21CD2822F00BFF49C /* BUYClient+Customers.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A3B2DCE1CD2822F00BFF49C /* BUYClient+Customers.m */; }; + 9A3B2DD51CD2829900BFF49C /* BUYAccountCredentials.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A3B2DD31CD2829900BFF49C /* BUYAccountCredentials.h */; }; + 9A3B2DD61CD2829900BFF49C /* BUYAccountCredentials.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A3B2DD31CD2829900BFF49C /* BUYAccountCredentials.h */; }; + 9A3B2DD71CD2829900BFF49C /* BUYAccountCredentials.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A3B2DD41CD2829900BFF49C /* BUYAccountCredentials.m */; }; + 9A3B2DD81CD2829900BFF49C /* BUYAccountCredentials.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A3B2DD41CD2829900BFF49C /* BUYAccountCredentials.m */; }; + 9A3B2DDA1CD282DA00BFF49C /* BUYClient_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A3B2DD91CD282DA00BFF49C /* BUYClient_Internal.h */; }; + 9A3B2DDB1CD282DA00BFF49C /* BUYClient_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A3B2DD91CD282DA00BFF49C /* BUYClient_Internal.h */; }; + 9A3B2DDD1CD28D7300BFF49C /* BUYSerializable.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A3B2DDC1CD28D6F00BFF49C /* BUYSerializable.m */; }; + 9A3B2DDE1CD28D7300BFF49C /* BUYSerializable.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A3B2DDC1CD28D6F00BFF49C /* BUYSerializable.m */; }; + 9A3B2DE01CD28E9900BFF49C /* BUYShopifyErrorCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A3B2DDF1CD28E9900BFF49C /* BUYShopifyErrorCodes.h */; }; + 9A3B2DE11CD28E9900BFF49C /* BUYShopifyErrorCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A3B2DDF1CD28E9900BFF49C /* BUYShopifyErrorCodes.h */; }; BE1007951B6038150031CEE7 /* BUYProductVariant+Options.h in Headers */ = {isa = PBXBuildFile; fileRef = BE1007931B6038150031CEE7 /* BUYProductVariant+Options.h */; }; BE1007961B6038150031CEE7 /* BUYProductVariant+Options.m in Sources */ = {isa = PBXBuildFile; fileRef = BE1007941B6038150031CEE7 /* BUYProductVariant+Options.m */; }; BE10079B1B6165EC0031CEE7 /* BUYOptionValueCell.h in Headers */ = {isa = PBXBuildFile; fileRef = BE1007991B6165EC0031CEE7 /* BUYOptionValueCell.h */; }; @@ -504,6 +518,13 @@ 90FC31A71B50371600AFAB51 /* BUYProductViewHeaderBackgroundImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BUYProductViewHeaderBackgroundImageView.m; path = "Product View/BUYProductViewHeaderBackgroundImageView.m"; sourceTree = "<group>"; }; 9A3B2DC71CD27D5B00BFF49C /* BUYCustomer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BUYCustomer.h; sourceTree = "<group>"; }; 9A3B2DC81CD27D5B00BFF49C /* BUYCustomer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYCustomer.m; sourceTree = "<group>"; }; + 9A3B2DCD1CD2822F00BFF49C /* BUYClient+Customers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "BUYClient+Customers.h"; sourceTree = "<group>"; }; + 9A3B2DCE1CD2822F00BFF49C /* BUYClient+Customers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "BUYClient+Customers.m"; sourceTree = "<group>"; }; + 9A3B2DD31CD2829900BFF49C /* BUYAccountCredentials.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BUYAccountCredentials.h; sourceTree = "<group>"; }; + 9A3B2DD41CD2829900BFF49C /* BUYAccountCredentials.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYAccountCredentials.m; sourceTree = "<group>"; }; + 9A3B2DD91CD282DA00BFF49C /* BUYClient_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BUYClient_Internal.h; sourceTree = "<group>"; }; + 9A3B2DDC1CD28D6F00BFF49C /* BUYSerializable.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BUYSerializable.m; sourceTree = "<group>"; }; + 9A3B2DDF1CD28E9900BFF49C /* BUYShopifyErrorCodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BUYShopifyErrorCodes.h; sourceTree = "<group>"; }; BE1007931B6038150031CEE7 /* BUYProductVariant+Options.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "BUYProductVariant+Options.h"; sourceTree = "<group>"; }; BE1007941B6038150031CEE7 /* BUYProductVariant+Options.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "BUYProductVariant+Options.m"; sourceTree = "<group>"; }; BE1007991B6165EC0031CEE7 /* BUYOptionValueCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BUYOptionValueCell.h; sourceTree = "<group>"; }; @@ -877,6 +898,23 @@ children = ( 841ADE2B1CB6F320000004B0 /* Persistent */, 841ADE2A1CB6F31C000004B0 /* Transient */, + 9A3B2DD31CD2829900BFF49C /* BUYAccountCredentials.h */, + 9A3B2DD41CD2829900BFF49C /* BUYAccountCredentials.m */, + 90AFAA601B01390F00F21C23 /* BUYAddress.h */, + 90AFAA611B01390F00F21C23 /* BUYAddress.m */, + F773744719C77A210039681C /* BUYCart.h */, + F773744819C77A210039681C /* BUYCart.m */, + 900396991B601DF400226B73 /* BUYCartLineItem.h */, + 9003969A1B601DF400226B73 /* BUYCartLineItem.m */, + BEB74A1B1B5490140005A300 /* BUYCheckout_Private.h */, + F773749419C77C260039681C /* BUYCheckout.h */, + F773749519C77C260039681C /* BUYCheckout.m */, + 9032F2D81BE9457A00BB9EEF /* BUYCheckoutAttribute.h */, + 9032F2D91BE9457A00BB9EEF /* BUYCheckoutAttribute.m */, + BEB74A8E1B55A3D00005A300 /* BUYCollection.h */, + 9089CC5D1BB48D06009726D6 /* BUYCollection.m */, + 900396F41B69563400226B73 /* BUYCollection+Additions.h */, + 900396F51B69563400226B73 /* BUYCollection+Additions.m */, F77374AA19C796BD0039681C /* BUYCreditCard.h */, F77374AB19C796BD0039681C /* BUYCreditCard.m */, 9A3B2DC71CD27D5B00BFF49C /* BUYCustomer.h */, @@ -888,6 +926,15 @@ 2AF52A931A7010B20087DB2C /* BUYObject.h */, 2AF52A941A7010B20087DB2C /* BUYObject.mm */, F76CFF1E19CB7C500079C703 /* BUYSerializable.h */, + 9A3B2DDC1CD28D6F00BFF49C /* BUYSerializable.m */, + 90AFAA641B01398A00F21C23 /* BUYShippingRate.h */, + 90AFAA651B01398A00F21C23 /* BUYShippingRate.m */, + 2AF52A831A700B0A0087DB2C /* BUYShop.h */, + 2AF52A841A700B0A0087DB2C /* BUYShop.m */, + 90AFAA5C1B011EA600F21C23 /* BUYTaxLine.h */, + 90AFAA5D1B011EA600F21C23 /* BUYTaxLine.m */, + BEB74A861B5589C80005A300 /* BUYCollection.h */, + BEB74A871B5589C80005A300 /* BUYCollection.m */, ); path = Models; sourceTree = "<group>"; @@ -895,6 +942,7 @@ F773744519C779C20039681C /* Utils */ = { isa = PBXGroup; children = ( + 9A3B2DDF1CD28E9900BFF49C /* BUYShopifyErrorCodes.h */, BE33B4F91B177EC80067982B /* BUYAddress+Additions.h */, BE33B4FA1B177EC80067982B /* BUYAddress+Additions.m */, F70CE40D1A8BF1D90055BEB8 /* BUYApplePayAdditions.h */, @@ -949,6 +997,11 @@ children = ( F7FDA17019C93F6F00AF4E93 /* BUYClient.h */, F7FDA17119C93F6F00AF4E93 /* BUYClient.m */, + 9A3B2DD91CD282DA00BFF49C /* BUYClient_Internal.h */, + 9A3B2DCD1CD2822F00BFF49C /* BUYClient+Customers.h */, + 9A3B2DCE1CD2822F00BFF49C /* BUYClient+Customers.m */, + BE2E1D381B5E8663009610DA /* BUYClient+Test.h */, + BE2E1D391B5E8663009610DA /* BUYClient+Test.m */, ); path = Data; sourceTree = "<group>"; @@ -966,6 +1019,7 @@ 901931271BC5B9BC00D1134E /* BUYAddress.h in Headers */, 901931281BC5B9BC00D1134E /* BUYApplePayHelpers.h in Headers */, 901931291BC5B9BC00D1134E /* BUYStoreViewController.h in Headers */, + 9A3B2DDB1CD282DA00BFF49C /* BUYClient_Internal.h in Headers */, 9019312A1BC5B9BC00D1134E /* BUYCreditCard.h in Headers */, 9019312B1BC5B9BC00D1134E /* BUYOption.h in Headers */, 9019312C1BC5B9BC00D1134E /* BUYProductVariantCell.h in Headers */, @@ -984,6 +1038,7 @@ 9019313B1BC5B9BC00D1134E /* BUYOptionValue.h in Headers */, 9019313C1BC5B9BC00D1134E /* BUYShop.h in Headers */, 9019313D1BC5B9BC00D1134E /* BUYShippingRate.h in Headers */, + 9A3B2DE11CD28E9900BFF49C /* BUYShopifyErrorCodes.h in Headers */, 9019313E1BC5B9BC00D1134E /* BUYApplePayAdditions.h in Headers */, 901931401BC5B9BC00D1134E /* BUYTheme+Additions.h in Headers */, 901931411BC5B9BC00D1134E /* BUYVariantOptionView.h in Headers */, @@ -1005,6 +1060,9 @@ 841ADE181CB6C942000004B0 /* NSRegularExpression+BUYAdditions.h in Headers */, 841ADE0C1CB6C942000004B0 /* NSDecimalNumber+BUYAdditions.h in Headers */, 901931491BC5B9BC00D1134E /* BUYGiftCard.h in Headers */, + BE6D059A1BD6BA6700772EBB /* NSDictionary+Additions.h in Headers */, + 9A3B2DD61CD2829900BFF49C /* BUYAccountCredentials.h in Headers */, + 9019314A1BC5B9BC00D1134E /* BUYClient+Test.h in Headers */, 9019314B1BC5B9BC00D1134E /* BUYNavigationController.h in Headers */, 9019314D1BC5B9BC00D1134E /* BUYVariantOptionBreadCrumbsView.h in Headers */, 9019314E1BC5B9BC00D1134E /* BUYProductViewFooter.h in Headers */, @@ -1029,6 +1087,7 @@ 901931651BC5B9BC00D1134E /* NSDecimalNumber+BUYAdditions.h in Headers */, 9A3B2DCA1CD27D5B00BFF49C /* BUYCustomer.h in Headers */, 901931661BC5B9BC00D1134E /* BUYCheckout.h in Headers */, + 9A3B2DD01CD2822F00BFF49C /* BUYClient+Customers.h in Headers */, 901931671BC5B9BC00D1134E /* BUYCart.h in Headers */, 901931681BC5B9BC00D1134E /* BUYProductViewController.h in Headers */, 901931691BC5B9BC00D1134E /* BUYProduct.h in Headers */, @@ -1047,6 +1106,7 @@ BE9A64531B503CBE0033E558 /* BUYAddress.h in Headers */, BE9A64741B503D2E0033E558 /* BUYApplePayHelpers.h in Headers */, BE9A647E1B503D930033E558 /* BUYStoreViewController.h in Headers */, + 9A3B2DDA1CD282DA00BFF49C /* BUYClient_Internal.h in Headers */, BE9A64551B503CC50033E558 /* BUYCreditCard.h in Headers */, BE9A645F1B503CE90033E558 /* BUYOption.h in Headers */, BEB74A6F1B5564260005A300 /* BUYProductVariantCell.h in Headers */, @@ -1065,6 +1125,8 @@ BE9A64661B503D010033E558 /* BUYShop.h in Headers */, BE9A644D1B503CA20033E558 /* BUYShippingRate.h in Headers */, BE9A646C1B503D180033E558 /* BUYApplePayAdditions.h in Headers */, + 9A3B2DE01CD28E9900BFF49C /* BUYShopifyErrorCodes.h in Headers */, + BE9A646A1B503D100033E558 /* BUYProduct+Options.h in Headers */, 906EAE431B836DE000976165 /* BUYTheme+Additions.h in Headers */, BEB74A7B1B5564810005A300 /* BUYVariantOptionView.h in Headers */, BE5DC3631B71022D00B2BC1E /* BUYMaskedCreditCard.h in Headers */, @@ -1086,6 +1148,9 @@ 841ADE171CB6C942000004B0 /* NSRegularExpression+BUYAdditions.h in Headers */, 841ADE0B1CB6C942000004B0 /* NSDecimalNumber+BUYAdditions.h in Headers */, BE9A64571B503CCC0033E558 /* BUYGiftCard.h in Headers */, + BE6D05991BD6BA6700772EBB /* NSDictionary+Additions.h in Headers */, + 9A3B2DD51CD2829900BFF49C /* BUYAccountCredentials.h in Headers */, + BE2E1D3A1B5E8663009610DA /* BUYClient+Test.h in Headers */, BEB74A671B55640C0005A300 /* BUYNavigationController.h in Headers */, 90DE92701B9897B6002EF4DA /* BUYVariantOptionBreadCrumbsView.h in Headers */, BEB74A711B5564300005A300 /* BUYProductViewFooter.h in Headers */, @@ -1110,6 +1175,7 @@ BE9A64701B503D230033E558 /* NSDecimalNumber+BUYAdditions.h in Headers */, 9A3B2DC91CD27D5B00BFF49C /* BUYCustomer.h in Headers */, BE9A644B1B503C9B0033E558 /* BUYCheckout.h in Headers */, + 9A3B2DCF1CD2822F00BFF49C /* BUYClient+Customers.h in Headers */, BE9A64491B503C940033E558 /* BUYCart.h in Headers */, BEB74A2D1B554E870005A300 /* BUYProductViewController.h in Headers */, BE9A64611B503CEF0033E558 /* BUYProduct.h in Headers */, @@ -1315,6 +1381,7 @@ 901931011BC5B9BC00D1134E /* BUYTheme+Additions.m in Sources */, 901931021BC5B9BC00D1134E /* BUYStoreViewController.m in Sources */, 901931031BC5B9BC00D1134E /* BUYOptionValue.m in Sources */, + 9A3B2DDE1CD28D7300BFF49C /* BUYSerializable.m in Sources */, 901931041BC5B9BC00D1134E /* BUYApplePayAdditions.m in Sources */, 901931051BC5B9BC00D1134E /* BUYOptionSelectionNavigationController.m in Sources */, 901931061BC5B9BC00D1134E /* BUYDiscount.m in Sources */, @@ -1342,12 +1409,14 @@ 901931161BC5B9BC00D1134E /* BUYShippingRate.m in Sources */, 841ADE061CB6C942000004B0 /* NSDate+BUYAdditions.m in Sources */, 901931181BC5B9BC00D1134E /* BUYGradientView.m in Sources */, + 9A3B2DD21CD2822F00BFF49C /* BUYClient+Customers.m in Sources */, 901931191BC5B9BC00D1134E /* BUYViewController.m in Sources */, 9019311A1BC5B9BC00D1134E /* BUYImageKit.m in Sources */, 9019311B1BC5B9BC00D1134E /* BUYAddress.m in Sources */, 9019311C1BC5B9BC00D1134E /* BUYOption.m in Sources */, 9019311D1BC5B9BC00D1134E /* BUYClient.m in Sources */, 9019311E1BC5B9BC00D1134E /* UIFont+BUYAdditions.m in Sources */, + 9A3B2DD81CD2829900BFF49C /* BUYAccountCredentials.m in Sources */, 9019311F1BC5B9BC00D1134E /* BUYProductView.m in Sources */, 901931201BC5B9BC00D1134E /* BUYCreditCard.m in Sources */, 901931211BC5B9BC00D1134E /* BUYProductImageCollectionViewCell.m in Sources */, @@ -1422,6 +1491,7 @@ 906EAE441B836DE000976165 /* BUYTheme+Additions.m in Sources */, BE9A647F1B503D960033E558 /* BUYStoreViewController.m in Sources */, BE9A64691B503D0C0033E558 /* BUYOptionValue.m in Sources */, + 9A3B2DDD1CD28D7300BFF49C /* BUYSerializable.m in Sources */, BE9A646D1B503D1C0033E558 /* BUYApplePayAdditions.m in Sources */, BEB74A2A1B554BFB0005A300 /* BUYOptionSelectionNavigationController.m in Sources */, BE9A64501B503CAD0033E558 /* BUYDiscount.m in Sources */, @@ -1449,12 +1519,14 @@ BE9A644E1B503CA60033E558 /* BUYShippingRate.m in Sources */, 841ADE051CB6C942000004B0 /* NSDate+BUYAdditions.m in Sources */, BEB74A661B5564030005A300 /* BUYGradientView.m in Sources */, + 9A3B2DD11CD2822F00BFF49C /* BUYClient+Customers.m in Sources */, BE9A64811B503D9E0033E558 /* BUYViewController.m in Sources */, 900E7C851B5DA559006F3C81 /* BUYImageKit.m in Sources */, BE9A64541B503CC30033E558 /* BUYAddress.m in Sources */, BE9A64601B503CEC0033E558 /* BUYOption.m in Sources */, BE9A64481B503C900033E558 /* BUYClient.m in Sources */, 9099444A1B71B76800C40A33 /* UIFont+BUYAdditions.m in Sources */, + 9A3B2DD71CD2829900BFF49C /* BUYAccountCredentials.m in Sources */, 900396AD1B627CB900226B73 /* BUYProductView.m in Sources */, BE9A64561B503CC90033E558 /* BUYCreditCard.m in Sources */, 904606B01B6BC8D700754173 /* BUYProductImageCollectionViewCell.m in Sources */, diff --git a/Mobile Buy SDK/Mobile Buy SDK/Data/BUYClient+Customers.h b/Mobile Buy SDK/Mobile Buy SDK/Data/BUYClient+Customers.h new file mode 100644 index 0000000..9968d5d --- /dev/null +++ b/Mobile Buy SDK/Mobile Buy SDK/Data/BUYClient+Customers.h @@ -0,0 +1,149 @@ +// +// BUYClient+Customers.h +// Mobile Buy SDK +// +// Created by Gabriel O'Flaherty-Chan on 2016-04-04. +// Copyright © 2016 Shopify Inc. All rights reserved. +// + +#import "BUYClient.h" + +@class BUYCustomer; +@class BUYOrder; +@class BUYAccountCredentials; + +NSString *const BUYClientCustomerAccessToken = @"X-Shopify-Customer-Access-Token"; + +/** + * 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 diff --git a/Mobile Buy SDK/Mobile Buy SDK/Data/BUYClient+Customers.m b/Mobile Buy SDK/Mobile Buy SDK/Data/BUYClient+Customers.m new file mode 100644 index 0000000..535a3fc --- /dev/null +++ b/Mobile Buy SDK/Mobile Buy SDK/Data/BUYClient+Customers.m @@ -0,0 +1,251 @@ +// +// BUYClient+Customers.m +// Mobile Buy SDK +// +// Created by Gabriel O'Flaherty-Chan on 2016-04-04. +// Copyright © 2016 Shopify Inc. All rights reserved. +// + +#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) { + block((json && !error ? [[BUYCustomer alloc] initWithDictionary:json] : nil), 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 == nil) { + [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 == nil) { + BUYAuthenticatedResponse *authenticatedResponse = [BUYAuthenticatedResponse responseFromJSON:json]; + self.customerToken = authenticatedResponse.accessToken; + + if (customerJSON == nil) { + [self getCustomerWithID:authenticatedResponse.customerID callback:^(BUYCustomer *customer, NSError *error) { + block(customer, self.customerToken, error); + }]; + } + else { + block([[BUYCustomer alloc] initWithDictionary:json], 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]; + 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 == nil) { + BUYAuthenticatedResponse *authenticatedResponse = [BUYAuthenticatedResponse responseFromJSON:json]; + accessToken = authenticatedResponse.accessToken; + } + + 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 == nil) { + 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 == nil) { + 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 (!error && ordersJSON) { + + NSMutableArray *container = [NSMutableArray new]; + for (NSDictionary *orderJSON in ordersJSON) { + [container addObject:[[BUYOrder alloc] initWithDictionary:orderJSON]]; + } + block([container copy], error); + + } else { + block(nil, error); + } + }]; +} + +#pragma mark - URL Formatting + +- (NSURLComponents *)URLComponentsForCustomers +{ + return [self customerURLComponents]; +} + +- (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 *)customerURLComponents +{ + return [self customerURLComponentsAppendingPath:nil]; +} + +- (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 diff --git a/Mobile Buy SDK/Mobile Buy SDK/Data/BUYClient.h b/Mobile Buy SDK/Mobile Buy SDK/Data/BUYClient.h index 1bdaa6e..311fea2 100644 --- a/Mobile Buy SDK/Mobile Buy SDK/Data/BUYClient.h +++ b/Mobile Buy SDK/Mobile Buy SDK/Data/BUYClient.h @@ -272,6 +272,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 diff --git a/Mobile Buy SDK/Mobile Buy SDK/Data/BUYClient_Internal.h b/Mobile Buy SDK/Mobile Buy SDK/Data/BUYClient_Internal.h new file mode 100644 index 0000000..a365849 --- /dev/null +++ b/Mobile Buy SDK/Mobile Buy SDK/Data/BUYClient_Internal.h @@ -0,0 +1,24 @@ +// +// BUYClient_Internal.h +// Mobile Buy SDK +// +// Created by Gabriel O'Flaherty-Chan on 2016-04-04. +// Copyright © 2016 Shopify Inc. All rights reserved. +// + +#import "BUYClient.h" +#import "BUYSerializable.h" + +static NSString *const kShopifyError = @"shopify"; + +@interface BUYClient (Internal) + +- (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; + +- (NSURLSessionDataTask *)requestForURL:(NSURL *)url method:(NSString *)method body:(NSData *)body additionalHeaders:(NSDictionary *)headers completionHandler:(void (^)(NSDictionary *json, NSURLResponse *response, NSError *error))completionHandler; +- (NSURLComponents *)URLComponentsForAPIPath:(NSString *)apiPath appendingPath:(NSString *)appendingPath queryItems:(NSDictionary*)queryItems; +- (NSError *)extractErrorFromResponse:(NSURLResponse *)response json:(NSDictionary *)json; + +@end diff --git a/Mobile Buy SDK/Mobile Buy SDK/Models/BUYSerializable.h b/Mobile Buy SDK/Mobile Buy SDK/Models/BUYSerializable.h index a113bb0..0ff7ff3 100644 --- a/Mobile Buy SDK/Mobile Buy SDK/Models/BUYSerializable.h +++ b/Mobile Buy SDK/Mobile Buy SDK/Models/BUYSerializable.h @@ -31,3 +31,7 @@ - (NSDictionary *)jsonDictionaryForCheckout; @end + +@interface NSDictionary (BUYSerializable) <BUYSerializable> + +@end \ No newline at end of file diff --git a/Mobile Buy SDK/Mobile Buy SDK/Models/BUYSerializable.m b/Mobile Buy SDK/Mobile Buy SDK/Models/BUYSerializable.m new file mode 100644 index 0000000..8331d0f --- /dev/null +++ b/Mobile Buy SDK/Mobile Buy SDK/Models/BUYSerializable.m @@ -0,0 +1,35 @@ +// +// BUYSerializable.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 "BUYSerializable.h" + +@implementation NSDictionary (BUYSerializable) + +- (NSDictionary *)jsonDictionaryForCheckout { + return self; +} + +@end diff --git a/Mobile Buy SDK/Mobile Buy SDK/Utils/BUYShopifyErrorCodes.h b/Mobile Buy SDK/Mobile Buy SDK/Utils/BUYShopifyErrorCodes.h new file mode 100644 index 0000000..2eef8b8 --- /dev/null +++ b/Mobile Buy SDK/Mobile Buy SDK/Utils/BUYShopifyErrorCodes.h @@ -0,0 +1,64 @@ +// +// BUYShopifyErrorCodes.h +// Mobile Buy SDK +// +// Created by Gabriel O'Flaherty-Chan on 2016-03-14. +// Copyright © 2016 Shopify Inc. All rights reserved. +// + +#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 */