Commit 6e3e7c1a by Brent Gulanowski

Update BUYObject class to adopt BUYObject protocol.

Also update to support new JSON serialization and dirty tracking.
parent 39be7c25
...@@ -175,11 +175,9 @@ ...@@ -175,11 +175,9 @@
@implementation BUYDirtyTracked @implementation BUYDirtyTracked
+ (void)initialize + (BOOL)tracksDirtyProperties
{ {
if (self == [BUYDirtyTracked class]) { return YES;
[self trackDirtyProperties];
}
} }
@end @end
......
...@@ -87,6 +87,10 @@ ...@@ -87,6 +87,10 @@
84980F2A1CB75AC200CFAB58 /* BUYObjectProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 84980F281CB75AC200CFAB58 /* BUYObjectProtocol.h */; }; 84980F2A1CB75AC200CFAB58 /* BUYObjectProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 84980F281CB75AC200CFAB58 /* BUYObjectProtocol.h */; };
84980F2C1CB75B5E00CFAB58 /* BUYModelManagerProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 84980F2B1CB75B5E00CFAB58 /* BUYModelManagerProtocol.h */; }; 84980F2C1CB75B5E00CFAB58 /* BUYModelManagerProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 84980F2B1CB75B5E00CFAB58 /* BUYModelManagerProtocol.h */; };
84980F2D1CB75B5E00CFAB58 /* BUYModelManagerProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 84980F2B1CB75B5E00CFAB58 /* BUYModelManagerProtocol.h */; }; 84980F2D1CB75B5E00CFAB58 /* BUYModelManagerProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 84980F2B1CB75B5E00CFAB58 /* BUYModelManagerProtocol.h */; };
84980F291CB75AC200CFAB58 /* BUYObjectProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 84980F281CB75AC200CFAB58 /* BUYObjectProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
84980F2A1CB75AC200CFAB58 /* BUYObjectProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 84980F281CB75AC200CFAB58 /* BUYObjectProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
84980F2C1CB75B5E00CFAB58 /* BUYModelManagerProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 84980F2B1CB75B5E00CFAB58 /* BUYModelManagerProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
84980F2D1CB75B5E00CFAB58 /* BUYModelManagerProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 84980F2B1CB75B5E00CFAB58 /* BUYModelManagerProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
84980F321CB75C2900CFAB58 /* NSEntityDescription+BUYAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 84980F2E1CB75C2900CFAB58 /* NSEntityDescription+BUYAdditions.h */; }; 84980F321CB75C2900CFAB58 /* NSEntityDescription+BUYAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 84980F2E1CB75C2900CFAB58 /* NSEntityDescription+BUYAdditions.h */; };
84980F331CB75C2900CFAB58 /* NSEntityDescription+BUYAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 84980F2E1CB75C2900CFAB58 /* NSEntityDescription+BUYAdditions.h */; }; 84980F331CB75C2900CFAB58 /* NSEntityDescription+BUYAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 84980F2E1CB75C2900CFAB58 /* NSEntityDescription+BUYAdditions.h */; };
84980F341CB75C2900CFAB58 /* NSEntityDescription+BUYAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 84980F2F1CB75C2900CFAB58 /* NSEntityDescription+BUYAdditions.m */; }; 84980F341CB75C2900CFAB58 /* NSEntityDescription+BUYAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 84980F2F1CB75C2900CFAB58 /* NSEntityDescription+BUYAdditions.m */; };
...@@ -119,6 +123,10 @@ ...@@ -119,6 +123,10 @@
849810971CB7E07900CFAB58 /* BUYFlatCollectionTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 849810901CB7E07900CFAB58 /* BUYFlatCollectionTransformer.h */; }; 849810971CB7E07900CFAB58 /* BUYFlatCollectionTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 849810901CB7E07900CFAB58 /* BUYFlatCollectionTransformer.h */; };
849810981CB7E07900CFAB58 /* BUYFlatCollectionTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 849810911CB7E07900CFAB58 /* BUYFlatCollectionTransformer.m */; }; 849810981CB7E07900CFAB58 /* BUYFlatCollectionTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 849810911CB7E07900CFAB58 /* BUYFlatCollectionTransformer.m */; };
849810991CB7E07900CFAB58 /* BUYFlatCollectionTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 849810911CB7E07900CFAB58 /* BUYFlatCollectionTransformer.m */; }; 849810991CB7E07900CFAB58 /* BUYFlatCollectionTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 849810911CB7E07900CFAB58 /* BUYFlatCollectionTransformer.m */; };
84D915431CC0359700D334FB /* BUYObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D915411CC0359700D334FB /* BUYObserver.h */; settings = {ATTRIBUTES = (Public, ); }; };
84D915441CC0359700D334FB /* BUYObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D915411CC0359700D334FB /* BUYObserver.h */; settings = {ATTRIBUTES = (Public, ); }; };
84D915451CC0359700D334FB /* BUYObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D915421CC0359700D334FB /* BUYObserver.m */; };
84D915461CC0359700D334FB /* BUYObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 84D915421CC0359700D334FB /* BUYObserver.m */; };
9003969B1B601DF400226B73 /* BUYCartLineItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 900396991B601DF400226B73 /* BUYCartLineItem.h */; settings = {ATTRIBUTES = (Public, ); }; }; 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 */; }; 9003969C1B601DF400226B73 /* BUYCartLineItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 9003969A1B601DF400226B73 /* BUYCartLineItem.m */; };
900396AC1B627CB900226B73 /* BUYProductView.h in Headers */ = {isa = PBXBuildFile; fileRef = 900396AA1B627CB900226B73 /* BUYProductView.h */; }; 900396AC1B627CB900226B73 /* BUYProductView.h in Headers */ = {isa = PBXBuildFile; fileRef = 900396AA1B627CB900226B73 /* BUYProductView.h */; };
...@@ -467,6 +475,8 @@ ...@@ -467,6 +475,8 @@
8498108F1CB7E07900CFAB58 /* BUYDeliveryRangeTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYDeliveryRangeTransformer.m; sourceTree = "<group>"; }; 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>"; }; 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>"; }; 849810911CB7E07900CFAB58 /* BUYFlatCollectionTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYFlatCollectionTransformer.m; sourceTree = "<group>"; };
84D915411CC0359700D334FB /* BUYObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BUYObserver.h; sourceTree = "<group>"; };
84D915421CC0359700D334FB /* BUYObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYObserver.m; sourceTree = "<group>"; };
900396991B601DF400226B73 /* BUYCartLineItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BUYCartLineItem.h; sourceTree = "<group>"; }; 900396991B601DF400226B73 /* BUYCartLineItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BUYCartLineItem.h; sourceTree = "<group>"; };
9003969A1B601DF400226B73 /* BUYCartLineItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYCartLineItem.m; sourceTree = "<group>"; }; 9003969A1B601DF400226B73 /* BUYCartLineItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BUYCartLineItem.m; sourceTree = "<group>"; };
900396AA1B627CB900226B73 /* BUYProductView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = BUYProductView.h; path = "Product View/BUYProductView.h"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 900396AA1B627CB900226B73 /* BUYProductView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = BUYProductView.h; path = "Product View/BUYProductView.h"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
...@@ -972,6 +982,8 @@ ...@@ -972,6 +982,8 @@
BE47340E1B66C4EF00AA721A /* BUYError.m */, BE47340E1B66C4EF00AA721A /* BUYError.m */,
2AF52A931A7010B20087DB2C /* BUYObject.h */, 2AF52A931A7010B20087DB2C /* BUYObject.h */,
2AF52A941A7010B20087DB2C /* BUYObject.mm */, 2AF52A941A7010B20087DB2C /* BUYObject.mm */,
84D915411CC0359700D334FB /* BUYObserver.h */,
84D915421CC0359700D334FB /* BUYObserver.m */,
F76CFF1E19CB7C500079C703 /* BUYSerializable.h */, F76CFF1E19CB7C500079C703 /* BUYSerializable.h */,
); );
path = Models; path = Models;
...@@ -1108,6 +1120,7 @@ ...@@ -1108,6 +1120,7 @@
901931571BC5B9BC00D1134E /* BUYObject.h in Headers */, 901931571BC5B9BC00D1134E /* BUYObject.h in Headers */,
901931581BC5B9BC00D1134E /* BUYRuntime.h in Headers */, 901931581BC5B9BC00D1134E /* BUYRuntime.h in Headers */,
901931591BC5B9BC00D1134E /* BUYCollection.h in Headers */, 901931591BC5B9BC00D1134E /* BUYCollection.h in Headers */,
84D915441CC0359700D334FB /* BUYObserver.h in Headers */,
9019315A1BC5B9BC00D1134E /* BUYProductImageCollectionViewCell.h in Headers */, 9019315A1BC5B9BC00D1134E /* BUYProductImageCollectionViewCell.h in Headers */,
84980F5F1CB7617E00CFAB58 /* BUYDateTransformer.h in Headers */, 84980F5F1CB7617E00CFAB58 /* BUYDateTransformer.h in Headers */,
84980F591CB7617500CFAB58 /* BUYURLTransformer.h in Headers */, 84980F591CB7617500CFAB58 /* BUYURLTransformer.h in Headers */,
...@@ -1197,6 +1210,7 @@ ...@@ -1197,6 +1210,7 @@
BE9A645D1B503CE30033E558 /* BUYObject.h in Headers */, BE9A645D1B503CE30033E558 /* BUYObject.h in Headers */,
BE9A646E1B503D1E0033E558 /* BUYRuntime.h in Headers */, BE9A646E1B503D1E0033E558 /* BUYRuntime.h in Headers */,
BEB74A901B55A3D00005A300 /* BUYCollection.h in Headers */, BEB74A901B55A3D00005A300 /* BUYCollection.h in Headers */,
84D915431CC0359700D334FB /* BUYObserver.h in Headers */,
904606AF1B6BC8D700754173 /* BUYProductImageCollectionViewCell.h in Headers */, 904606AF1B6BC8D700754173 /* BUYProductImageCollectionViewCell.h in Headers */,
84980F5E1CB7617E00CFAB58 /* BUYDateTransformer.h in Headers */, 84980F5E1CB7617E00CFAB58 /* BUYDateTransformer.h in Headers */,
84980F581CB7617500CFAB58 /* BUYURLTransformer.h in Headers */, 84980F581CB7617500CFAB58 /* BUYURLTransformer.h in Headers */,
...@@ -1416,6 +1430,7 @@ ...@@ -1416,6 +1430,7 @@
901930FD1BC5B9BC00D1134E /* BUYTaxLine.m in Sources */, 901930FD1BC5B9BC00D1134E /* BUYTaxLine.m in Sources */,
901930FE1BC5B9BC00D1134E /* BUYCollection+Additions.m in Sources */, 901930FE1BC5B9BC00D1134E /* BUYCollection+Additions.m in Sources */,
84980F351CB75C2900CFAB58 /* NSEntityDescription+BUYAdditions.m in Sources */, 84980F351CB75C2900CFAB58 /* NSEntityDescription+BUYAdditions.m in Sources */,
84D915461CC0359700D334FB /* BUYObserver.m in Sources */,
901930FF1BC5B9BC00D1134E /* BUYVariantOptionBreadCrumbsView.m in Sources */, 901930FF1BC5B9BC00D1134E /* BUYVariantOptionBreadCrumbsView.m in Sources */,
901931011BC5B9BC00D1134E /* BUYTheme+Additions.m in Sources */, 901931011BC5B9BC00D1134E /* BUYTheme+Additions.m in Sources */,
901931021BC5B9BC00D1134E /* BUYStoreViewController.m in Sources */, 901931021BC5B9BC00D1134E /* BUYStoreViewController.m in Sources */,
...@@ -1531,6 +1546,7 @@ ...@@ -1531,6 +1546,7 @@
BE9A64521B503CB80033E558 /* BUYTaxLine.m in Sources */, BE9A64521B503CB80033E558 /* BUYTaxLine.m in Sources */,
900396F71B69563400226B73 /* BUYCollection+Additions.m in Sources */, 900396F71B69563400226B73 /* BUYCollection+Additions.m in Sources */,
84980F341CB75C2900CFAB58 /* NSEntityDescription+BUYAdditions.m in Sources */, 84980F341CB75C2900CFAB58 /* NSEntityDescription+BUYAdditions.m in Sources */,
84D915451CC0359700D334FB /* BUYObserver.m in Sources */,
90DE92711B9897B6002EF4DA /* BUYVariantOptionBreadCrumbsView.m in Sources */, 90DE92711B9897B6002EF4DA /* BUYVariantOptionBreadCrumbsView.m in Sources */,
906EAE441B836DE000976165 /* BUYTheme+Additions.m in Sources */, 906EAE441B836DE000976165 /* BUYTheme+Additions.m in Sources */,
BE9A647F1B503D960033E558 /* BUYStoreViewController.m in Sources */, BE9A647F1B503D960033E558 /* BUYStoreViewController.m in Sources */,
......
...@@ -57,6 +57,9 @@ FOUNDATION_EXPORT const unsigned char BuyVersionString[]; ...@@ -57,6 +57,9 @@ FOUNDATION_EXPORT const unsigned char BuyVersionString[];
#import <Buy/BUYApplePayHelpers.h> #import <Buy/BUYApplePayHelpers.h>
#import <Buy/BUYClient.h> #import <Buy/BUYClient.h>
#import <Buy/BUYError.h> #import <Buy/BUYError.h>
#import <Buy/BUYModelManagerProtocol.h>
#import <Buy/BUYObjectProtocol.h>
#import <Buy/BUYObserver.h>
#import <Buy/BUYPaymentButton.h> #import <Buy/BUYPaymentButton.h>
#import <Buy/BUYProductViewController.h> #import <Buy/BUYProductViewController.h>
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
#import <CoreData/CoreData.h> #import <CoreData/CoreData.h>
@protocol BUYObject;
/** /**
* A protocol for defining an object that can store and retrieve model objects from a data store or other cache. * A protocol for defining an object that can store and retrieve model objects from a data store or other cache.
*/ */
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <Buy/BUYObjectProtocol.h>
/** /**
* This is the base class for all Shopify model objects. * This is the base class for all Shopify model objects.
* This class takes care of convertion .json responses into * This class takes care of convertion .json responses into
...@@ -33,7 +35,7 @@ ...@@ -33,7 +35,7 @@
* *
* You will generally not need to interact with this class directly. * You will generally not need to interact with this class directly.
*/ */
@interface BUYObject : NSObject @interface BUYObject : NSObject<BUYObject>
/** /**
* The identifier of any Shopify model object. * The identifier of any Shopify model object.
...@@ -59,6 +61,5 @@ ...@@ -59,6 +61,5 @@
- (NSSet *)dirtyProperties; - (NSSet *)dirtyProperties;
- (void)markPropertyAsDirty:(NSString *)property; - (void)markPropertyAsDirty:(NSString *)property;
- (void)markAsClean; - (void)markAsClean;
+ (void)trackDirtyProperties;
@end @end
...@@ -25,37 +25,21 @@ ...@@ -25,37 +25,21 @@
// //
#import "BUYObject.h" #import "BUYObject.h"
#import "BUYModelManagerProtocol.h"
#import "BUYObserver.h"
#import "BUYRuntime.h" #import "BUYRuntime.h"
#import <objc/runtime.h> #import "NSDictionary+BUYAdditions.h"
#import <objc/message.h> #import "NSException+BUYAdditions.h"
#import "NSEntityDescription+BUYAdditions.h"
namespace shopify @interface BUYObject ()
{ @property (nonatomic, readwrite, weak) id<BUYModelManager> modelManager;
namespace mobilebuysdk @property (nonatomic) BUYObserver *dirtyObserver;
{ @end
/**
* Creates and returns a block that is used as the setter method for the persisted subclasses
* The method implementation calls through to its superclasses implementation and then marks
* the attribute as dirty.
*/
template <typename T>
id attribute_setter(NSString *propertyName, SEL selector)
{
id setterBlock = ^ void (id _self, T value) {
// we need to cast objc_msgSend so that the compiler inserts proper type information for the passed in value
// not doing so results in strange behaviour (for example, floats never get set)
void (*typed_objc_msgSend)(id, SEL, T) = (void (*)(id, SEL, T))objc_msgSend;
typed_objc_msgSend(_self, selector, value);
[_self markPropertyAsDirty:propertyName];
};
return setterBlock;
}
}
}
@implementation BUYObject { @implementation BUYObject
NSMutableSet *_dirtyProperties;
} #pragma mark - Deprecated
- (instancetype)init - (instancetype)init
{ {
...@@ -64,18 +48,7 @@ namespace shopify ...@@ -64,18 +48,7 @@ namespace shopify
- (instancetype)initWithDictionary:(NSDictionary *)dictionary - (instancetype)initWithDictionary:(NSDictionary *)dictionary
{ {
self = [super init]; return [self initWithModelManager:nil JSONDictionary:dictionary];
if (self) {
_dirtyProperties = [[NSMutableSet alloc] init];
[self updateWithDictionary:dictionary];
[self markAsClean];
}
return self;
}
- (void)updateWithDictionary:(NSDictionary *)dictionary
{
_identifier = dictionary[@"id"];
} }
+ (NSArray *)convertJSONArray:(NSArray*)json block:(void (^)(id obj))createdBlock + (NSArray *)convertJSONArray:(NSArray*)json block:(void (^)(id obj))createdBlock
...@@ -109,157 +82,141 @@ namespace shopify ...@@ -109,157 +82,141 @@ namespace shopify
- (BOOL)isDirty - (BOOL)isDirty
{ {
return [_dirtyProperties count] > 0; return [self.dirtyObserver hasChanges];
} }
- (NSSet *)dirtyProperties - (NSSet *)dirtyProperties
{ {
return [NSSet setWithSet:_dirtyProperties]; return self.dirtyObserver.changedProperties;
} }
- (void)markPropertyAsDirty:(NSString *)property - (void)markPropertyAsDirty:(NSString *)property
{ {
[_dirtyProperties addObject:property]; [self.dirtyObserver markPropertyChanged:property];
} }
- (void)markAsClean - (void)markAsClean
{ {
[_dirtyProperties removeAllObjects]; [self.dirtyObserver reset];
} }
+ (id)setterBlockForSelector:(SEL)selector property:(NSString *)property typeEncoding:(const char *)typeEncoding - (BOOL)isEqual:(id)object
{ {
id setterBlock; if (self == object) return YES;
switch (typeEncoding[0]) {
case '@': // object if (![object isKindOfClass:self.class]) return NO;
{
setterBlock = shopify::mobilebuysdk::attribute_setter<id>(property, selector); BOOL same = ([self.identifier isEqual:((BUYObject*)object).identifier]);
break;
} return same;
case 'B': // C++ style bool/_Bool
{
setterBlock = shopify::mobilebuysdk::attribute_setter<bool>(property, selector);
break;
}
case 'c': // char
{
setterBlock = shopify::mobilebuysdk::attribute_setter<char>(property, selector);
break;
}
case 'C': // unsigned char
{
setterBlock = shopify::mobilebuysdk::attribute_setter<unsigned char>(property, selector);
break;
}
case 'i': // int
{
setterBlock = shopify::mobilebuysdk::attribute_setter<int>(property, selector);
break;
}
case 'I': // unsigned int
{
setterBlock = shopify::mobilebuysdk::attribute_setter<unsigned int>(property, selector);
break;
}
case 's': // short
{
setterBlock = shopify::mobilebuysdk::attribute_setter<short>(property, selector);
break;
}
case 'S': // unsigned short
{
setterBlock = shopify::mobilebuysdk::attribute_setter<unsigned short>(property, selector);
break;
}
case 'l': // long
{
setterBlock = shopify::mobilebuysdk::attribute_setter<long>(property, selector);
break;
}
case 'L': // unsigned long
{
setterBlock = shopify::mobilebuysdk::attribute_setter<unsigned long>(property, selector);
break;
}
case 'q': // long long
{
setterBlock = shopify::mobilebuysdk::attribute_setter<long long>(property, selector);
break;
}
case 'Q': // unsigned long long
{
setterBlock = shopify::mobilebuysdk::attribute_setter<unsigned long long>(property, selector);
break;
}
case 'f': // float
{
setterBlock = shopify::mobilebuysdk::attribute_setter<float>(property, selector);
break;
}
case 'd': // double
{
setterBlock = shopify::mobilebuysdk::attribute_setter<double>(property, selector);
break;
}
}
return setterBlock;
} }
+ (void)wrapProperty:(NSString *)property - (NSUInteger)hash
{ {
SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@:", [NSString stringWithFormat:@"%@%@",[[property substringToIndex:1] uppercaseString], [property substringFromIndex:1]]]); NSUInteger hash = [self.identifier hash];
return hash;
}
//Get the setter. don't worry about readonly properties as they're irrelevant for dirty tracking - (void)trackDirtyProperties:(NSArray *)properties
if (setter && [self instancesRespondToSelector:setter]) { {
Method setterMethod = class_getInstanceMethod(self, setter); self.dirtyObserver = [BUYObserver observeProperties:properties ofObject:self];
IMP setterImpl = method_getImplementation(setterMethod); }
NSMethodSignature *methodSignature = [self instanceMethodSignatureForSelector:setter]; #pragma mark - Dynamic JSON Serialization
if ([methodSignature numberOfArguments] == 3) {
const char *typeEncoding = [methodSignature getArgumentTypeAtIndex:2];
SEL newSetter = NSSelectorFromString([NSString stringWithFormat:@"buy_%@", NSStringFromSelector(setter)]);
id setterBlock = [self setterBlockForSelector:newSetter property:property typeEncoding:typeEncoding]; + (NSArray *)propertyNames
{
static NSMutableDictionary *namesCache;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
namesCache = [NSMutableDictionary dictionary];
});
if (setterBlock) { NSString *className = NSStringFromClass(self);
//Create 'buy_setX:' that uses the existing implementation NSArray *names = namesCache[className];
class_addMethod(self, newSetter, setterImpl, method_getTypeEncoding(setterMethod)); if (names == nil) {
NSMutableSet *allNames = [class_getBUYProperties(self) mutableCopy];
[allNames removeObject:NSStringFromSelector(@selector(dirtyObserver))];
names = [allNames allObjects];
namesCache[className] = names;
}
//Create a new impmlementation return names;
IMP newImpl = imp_implementationWithBlock(setterBlock); }
//Then attach that implementation to 'setX:'. This way calling 'setX:' calls our implementation, and 'buy_setX:' calls the original implementation. + (NSEntityDescription *)entity
class_replaceMethod(self, setter, newImpl, method_getTypeEncoding(setterMethod)); {
} @throw BUYAbstractMethod();
} }
}
+ (NSString *)entityName
{
@throw BUYAbstractMethod();
} }
+ (void)trackDirtyProperties - (NSDictionary *)JSONEncodedProperties
{ {
NSSet *properties = class_getBUYProperties(self); return self.entity.JSONEncodedProperties;
for (NSString *property in properties) { }
if ([property length] > 0) {
[self wrapProperty:property]; - (instancetype)initWithModelManager:(id<BUYModelManager>)modelManager JSONDictionary:(NSDictionary *)dictionary
{
self = [super init];
if (self) {
self.modelManager = modelManager;
[self updateWithDictionary:dictionary];
if ([[self class] tracksDirtyProperties]) {
[self trackDirtyProperties:[[self class] propertyNames]];
} }
} }
return self;
} }
- (BOOL)isEqual:(id)object - (void)updateWithDictionary:(NSDictionary *)dictionary
{ {
if (self == object) return YES; _identifier = dictionary[@"id"];
[self markAsClean];
}
if (![object isKindOfClass:self.class]) return NO; - (NSDictionary *)jsonDictionaryForCheckout
{
return self.JSONDictionary;
}
BOOL same = ([self.identifier isEqual:((BUYObject*)object).identifier]); + (NSPredicate *)fetchPredicateWithJSON:(NSDictionary *)JSONDictionary
{
return nil;
}
return same; + (BOOL)isPersistentClass
{
return NO;
} }
- (NSUInteger)hash + (BOOL)tracksDirtyProperties
{ {
NSUInteger hash = [self.identifier hash]; return NO;
return hash; }
- (NSEntityDescription *)entity
{
return [self.modelManager buy_entityWithName:[[self class] entityName]];
}
- (NSDictionary *)JSONDictionary
{
// JSON generation starts in `-buy_JSONForObject`.
// Both persistent and transient objects go through this interface.
return [self.entity buy_JSONForObject:self];
}
- (void)setJSONDictionary:(NSDictionary *)JSONDictionary
{
// JSON parsing starts in `-buy_updateObject:withJSON:`.
// Both persistent and transient objects go through this interface.
if ([JSONDictionary count]) {
[self.entity buy_updateObject:self withJSON:JSONDictionary];
}
} }
@end @end
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
// //
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
@protocol BUYModelManager; @protocol BUYModelManager;
...@@ -75,7 +76,7 @@ ...@@ -75,7 +76,7 @@
/** /**
* Use the values in the given dictionary to update properties. * Use the values in the given dictionary to update properties.
*/ */
- (void)updateWithJSONDictionary:(NSDictionary *)dictionary; - (void)updateWithDictionary:(NSDictionary *)dictionary;
@optional @optional
......
//
// BUYObserver.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 <Foundation/Foundation.h>
@interface BUYObserver : NSObject
@property (nonatomic, readonly) NSObject *object;
@property (nonatomic, readonly) NSArray *observedProperties;
@property (nonatomic, readonly) NSSet *changedProperties;
@property (nonatomic, readonly) BOOL hasChanges;
- (instancetype)init NS_UNAVAILABLE;
- (void)markPropertyChanged:(NSString *)property;
- (void)reset;
- (void)cancel;
+ (instancetype)observeProperties:(NSArray<NSString *> *)properties ofObject:(id)object;
@end
//
// BUYObserver.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 "BUYObserver.h"
static void * kBUYObserverContext = &kBUYObserverContext;
@interface BUYObserver ()
@property (nonatomic) NSObject *object;
@property (nonatomic) NSArray *observedProperties;
@end
@implementation BUYObserver {
NSMutableSet *_changedProperties;
}
- (instancetype)initWithObject:(id)object properties:(NSArray<NSString *> *)properties
{
self = [super init];
if (self) {
self.object = object;
self.observedProperties = properties;
_changedProperties = [NSMutableSet set];
[self startObserving];
}
return self;
}
- (void)dealloc
{
[self stopObserving];
}
#pragma mark - Accessors
- (NSSet *)changedProperties
{
return [_changedProperties copy];
}
- (void)addChangedPropertiesObject:(NSString *)object
{
[_changedProperties addObject:object];
}
- (void)removeChangedProperties:(NSSet *)objects
{
[_changedProperties minusSet:objects];
}
#pragma mark - Key Value Observing
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
if (context == kBUYObserverContext) {
[self markPropertyChanged:keyPath];
}
else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)startObserving
{
for (NSString *property in _observedProperties) {
[_object addObserver:self forKeyPath:property options:0 context:kBUYObserverContext];
}
}
- (void)stopObserving
{
for (NSString *property in _observedProperties) {
[_object removeObserver:self forKeyPath:property];
}
}
#pragma mark - BUYObserver
- (void)markPropertyChanged:(NSString *)property
{
[self addChangedPropertiesObject:property];
}
- (BOOL)hasChanges
{
return _changedProperties.count > 0;
}
- (void)reset {
[self removeChangedProperties:[_changedProperties copy]];
}
- (void)cancel {
[self reset];
[self stopObserving];
_changedProperties = nil;
_observedProperties = nil;
_object = nil;
}
+ (instancetype)observeProperties:(NSArray<NSString *> *)properties ofObject:(id)object
{
return [[self alloc] initWithObject:object properties:properties];
}
@end
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
@import Foundation; #import <Foundation/Foundation.h>
@protocol BUYSerializable <NSObject> @protocol BUYSerializable <NSObject>
......
...@@ -28,13 +28,6 @@ ...@@ -28,13 +28,6 @@
@implementation BUYShop @implementation BUYShop
+ (void)initialize
{
if (self == [BUYShop class]) {
[self trackDirtyProperties];
}
}
- (void)updateWithDictionary:(NSDictionary *)dictionary - (void)updateWithDictionary:(NSDictionary *)dictionary
{ {
[super updateWithDictionary:dictionary]; [super updateWithDictionary:dictionary];
......
...@@ -46,11 +46,9 @@ ...@@ -46,11 +46,9 @@
@implementation BUYCheckout @implementation BUYCheckout
+ (void)initialize + (BOOL)tracksDirtyProperties
{ {
if (self == [BUYCheckout class]) { return YES;
[self trackDirtyProperties];
}
} }
- (instancetype)initWithCart:(BUYCart *)cart - (instancetype)initWithCart:(BUYCart *)cart
......
...@@ -53,6 +53,9 @@ ...@@ -53,6 +53,9 @@
#import "BUYApplePayHelpers.h" #import "BUYApplePayHelpers.h"
#import "BUYClient.h" #import "BUYClient.h"
#import "BUYError.h" #import "BUYError.h"
#import "BUYModelManagerProtocol.h"
#import "BUYObjectProtocol.h"
#import "BUYObserver.h"
#import "BUYPaymentButton.h" #import "BUYPaymentButton.h"
#import "BUYProductViewController.h" #import "BUYProductViewController.h"
......
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