iOS Parse (Reduce API Requests) 每周在同一天执行一次函数
iOS Parse (Reduce API Requests) Perform function once a week every week on the same day
使用 Parse,我试图减少要解析的网络调用数量,以限制我对非必要数据的 API 请求。为此,我认为最好每周只在同一天调用一次函数,并将与前一周的任何差异上传到安装 class(在我的情况下用于同步推送通知渠道)
那么如何每周在特定的一天调用一次函数呢?如果这一天已经过去了,你会怎么办?假设您希望每个星期四都发生一些事情,但用户直到星期四才打开应用程序,您应该在星期四同步数据?
您要做的第一件事是检查今天是否是您希望同步数据的日子。因此,例如,我希望每个星期四调用一个函数来将对象数组同步到 'channels' 列。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([self isTodayThursday]) {
} else {
}
}
- (BOOL) isTodayThursday {
NSDateFormatter *nameOfDayOfWeekFormatter = [[NSDateFormatter alloc] init];
[nameOfDayOfWeekFormatter setDateFormat:@"EEEE"];
if ([[nameOfDayOfWeekFormatter stringFromDate:[NSDate date]] isEqualToString:@"Thursday"]) {
return YES;
}
return NO;
}
很简单,这里我们要检查星期几的名称是否为星期四。现在我们要确保我们没有堆叠同步,并确保我们今天已经同步或还没有同步,如果是星期四的话。我们通过验证 NSUserDefaults
中的对象来做到这一点:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([self isTodayThursday]) {
if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"lastSynced"] isEqualToString:[[self dateFormatter] stringFromDate:[NSDate date]]]) {
//So we check against a custom date formatter that we want of the last sync date we performed, and if it's already been synced today, then do nothing
} else {
//if not, sync plist (or whatever we want) to channels array
[self syncPlistToParse];
}
} else {
}
}
- (NSDateFormatter *)dateFormatter {
NSDateFormatter *formatedDate = [[NSDateFormatter alloc] init];
[formatedDate setDateFormat:@"yyyyMMdd"];
NSLog(@"Today is %@", [formatedDate stringFromDate:[NSDate date]]);
return formatedDate;
}
将 plist 同步到 PFInstallation
通道数组
-(void)syncPlistToParse {
//First obtain whatever data you have from where ever you store it for the week. I chose to save all the data in a plist until the next sync date to reduce API calls.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [paths objectAtIndex:0];
NSString *plistPath = [path stringByAppendingPathComponent:@"AlertSubscriptions.plist"];
NSMutableArray *array = [[NSMutableArray alloc] initWithContentsOfFile:plistPath];
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation addUniqueObjectsFromArray:array forKey:@"channels"];
[currentInstallation saveInBackgroundWithBlock:^(BOOL success, NSError *error) {
if (success) {
//if successful save lastSynced date to NSUserDefaults to today
[[NSUserDefaults standardUserDefaults] setObject:[[self dateFormatter] stringFromDate:[NSDate date]] forKey:@"lastSynced"];
} else {
//It wasn't a successful save so you can do whatever you want on an unsuccessful save, but I chose to use `saveEventually` as a back-up for connectivity issues etc
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation addUniqueObjectsFromArray:array forKey:@"channels"];
[currentInstallation saveEventually];
}
}];
}
那么,如果今天 不是 星期四,我们如何确定它是在上次同步日期之前还是之后,以及我们如何确定如何设置下一个同步日期?
让我们以这些日期为例:
June 2015
---------------------------------------------
|SUN | MON | TUES | WED | THURS | FRI | SAT |
---------------------------------------------
| | | | | 18 | 19 | 20 |
---------------------------------------------
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
---------------------------------------------
| 28 | 29 | 30 | 1 | 02 | 03 | 04 |
---------------------------------------------
假设最后一次同步的星期四是 6 月 18 日,而用户直到 6 月 26 日(星期五)才打开他们的应用程序,我们需要对此进行验证。我们已经检查过今天是否是星期四,我们知道不是,因为它是 26 号星期五。所以我们需要检查和平衡两个差异,最重要的是,我们需要检查那个星期五是否在当前同步周期(第 19 个星期五)的同一周内,但在这种情况下,因为当前周周期已经通过了,因为我们直到下一个周期(应该是 6 月 25 日)才打开它
if ([self isTodayThursday]) {
if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"lastSynced"] isEqualToString:[[self dateFormatter] stringFromDate:[NSDate date]]]) {
//So we check against a custom date formatter that we want of the last sync date we performed, and if it's already been synced today, then do nothing
} else {
//if not, sync plist (or whatever we want) to channels array
[self syncPlistToParse];
}
} else {
//Not Thursday. So we need to check if its within the same week of the sync cycle or passed that date
int lastSyncDate = [[self lastSyncDate] intValue];
NSLog(@"lastSyncDate int %d", lastSyncDate);
int nextSyncDate = [[self nextSyncDate] intValue];
NSLog(@"nextSyncDate int %d", nextSyncDate);
if (lastSyncDate < nextSyncDate) {
NSLog(@"next sync date is this coming thursday");
} else {
// if not before thursday (already passed Thursday), sync and save today as lastSynced
[self syncPlistToParse];
}
}
}
- (NSString *)lastSyncDate {
NSString *date = [[NSUserDefaults standardUserDefaults] objectForKey:@"lastSynced"];
return date;
}
- (NSString *)nextSyncDate {
NSCalendar *cal = [NSCalendar currentCalendar];
NSDate *lastSync = [[self dateFormatter] dateFromString:[self lastSyncDate]];
NSDateComponents *components = [cal components:(NSCalendarUnitDay) fromDate:lastSync];
[components setDay: +[self daysUntilNearestThursday]];
NSDate *nextSync = [cal dateByAddingComponents:components toDate:lastSync options:0];
return [[self dateFormatter] stringFromDate:nextSync];
}
-(int)daysUntilNearestThursday {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"EEEE"];
//Note the day of the week is including today thats why it's always +1
if ([[dateFormatter stringFromDate:[NSDate date]] isEqualToString:@"Monday"]) {
return 4;
}
if ([[dateFormatter stringFromDate:[NSDate date]] isEqualToString:@"Tuesday"]) {
return 3;
}
if ([[dateFormatter stringFromDate:[NSDate date]] isEqualToString:@"Wednesday"]) {
return 2;
}
if ([[dateFormatter stringFromDate:[NSDate date]] isEqualToString:@"Friday"]) {
return 7;
}
if ([[dateFormatter stringFromDate:[NSDate date]] isEqualToString:@"Saturday"]) {
return 6;
}
if ([[dateFormatter stringFromDate:[NSDate date]] isEqualToString:@"Sunday"]) {
return 5;
}
return 0;
}
注意 nextSyncDate 是什么并不重要,因为我们总是首先检查它是否是星期四,然后相应地采取行动
这是我的个人意见,有人问过我这个问题,这就是我想出的。我觉得那里有一个更简单的解决方案,可以将 nextSync 准确地重置为最近的星期四,而不是 [components setDay: +[self daysUntilNearestThursday]];
因此,无论如何,请添加 input/suggestions 和评论,以便为未来的问题寻求者提供更好的机会。我认为 Parse 很棒,但同时它也是一项业务,他们必须采取相应的行动,但是某些事情,例如 API 请求可扩展应用程序(2,000 多个用户)的所有内容是一种不好的做法,如果您打算保留开发人员。这是我为帮助我的朋友而想到的另一种选择,它需要工作,并不是说这是唯一的方法,只是我认为对他们的项目有用的方法。请不要害羞,添加编辑或更正。
在实践中,我发现固定间隔在更多情况下比日历里程碑更有意义。有了它,加上很少的 NSDate 逻辑,我可以让我的模型保证它的数据最多过时 N 秒。
为此,我让模型单例仅跟踪上次更新的日期:
// initialize this to [NSDate distantPast]
@property(nonatomic,strong) NSDate *lastUpdate;
接口还提供了异步更新的方法,如:
- (void)updateWithCompletion:(void (^)(BOOL, NSError *))completion;
我重写 lastUpdate
的合成 getter/setter 来包装持久性:
// user defaults in this case, but there are several ways to persist a date
- (NSDate *)lastUpdate {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
return [defaults valueForKey:@"lastUpdate"];
}
- (void)setLastUpdate:(NSDate *)lastUpdate {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setValue:lastUpdate forKey:@"lastUpdate"];
[defaults synchronize];
}
最后,异步更新不透明地决定当前数据是否足够好,或者我们是否应该调用 parse.com api...
- (void)updateWithCompletion:(void (^)(BOOL, NSError *))completion {
NSTimeInterval sinceLastUpdate = -[self.lastUpdate timeIntervalSinceNow];
NSTimeInterval updatePeriod = [self updatePeriod];
if (sinceLastUpdate < updatePeriod) {
// our current data is new enough, so
return completion(YES, nil);
} else {
// our current data is stale, so call parse.com to update...
[...inBackgroundWithBlock:(NSArray *objects, NSError *error) {
if (!error) {
// finish the update here and persist the objects, then...
self.lastUpdate = [NSDate date];
completion(YES, nil);
} else {
completion(NO, error);
}
}];
}
}
方法 updatePeriod
回答任何 NSTimeInterval
应用程序认为可接受的数据年龄。通常,我会以相当高的频率(比如每天)从 parse.config 获取此信息。这样,我可以调整模型更新的频率,因为我认为适合野外客户。
所以用很少的 NSDate
逻辑,我用它来保持客户端 "up-to-date-enough",甚至 "enough" 部分也可以动态决定。
编辑 - 我们仍然可以保持简洁并将我们的模型到期时间设置为日历日。我会这样做:
- (NSDate *)lastSaturday {
NSDate *now = [NSDate date];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *startOfWeek;
[calendar rangeOfUnit:NSCalendarUnitWeekOfMonth startDate:&startOfWeek interval:NULL forDate:now];
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setDay:-1];
return [calendar dateByAddingComponents:components toDate:startOfWeek options:0];
}
现在,不是在间隔到期时更新,而是在上周六(或您想要的任何工作日)之前更新...通过调整 setDay:-n 进行调整)
// change the date condition in updateWithCompletion
if (self.lastUpdate == [self.lastUpdate earlierDate:[self lastSaturday]) {
// do the update
} else {
// no need to update
}
使用 Parse,我试图减少要解析的网络调用数量,以限制我对非必要数据的 API 请求。为此,我认为最好每周只在同一天调用一次函数,并将与前一周的任何差异上传到安装 class(在我的情况下用于同步推送通知渠道)
那么如何每周在特定的一天调用一次函数呢?如果这一天已经过去了,你会怎么办?假设您希望每个星期四都发生一些事情,但用户直到星期四才打开应用程序,您应该在星期四同步数据?
您要做的第一件事是检查今天是否是您希望同步数据的日子。因此,例如,我希望每个星期四调用一个函数来将对象数组同步到 'channels' 列。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([self isTodayThursday]) {
} else {
}
}
- (BOOL) isTodayThursday {
NSDateFormatter *nameOfDayOfWeekFormatter = [[NSDateFormatter alloc] init];
[nameOfDayOfWeekFormatter setDateFormat:@"EEEE"];
if ([[nameOfDayOfWeekFormatter stringFromDate:[NSDate date]] isEqualToString:@"Thursday"]) {
return YES;
}
return NO;
}
很简单,这里我们要检查星期几的名称是否为星期四。现在我们要确保我们没有堆叠同步,并确保我们今天已经同步或还没有同步,如果是星期四的话。我们通过验证 NSUserDefaults
中的对象来做到这一点:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([self isTodayThursday]) {
if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"lastSynced"] isEqualToString:[[self dateFormatter] stringFromDate:[NSDate date]]]) {
//So we check against a custom date formatter that we want of the last sync date we performed, and if it's already been synced today, then do nothing
} else {
//if not, sync plist (or whatever we want) to channels array
[self syncPlistToParse];
}
} else {
}
}
- (NSDateFormatter *)dateFormatter {
NSDateFormatter *formatedDate = [[NSDateFormatter alloc] init];
[formatedDate setDateFormat:@"yyyyMMdd"];
NSLog(@"Today is %@", [formatedDate stringFromDate:[NSDate date]]);
return formatedDate;
}
将 plist 同步到 PFInstallation
通道数组
-(void)syncPlistToParse {
//First obtain whatever data you have from where ever you store it for the week. I chose to save all the data in a plist until the next sync date to reduce API calls.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [paths objectAtIndex:0];
NSString *plistPath = [path stringByAppendingPathComponent:@"AlertSubscriptions.plist"];
NSMutableArray *array = [[NSMutableArray alloc] initWithContentsOfFile:plistPath];
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation addUniqueObjectsFromArray:array forKey:@"channels"];
[currentInstallation saveInBackgroundWithBlock:^(BOOL success, NSError *error) {
if (success) {
//if successful save lastSynced date to NSUserDefaults to today
[[NSUserDefaults standardUserDefaults] setObject:[[self dateFormatter] stringFromDate:[NSDate date]] forKey:@"lastSynced"];
} else {
//It wasn't a successful save so you can do whatever you want on an unsuccessful save, but I chose to use `saveEventually` as a back-up for connectivity issues etc
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation addUniqueObjectsFromArray:array forKey:@"channels"];
[currentInstallation saveEventually];
}
}];
}
那么,如果今天 不是 星期四,我们如何确定它是在上次同步日期之前还是之后,以及我们如何确定如何设置下一个同步日期?
让我们以这些日期为例:
June 2015
---------------------------------------------
|SUN | MON | TUES | WED | THURS | FRI | SAT |
---------------------------------------------
| | | | | 18 | 19 | 20 |
---------------------------------------------
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
---------------------------------------------
| 28 | 29 | 30 | 1 | 02 | 03 | 04 |
---------------------------------------------
假设最后一次同步的星期四是 6 月 18 日,而用户直到 6 月 26 日(星期五)才打开他们的应用程序,我们需要对此进行验证。我们已经检查过今天是否是星期四,我们知道不是,因为它是 26 号星期五。所以我们需要检查和平衡两个差异,最重要的是,我们需要检查那个星期五是否在当前同步周期(第 19 个星期五)的同一周内,但在这种情况下,因为当前周周期已经通过了,因为我们直到下一个周期(应该是 6 月 25 日)才打开它
if ([self isTodayThursday]) {
if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"lastSynced"] isEqualToString:[[self dateFormatter] stringFromDate:[NSDate date]]]) {
//So we check against a custom date formatter that we want of the last sync date we performed, and if it's already been synced today, then do nothing
} else {
//if not, sync plist (or whatever we want) to channels array
[self syncPlistToParse];
}
} else {
//Not Thursday. So we need to check if its within the same week of the sync cycle or passed that date
int lastSyncDate = [[self lastSyncDate] intValue];
NSLog(@"lastSyncDate int %d", lastSyncDate);
int nextSyncDate = [[self nextSyncDate] intValue];
NSLog(@"nextSyncDate int %d", nextSyncDate);
if (lastSyncDate < nextSyncDate) {
NSLog(@"next sync date is this coming thursday");
} else {
// if not before thursday (already passed Thursday), sync and save today as lastSynced
[self syncPlistToParse];
}
}
}
- (NSString *)lastSyncDate {
NSString *date = [[NSUserDefaults standardUserDefaults] objectForKey:@"lastSynced"];
return date;
}
- (NSString *)nextSyncDate {
NSCalendar *cal = [NSCalendar currentCalendar];
NSDate *lastSync = [[self dateFormatter] dateFromString:[self lastSyncDate]];
NSDateComponents *components = [cal components:(NSCalendarUnitDay) fromDate:lastSync];
[components setDay: +[self daysUntilNearestThursday]];
NSDate *nextSync = [cal dateByAddingComponents:components toDate:lastSync options:0];
return [[self dateFormatter] stringFromDate:nextSync];
}
-(int)daysUntilNearestThursday {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"EEEE"];
//Note the day of the week is including today thats why it's always +1
if ([[dateFormatter stringFromDate:[NSDate date]] isEqualToString:@"Monday"]) {
return 4;
}
if ([[dateFormatter stringFromDate:[NSDate date]] isEqualToString:@"Tuesday"]) {
return 3;
}
if ([[dateFormatter stringFromDate:[NSDate date]] isEqualToString:@"Wednesday"]) {
return 2;
}
if ([[dateFormatter stringFromDate:[NSDate date]] isEqualToString:@"Friday"]) {
return 7;
}
if ([[dateFormatter stringFromDate:[NSDate date]] isEqualToString:@"Saturday"]) {
return 6;
}
if ([[dateFormatter stringFromDate:[NSDate date]] isEqualToString:@"Sunday"]) {
return 5;
}
return 0;
}
注意 nextSyncDate 是什么并不重要,因为我们总是首先检查它是否是星期四,然后相应地采取行动
这是我的个人意见,有人问过我这个问题,这就是我想出的。我觉得那里有一个更简单的解决方案,可以将 nextSync 准确地重置为最近的星期四,而不是 [components setDay: +[self daysUntilNearestThursday]];
因此,无论如何,请添加 input/suggestions 和评论,以便为未来的问题寻求者提供更好的机会。我认为 Parse 很棒,但同时它也是一项业务,他们必须采取相应的行动,但是某些事情,例如 API 请求可扩展应用程序(2,000 多个用户)的所有内容是一种不好的做法,如果您打算保留开发人员。这是我为帮助我的朋友而想到的另一种选择,它需要工作,并不是说这是唯一的方法,只是我认为对他们的项目有用的方法。请不要害羞,添加编辑或更正。
在实践中,我发现固定间隔在更多情况下比日历里程碑更有意义。有了它,加上很少的 NSDate 逻辑,我可以让我的模型保证它的数据最多过时 N 秒。
为此,我让模型单例仅跟踪上次更新的日期:
// initialize this to [NSDate distantPast]
@property(nonatomic,strong) NSDate *lastUpdate;
接口还提供了异步更新的方法,如:
- (void)updateWithCompletion:(void (^)(BOOL, NSError *))completion;
我重写 lastUpdate
的合成 getter/setter 来包装持久性:
// user defaults in this case, but there are several ways to persist a date
- (NSDate *)lastUpdate {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
return [defaults valueForKey:@"lastUpdate"];
}
- (void)setLastUpdate:(NSDate *)lastUpdate {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setValue:lastUpdate forKey:@"lastUpdate"];
[defaults synchronize];
}
最后,异步更新不透明地决定当前数据是否足够好,或者我们是否应该调用 parse.com api...
- (void)updateWithCompletion:(void (^)(BOOL, NSError *))completion {
NSTimeInterval sinceLastUpdate = -[self.lastUpdate timeIntervalSinceNow];
NSTimeInterval updatePeriod = [self updatePeriod];
if (sinceLastUpdate < updatePeriod) {
// our current data is new enough, so
return completion(YES, nil);
} else {
// our current data is stale, so call parse.com to update...
[...inBackgroundWithBlock:(NSArray *objects, NSError *error) {
if (!error) {
// finish the update here and persist the objects, then...
self.lastUpdate = [NSDate date];
completion(YES, nil);
} else {
completion(NO, error);
}
}];
}
}
方法 updatePeriod
回答任何 NSTimeInterval
应用程序认为可接受的数据年龄。通常,我会以相当高的频率(比如每天)从 parse.config 获取此信息。这样,我可以调整模型更新的频率,因为我认为适合野外客户。
所以用很少的 NSDate
逻辑,我用它来保持客户端 "up-to-date-enough",甚至 "enough" 部分也可以动态决定。
编辑 - 我们仍然可以保持简洁并将我们的模型到期时间设置为日历日。我会这样做:
- (NSDate *)lastSaturday {
NSDate *now = [NSDate date];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *startOfWeek;
[calendar rangeOfUnit:NSCalendarUnitWeekOfMonth startDate:&startOfWeek interval:NULL forDate:now];
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setDay:-1];
return [calendar dateByAddingComponents:components toDate:startOfWeek options:0];
}
现在,不是在间隔到期时更新,而是在上周六(或您想要的任何工作日)之前更新...通过调整 setDay:-n 进行调整)
// change the date condition in updateWithCompletion
if (self.lastUpdate == [self.lastUpdate earlierDate:[self lastSaturday]) {
// do the update
} else {
// no need to update
}