diff --git a/AVOS/LeanCloudObjcTests/LCLeaderboardTestCase.swift b/AVOS/LeanCloudObjcTests/LCLeaderboardTestCase.swift index 54202506..bbf8a4d4 100644 --- a/AVOS/LeanCloudObjcTests/LCLeaderboardTestCase.swift +++ b/AVOS/LeanCloudObjcTests/LCLeaderboardTestCase.swift @@ -565,4 +565,86 @@ class LCLeaderboardTestCase: BaseTestCase { } } } + + func testGetGroupUserRankings() { + let object = LCObject() + XCTAssertTrue(object.save()) + let user = LCUser() + let objectFieldKey = "objectField" + user[objectFieldKey] = object + expecting { exp in + user.login(withAuthData: ["openid" : uuid], platformId: "test", options: nil) { _, error in + XCTAssertNil(error) + exp.fulfill() + } + } + guard let userObjectId = user.objectId else { + XCTFail() + return + } + + let statisticName0 = "test-user-0" + let statisticName1 = "test-user-1" + var statistic0version = -1 + var statistic1version = -1 + + expecting { exp in + LCLeaderboard.updateCurrentUserStatistics( + [statisticName0 : 100, + statisticName1 : 100]) + { statistics, error in + XCTAssertEqual(statistics?.count, 2) + XCTAssertNil(error) + XCTAssertNotEqual(statistics?.first?.name, statistics?.last?.name) + for item in statistics ?? [] { + XCTAssertTrue([statisticName0, statisticName1].contains(item.name ?? "")) + XCTAssertEqual(item.value, 100) + XCTAssertGreaterThanOrEqual(item.version, 0) + if (item.name ?? "") == statisticName0 { + statistic0version = item.version + } else { + statistic1version = item.version + } + } + exp.fulfill() + } + } + + let leaderboard0 = LCLeaderboard(statisticName: statisticName0) + let leaderboard1 = LCLeaderboard(statisticName: statisticName1) + let option = LCLeaderboardQueryOption() + option.selectKeys = [objectFieldKey] + option.includeKeys = [objectFieldKey] + + expecting(count: 2) { exp in + leaderboard0.limit = 1 + leaderboard0.includeStatistics = [statisticName1] + leaderboard0.version = statistic0version + leaderboard0.getGroupUserResults(withUserIds: [userObjectId], aroundUser: userObjectId, option: option) { rankings, error in + XCTAssertEqual(rankings?.count, 1) + XCTAssertNil(error) + for item in rankings ?? [] { + XCTAssertEqual(item.statisticName, leaderboard0.statisticName) + XCTAssertGreaterThanOrEqual(item.rank, 0) + XCTAssertEqual(item.value, 100) + XCTAssertEqual(item.includedStatistics?.first?.name, statisticName1) + XCTAssertEqual(item.includedStatistics?.first?.value, 100) + XCTAssertEqual(item.user?.objectId, userObjectId) + XCTAssertNotNil((item.user?[objectFieldKey] as? LCObject)?.createdAt) + XCTAssertNil(item.object) + XCTAssertNil(item.entity) + } + exp.fulfill() + } + leaderboard1.skip = 1 + leaderboard1.version = statistic1version + leaderboard1.getGroupUserResults(withUserIds: [userObjectId], option: option) { rankings, error in + for item in rankings ?? [] { + XCTAssertNotEqual(item.rank, 0) + } + XCTAssertNil(error) + exp.fulfill() + } + } + } } diff --git a/AVOS/Sources/Foundation/Leaderboard/LCLeaderboard.h b/AVOS/Sources/Foundation/Leaderboard/LCLeaderboard.h index afacfecd..6bfbd96a 100644 --- a/AVOS/Sources/Foundation/Leaderboard/LCLeaderboard.h +++ b/AVOS/Sources/Foundation/Leaderboard/LCLeaderboard.h @@ -184,6 +184,24 @@ NS_ASSUME_NONNULL_BEGIN - (void)getEntityResultsAroundEntity:(NSString * _Nullable)entity callback:(void (^)(NSArray * _Nullable rankings, NSInteger count, NSError * _Nullable error))callback; +/// Get rankings of a group of user on this leaderboard. +/// @param userIds A group of user's object id. +/// @param option The query option, see `LCLeaderboardQueryOption`. +/// @param callback Result callback. +- (void)getGroupUserResultsWithUserIds:(NSArray *)userIds + option:(LCLeaderboardQueryOption * _Nullable)option + callback:(void (^)(NSArray * _Nullable rankings, NSError * _Nullable error))callback; + +/// Get rankings of a group of user around one user on this leaderboard. +/// @param userIds A group of user's object id. +/// @param userId The object id of the around user. +/// @param option The query option, see `LCLeaderboardQueryOption`. +/// @param callback Result callback. +- (void)getGroupUserResultsWithUserIds:(NSArray *)userIds + aroundUser:(NSString * _Nullable)userId + option:(LCLeaderboardQueryOption * _Nullable)option + callback:(void (^)(NSArray * _Nullable rankings, NSError * _Nullable error))callback; + @end NS_ASSUME_NONNULL_END diff --git a/AVOS/Sources/Foundation/Leaderboard/LCLeaderboard.m b/AVOS/Sources/Foundation/Leaderboard/LCLeaderboard.m index 5002e7a4..fabc6786 100644 --- a/AVOS/Sources/Foundation/Leaderboard/LCLeaderboard.m +++ b/AVOS/Sources/Foundation/Leaderboard/LCLeaderboard.m @@ -256,6 +256,7 @@ - (void)getStatisticsWithIdentities:(NSArray *)identities }); return; } + NSDictionary *parameters = @{ @"ids" : identities }; NSString *path = [NSString stringWithFormat:@"leaderboard/%@/statistics/%@", leaderboardPath, self.statisticName]; if (option) { NSMutableArray *queryStrings = [NSMutableArray array]; @@ -274,7 +275,7 @@ - (void)getStatisticsWithIdentities:(NSArray *)identities } } [[LCPaasClient sharedInstance] postObject:path - withParameters:identities + withParameters:parameters block:^(id _Nullable object, NSError * _Nullable error) { [[self class] handleStatisticsCallback:callback error:error object:object]; }]; @@ -389,6 +390,98 @@ - (void)getResultsAroundIdentity:(NSString *)identity }]; } +- (void)getGroupUserResultsWithUserIds:(NSArray *)userIds + option:(LCLeaderboardQueryOption *)option + callback:(void (^)(NSArray * _Nullable, NSError * _Nullable))callback +{ + [self getGroupUserResultsWithIdentities:userIds + leaderboardPath:LCLeaderboardPathUser + aroundIdentity:nil + option:option + callback:callback]; +} + +- (void)getGroupUserResultsWithUserIds:(NSArray *)userIds + aroundUser:(NSString *)userId + option:(LCLeaderboardQueryOption *)option + callback:(void (^)(NSArray * _Nullable, NSError * _Nullable))callback +{ + [self getGroupUserResultsWithIdentities:userIds + leaderboardPath:LCLeaderboardPathUser + aroundIdentity:userId + option:option + callback:callback]; +} + +- (void)getGroupUserResultsWithIdentities:(NSArray *)identities + leaderboardPath:(LCLeaderboardPath)leaderboardPath + aroundIdentity:(NSString *)identity + option:(LCLeaderboardQueryOption *)option + callback:(void (^)(NSArray * _Nullable, NSError * _Nullable))callback +{ + if (!identities || identities.count == 0) { + NSError *error = LCError(LCErrorInternalErrorCodeInconsistency, @"First parameter invalid.", nil); + [LCUtils callArrayResultBlock:callback array:nil error:error]; + return; + } + NSDictionary *parameters = @{ @"ids" : identities }; + NSString *path = [NSString stringWithFormat:@"leaderboard/leaderboards/%@/%@/group/ranks", leaderboardPath, self.statisticName]; + if (identity && identity.length > 0) { + path = [path stringByAppendingPathComponent:identity]; + } + NSMutableDictionary *urlQueryDictionary = [NSMutableDictionary dictionary]; + [[self class] trySetOption:option parameters:urlQueryDictionary]; + if (!identity && self.skip > 0) { + urlQueryDictionary[@"startPosition"] = @(self.skip).stringValue; + } + if (self.limit > 0) { + urlQueryDictionary[@"maxResultsCount"] = @(self.limit).stringValue; + } + if (self.includeStatistics && self.includeStatistics.count > 0) { + urlQueryDictionary[@"includeStatistics"] = [self.includeStatistics componentsJoinedByString:@","]; + } + if (self.version > -1) { + urlQueryDictionary[@"version"] = @(self.version).stringValue; + } + NSString *urlQuery; + if (urlQueryDictionary.count > 0) { + NSMutableArray *queryItems = [NSMutableArray arrayWithCapacity:urlQueryDictionary.count]; + for (NSString *key in urlQueryDictionary) { + NSString *value = urlQueryDictionary[key]; + NSURLQueryItem *queryItem = [NSURLQueryItem queryItemWithName:key value:value]; + [queryItems addObject:queryItem]; + } + NSURLComponents *urlComponents = [NSURLComponents componentsWithString:@"http://example.com"]; + urlComponents.queryItems = queryItems; + urlQuery = urlComponents.URL.query; + } + if (urlQuery) { + path = [path stringByAppendingFormat:@"?%@", urlQuery]; + } + [[LCPaasClient sharedInstance] postObject:path + withParameters:parameters + block:^(id _Nullable object, NSError * _Nullable error) { + if (error) { + [LCUtils callArrayResultBlock:callback array:nil error:error]; + return; + } + if ([NSDictionary _lc_isTypeOf:object]) { + NSArray *results = [NSArray _lc_decoding:object key:@"results"]; + NSMutableArray *rankings = [NSMutableArray arrayWithCapacity:results.count]; + for (NSDictionary *item in results) { + if ([NSDictionary _lc_isTypeOf:item]) { + LCLeaderboardRanking *ranking = [[LCLeaderboardRanking alloc] initWithDictionary:item]; + [rankings addObject:ranking]; + } + } + [LCUtils callArrayResultBlock:callback array:rankings error:nil]; + } else { + NSError *error = LCError(LCErrorInternalErrorCodeMalformedData, @"Malformed response data.", nil); + [LCUtils callArrayResultBlock:callback array:nil error:error]; + } + }]; +} + // MARK: Misc + (void)trySetOption:(LCLeaderboardQueryOption * _Nullable)option parameters:(NSMutableDictionary *)parameters { diff --git a/AVOS/Sources/Foundation/UserAgent.h b/AVOS/Sources/Foundation/UserAgent.h index 3dddb91c..fac706ee 100644 --- a/AVOS/Sources/Foundation/UserAgent.h +++ b/AVOS/Sources/Foundation/UserAgent.h @@ -1 +1 @@ -#define SDK_VERSION @"13.8.0" +#define SDK_VERSION @"13.9.0" diff --git a/LeanCloudObjc.podspec b/LeanCloudObjc.podspec index b4799548..383fa207 100644 --- a/LeanCloudObjc.podspec +++ b/LeanCloudObjc.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'LeanCloudObjc' - s.version = '13.8.0' + s.version = '13.9.0' s.homepage = 'https://leancloud.cn/' s.summary = 'LeanCloud Objective-C SDK' s.authors = 'LeanCloud'