Skip to content

Commit 61f3320

Browse files
authored
Implement Error handling instead of exceptions (#1237)
* wip * wip * Initial refactoring of Consistency Assertion to Consistency errors - Make Tried to get invalid local id a soft error - Make Tried to save an object with a new, unsaved child. a soft error - Make Tried to save an object with a pointer to a new, unsaved object. a soft error - Make Attempted to find non-existent uuid a soft error * Soft errors for User consistency checks `_checkSaveParametersWithCurrentUser` * Bumps to latest Swift * Proper failable methods for the LocalIdStore * Adds precondition for checking if user is not merging on itself * Swallow consistency error when decoding * Base error checking on return parameters as unsafe to check error / *error * Restore behaviour for nil tranformed objects without errors * proper ARC modifier for NSError** * Transform assertions to errors in children collection * Adds PFPrecondition instead of Exception when saving cycles * Properly bail on encoding errors, add tests * nits: TODOs * Improve handling of failures for localId resolutiosn * Report missing localId as soft errors * Refactor: Rename macros in PFPreconditon, more consistency on arguments * Better error propagation in resolving localIds * More tests for failures of localId resolutions * Bump version to 1.16.1-alpha.1 * Bump podspec to 1.17.0-alpha.1 * Fix issue #916 - As per discussion over there, @nlutsenko and @richardjrossiii were A1 on the resolution Thanks @czgarrett for the fix Closes #931 * Fixes #1184 - Prevent deadlock by copying estimatedData before traversal * Adds r/w accessors in swift for the PFACL fixes #1083 * Adds CHANGELOG.md * Bumps to 1.17.0-alpha.2 * Fixes flaky test * Provide more context when asserting OfflineStore objectId assignment issues * Bolts doesnt infer failed tasks with NSError as a return type, always return failed tasks if needed * Bump to 1.17.0-alpha.3 * Releases on travis * run pod trunk push with verbose to not stall travis * Review nits * Do no track dimensions if they are nil * Bump version to 1.17.0-alpha.4 * Adds precondition to prevent crash * Fixes errors * Bumps to 1.17.0-alpha.5
1 parent 61d51c4 commit 61f3320

File tree

104 files changed

+1186
-535
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

104 files changed

+1186
-535
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ jobs:
6464
- run: *prepare
6565
- run: |
6666
xcrun simctl create "Apple TV 1080p" com.apple.CoreSimulator.SimDeviceType.Apple-TV-1080p com.apple.CoreSimulator.SimRuntime.tvOS-11-0
67-
bundle exec rake test:deployment
67+
bundle exec rake package:release
6868
jazzy:
6969
<<: *defaults
7070
steps:

.travis.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ jobs:
2020
- xcrun simctl create "Apple TV 1080p" com.apple.CoreSimulator.SimDeviceType.Apple-TV-1080p com.apple.CoreSimulator.SimRuntime.tvOS-11-2
2121
- bundle exec rake test:deployment
2222
- ./Scripts/jazzy.sh
23+
- xcrun simctl create "Apple TV 1080p" com.apple.CoreSimulator.SimDeviceType.Apple-TV-1080p com.apple.CoreSimulator.SimRuntime.tvOS-11-0
24+
- bundle exec rake package:release
2325
deploy:
2426
- provider: releases
2527
api_key:

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Parse-SDK-iOS-OSX Chnagelog
2+
3+
### master
4+
5+
* Fixes NSInternalInconsistencyException handling starting Bolts 1.9.0 by emitting soft NSErrors
6+
* Fixes issue affecting public getter/setters in PFACL's in Swift (#1083)
7+
* Prevent deadlocks when saving objects with cicrular references (#916)
8+
* Prevent deadlocks when running fetchAll with circluar references (#1184)
9+
* Adds NSNotification when an invalid session token is encountered
10+

Gemfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,3 @@ gem 'xcpretty'
77
gem 'xcodeproj'
88
gem 'cocoapods'
99
gem 'jazzy', '~> 0.9.0'
10-

Parse.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'Parse'
3-
s.version = '1.16.0'
3+
s.version = '1.17.0-alpha.5'
44
s.license = { :type => 'BSD', :file => 'LICENSE' }
55
s.homepage = 'http://parseplatform.org/'
66
s.summary = 'A library that gives you access to the powerful Parse cloud platform from your iOS/OS X/watchOS/tvOS app.'

Parse/Parse.xcodeproj/project.pbxproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6826,6 +6826,9 @@
68266826
LastSwiftMigration = 0900;
68276827
TestTargetID = 4AE33A0A1F5451AD0088DCA0;
68286828
};
6829+
81C09F501AF97A490043B49C = {
6830+
LastSwiftMigration = 0920;
6831+
};
68296832
81C3821B19CCA89E0066284A = {
68306833
CreatedOnToolsVersion = 6.0.1;
68316834
LastSwiftMigration = 0830;
@@ -8588,13 +8591,15 @@
85888591
isa = XCBuildConfiguration;
85898592
baseConfigurationReference = F55ABB5A1B4F39DA00A0ECD5 /* ParseUnitTests-macOS.xcconfig */;
85908593
buildSettings = {
8594+
SWIFT_VERSION = 4.0;
85918595
};
85928596
name = Debug;
85938597
};
85948598
81C09F851AF97A490043B49C /* Release */ = {
85958599
isa = XCBuildConfiguration;
85968600
baseConfigurationReference = F55ABB5A1B4F39DA00A0ECD5 /* ParseUnitTests-macOS.xcconfig */;
85978601
buildSettings = {
8602+
SWIFT_VERSION = 4.0;
85988603
};
85998604
name = Release;
86008605
};

Parse/Parse/Internal/ACL/PFACLPrivate.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
/*
2121
Gets the encoded format for an ACL.
2222
*/
23-
- (NSDictionary *)encodeIntoDictionary;
23+
- (NSDictionary *)encodeIntoDictionary:(NSError **)error;
2424

2525
/*
2626
Creates a new ACL object from an existing dictionary.

Parse/Parse/Internal/Analytics/Controller/PFAnalyticsController.m

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,11 @@ + (instancetype)controllerWithDataSource:(id<PFEventuallyQueueProvider>)dataSour
7777
@weakify(self);
7878
return [[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{
7979
@strongify(self);
80-
NSDictionary *encodedDimensions = [[PFNoObjectEncoder objectEncoder] encodeObject:dimensions];
80+
NSError *error;
81+
NSDictionary *encodedDimensions = [[PFNoObjectEncoder objectEncoder] encodeObject:dimensions error:&error];
82+
if (encodedDimensions == nil) {
83+
return [BFTask taskWithError:error];
84+
}
8185
PFRESTCommand *command = [PFRESTAnalyticsCommand trackEventCommandWithEventName:name
8286
dimensions:encodedDimensions
8387
sessionToken:sessionToken];

Parse/Parse/Internal/CloudCode/PFCloudCodeController.m

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,14 @@ - (BFTask *)callCloudCodeFunctionAsync:(NSString *)functionName
4747
@weakify(self);
4848
return [[[BFTask taskFromExecutor:[BFExecutor defaultPriorityBackgroundExecutor] withBlock:^id{
4949
@strongify(self);
50-
NSDictionary *encodedParameters = [[PFNoObjectEncoder objectEncoder] encodeObject:parameters];
50+
NSError *error;
51+
NSDictionary *encodedParameters = [[PFNoObjectEncoder objectEncoder] encodeObject:parameters error:&error];
52+
PFPreconditionReturnFailedTask(encodedParameters, error);
5153
PFRESTCloudCommand *command = [PFRESTCloudCommand commandForFunction:functionName
5254
withParameters:encodedParameters
53-
sessionToken:sessionToken];
55+
sessionToken:sessionToken
56+
error:&error];
57+
PFPreconditionReturnFailedTask(command, error);
5458
return [self.dataSource.commandRunner runCommandAsync:command withOptions:PFCommandRunningOptionRetryIfFailed];
5559
}] continueWithSuccessBlock:^id(BFTask *task) {
5660
return ((PFCommandResult *)(task.result)).result[@"result"];

Parse/Parse/Internal/Commands/CommandRunner/URLRequestConstructor/PFCommandURLRequestConstructor.m

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@ + (instancetype)constructorWithDataSource:(id<PFInstallationIdentifierStoreProvi
7171
} else {
7272
parameters = command.parameters;
7373
}
74-
requestParameters = [[PFPointerObjectEncoder objectEncoder] encodeObject:parameters];
74+
NSError *error = nil;
75+
requestParameters = [[PFPointerObjectEncoder objectEncoder] encodeObject:parameters error:&error];
76+
PFPreconditionReturnFailedTask(requestParameters, error);
7577
}
7678

7779
return [PFHTTPURLRequestConstructor urlRequestWithURL:url

Parse/Parse/Internal/Commands/CommandRunner/URLSession/PFURLSessionCommandRunner.m

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,9 @@ - (void)dealloc {
147147
withOptions:(PFCommandRunningOptions)options
148148
cancellationToken:(BFCancellationToken *)cancellationToken {
149149
return [self _performCommandRunningBlock:^id {
150-
[command resolveLocalIds];
150+
NSError *error;
151+
BOOL success = [command resolveLocalIds:&error];
152+
PFPreconditionReturnFailedTask(success, error);
151153
return [[self.requestConstructor getDataURLRequestAsyncForCommand:command] continueWithSuccessBlock:^id(BFTask <NSURLRequest *>*task) {
152154
return [_session performDataURLRequestAsync:task.result forCommand:command cancellationToken:cancellationToken];
153155
}];
@@ -168,7 +170,9 @@ - (void)dealloc {
168170
return [self _performCommandRunningBlock:^id {
169171
@strongify(self);
170172

171-
[command resolveLocalIds];
173+
NSError *error;
174+
BOOL success = [command resolveLocalIds:&error];
175+
PFPreconditionReturnFailedTask(success, error);
172176
return [[self.requestConstructor getFileUploadURLRequestAsyncForCommand:command
173177
withContentType:contentType
174178
contentSourceFilePath:sourceFilePath] continueWithSuccessBlock:^id(BFTask<NSURLRequest *> *task) {

Parse/Parse/Internal/Commands/PFRESTAnalyticsCommand.m

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,12 @@ + (instancetype)_trackEventCommandWithEventName:(NSString *)eventName
5151
if (!dictionary[@"at"]) {
5252
dictionary[@"at"] = [NSDate date];
5353
}
54-
54+
// TODO: flovilmart do not swallow error here
5555
return [self commandWithHTTPPath:httpPath
5656
httpMethod:PFHTTPRequestMethodPOST
5757
parameters:dictionary
58-
sessionToken:sessionToken];
58+
sessionToken:sessionToken
59+
error:nil];
5960
}
6061

6162
@end

Parse/Parse/Internal/Commands/PFRESTCloudCommand.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ NS_ASSUME_NONNULL_BEGIN
1515

1616
+ (instancetype)commandForFunction:(NSString *)function
1717
withParameters:(nullable NSDictionary *)parameters
18-
sessionToken:(nullable NSString *)sessionToken;
18+
sessionToken:(nullable NSString *)sessionToken
19+
error:(NSError **)error;
1920

2021
@end
2122

Parse/Parse/Internal/Commands/PFRESTCloudCommand.m

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ @implementation PFRESTCloudCommand
1616

1717
+ (instancetype)commandForFunction:(NSString *)function
1818
withParameters:(NSDictionary *)parameters
19-
sessionToken:(NSString *)sessionToken {
19+
sessionToken:(NSString *)sessionToken
20+
error:(NSError **)error {
2021
NSString *path = [NSString stringWithFormat:@"functions/%@", function];
2122
return [self commandWithHTTPPath:path
2223
httpMethod:PFHTTPRequestMethodPOST
2324
parameters:parameters
24-
sessionToken:sessionToken];
25+
sessionToken:sessionToken
26+
error:error];
2527
}
2628

2729
@end

Parse/Parse/Internal/Commands/PFRESTCommand.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@ NS_ASSUME_NONNULL_BEGIN
3232
+ (instancetype)commandWithHTTPPath:(NSString *)path
3333
httpMethod:(NSString *)httpMethod
3434
parameters:(nullable NSDictionary *)parameters
35-
sessionToken:(nullable NSString *)sessionToken;
35+
sessionToken:(nullable NSString *)sessionToken
36+
error:(NSError **)error;
3637

3738
+ (instancetype)commandWithHTTPPath:(NSString *)path
3839
httpMethod:(NSString *)httpMethod
3940
parameters:(nullable NSDictionary *)parameters
4041
operationSetUUID:(nullable NSString *)operationSetIdentifier
41-
sessionToken:(nullable NSString *)sessionToken;
42+
sessionToken:(nullable NSString *)sessionToken
43+
error:(NSError **)error;
4244

4345
@end
4446

Parse/Parse/Internal/Commands/PFRESTCommand.m

Lines changed: 64 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,22 @@ @implementation PFRESTCommand
4343
+ (instancetype)commandWithHTTPPath:(NSString *)path
4444
httpMethod:(NSString *)httpMethod
4545
parameters:(NSDictionary *)parameters
46-
sessionToken:(NSString *)sessionToken {
46+
sessionToken:(NSString *)sessionToken
47+
error:(NSError **) error {
4748
return [self commandWithHTTPPath:path
4849
httpMethod:httpMethod
4950
parameters:parameters
5051
operationSetUUID:nil
51-
sessionToken:sessionToken];
52+
sessionToken:sessionToken
53+
error:error];
5254
}
5355

5456
+ (instancetype)commandWithHTTPPath:(NSString *)path
5557
httpMethod:(NSString *)httpMethod
5658
parameters:(NSDictionary *)parameters
5759
operationSetUUID:(NSString *)operationSetIdentifier
58-
sessionToken:(NSString *)sessionToken {
60+
sessionToken:(NSString *)sessionToken
61+
error:(NSError **)error {
5962
PFRESTCommand *command = [[self alloc] init];
6063
command.httpPath = path;
6164
command.httpMethod = httpMethod;
@@ -105,12 +108,13 @@ + (instancetype)commandFromDictionaryRepresentation:(NSDictionary *)dictionary {
105108
PFRESTCommand *command = [self commandWithHTTPPath:dictionary[PFRESTCommandHTTPPathEncodingKey]
106109
httpMethod:dictionary[PFRESTCommandHTTPMethodEncodingKey]
107110
parameters:dictionary[PFRESTCommandParametersEncodingKey]
108-
sessionToken:dictionary[PFRESTCommandSessionTokenEncodingKey]];
111+
sessionToken:dictionary[PFRESTCommandSessionTokenEncodingKey]
112+
error:nil];
109113
command.localId = dictionary[PFRESTCommandLocalIdEncodingKey];
110114
return command;
111115
}
112116

113-
- (NSDictionary *)dictionaryRepresentation {
117+
- (NSDictionary *)dictionaryRepresentation:(NSError **)error {
114118
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
115119
if (self.httpPath) {
116120
dictionary[PFRESTCommandHTTPPathEncodingKey] = self.httpPath;
@@ -119,7 +123,10 @@ - (NSDictionary *)dictionaryRepresentation {
119123
dictionary[PFRESTCommandHTTPMethodEncodingKey] = self.httpMethod;
120124
}
121125
if (self.parameters) {
122-
NSDictionary *parameters = [[PFPointerOrLocalIdObjectEncoder objectEncoder] encodeObject:self.parameters];
126+
NSDictionary *parameters = [[PFPointerOrLocalIdObjectEncoder objectEncoder] encodeObject:self.parameters error:error];
127+
if (!parameters) {
128+
return nil;
129+
}
123130
dictionary[PFRESTCommandParametersEncodingKey] = parameters;
124131
}
125132
if (self.sessionToken) {
@@ -135,6 +142,7 @@ + (BOOL)isValidDictionaryRepresentation:(NSDictionary *)dictionary {
135142
return dictionary[PFRESTCommandHTTPPathEncodingKey] != nil;
136143
}
137144

145+
138146
#pragma mark Local Identifiers
139147

140148
/**
@@ -166,66 +174,91 @@ - (void)maybeChangeServerOperation {
166174
}
167175
}
168176

169-
+ (BOOL)forEachLocalIdIn:(id)object doBlock:(BOOL(^)(PFObject *pointer))block {
170-
__block BOOL modified = NO;
177+
+ (BOOL)forEachLocalIdIn:(id)object
178+
doBlock:(BOOL(^)(PFObject *pointer, BOOL *modified, NSError **error))block
179+
modified:(BOOL *)modified error:(NSError **)error {
171180

172181
// If this is a Pointer with a local id, try to resolve it.
173182
if ([object isKindOfClass:[PFObject class]] && !((PFObject *)object).objectId) {
174-
return block(object);
183+
__block BOOL blockModified = NO;
184+
BOOL success = block(object, &blockModified, error);
185+
if (blockModified) {
186+
*modified = YES;
187+
}
188+
return success;
175189
}
176190

177191
if ([object isKindOfClass:[NSDictionary class]]) {
192+
__block NSError *localError;
193+
__block BOOL hasFailed = NO;
178194
[object enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
179-
if ([[self class] forEachLocalIdIn:obj doBlock:block]) {
180-
modified = YES;
195+
if (![[self class] forEachLocalIdIn:obj doBlock:block modified:modified error:&localError]) {
196+
*stop = YES;
197+
hasFailed = YES;
181198
}
182199
}];
200+
if (hasFailed && error) {
201+
*error = localError;
202+
return NO;
203+
}
183204
} else if ([object isKindOfClass:[NSArray class]]) {
184205
for (id value in object) {
185-
if ([[self class] forEachLocalIdIn:value doBlock:block]) {
186-
modified = YES;
206+
if (![[self class] forEachLocalIdIn:value doBlock:block modified:modified error:error]) {
207+
return NO;
187208
}
188209
}
189210
} else if ([object isKindOfClass:[PFAddOperation class]]) {
190211
for (id value in ((PFAddOperation *)object).objects) {
191-
if ([[self class] forEachLocalIdIn:value doBlock:block]) {
192-
modified = YES;
212+
if (![[self class] forEachLocalIdIn:value doBlock:block modified:modified error:error]) {
213+
return NO;
193214
}
194215
}
195216
} else if ([object isKindOfClass:[PFAddUniqueOperation class]]) {
196217
for (id value in ((PFAddUniqueOperation *)object).objects) {
197-
if ([[self class] forEachLocalIdIn:value doBlock:block]) {
198-
modified = YES;
218+
if (![[self class] forEachLocalIdIn:value doBlock:block modified:modified error:error]) {
219+
return NO;
199220
}
200221
}
201222
} else if ([object isKindOfClass:[PFRemoveOperation class]]) {
202223
for (id value in ((PFRemoveOperation *)object).objects) {
203-
if ([[self class] forEachLocalIdIn:value doBlock:block]) {
204-
modified = YES;
224+
if (![[self class] forEachLocalIdIn:value doBlock:block modified:modified error:error]) {
225+
return NO;
205226
}
206227
}
207228
}
208-
209-
return modified;
229+
return YES;
210230
}
211231

212-
- (void)forEachLocalId:(BOOL(^)(PFObject *pointer))block {
232+
- (BOOL)forEachLocalId:(BOOL(^)(PFObject *pointer, BOOL *modified, NSError **error))block error:(NSError **)error {
213233
NSDictionary *data = [[PFDecoder objectDecoder] decodeObject:self.parameters];
214234
if (!data) {
215-
return;
235+
return YES;
216236
}
217-
218-
if ([[self class] forEachLocalIdIn:data doBlock:block]) {
219-
self.parameters = [[PFPointerOrLocalIdObjectEncoder objectEncoder] encodeObject:data];
237+
BOOL modified = NO;
238+
if ([[self class] forEachLocalIdIn:data doBlock:block modified:&modified error:error]) {
239+
self.parameters = [[PFPointerOrLocalIdObjectEncoder objectEncoder] encodeObject:data error:error];
240+
if (self.parameters && !(error && *error)) {
241+
return YES;
242+
}
220243
}
244+
return NO;
221245
}
222246

223-
- (void)resolveLocalIds {
224-
[self forEachLocalId:^(PFObject *pointer) {
225-
[pointer resolveLocalId];
226-
return YES;
227-
}];
247+
- (BOOL)resolveLocalIds:(NSError * __autoreleasing *)error {
248+
BOOL paramEncodingSucceeded = [self forEachLocalId:^(PFObject *pointer, BOOL *modified, NSError **blockError) {
249+
NSError *localError;
250+
BOOL success = [pointer resolveLocalId:&localError];
251+
*modified = YES;
252+
if (!success && localError) {
253+
*blockError = localError;
254+
}
255+
return success;
256+
} error: error];
257+
if (!paramEncodingSucceeded && *error) {
258+
return NO;
259+
}
228260
[self maybeChangeServerOperation];
261+
return YES;
229262
}
230263

231264
@end

Parse/Parse/Internal/Commands/PFRESTConfigCommand.m

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ + (instancetype)configFetchCommandWithSessionToken:(NSString *)sessionToken {
1818
return [self commandWithHTTPPath:@"config"
1919
httpMethod:PFHTTPRequestMethodGET
2020
parameters:nil
21-
sessionToken:sessionToken];
21+
sessionToken:sessionToken
22+
error:nil];
2223
}
2324

2425
+ (instancetype)configUpdateCommandWithConfigParameters:(NSDictionary *)parameters
@@ -27,7 +28,8 @@ + (instancetype)configUpdateCommandWithConfigParameters:(NSDictionary *)paramete
2728
return [self commandWithHTTPPath:@"config"
2829
httpMethod:PFHTTPRequestMethodPUT
2930
parameters:commandParameters
30-
sessionToken:sessionToken];
31+
sessionToken:sessionToken
32+
error:nil];
3133
}
3234

3335
@end

0 commit comments

Comments
 (0)