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 @@
@implementation BUYDirtyTracked
+ (void)initialize
+ (BOOL)tracksDirtyProperties
{
if (self == [BUYDirtyTracked class]) {
[self trackDirtyProperties];
}
return YES;
}
@end
......
......@@ -87,6 +87,10 @@
84980F2A1CB75AC200CFAB58 /* BUYObjectProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 84980F281CB75AC200CFAB58 /* BUYObjectProtocol.h */; };
84980F2C1CB75B5E00CFAB58 /* 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 */; };
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 */; };
......@@ -119,6 +123,10 @@
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 */; };
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, ); }; };
9003969C1B601DF400226B73 /* BUYCartLineItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 9003969A1B601DF400226B73 /* BUYCartLineItem.m */; };
900396AC1B627CB900226B73 /* BUYProductView.h in Headers */ = {isa = PBXBuildFile; fileRef = 900396AA1B627CB900226B73 /* BUYProductView.h */; };
......@@ -467,6 +475,8 @@
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>"; };
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>"; };
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; };
......@@ -972,6 +982,8 @@
BE47340E1B66C4EF00AA721A /* BUYError.m */,
2AF52A931A7010B20087DB2C /* BUYObject.h */,
2AF52A941A7010B20087DB2C /* BUYObject.mm */,
84D915411CC0359700D334FB /* BUYObserver.h */,
84D915421CC0359700D334FB /* BUYObserver.m */,
F76CFF1E19CB7C500079C703 /* BUYSerializable.h */,
);
path = Models;
......@@ -1108,6 +1120,7 @@
901931571BC5B9BC00D1134E /* BUYObject.h in Headers */,
901931581BC5B9BC00D1134E /* BUYRuntime.h in Headers */,
901931591BC5B9BC00D1134E /* BUYCollection.h in Headers */,
84D915441CC0359700D334FB /* BUYObserver.h in Headers */,
9019315A1BC5B9BC00D1134E /* BUYProductImageCollectionViewCell.h in Headers */,
84980F5F1CB7617E00CFAB58 /* BUYDateTransformer.h in Headers */,
84980F591CB7617500CFAB58 /* BUYURLTransformer.h in Headers */,
......@@ -1197,6 +1210,7 @@
BE9A645D1B503CE30033E558 /* BUYObject.h in Headers */,
BE9A646E1B503D1E0033E558 /* BUYRuntime.h in Headers */,
BEB74A901B55A3D00005A300 /* BUYCollection.h in Headers */,
84D915431CC0359700D334FB /* BUYObserver.h in Headers */,
904606AF1B6BC8D700754173 /* BUYProductImageCollectionViewCell.h in Headers */,
84980F5E1CB7617E00CFAB58 /* BUYDateTransformer.h in Headers */,
84980F581CB7617500CFAB58 /* BUYURLTransformer.h in Headers */,
......@@ -1416,6 +1430,7 @@
901930FD1BC5B9BC00D1134E /* BUYTaxLine.m in Sources */,
901930FE1BC5B9BC00D1134E /* BUYCollection+Additions.m in Sources */,
84980F351CB75C2900CFAB58 /* NSEntityDescription+BUYAdditions.m in Sources */,
84D915461CC0359700D334FB /* BUYObserver.m in Sources */,
901930FF1BC5B9BC00D1134E /* BUYVariantOptionBreadCrumbsView.m in Sources */,
901931011BC5B9BC00D1134E /* BUYTheme+Additions.m in Sources */,
901931021BC5B9BC00D1134E /* BUYStoreViewController.m in Sources */,
......@@ -1531,6 +1546,7 @@
BE9A64521B503CB80033E558 /* BUYTaxLine.m in Sources */,
900396F71B69563400226B73 /* BUYCollection+Additions.m in Sources */,
84980F341CB75C2900CFAB58 /* NSEntityDescription+BUYAdditions.m in Sources */,
84D915451CC0359700D334FB /* BUYObserver.m in Sources */,
90DE92711B9897B6002EF4DA /* BUYVariantOptionBreadCrumbsView.m in Sources */,
906EAE441B836DE000976165 /* BUYTheme+Additions.m in Sources */,
BE9A647F1B503D960033E558 /* BUYStoreViewController.m in Sources */,
......
......@@ -57,6 +57,9 @@ FOUNDATION_EXPORT const unsigned char BuyVersionString[];
#import <Buy/BUYApplePayHelpers.h>
#import <Buy/BUYClient.h>
#import <Buy/BUYError.h>
#import <Buy/BUYModelManagerProtocol.h>
#import <Buy/BUYObjectProtocol.h>
#import <Buy/BUYObserver.h>
#import <Buy/BUYPaymentButton.h>
#import <Buy/BUYProductViewController.h>
......
......@@ -26,6 +26,8 @@
#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.
*/
......
......@@ -26,6 +26,8 @@
#import <Foundation/Foundation.h>
#import <Buy/BUYObjectProtocol.h>
/**
* This is the base class for all Shopify model objects.
* This class takes care of convertion .json responses into
......@@ -33,7 +35,7 @@
*
* 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.
......@@ -59,6 +61,5 @@
- (NSSet *)dirtyProperties;
- (void)markPropertyAsDirty:(NSString *)property;
- (void)markAsClean;
+ (void)trackDirtyProperties;
@end
......@@ -25,37 +25,21 @@
//
#import "BUYObject.h"
#import "BUYModelManagerProtocol.h"
#import "BUYObserver.h"
#import "BUYRuntime.h"
#import <objc/runtime.h>
#import <objc/message.h>
#import "NSDictionary+BUYAdditions.h"
#import "NSException+BUYAdditions.h"
#import "NSEntityDescription+BUYAdditions.h"
namespace shopify
{
namespace mobilebuysdk
{
/**
* 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;
}
}
}
@interface BUYObject ()
@property (nonatomic, readwrite, weak) id<BUYModelManager> modelManager;
@property (nonatomic) BUYObserver *dirtyObserver;
@end
@implementation BUYObject {
NSMutableSet *_dirtyProperties;
}
@implementation BUYObject
#pragma mark - Deprecated
- (instancetype)init
{
......@@ -64,18 +48,7 @@ namespace shopify
- (instancetype)initWithDictionary:(NSDictionary *)dictionary
{
self = [super init];
if (self) {
_dirtyProperties = [[NSMutableSet alloc] init];
[self updateWithDictionary:dictionary];
[self markAsClean];
}
return self;
}
- (void)updateWithDictionary:(NSDictionary *)dictionary
{
_identifier = dictionary[@"id"];
return [self initWithModelManager:nil JSONDictionary:dictionary];
}
+ (NSArray *)convertJSONArray:(NSArray*)json block:(void (^)(id obj))createdBlock
......@@ -109,157 +82,141 @@ namespace shopify
- (BOOL)isDirty
{
return [_dirtyProperties count] > 0;
return [self.dirtyObserver hasChanges];
}
- (NSSet *)dirtyProperties
{
return [NSSet setWithSet:_dirtyProperties];
return self.dirtyObserver.changedProperties;
}
- (void)markPropertyAsDirty:(NSString *)property
{
[_dirtyProperties addObject:property];
[self.dirtyObserver markPropertyChanged:property];
}
- (void)markAsClean
{
[_dirtyProperties removeAllObjects];
[self.dirtyObserver reset];
}
+ (id)setterBlockForSelector:(SEL)selector property:(NSString *)property typeEncoding:(const char *)typeEncoding
- (BOOL)isEqual:(id)object
{
id setterBlock;
switch (typeEncoding[0]) {
case '@': // object
{
setterBlock = shopify::mobilebuysdk::attribute_setter<id>(property, selector);
break;
}
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;
if (self == object) return YES;
if (![object isKindOfClass:self.class]) return NO;
BOOL same = ([self.identifier isEqual:((BUYObject*)object).identifier]);
return same;
}
- (NSUInteger)hash
{
NSUInteger hash = [self.identifier hash];
return hash;
}
+ (void)wrapProperty:(NSString *)property
- (void)trackDirtyProperties:(NSArray *)properties
{
SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@:", [NSString stringWithFormat:@"%@%@",[[property substringToIndex:1] uppercaseString], [property substringFromIndex:1]]]);
self.dirtyObserver = [BUYObserver observeProperties:properties ofObject:self];
}
#pragma mark - Dynamic JSON Serialization
+ (NSArray *)propertyNames
{
static NSMutableDictionary *namesCache;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
namesCache = [NSMutableDictionary dictionary];
});
//Get the setter. don't worry about readonly properties as they're irrelevant for dirty tracking
if (setter && [self instancesRespondToSelector:setter]) {
Method setterMethod = class_getInstanceMethod(self, setter);
IMP setterImpl = method_getImplementation(setterMethod);
NSMethodSignature *methodSignature = [self instanceMethodSignatureForSelector:setter];
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];
if (setterBlock) {
//Create 'buy_setX:' that uses the existing implementation
class_addMethod(self, newSetter, setterImpl, method_getTypeEncoding(setterMethod));
//Create a new impmlementation
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.
class_replaceMethod(self, setter, newImpl, method_getTypeEncoding(setterMethod));
}
}
NSString *className = NSStringFromClass(self);
NSArray *names = namesCache[className];
if (names == nil) {
NSMutableSet *allNames = [class_getBUYProperties(self) mutableCopy];
[allNames removeObject:NSStringFromSelector(@selector(dirtyObserver))];
names = [allNames allObjects];
namesCache[className] = names;
}
return names;
}
+ (NSEntityDescription *)entity
{
@throw BUYAbstractMethod();
}
+ (NSString *)entityName
{
@throw BUYAbstractMethod();
}
- (NSDictionary *)JSONEncodedProperties
{
return self.entity.JSONEncodedProperties;
}
+ (void)trackDirtyProperties
- (instancetype)initWithModelManager:(id<BUYModelManager>)modelManager JSONDictionary:(NSDictionary *)dictionary
{
NSSet *properties = class_getBUYProperties(self);
for (NSString *property in properties) {
if ([property length] > 0) {
[self wrapProperty:property];
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;
if (![object isKindOfClass:self.class]) return NO;
BOOL same = ([self.identifier isEqual:((BUYObject*)object).identifier]);
return same;
_identifier = dictionary[@"id"];
[self markAsClean];
}
- (NSUInteger)hash
- (NSDictionary *)jsonDictionaryForCheckout
{
NSUInteger hash = [self.identifier hash];
return hash;
return self.JSONDictionary;
}
+ (NSPredicate *)fetchPredicateWithJSON:(NSDictionary *)JSONDictionary
{
return nil;
}
+ (BOOL)isPersistentClass
{
return NO;
}
+ (BOOL)tracksDirtyProperties
{
return NO;
}
- (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
......@@ -25,6 +25,7 @@
//
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
@protocol BUYModelManager;
......@@ -75,7 +76,7 @@
/**
* Use the values in the given dictionary to update properties.
*/
- (void)updateWithJSONDictionary:(NSDictionary *)dictionary;
- (void)updateWithDictionary:(NSDictionary *)dictionary;
@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 @@
// THE SOFTWARE.
//
@import Foundation;
#import <Foundation/Foundation.h>
@protocol BUYSerializable <NSObject>
......
......@@ -28,13 +28,6 @@
@implementation BUYShop
+ (void)initialize
{
if (self == [BUYShop class]) {
[self trackDirtyProperties];
}
}
- (void)updateWithDictionary:(NSDictionary *)dictionary
{
[super updateWithDictionary:dictionary];
......
......@@ -46,11 +46,9 @@
@implementation BUYCheckout
+ (void)initialize
+ (BOOL)tracksDirtyProperties
{
if (self == [BUYCheckout class]) {
[self trackDirtyProperties];
}
return YES;
}
- (instancetype)initWithCart:(BUYCart *)cart
......
......@@ -53,6 +53,9 @@
#import "BUYApplePayHelpers.h"
#import "BUYClient.h"
#import "BUYError.h"
#import "BUYModelManagerProtocol.h"
#import "BUYObjectProtocol.h"
#import "BUYObserver.h"
#import "BUYPaymentButton.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