diff --git a/AVOS/AVOS.xcodeproj/project.pbxproj b/AVOS/AVOS.xcodeproj/project.pbxproj index c408dd01..958869ab 100644 --- a/AVOS/AVOS.xcodeproj/project.pbxproj +++ b/AVOS/AVOS.xcodeproj/project.pbxproj @@ -51,9 +51,9 @@ D30B6A5A24A09D23006ABE09 /* LCRequestManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 7068A7221BA3614D0004623A /* LCRequestManager.m */; }; D30B6A5B24A09D23006ABE09 /* LCRelation_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C841A581A5A79FF00C5C6C4 /* LCRelation_Internal.h */; }; D30B6A5C24A09D23006ABE09 /* LCSubclassing.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C841A591A5A79FF00C5C6C4 /* LCSubclassing.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D30B6A5D24A09D23006ABE09 /* LCSaveOption.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EB9F21C44D68400BA917F /* LCSaveOption.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D30B6A5E24A09D23006ABE09 /* LCSaveOption_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EB9FE1C44F3D600BA917F /* LCSaveOption_internal.h */; }; - D30B6A5F24A09D23006ABE09 /* LCSaveOption.m in Sources */ = {isa = PBXBuildFile; fileRef = 830EB9F31C44D68400BA917F /* LCSaveOption.m */; }; + D30B6A5D24A09D23006ABE09 /* LCObjectOption.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EB9F21C44D68400BA917F /* LCObjectOption.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D30B6A5E24A09D23006ABE09 /* LCObjectOption_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EB9FE1C44F3D600BA917F /* LCObjectOption_Internal.h */; }; + D30B6A5F24A09D23006ABE09 /* LCObjectOption.m in Sources */ = {isa = PBXBuildFile; fileRef = 830EB9F31C44D68400BA917F /* LCObjectOption.m */; }; D30B6A6024A09D52006ABE09 /* LCInstallation.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C841A5B1A5A79FF00C5C6C4 /* LCInstallation.h */; settings = {ATTRIBUTES = (Public, ); }; }; D30B6A6124A09D52006ABE09 /* LCInstallation.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C841A5C1A5A79FF00C5C6C4 /* LCInstallation.m */; }; D30B6A6224A09D52006ABE09 /* LCInstallation_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C841A5D1A5A79FF00C5C6C4 /* LCInstallation_Internal.h */; }; @@ -348,9 +348,9 @@ 830E7CFA1B1CAD3B005F4B22 /* LCDatabaseMigrator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCDatabaseMigrator.h; sourceTree = ""; }; 830E7CFB1B1CAD3B005F4B22 /* LCDatabaseMigrator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCDatabaseMigrator.m; sourceTree = ""; }; 830E7CFE1B1CB07B005F4B22 /* LCDatabaseCommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCDatabaseCommon.h; sourceTree = ""; }; - 830EB9F21C44D68400BA917F /* LCSaveOption.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCSaveOption.h; sourceTree = ""; }; - 830EB9F31C44D68400BA917F /* LCSaveOption.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCSaveOption.m; sourceTree = ""; }; - 830EB9FE1C44F3D600BA917F /* LCSaveOption_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCSaveOption_internal.h; sourceTree = ""; }; + 830EB9F21C44D68400BA917F /* LCObjectOption.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCObjectOption.h; sourceTree = ""; }; + 830EB9F31C44D68400BA917F /* LCObjectOption.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCObjectOption.m; sourceTree = ""; }; + 830EB9FE1C44F3D600BA917F /* LCObjectOption_Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCObjectOption_Internal.h; sourceTree = ""; }; 830F159E1F010F260009B4D0 /* LCIMRecalledMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCIMRecalledMessage.h; sourceTree = ""; }; 830F159F1F010F260009B4D0 /* LCIMRecalledMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCIMRecalledMessage.m; sourceTree = ""; }; 831E29581D882CBB006E502B /* LCIMMessageOption.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCIMMessageOption.h; sourceTree = ""; }; @@ -944,9 +944,9 @@ 7068A7221BA3614D0004623A /* LCRequestManager.m */, 8C841A581A5A79FF00C5C6C4 /* LCRelation_Internal.h */, 8C841A591A5A79FF00C5C6C4 /* LCSubclassing.h */, - 830EB9F21C44D68400BA917F /* LCSaveOption.h */, - 830EB9FE1C44F3D600BA917F /* LCSaveOption_internal.h */, - 830EB9F31C44D68400BA917F /* LCSaveOption.m */, + 830EB9F21C44D68400BA917F /* LCObjectOption.h */, + 830EB9FE1C44F3D600BA917F /* LCObjectOption_Internal.h */, + 830EB9F31C44D68400BA917F /* LCObjectOption.m */, ); path = Object; sourceTree = ""; @@ -1484,7 +1484,7 @@ D30B6B2324A09F59006ABE09 /* AVIMGenericCommand+AVIMMessagesAdditions.h in Headers */, D30B6AF824A09F1F006ABE09 /* LCGPBMessage.h in Headers */, D30B6A2924A09B1F006ABE09 /* LCApplication_Internal.h in Headers */, - D30B6A5E24A09D23006ABE09 /* LCSaveOption_internal.h in Headers */, + D30B6A5E24A09D23006ABE09 /* LCObjectOption_Internal.h in Headers */, D30B6A9E24A09E35006ABE09 /* LCErrorUtils.h in Headers */, D30B6A6824A09D62006ABE09 /* LCCloudQueryResult_Internal.h in Headers */, D30B6AAB24A09E35006ABE09 /* LCKeyValueStore.h in Headers */, @@ -1528,7 +1528,7 @@ D30B6A8124A09DCA006ABE09 /* LCNetworking.h in Headers */, D30B6B3B24A09F79006ABE09 /* LCIMConversationCacheStore.h in Headers */, D30B6A4224A09CC2006ABE09 /* LCScheduler.h in Headers */, - D30B6A5D24A09D23006ABE09 /* LCSaveOption.h in Headers */, + D30B6A5D24A09D23006ABE09 /* LCObjectOption.h in Headers */, D30B6AEF24A09F1E006ABE09 /* LCGPBEmpty.pbobjc.h in Headers */, D30B6AEB24A09F1E006ABE09 /* LCGPBDictionary.h in Headers */, D30B6A7E24A09DCA006ABE09 /* LCCompatibilityMacros.h in Headers */, @@ -1687,7 +1687,7 @@ D30B6AB524A09E35006ABE09 /* LCDynamicObject.m in Sources */, D30B6A4124A09CC2006ABE09 /* LCPersistenceUtils.m in Sources */, D30B6A4324A09CC2006ABE09 /* LCScheduler.m in Sources */, - D30B6A5F24A09D23006ABE09 /* LCSaveOption.m in Sources */, + D30B6A5F24A09D23006ABE09 /* LCObjectOption.m in Sources */, D30B6A5824A09D23006ABE09 /* LCRelation.m in Sources */, D30B6A7224A09D7B006ABE09 /* LCPaasClient.m in Sources */, D30B6B4E24A09F85006ABE09 /* LCIMFileMessage.m in Sources */, diff --git a/AVOS/LeanCloudObjc/Foundation.h b/AVOS/LeanCloudObjc/Foundation.h index 98100047..32c40006 100644 --- a/AVOS/LeanCloudObjc/Foundation.h +++ b/AVOS/LeanCloudObjc/Foundation.h @@ -26,7 +26,7 @@ // Object #import "LCObject.h" #import "LCObject+Subclass.h" -#import "LCSaveOption.h" +#import "LCObjectOption.h" #import "LCSubclassing.h" #import "LCRelation.h" diff --git a/AVOS/LeanCloudObjcTests/LCUserTestCase.swift b/AVOS/LeanCloudObjcTests/LCUserTestCase.swift index cfa60cfe..772544d5 100644 --- a/AVOS/LeanCloudObjcTests/LCUserTestCase.swift +++ b/AVOS/LeanCloudObjcTests/LCUserTestCase.swift @@ -372,4 +372,83 @@ class LCUserTestCase: BaseTestCase { } } } + + func testFetch() { + let objectWithInvalidID = LCObject(objectId: uuid) + XCTAssertFalse(objectWithInvalidID.fetch()) + do { + try objectWithInvalidID.fetchAndThrows() + XCTFail() + } catch { + XCTAssertEqual((error as NSError).code, kLCErrorObjectNotFound) + } + do { + try objectWithInvalidID.fetch(with: nil) + XCTFail() + } catch { + XCTAssertEqual((error as NSError).code, kLCErrorObjectNotFound) + } + do { + try LCObject.fetchAll([objectWithInvalidID], error: ()) + XCTFail() + } catch { + XCTAssertNotNil(error) + } + + let validObject = LCObject() + let childObject = LCObject() + let key = "childObject" + validObject[key] = childObject + XCTAssertTrue(validObject.save()) + + guard let objectId = validObject.objectId else { + XCTFail() + return + } + + expecting { exp in + let object = LCObject(objectId: objectId) + object.fetchInBackground { obj, error in + XCTAssertTrue(Thread.isMainThread) + XCTAssertTrue(obj === object) + XCTAssertNil(error) + let child = object[key] as? LCObject + XCTAssertEqual(child?.objectId, childObject.objectId) + XCTAssertNil(child?.createdAt) + XCTAssertNil(child?.updatedAt) + exp.fulfill() + } + } + + expecting { exp in + let object = LCObject(objectId: objectId) + let option = LCObjectFetchOption() + option.selectKeys = [key] + option.includeKeys = [key] + object.fetchInBackground(with: option) { obj, error in + XCTAssertTrue(Thread.isMainThread) + XCTAssertTrue(obj === object) + XCTAssertNil(error) + let child = object[key] as? LCObject + XCTAssertEqual(child?.objectId, childObject.objectId) + XCTAssertNotNil(child?.createdAt) + XCTAssertNotNil(child?.updatedAt) + exp.fulfill() + } + } + + expecting { exp in + let object = LCObject(objectId: objectId) + LCObject.fetchAll(inBackground: [object]) { objects, error in + XCTAssertTrue(Thread.isMainThread) + XCTAssertEqual(objects?.count, 1) + let child = (objects?.first as? LCObject)?[key] as? LCObject + XCTAssertEqual(child?.objectId, childObject.objectId) + XCTAssertNil(child?.createdAt) + XCTAssertNil(child?.updatedAt) + XCTAssertNil(error) + exp.fulfill() + } + } + } } diff --git a/AVOS/Sources/Foundation/Object/LCObject.h b/AVOS/Sources/Foundation/Object/LCObject.h index 33919381..936e7995 100644 --- a/AVOS/Sources/Foundation/Object/LCObject.h +++ b/AVOS/Sources/Foundation/Object/LCObject.h @@ -7,6 +7,7 @@ @class LCRelation; @class LCACL; @class LCSaveOption; +@class LCObjectFetchOption; NS_ASSUME_NONNULL_BEGIN @@ -343,151 +344,28 @@ NS_ASSUME_NONNULL_BEGIN + (void)saveAllInBackground:(NSArray *)objects block:(LCBooleanResultBlock)block; -#pragma mark - Refresh +// MARK: Fetch -/*! @name Getting an Object from LeanCloud */ - -/*! - Gets whether the LCObject has been fetched. - @return YES if the LCObject is new or has been fetched or refreshed. NO otherwise. - */ -- (BOOL)isDataAvailable; - -#if LC_IOS_ONLY -// Deprecated and intentionally not available on the new OS X SDK - -/*! - Refreshes the LCObject with the current data from the server. - */ -- (void)refresh; - -/*! - Refreshes the LCObject with the current data from the server. - @param keys Pointer to an NSArray that contains objects specified by the keys want to fetch. - */ -- (void)refreshWithKeys:(NSArray *)keys; - -/*! - Refreshes the LCObject with the current data from the server and sets an error if it occurs. - @param error Pointer to an NSError that will be set if necessary. - @return success or not - */ -- (BOOL)refresh:(NSError **)error; - -/*! - An alias of `-[LCObject refresh:]` methods that supports Swift exception. - @seealso `-[LCObject refresh:]` - */ -- (BOOL)refreshAndThrowsWithError:(NSError **)error; - -/*! - Refreshes the LCObject asynchronously and executes the given callback block. - @param block The block to execute. The block should have the following argument signature: (LCObject *object, NSError *error) - */ -- (void)refreshInBackgroundWithBlock:(LCObjectResultBlock)block; - -/*! - Refreshes the LCObject with the current data from the server. - @param keys Pointer to an NSArray that contains objects specified by the keys want to fetch. - @param block The block to execute. The block should have the following argument signature: (LCObject *object, NSError *error) - */ -- (void)refreshInBackgroundWithKeys:(NSArray *)keys - block:(LCObjectResultBlock)block; - -#endif - -#pragma mark - Fetch - -/*! - Fetches the LCObject with the current data from the server. - */ +/// Fetching the data of the object from the server synchronously. - (BOOL)fetch; -/*! - Fetches the LCObject with the current data from the server and sets an error if it occurs. - @param error Pointer to an NSError that will be set if necessary. - @return success or not - */ -- (BOOL)fetch:(NSError **)error; -/*! - An alias of `-[LCObject fetch:]` methods that supports Swift exception. - @seealso `-[LCObject fetch:]` - */ -- (BOOL)fetchAndThrowsWithError:(NSError **)error; +/// Fetching the data of the object from the server synchronously. +/// @param error The pointer of `NSError *`. +- (BOOL)fetchAndThrowsWithError:(NSError * __autoreleasing *)error; -/*! - Fetches the LCObject with the current data and specified keys from the server and sets an error if it occurs. - @param keys Pointer to an NSArray that contains objects specified by the keys want to fetch. - */ -- (void)fetchWithKeys:(nullable NSArray *)keys; +/// Fetching the data of the object from the server synchronously. +/// @param option See `LCObjectFetchOption`. +/// @param error The pointer of `NSError *`. +- (BOOL)fetchWithOption:(nullable LCObjectFetchOption *)option error:(NSError * __autoreleasing *)error; -/*! - Fetches the LCObject with the current data and specified keys from the server and sets an error if it occurs. - @param keys Pointer to an NSArray that contains objects specified by the keys want to fetch. - @param error Pointer to an NSError that will be set if necessary. - @return success or not - */ -- (BOOL)fetchWithKeys:(nullable NSArray *)keys - error:(NSError **)error; - -/*! - Fetches the LCObject's data from the server if isDataAvailable is false. - */ -- (LCObject *)fetchIfNeeded; - -/*! - Fetches the LCObject's data from the server if isDataAvailable is false. - @param error Pointer to an NSError that will be set if necessary. - */ -- (LCObject *)fetchIfNeeded:(NSError **)error; - -/*! - An alias of `-[LCObject fetchIfNeeded:]` methods that supports Swift exception. - @seealso `-[LCObject fetchIfNeeded:]` - */ -- (LCObject *)fetchIfNeededAndThrowsWithError:(NSError **)error; - -/*! - Fetches the LCObject's data from the server if isDataAvailable is false. - @param keys Pointer to an NSArray that contains objects specified by the keys want to fetch. - */ -- (LCObject *)fetchIfNeededWithKeys:(nullable NSArray *)keys; - -/*! - Fetches the LCObject's data from the server if isDataAvailable is false. - @param keys Pointer to an NSArray that contains objects specified by the keys want to fetch. - @param error Pointer to an NSError that will be set if necessary. - */ -- (LCObject *)fetchIfNeededWithKeys:(nullable NSArray *)keys - error:(NSError **)error; - -/*! - Fetches the LCObject asynchronously and executes the given callback block. - @param block The block to execute. The block should have the following argument signature: (LCObject *object, NSError *error) - */ +/// Fetching the data of the object from the server asynchronously. +/// @param block Result callback. - (void)fetchInBackgroundWithBlock:(LCObjectResultBlock)block; -/*! - Fetches the LCObject asynchronously and executes the given callback block. - @param keys Pointer to an NSArray that contains objects specified by the keys want to fetch. - @param block The block to execute. The block should have the following argument signature: (LCObject *object, NSError *error) - */ -- (void)fetchInBackgroundWithKeys:(nullable NSArray *)keys - block:(LCObjectResultBlock)block; - -/*! - Fetches the LCObject's data asynchronously if isDataAvailable is false, then calls the callback block. - @param block The block to execute. The block should have the following argument signature: (LCObject *object, NSError *error) - */ -- (void)fetchIfNeededInBackgroundWithBlock:(LCObjectResultBlock)block; - -/*! @name Getting Many Objects from LeanCloud */ - -/*! - Fetches all of the LCObjects with the current data from the server - @param objects The list of objects to fetch. - */ -+ (void)fetchAll:(NSArray *)objects; +/// Fetching the data of the object from the server asynchronously. +/// @param option See `LCObjectFetchOption`. +/// @param block Result callback. +- (void)fetchInBackgroundWithOption:(nullable LCObjectFetchOption *)option block:(LCObjectResultBlock)block; /*! Fetches all of the LCObjects with the current data from the server and sets an error if it occurs. @@ -495,37 +373,14 @@ NS_ASSUME_NONNULL_BEGIN @param error Pointer to an NSError that will be set if necessary @return success or not */ -+ (BOOL)fetchAll:(NSArray *)objects error:(NSError **)error; - -/*! - Fetches all of the LCObjects with the current data from the server - @param objects The list of objects to fetch. - */ -+ (void)fetchAllIfNeeded:(NSArray *)objects; - -/*! - Fetches all of the LCObjects with the current data from the server and sets an error if it occurs. - @param objects The list of objects to fetch. - @param error Pointer to an NSError that will be set if necessary - @return success or not - */ -+ (BOOL)fetchAllIfNeeded:(NSArray *)objects error:(NSError **)error; - -/*! - Fetches all of the LCObjects with the current data from the server asynchronously and calls the given block. - @param objects The list of objects to fetch. - @param block The block to execute. The block should have the following argument signature: (NSArray *objects, NSError *error) - */ -+ (void)fetchAllInBackground:(NSArray *)objects - block:(LCArrayResultBlock)block; ++ (BOOL)fetchAll:(NSArray *)objects error:(NSError * __autoreleasing *)error; /*! Fetches all of the LCObjects with the current data from the server asynchronously and calls the given block. @param objects The list of objects to fetch. @param block The block to execute. The block should have the following argument signature: (NSArray *objects, NSError *error) */ -+ (void)fetchAllIfNeededInBackground:(NSArray *)objects - block:(LCArrayResultBlock)block; ++ (void)fetchAllInBackground:(NSArray *)objects block:(LCArrayResultBlock)block; #pragma mark - Delete @@ -629,6 +484,113 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)objectFromDictionary:(NSDictionary *)dictionary; +// MARK: Deprecated + +/*! + Fetches the LCObject with the current data from the server and sets an error if it occurs. + @param error Pointer to an NSError that will be set if necessary. + @return success or not + */ +- (BOOL)fetch:(NSError * __autoreleasing *)error +__deprecated_msg("Deprecated! please use `-[LCObject fetchAndThrowsWithError:]` instead, this method may be removed in the future."); + +/*! + Fetches the LCObject with the current data and specified keys from the server and sets an error if it occurs. + @param keys Pointer to an NSArray that contains objects specified by the keys want to fetch. + */ +- (void)fetchWithKeys:(nullable NSArray *)keys +__deprecated_msg("Deprecated! please use `-[LCObject fetchWithOption:error:]` instead, this method may be removed in the future."); + +/*! + Fetches the LCObject with the current data and specified keys from the server and sets an error if it occurs. + @param keys Pointer to an NSArray that contains objects specified by the keys want to fetch. + @param error Pointer to an NSError that will be set if necessary. + @return success or not + */ +- (BOOL)fetchWithKeys:(nullable NSArray *)keys error:(NSError **)error +__deprecated_msg("Deprecated! please use `-[LCObject fetchWithOption:error:]` instead, this method may be removed in the future."); + +/*! + Fetches the LCObject's data from the server if isDataAvailable is false. + */ +- (LCObject *)fetchIfNeeded +__deprecated_msg("Deprecated! please use `-[LCObject fetchAndThrowsWithError:]` instead, this method may be removed in the future."); + +/*! + Fetches the LCObject's data from the server if isDataAvailable is false. + @param error Pointer to an NSError that will be set if necessary. + */ +- (LCObject *)fetchIfNeeded:(NSError **)error +__deprecated_msg("Deprecated! please use `-[LCObject fetchAndThrowsWithError:]` instead, this method may be removed in the future."); + +/*! + An alias of `-[LCObject fetchIfNeeded:]` methods that supports Swift exception. + @seealso `-[LCObject fetchIfNeeded:]` + */ +- (LCObject *)fetchIfNeededAndThrowsWithError:(NSError **)error +__deprecated_msg("Deprecated! please use `-[LCObject fetchAndThrowsWithError:]` instead, this method may be removed in the future."); + +/*! + Fetches the LCObject's data from the server if isDataAvailable is false. + @param keys Pointer to an NSArray that contains objects specified by the keys want to fetch. + */ +- (LCObject *)fetchIfNeededWithKeys:(nullable NSArray *)keys +__deprecated_msg("Deprecated! please use `-[LCObject fetchWithOption:error:]` instead, this method may be removed in the future."); + +/*! + Fetches the LCObject's data from the server if isDataAvailable is false. + @param keys Pointer to an NSArray that contains objects specified by the keys want to fetch. + @param error Pointer to an NSError that will be set if necessary. + */ +- (LCObject *)fetchIfNeededWithKeys:(nullable NSArray *)keys error:(NSError **)error +__deprecated_msg("Deprecated! please use `-[LCObject fetchWithOption:error:]` instead, this method may be removed in the future."); + +/*! + Fetches the LCObject asynchronously and executes the given callback block. + @param keys Pointer to an NSArray that contains objects specified by the keys want to fetch. + @param block The block to execute. The block should have the following argument signature: (LCObject *object, NSError *error) + */ +- (void)fetchInBackgroundWithKeys:(nullable NSArray *)keys block:(LCObjectResultBlock)block +__deprecated_msg("Deprecated! please use `-[LCObject fetchInBackgroundWithOption:block:]` instead, this method may be removed in the future."); + +/*! + Fetches the LCObject's data asynchronously if isDataAvailable is false, then calls the callback block. + @param block The block to execute. The block should have the following argument signature: (LCObject *object, NSError *error) + */ +- (void)fetchIfNeededInBackgroundWithBlock:(LCObjectResultBlock)block +__deprecated_msg("Deprecated! please use `-[LCObject fetchInBackgroundWithOption:block:]` instead, this method may be removed in the future."); + +/*! + Fetches all of the LCObjects with the current data from the server + @param objects The list of objects to fetch. + */ ++ (void)fetchAll:(NSArray *)objects +__deprecated_msg("Deprecated! please use `+[LCObject fetchAll:error:]` instead, this method may be removed in the future."); + +/*! + Fetches all of the LCObjects with the current data from the server + @param objects The list of objects to fetch. + */ ++ (void)fetchAllIfNeeded:(NSArray *)objects +__deprecated_msg("Deprecated! please use `+[LCObject fetchAll:error:]` instead, this method may be removed in the future."); + +/*! + Fetches all of the LCObjects with the current data from the server and sets an error if it occurs. + @param objects The list of objects to fetch. + @param error Pointer to an NSError that will be set if necessary + @return success or not + */ ++ (BOOL)fetchAllIfNeeded:(NSArray *)objects error:(NSError **)error +__deprecated_msg("Deprecated! please use `+[LCObject fetchAll:error:]` instead, this method may be removed in the future."); + +/*! + Fetches all of the LCObjects with the current data from the server asynchronously and calls the given block. + @param objects The list of objects to fetch. + @param block The block to execute. The block should have the following argument signature: (NSArray *objects, NSError *error) + */ ++ (void)fetchAllIfNeededInBackground:(NSArray *)objects block:(LCArrayResultBlock)block +__deprecated_msg("Deprecated! please use `+[LCObject fetchAllInBackground:block:]` instead, this method may be removed in the future."); + @end NS_ASSUME_NONNULL_END diff --git a/AVOS/Sources/Foundation/Object/LCObject.m b/AVOS/Sources/Foundation/Object/LCObject.m index 6cb25d46..e2858330 100644 --- a/AVOS/Sources/Foundation/Object/LCObject.m +++ b/AVOS/Sources/Foundation/Object/LCObject.m @@ -17,8 +17,8 @@ #import "LCLogger.h" #import "LCObject+Subclass.h" #import "LCSubclassing.h" -#import "LCSaveOption.h" -#import "LCSaveOption_internal.h" +#import "LCObjectOption.h" +#import "LCObjectOption_Internal.h" #import "LCUser_Internal.h" #import "LCInstallation_Internal.h" #import "LCFile_Internal.h" @@ -1331,308 +1331,154 @@ + (void)saveAllInBackground:(NSArray *)objects }); } -- (BOOL)isDataAvailable -{ - return self.objectId.length > 0 && self.createdAt != nil; -} - -#pragma mark - Refresh - -- (void)refresh -{ - [self refresh:NULL]; -} - -- (void)refreshWithKeys:(NSArray *)keys { - [self refreshWithBlock:NULL keys:keys waitUntilDone:YES error:nil]; -} - -- (BOOL)refresh:(NSError **)error -{ - return [self refreshWithBlock:NULL keys:nil waitUntilDone:YES error:error]; -} - -- (BOOL)refreshAndThrowsWithError:(NSError * _Nullable __autoreleasing *)error { - return [self refresh:error]; +- (BOOL)isDataAvailable { + return [self hasValidObjectId]; } -- (void)refreshInBackgroundWithBlock:(LCObjectResultBlock)block { - [self refreshWithBlock:block keys:nil waitUntilDone:NO error:NULL]; -} +// MARK: Fetch -- (void)refreshInBackgroundWithKeys:(NSArray *)keys - block:(LCObjectResultBlock)block { - [self refreshWithBlock:block keys:keys waitUntilDone:NO error:NULL]; -} - -- (BOOL)refreshWithBlock:(LCObjectResultBlock)block - keys:(NSArray *)keys - waitUntilDone:(BOOL)wait - error:(NSError **)theError { - - BOOL __block hasCalledBack = NO; - NSError __block *blockError = nil; - - NSString *path = [self myObjectPath]; - NSDictionary * parameters = nil; - if (keys) { - parameters = [LCQuery dictionaryFromIncludeKeys:keys]; - } - [[LCPaasClient sharedInstance] getObject:path withParameters:parameters block:^(id object, NSError *error) { - - if (error == nil) - { - [self removeLocalData]; - [LCObjectUtils copyDictionary:object toObject:self]; - } - else - { - - } - if (self == [LCUser currentUser]) { - [[self class] changeCurrentUser:(LCUser *)self save:YES]; - } - [LCUtils callObjectResultBlock:block object:self error:error]; - - if (wait) { - blockError = error; - hasCalledBack = YES; - } - }]; - - // wait until called back if necessary - if (wait) { - [LCUtils warnMainThreadIfNecessary]; - - LC_WAIT_TIL_TRUE(hasCalledBack, 0.1); - }; - - if (theError != NULL && blockError) { - *theError = blockError; - return NO; - } - return YES; -} - -#pragma mark - Fetch - -- (BOOL)fetch -{ - return [self fetch:NULL]; -} - -- (BOOL)fetch:(NSError **)error -{ - return [self fetchWithKeys:nil error:error]; +- (BOOL)fetch { + return [self fetchAndThrowsWithError:nil]; } - (BOOL)fetchAndThrowsWithError:(NSError * _Nullable __autoreleasing *)error { - return [self fetch:error]; + return [self fetchWithOption:nil error:error]; } -- (void)fetchWithKeys:(NSArray *)keys { - [self fetchWithKeys:keys error:nil]; -} - -- (BOOL)fetchWithKeys:(NSArray *)keys - error:(NSError **)error { - __block NSError *blockError; - __block BOOL hasCallback = NO; - [self internalFetchInBackgroundWithKeys:keys block:^(LCObject *object, NSError *error) { - blockError = error; - hasCallback = YES; - }]; - LC_WAIT_TIL_TRUE(hasCallback, 0.1); - if (blockError && error != NULL) { - *error = blockError; - } - return blockError == nil; -} - -- (LCObject *)fetchIfNeeded -{ - return [self fetchIfNeededWithKeys:nil]; -} - -- (LCObject *)fetchIfNeeded:(NSError **)error -{ - return [self fetchIfNeededWithKeys:nil error:error]; -} - -- (LCObject *)fetchIfNeededAndThrowsWithError:(NSError * _Nullable __autoreleasing *)error { - return [self fetchIfNeeded:error]; -} - -- (LCObject *)fetchIfNeededWithKeys:(NSArray *)keys { - return [self fetchIfNeededWithKeys:keys error:nil]; -} - -- (LCObject *)fetchIfNeededWithKeys:(NSArray *)keys - error:(NSError **)error { - if (![self isDataAvailable]) { - [self fetchWithKeys:keys error:error]; +- (BOOL)fetchWithOption:(LCObjectFetchOption *)option error:(NSError *__autoreleasing _Nullable *)error { + __block NSError *resultError; + [self fetchInBackgroundWithOption:option block:^(LCObject * _Nullable object, NSError * _Nullable error) { + resultError = error; + } wait:true]; + if (error) { + *error = resultError; } - return self; + return !resultError; } -- (void)fetchInBackgroundWithBlock:(LCObjectResultBlock)resultBlock -{ - [self fetchInBackgroundWithKeys:nil block:resultBlock]; +- (void)fetchInBackgroundWithBlock:(LCObjectResultBlock)block { + [self fetchInBackgroundWithOption:nil block:block]; } -- (void)fetchInBackgroundWithKeys:(NSArray *)keys - block:(LCObjectResultBlock)block { - [self internalFetchInBackgroundWithKeys:keys block:^(LCObject *object, NSError *error) { +- (void)fetchInBackgroundWithOption:(LCObjectFetchOption *)option block:(LCObjectResultBlock)block { + [self fetchInBackgroundWithOption:option block:^(LCObject * _Nullable object, NSError * _Nullable error) { [LCUtils callObjectResultBlock:block object:object error:error]; - }]; + } wait:false]; } - (BOOL)handleFetchResult:(id)object error:(NSError * __autoreleasing *)error { - if ([object allKeys].count <= 0) { - if (error != NULL) { - *error = LCError(kLCErrorObjectNotFound, @"not found the object to fetch", nil); + if (![NSDictionary _lc_isTypeOf:object]) { + if (error) { + *error = LCError(LCErrorInternalErrorCodeMalformedData, + @"Malformed data.", + @{ @"object" : object ?: [NSNull null] }); } return false; - } else { - [self removeLocalData]; - [LCObjectUtils copyDictionary:object toObject:self]; - if (self == [LCUser currentUser]) { - [[self class] changeCurrentUser:(LCUser *)self save:YES]; + } + NSDictionary *dictionary = object; + if (dictionary.count == 0) { + if (error) { + *error = LCError(kLCErrorObjectNotFound, @"Object NOT found.", nil); } - return true; + return false; + } + [self removeLocalData]; + [LCObjectUtils copyDictionary:dictionary toObject:self]; + if (self == [LCUser currentUser]) { + [[self class] changeCurrentUser:(LCUser *)self save:true]; } + return true; } -- (void)internalFetchInBackgroundWithKeys:(NSArray *)keys - block:(LCObjectResultBlock)resultBlock +- (void)fetchInBackgroundWithOption:(nullable LCObjectFetchOption *)option + block:(LCObjectResultBlock)block + wait:(BOOL)wait { - if (![self hasValidObjectId]) { - NSError *error = LCError(kLCErrorMissingObjectId, @"Missing ObjectId", nil); - [LCUtils callObjectResultBlock:resultBlock object:nil error:error]; + NSError *error = LCError(kLCErrorMissingObjectId, @"Missing object id.", nil); + block(nil, error); return; } - - NSString *path = [self myObjectPath]; - NSDictionary * parameters = nil; - if (keys) { - parameters = [LCQuery dictionaryFromIncludeKeys:keys]; + NSMutableDictionary *parameters; + if (option) { + parameters = [NSMutableDictionary dictionary]; + if (option.selectKeys.count > 0) { + parameters[@"keys"] = [option.selectKeys componentsJoinedByString:@","]; + } + if (option.includeKeys.count > 0) { + parameters[@"include"] = [option.includeKeys componentsJoinedByString:@","]; + } } - [[LCPaasClient sharedInstance] getObject:path withParameters:parameters block:^(id object, NSError *error) { + [[LCPaasClient sharedInstance] getObject:[self myObjectPath] + withParameters:(parameters.count > 0 ? parameters : nil) + block:^(id object, NSError *error) { if (!error) { NSError *theError; [self handleFetchResult:object error:&theError]; error = theError; } - if (resultBlock) { - resultBlock(self, error); - } - }]; + block(self, error); + } wait:wait]; } -- (void)fetchIfNeededInBackgroundWithBlock:(LCObjectResultBlock)block -{ - if (![self isDataAvailable]) { - [self fetchInBackgroundWithBlock:^(LCObject *object, NSError *error) { - if (block) block(object, error); - }]; - } else { - if (block) block(self, nil); ++ (BOOL)fetchAll:(NSArray *)objects error:(NSError *__autoreleasing _Nullable *)error { + __block NSError *resultError; + [self fetchAllInBackground:objects block:^(NSArray * _Nullable objects, NSError * _Nullable error) { + resultError = error; + } wait:true]; + if (error) { + *error = resultError; } + return !resultError; } -+ (void)fetchAll:(NSArray *)objects -{ - [[self class] fetchAll:objects error:NULL]; -} - -+ (BOOL)fetchAll:(NSArray *)objects error:(NSError **)error -{ - return [[self class] fetchAll:objects error:error checkIfNeed:NO]; -} - -+ (void)fetchAllIfNeeded:(NSArray *)objects -{ - [[self class] fetchAllIfNeeded:objects error:NULL]; ++ (void)fetchAllInBackground:(NSArray *)objects block:(LCArrayResultBlock)block { + [self fetchAllInBackground:objects block:^(NSArray * _Nullable objects, NSError * _Nullable error) { + [LCUtils callArrayResultBlock:block array:objects error:error]; + } wait:false]; } -+ (BOOL)fetchAllIfNeeded:(NSArray *)objects error:(NSError **)error ++ (void)fetchAllInBackground:(NSArray *)objects + block:(LCArrayResultBlock)block + wait:(BOOL)wait { - return [[self class] fetchAll:objects error:error checkIfNeed:YES]; -} - -+ (BOOL)fetchAll:(NSArray *)objects error:(NSError **)error checkIfNeed:(BOOL)check { - objects = [objects copy]; - NSError __block *retError; - if (![self isValidObjects:objects error:&retError]) { - return NO; + NSError *error; + if (![self isValidObjects:objects error:&error]) { + block(nil, error); + return; } - NSMutableArray *fetches = [NSMutableArray array]; - NSMutableArray *fetchObjects = [NSMutableArray array]; - // Add a task to the group - for (LCObject *obj in objects) { - if ([obj isKindOfClass:[LCObject class]]) { - if (!check || ![obj isDataAvailable]) { - NSDictionary* fetch = [LCPaasClient batchMethod:@"GET" path:[obj myObjectPath] body:nil parameters:nil]; - [fetches addObject:fetch]; - [fetchObjects addObject:obj]; - } - } + NSMutableArray *requests = [NSMutableArray arrayWithCapacity:objects.count]; + NSMutableArray *fetchedObjects = [NSMutableArray arrayWithCapacity:objects.count]; + for (LCObject *object in objects) { + NSDictionary *request = [LCPaasClient batchMethod:@"GET" path:[object myObjectPath] body:nil parameters:nil]; + [requests addObject:request]; + [fetchedObjects addObject:object]; } - if (fetches.count > 0) { - __block BOOL hasCalledBlcok = NO; - [[LCPaasClient sharedInstance] postBatchObject:fetches block:^(NSArray *results, NSError *error) { + if (requests.count > 0) { + [[LCPaasClient sharedInstance] postBatchObject:requests headerMap:nil block:^(NSArray * _Nullable results, NSError * _Nullable error) { if (error) { - retError = error; - } else { - if (results.count == fetches.count) { - for (NSInteger i = 0; i < fetches.count; i++) { - LCObject *object = fetchObjects[i]; - id result = results[i]; - if ([result isKindOfClass:[NSDictionary class]]) { - NSError *theError; - [object handleFetchResult:result error:&theError]; - if (theError && retError == nil) { - // retError 只需记录第一个错误 - retError = theError; - } - } else { - if (retError == nil) { - retError = result; - } - } - } + block(nil, error); + return; + } + if (results.count != fetchedObjects.count) { + block(nil, LCError(LCErrorInternalErrorCodeMalformedData, @"Malformed data, inconsistent quantity.", nil)); + return; + } + NSError *firstError; + for (NSInteger i = 0; i < results.count; i++) { + LCObject *object = fetchedObjects[i]; + id result = results[i]; + if (firstError) { + [object handleFetchResult:result error:nil]; + } else { + [object handleFetchResult:result error:&firstError]; } } - hasCalledBlcok = YES; - }]; - LC_WAIT_TIL_TRUE(hasCalledBlcok, 0.1); - } - if (retError && error) { - *error=retError; - return NO; + block(fetchedObjects, firstError); + } wait:wait]; + } else { + block(@[], nil); } - return YES; -} - -+ (void)fetchAllInBackground:(NSArray *)objects - block:(LCArrayResultBlock)block -{ - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ - NSError *error; - [[self class] fetchAll:objects error:&error]; - [LCUtils callArrayResultBlock:block array:objects error:error]; - }); -} - -+ (void)fetchAllIfNeededInBackground:(NSArray *)objects - block:(LCArrayResultBlock)block -{ - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ - NSError *error; - [[self class] fetchAllIfNeeded:objects error:&error]; - [LCUtils callArrayResultBlock:block array:objects error:error]; - }); } #pragma mark - delete @@ -1708,16 +1554,30 @@ - (void)deleteEventuallyWithBlock:(LCIdResultBlock)block { }]; } -// check className & objectId + (BOOL)isValidObjects:(NSArray *)objects error:(NSError **)error { - for(LCObject * object in [objects copy]) { - if (object.className.length <= 0 || ![object hasValidObjectId]) { - if (error != NULL) - *error = LCError(kLCErrorMissingObjectId, @"Invaid className or objectId", nil); - return NO; + for (LCObject *object in objects) { + if (![LCObject _lc_isTypeOf:object]) { + if (error) { + NSString *class = NSStringFromClass([object class]); + NSString *reason = [NSString stringWithFormat:@"Invalid type, `%@` is NOT kind of `LCObject`.", class]; + *error = LCError(LCErrorInternalErrorCodeInvalidType, reason, nil); + } + return false; + } + if (object.className.length == 0) { + if (error) { + *error = LCError(kLCErrorInvalidClassName, @"Invalid class name.", @{ @"object" : object }); + } + return false; + } + if (![object hasValidObjectId]) { + if (error) { + *error = LCError(kLCErrorMissingObjectId, @"Missing object id.", @{ @"object" : object }); + } + return false; } } - return YES; + return true; } + (BOOL)deleteAll:(NSArray *)objects { @@ -1948,7 +1808,7 @@ - (BOOL)moveIfNeedWithKey:(NSString *)key from:(NSMutableDictionary *)from to:(N return NO; } --(BOOL)hasValidObjectId { +- (BOOL)hasValidObjectId { return (self.objectId.length > 0); } @@ -2007,6 +1867,79 @@ -(void)removeLocalData { [self._requestManager clear]; } +// MARK: Deprecated + +- (BOOL)fetch:(NSError *__autoreleasing _Nullable *)error { + return [self fetchAndThrowsWithError:error]; +} + +- (void)fetchWithKeys:(NSArray *)keys { + LCObjectFetchOption *option = [LCObjectFetchOption new]; + option.includeKeys = keys; + [self fetchWithOption:option error:nil]; +} + +- (BOOL)fetchWithKeys:(NSArray *)keys error:(NSError **)error { + LCObjectFetchOption *option = [LCObjectFetchOption new]; + option.includeKeys = keys; + return [self fetchWithOption:option error:error]; +} + +- (LCObject *)fetchIfNeeded { + [self fetch]; + return self; +} + +- (LCObject *)fetchIfNeeded:(NSError **)error { + [self fetchAndThrowsWithError:error]; + return self; +} + +- (LCObject *)fetchIfNeededAndThrowsWithError:(NSError * _Nullable __autoreleasing *)error { + [self fetchAndThrowsWithError:error]; + return self; +} + +- (LCObject *)fetchIfNeededWithKeys:(NSArray *)keys { + LCObjectFetchOption *option = [LCObjectFetchOption new]; + option.includeKeys = keys; + [self fetchWithOption:option error:nil]; + return self; +} + +- (LCObject *)fetchIfNeededWithKeys:(NSArray *)keys error:(NSError **)error { + LCObjectFetchOption *option = [LCObjectFetchOption new]; + option.includeKeys = keys; + [self fetchWithOption:option error:error]; + return self; +} + +- (void)fetchIfNeededInBackgroundWithBlock:(LCObjectResultBlock)block { + [self fetchInBackgroundWithBlock:block]; +} + +- (void)fetchInBackgroundWithKeys:(NSArray *)keys block:(LCObjectResultBlock)block { + LCObjectFetchOption *option = [LCObjectFetchOption new]; + option.includeKeys = keys; + [self fetchInBackgroundWithOption:option block:block]; +} + ++ (void)fetchAll:(NSArray *)objects { + [self fetchAll:objects error:nil]; +} + ++ (void)fetchAllIfNeeded:(NSArray *)objects { + [self fetchAll:objects error:nil]; +} + ++ (BOOL)fetchAllIfNeeded:(NSArray *)objects error:(NSError **)error { + return [self fetchAll:objects error:error]; +} + ++ (void)fetchAllIfNeededInBackground:(NSArray *)objects block:(LCArrayResultBlock)block { + [self fetchAllInBackground:objects block:block]; +} + @end diff --git a/AVOS/Sources/Foundation/Object/LCObjectOption.h b/AVOS/Sources/Foundation/Object/LCObjectOption.h new file mode 100644 index 00000000..f4f3e12e --- /dev/null +++ b/AVOS/Sources/Foundation/Object/LCObjectOption.h @@ -0,0 +1,33 @@ +// +// LCObjectOption.h +// LeanCloud +// +// Created by Tang Tianyong on 1/12/16. +// Copyright © 2016 LeanCloud Inc. All rights reserved. +// + +#import + +@class LCQuery; + +NS_ASSUME_NONNULL_BEGIN + +@interface LCSaveOption : NSObject + +@property (nonatomic, assign) BOOL fetchWhenSave; +@property (nonatomic, strong, nullable) LCQuery *query; + +@end + +/// Option for fetch-method of LCObject. +@interface LCObjectFetchOption : NSObject + +/// Selecting which only key-value will be returned. +/// If the key is prefixed with `-`, means which only key-value will NOT be returned. +@property (nonatomic, nullable) NSArray *selectKeys; +/// Selecting which pointer's all value will be returned. +@property (nonatomic, nullable) NSArray *includeKeys; + +@end + +NS_ASSUME_NONNULL_END diff --git a/AVOS/Sources/Foundation/Object/LCSaveOption.m b/AVOS/Sources/Foundation/Object/LCObjectOption.m similarity index 82% rename from AVOS/Sources/Foundation/Object/LCSaveOption.m rename to AVOS/Sources/Foundation/Object/LCObjectOption.m index 6449ead8..d0e54be9 100644 --- a/AVOS/Sources/Foundation/Object/LCSaveOption.m +++ b/AVOS/Sources/Foundation/Object/LCObjectOption.m @@ -6,8 +6,8 @@ // Copyright © 2016 LeanCloud Inc. All rights reserved. // -#import "LCSaveOption.h" -#import "LCSaveOption_internal.h" +#import "LCObjectOption.h" +#import "LCObjectOption_Internal.h" #import "LCQuery.h" #import "LCQuery_Internal.h" @@ -26,3 +26,7 @@ - (NSDictionary *)dictionary { } @end + +@implementation LCObjectFetchOption + +@end diff --git a/AVOS/Sources/Foundation/Object/LCSaveOption_internal.h b/AVOS/Sources/Foundation/Object/LCObjectOption_Internal.h similarity index 100% rename from AVOS/Sources/Foundation/Object/LCSaveOption_internal.h rename to AVOS/Sources/Foundation/Object/LCObjectOption_Internal.h diff --git a/AVOS/Sources/Foundation/Object/LCObject_Internal.h b/AVOS/Sources/Foundation/Object/LCObject_Internal.h index be70f031..246771ad 100644 --- a/AVOS/Sources/Foundation/Object/LCObject_Internal.h +++ b/AVOS/Sources/Foundation/Object/LCObject_Internal.h @@ -85,7 +85,7 @@ NSString *request_object_id(NSDictionary *request) { - (NSMutableArray *) buildSaveRequests; --(BOOL)hasValidObjectId; +- (BOOL)hasValidObjectId; - (void)setObject:(id)object forKey:(NSString *)key diff --git a/AVOS/Sources/Foundation/Object/LCSaveOption.h b/AVOS/Sources/Foundation/Object/LCSaveOption.h deleted file mode 100644 index 829302ed..00000000 --- a/AVOS/Sources/Foundation/Object/LCSaveOption.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// LCSaveOption.h -// LeanCloud -// -// Created by Tang Tianyong on 1/12/16. -// Copyright © 2016 LeanCloud Inc. All rights reserved. -// - -#import - -@class LCQuery; - -NS_ASSUME_NONNULL_BEGIN - -@interface LCSaveOption : NSObject - -@property (nonatomic, assign) BOOL fetchWhenSave; -@property (nonatomic, strong, nullable) LCQuery *query; - -@end - -NS_ASSUME_NONNULL_END diff --git a/AVOS/Sources/Foundation/Request/LCPaasClient.h b/AVOS/Sources/Foundation/Request/LCPaasClient.h index b7a5b712..0e049b88 100644 --- a/AVOS/Sources/Foundation/Request/LCPaasClient.h +++ b/AVOS/Sources/Foundation/Request/LCPaasClient.h @@ -59,20 +59,27 @@ FOUNDATION_EXPORT NSString * const LCHeaderFieldNameProduction; - (void)getObject:(NSString *)path withParameters:(NSDictionary *)parameters block:(LCIdResultBlock)block; - +- (void)getObject:(NSString *)path + withParameters:(NSDictionary *)parameters + block:(LCIdResultBlock)block + wait:(BOOL)wait; - (void)getObject:(NSString *)path withParameters:(NSDictionary *)parameters policy:(LCCachePolicy)policy maxCacheAge:(NSTimeInterval)maxCacheAge block:(LCIdResultBlock)block; --(void)putObject:(NSString *)path - withParameters:(NSDictionary *)parameters - sessionToken:(NSString *)sessionToken - block:(LCIdResultBlock)block; +- (void)putObject:(NSString *)path + withParameters:(NSDictionary *)parameters + sessionToken:(NSString *)sessionToken + block:(LCIdResultBlock)block; --(void)postBatchObject:(NSArray *)parameterArray block:(LCArrayResultBlock)block; --(void)postBatchObject:(NSArray *)parameterArray headerMap:(NSDictionary *)headerMap eventually:(BOOL)isEventually block:(LCArrayResultBlock)block; +- (void)postBatchObject:(NSArray *)parameterArray + block:(LCArrayResultBlock)block; +- (void)postBatchObject:(NSArray *)parameterArray + headerMap:(NSDictionary *)headerMap + block:(LCArrayResultBlock)block + wait:(BOOL)wait; -(void)postBatchSaveObject:(NSArray *)parameterArray headerMap:(NSDictionary *)headerMap eventually:(BOOL)isEventually block:(LCIdResultBlock)block; diff --git a/AVOS/Sources/Foundation/Request/LCPaasClient.m b/AVOS/Sources/Foundation/Request/LCPaasClient.m index a4e94a27..4ae90f34 100644 --- a/AVOS/Sources/Foundation/Request/LCPaasClient.m +++ b/AVOS/Sources/Foundation/Request/LCPaasClient.m @@ -258,36 +258,42 @@ - (NSMutableURLRequest *)requestWithPath:(NSString *)path return request; } -- (void)getObject:(NSString *)path - withParameters:(NSDictionary *)parameters - block:(LCIdResultBlock)block { - [self getObjectFromNetworkWithPath:path withParameters:parameters policy:kLCCachePolicyIgnoreCache block:block]; +- (void)getObject:(NSString *)path withParameters:(NSDictionary *)parameters block:(LCIdResultBlock)block { + [self getObject:path withParameters:parameters block:block wait:false]; +} + +- (void)getObject:(NSString *)path withParameters:(NSDictionary *)parameters block:(LCIdResultBlock)block wait:(BOOL)wait { + [self getObjectFromNetworkWithPath:path withParameters:parameters policy:kLCCachePolicyIgnoreCache block:block wait:wait]; } --(void)getObjectFromNetworkWithPath:(NSString *)path - withParameters:(NSDictionary *)parameters - policy:(LCCachePolicy)policy - block:(LCIdResultBlock)block +- (void)getObjectFromNetworkWithPath:(NSString *)path + withParameters:(NSDictionary *)parameters + policy:(LCCachePolicy)policy + block:(LCIdResultBlock)block + wait:(BOOL)wait { NSURLRequest *request = [self requestWithPath:path method:@"GET" headers:nil parameters:parameters]; - /* If GET request too heavy, - wrap it into a POST request and ignore cache policy. */ if (parameters && request.URL.absoluteString.length > 4096) { + /* If GET request too heavy, wrap it into a POST request and ignore cache policy. */ NSDictionary *request = [LCPaasClient batchMethod:@"GET" path:path body:nil parameters:parameters]; - [self postBatchObject:@[request] block:^(NSArray * _Nullable objects, NSError * _Nullable error) { - if (!error) { - [LCUtils callIdResultBlock:block object:objects.firstObject error:nil]; + [self postBatchObject:@[request] headerMap:nil block:^(NSArray * _Nullable objects, NSError * _Nullable error) { + if (error) { + block(nil, error); } else { - [LCUtils callIdResultBlock:block object:nil error:error]; + block(objects.firstObject, nil); } - }]; + } wait:wait]; } else { BOOL needCache = (policy != kLCCachePolicyIgnoreCache); - [self performRequest:request saveResult:needCache block:block]; + [self performRequest:request saveResult:needCache block:block wait:wait]; } } +- (void)getObjectFromNetworkWithPath:(NSString *)path withParameters:(NSDictionary *)parameters policy:(LCCachePolicy)policy block:(LCIdResultBlock)block { + [self getObjectFromNetworkWithPath:path withParameters:parameters policy:policy block:block wait:false]; +} + - (void)getObject:(NSString *)path withParameters:(NSDictionary *)parameters policy:(LCCachePolicy)policy maxCacheAge:(NSTimeInterval)maxCacheAge block:(LCIdResultBlock)block { NSString *key = [self absoluteStringFromPath:path parameters:parameters]; @@ -362,10 +368,10 @@ - (NSString *)JSONStringFromDictionary:(NSDictionary *)dictionary { } } --(void)putObject:(NSString *)path - withParameters:(NSDictionary *)parameters - sessionToken:(NSString *)sessionToken - block:(LCIdResultBlock)block +- (void)putObject:(NSString *)path + withParameters:(NSDictionary *)parameters + sessionToken:(NSString *)sessionToken + block:(LCIdResultBlock)block { NSMutableURLRequest *request = [self requestWithPath:path method:@"PUT" headers:nil parameters:parameters]; @@ -373,60 +379,43 @@ -(void)putObject:(NSString *)path [request setValue:sessionToken forHTTPHeaderField:LCHeaderFieldNameSession]; } - [self performRequest:request saveResult:NO block:block]; + [self performRequest:request block:block]; } --(void)postBatchObject:(NSArray *)parameterArray block:(LCArrayResultBlock)block { - [self postBatchObject:parameterArray headerMap:nil eventually:NO block:block]; +- (void)postBatchObject:(NSArray *)parameterArray block:(LCArrayResultBlock)block { + [self postBatchObject:parameterArray headerMap:nil block:block wait:false]; } --(void)postBatchObject:(NSArray *)requests headerMap:(NSDictionary *)headerMap eventually:(BOOL)isEventually block:(LCArrayResultBlock)block { +- (void)postBatchObject:(NSArray *)requests headerMap:(NSDictionary *)headerMap block:(LCArrayResultBlock)block wait:(BOOL)wait +{ NSString *path = [LCObjectUtils batchPath]; NSDictionary *parameters = @{@"requests": requests ?: @[]}; NSMutableURLRequest *request = [self requestWithPath:path method:@"POST" headers:headerMap parameters:parameters]; - LCIdResultBlock handleResultBlock = ^(NSArray *objects, NSError *error) { - // 区分某个删除失败还是网络请求失败,两种情况 error 都不为空 + [self performRequest:request saveResult:false block:^(NSArray *objects, NSError *error) { if (objects.count != requests.count) { - // 网络请求失败,子操作数量不一致 if (error) { block(nil, error); } else { block(nil, LCErrorInternalServer(@"The batch count of server response is not equal to request count")); } } else { - // 网络请求成功 NSMutableArray *results = [NSMutableArray array]; for (NSDictionary *object in objects) { - id success = object[@"success"]; - if (success) { - [results addObject:success]; - continue; } - id error = object[@"error"]; - if (error) { - [results addObject:error]; - continue; } } block(results, nil); } - }; - - if (isEventually) { - NSString *filePath = [self archiveRequest:request]; - [self handleArchivedRequestAtPath:filePath block:handleResultBlock]; - } else { - [self performRequest:request saveResult:NO block:handleResultBlock]; - } + } wait:wait]; } -(void)postBatchSaveObject:(NSArray *)requests headerMap:(NSDictionary *)headerMap eventually:(BOOL)isEventually block:(LCIdResultBlock)block { @@ -438,7 +427,7 @@ -(void)postBatchSaveObject:(NSArray *)requests headerMap:(NSDictionary *)headerM NSString *filePath = [self archiveRequest:request]; [self handleArchivedRequestAtPath:filePath block:block]; } else { - [self performRequest:request saveResult:NO block:block]; + [self performRequest:request block:block]; } } @@ -452,7 +441,7 @@ - (void)postObject:(NSString *)path withParameters:(id)parameters eventually:(BO NSString *filePath = [self archiveRequest:request]; [self handleArchivedRequestAtPath:filePath block:block]; } else { - [self performRequest:request saveResult:NO block:block]; + [self performRequest:request block:block]; } } @@ -470,17 +459,26 @@ - (void)deleteObject:(NSString *)path withParameters:(NSDictionary *)parameters NSString *filePath = [self archiveRequest:request]; [self handleArchivedRequestAtPath:filePath block:block]; } else { - [self performRequest:request saveResult:NO block:block]; + [self performRequest:request block:block]; } } #pragma mark - The final method for network -- (void)performRequest:(NSURLRequest *)request saveResult:(BOOL)saveResult block:(LCIdResultBlock)block { - [self performRequest:request saveResult:saveResult block:block retryTimes:0]; +- (void)performRequest:(NSURLRequest *)request block:(LCIdResultBlock)block { + [self performRequest:request saveResult:false block:block wait:false]; } -- (void)performRequest:(NSURLRequest *)request saveResult:(BOOL)saveResult block:(LCIdResultBlock)block retryTimes:(NSInteger)retryTimes { +- (void)performRequest:(NSURLRequest *)request saveResult:(BOOL)saveResult block:(LCIdResultBlock)block wait:(BOOL)wait { + [self performRequest:request saveResult:saveResult block:block retryTimes:0 wait:wait]; +} + +- (void)performRequest:(NSURLRequest *)request + saveResult:(BOOL)saveResult + block:(LCIdResultBlock)block + retryTimes:(NSInteger)retryTimes + wait:(BOOL)wait +{ NSURL *URL = request.URL; NSString *URLString = URL.absoluteString; NSMutableURLRequest *mutableRequest = [request mutableCopy]; @@ -493,17 +491,14 @@ - (void)performRequest:(NSURLRequest *)request saveResult:(BOOL)saveResult block } [self performRequest:mutableRequest - success:^(NSHTTPURLResponse *response, id responseObject) - { + success: + ^(NSHTTPURLResponse *response, id responseObject) { if (block) { - block(responseObject, nil); } - if (self.isLastModifyEnabled && [request.HTTPMethod isEqualToString:@"GET"]) { NSString *URLMD5 = [URLString _lc_MD5String]; NSString *lastModified = [response.allHeaderFields objectForKey:@"Last-Modified"]; - if (lastModified && ![self.lastModify[URLMD5] isEqualToString:lastModified]) { [[LCCacheManager sharedInstance] saveJSON:responseObject forKey:URLString]; [self.lastModify setObject:lastModified forKey:URLMD5]; @@ -512,10 +507,9 @@ - (void)performRequest:(NSURLRequest *)request saveResult:(BOOL)saveResult block [[LCCacheManager sharedInstance] saveJSON:responseObject forKey:URLString]; } } - failure:^(NSHTTPURLResponse *response, id responseObject, NSError *error) - { + failure: + ^(NSHTTPURLResponse *response, id responseObject, NSError *error) { NSInteger statusCode = response.statusCode; - if (statusCode == 304) { // 304 is not error [[LCCacheManager sharedInstance] getWithKey:URLString maxCacheAge:3600 * 24 * 30 block:^(id object, NSError *error) { @@ -524,7 +518,7 @@ - (void)performRequest:(NSURLRequest *)request saveResult:(BOOL)saveResult block [self.lastModify removeObjectForKey:[URLString _lc_MD5String]]; [[LCCacheManager sharedInstance] clearCacheForKey:URLString]; [mutableRequest setValue:@"" forHTTPHeaderField:@"If-Modified-Since"]; - [self performRequest:mutableRequest saveResult:saveResult block:block retryTimes:retryTimes + 1]; + [self performRequest:mutableRequest saveResult:saveResult block:block retryTimes:retryTimes + 1 wait:wait]; } else { if (block) block(object, error); @@ -539,7 +533,7 @@ - (void)performRequest:(NSURLRequest *)request saveResult:(BOOL)saveResult block block(responseObject, error); } } - }]; + } wait:wait]; } - (void)performRequest:(NSURLRequest *)request @@ -557,45 +551,33 @@ - (void)performRequest:(NSURLRequest *)request failure:(void (^)(NSHTTPURLResponse *response, id responseObject, NSError *error))failureBlock wait:(BOOL)wait { - dispatch_semaphore_t semaphore; NSString *path = request.URL.path; LCLoggerDebug(LCLoggerDomainNetwork, LC_REST_REQUEST_LOG_FORMAT, path, [request _lc_cURLCommand]); - - NSDate *operationEnqueueDate = [NSDate date]; - - if (wait) + dispatch_semaphore_t semaphore; + if (wait) { semaphore = dispatch_semaphore_create(0); - NSURLSessionDataTask *dataTask = [self.sessionManager dataTaskWithRequest:request completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) { - /* As Apple say: - > Whenever you make an HTTP request, - > the NSURLResponse object you get back is actually an instance of the NSHTTPURLResponse class. - */ + } + NSDate *operationEnqueueDate = [NSDate date]; + NSURLSessionDataTask *dataTask = [self.sessionManager dataTaskWithRequest:request + completionHandler: + ^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) { NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse *)response; - if (error) { - NSError *callbackError = nil; if ([NSDictionary _lc_isTypeOf:responseObject]) { - NSMutableDictionary *userInfo = ((NSDictionary *)responseObject).mutableCopy; - // decoding 'code' NSNumber *code = [NSNumber _lc_decoding:userInfo key:@"code"]; - if (code != nil) { - // decoding 'error' NSString *reason = [NSString _lc_decoding:userInfo key:@"error"]; - [userInfo removeObjectsForKeys:@[@"code", @"error"]]; - /* for compatibility */ id data = error.userInfo[LCNetworkingOperationFailingURLResponseDataErrorKey]; if (data) { userInfo[LCNetworkingOperationFailingURLResponseDataErrorKey] = data; } userInfo[kLeanCloudRESTAPIResponseError] = responseObject; - callbackError = LCError(code.integerValue, reason, userInfo); } else { callbackError = error; @@ -603,35 +585,29 @@ - (void)performRequest:(NSURLRequest *)request } else { callbackError = error; } - NSTimeInterval costTime = -([operationEnqueueDate timeIntervalSinceNow] * 1000); LCLoggerDebug(LCLoggerDomainNetwork, LC_REST_RESPONSE_LOG_FORMAT, path, costTime, callbackError); - if (failureBlock) { failureBlock(HTTPResponse, responseObject, callbackError); } } else { - NSTimeInterval costTime = -([operationEnqueueDate timeIntervalSinceNow] * 1000); LCLoggerDebug(LCLoggerDomainNetwork, LC_REST_RESPONSE_LOG_FORMAT, path, costTime, responseObject); - if (successBlock) { successBlock(HTTPResponse, responseObject); } } - - if (wait) + if (wait) { dispatch_semaphore_signal(semaphore); + } }]; - [self.lock lock]; [self.requestTable setObject:dataTask forKey:request.URL.absoluteString]; [self.lock unlock]; - [dataTask resume]; - - if (wait) + if (wait) { dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + } } - (BOOL)validateStatusCode:(NSInteger)statusCode { @@ -708,7 +684,7 @@ - (void)handleArchivedRequestAtPath:(NSString *)path block:(LCIdResultBlock)bloc [self.runningArchivedRequests addObject:path]; } - [self performRequest:request saveResult:NO block:^(id object, NSError *error) { + [self performRequest:request block:^(id object, NSError *error) { if (!error) { [fileManager removeItemAtPath:path error:NULL]; } else { diff --git a/LeanCloudObjc.podspec b/LeanCloudObjc.podspec index c48b83f5..8f8ae48d 100644 --- a/LeanCloudObjc.podspec +++ b/LeanCloudObjc.podspec @@ -37,7 +37,7 @@ Pod::Spec.new do |s| 'AVOS/Sources/Foundation/Leaderboard/LCLeaderboard.h', 'AVOS/Sources/Foundation/ACL/LCACL.h', 'AVOS/Sources/Foundation/ACL/LCRole.h', - 'AVOS/Sources/Foundation/Object/LCSaveOption.h', + 'AVOS/Sources/Foundation/Object/LCObjectOption.h', 'AVOS/Sources/Foundation/LCApplication.h', 'AVOS/Sources/Foundation/CloudCode/LCCloud.h', 'AVOS/Sources/Foundation/File/LCFile.h',