如何在iOS 13(背景提取)的Objective-c中使用B​​GTask

时间:2020-05-26 15:47:43

标签: ios objective-c

因此,我想构建一个iOS,对于Objective-C来说我是一个新手,而我想实现的一项功能是能够发送API请求并在应用程序不是“焦点/背景”。我已经针对iOS 13的 BGTask API进行了几天的研究,并创建了一个项目以查看我是否可以使“后台获取”工作。我无法。我很确定我已经正确设置了所有内容,但是过去几天我都没有一次能够在iPhone上触发后台获取功能。

  • 我正在使用实际的iOS设备在iOS 13.4.1上进行测试
  • 在Info.plist中正确设置了“允许的后台任务计划程序标识符”
  • 应用已签名
  • 背景模式
  • 中检查了背景处理和背景提取
  • 我按照Apple文档的规定等待了15分钟

这是我的代码。所有这些只是使用Objective-C的空白iOS项目。我只编辑了 AppDelegate.m Info.plist

AppDelegate.m

#import "AppDelegate.h"
#import <BackgroundTasks/BackgroundTasks.h>


static NSString* TaskID = @"com.myapp.task";

@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.


    [[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:TaskID
                                                          usingQueue:nil
                                                       launchHandler:^(BGProcessingTask *task) {

        [self handleAppRefreshTask:task];
    }];

    return YES;
}


#pragma mark - UISceneSession lifecycle


- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {
    // Called when a new scene session is being created.
    // Use this method to select a configuration to create the new scene with.
    return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role];
}


- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<UISceneSession *> *)sceneSessions {
    // Called when the user discards a scene session.
    // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
    // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}


-(void)handleAppRefreshTask:(BGProcessingTask *)task {
    //do things with task
  NSLog(@"Process started!");
  task.expirationHandler = ^{
    NSLog(@"WARNING: expired before finish was executed.");

  };

  NSString *targetUrl = @"https://webhook.site/1b274a6f-016f-4edf-8e31-4ed7058eaeac";
  NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
  [request setHTTPMethod:@"GET"];
  [request setURL:[NSURL URLWithString:targetUrl]];

  [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:
    ^(NSData * _Nullable data,
      NSURLResponse * _Nullable response,
      NSError * _Nullable error) {

        NSString *myString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"Data received: %@", myString);
  }] resume];

  task.expirationHandler = ^{
    NSLog(@"WARNING: expired before finish was executed.");

  };

  [task setTaskCompletedWithSuccess:YES];
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
  NSLog(@"Entering background");
  BGProcessingTaskRequest *request = [[BGProcessingTaskRequest alloc] initWithIdentifier:TaskID];
  request.requiresNetworkConnectivity = true;
  request.requiresExternalPower = false;
  request.earliestBeginDate = [NSDate dateWithTimeIntervalSinceNow:60];

  @try {
    [[BGTaskScheduler sharedScheduler] submitTaskRequest:request error:nil];
  }
  @catch(NSException *e){
    NSLog(@" Unable to submit request");
  }


}

@end

iOS 13中的后台获取是否中断?即使单击Xco​​de调试菜单中的“模拟后台提取”也不起作用。它只是关闭了应用程序,没有任何反应。有人可以帮忙/提供任何建议吗?

1 个答案:

答案 0 :(得分:1)

一些观察结果:

  1. setTaskCompletedWithSuccess应该在网络请求的完成处理程序中。在请求有机会运行并且处理了结果之前,您不希望将任务标记为已完成。

  2. 您正在呼叫submitTaskRequest,但传递nil作为NSError参考。您还已经将其包装在异常处理程序中。但是,此API调用不会引发异常,而只是传回错误。但是您必须为其提供错误参考。例如

    NSLog(@"Entering background");
    BGProcessingTaskRequest *request = [[BGProcessingTaskRequest alloc] initWithIdentifier:TaskID];
    request.requiresNetworkConnectivity = true;
    request.requiresExternalPower = false;
    request.earliestBeginDate = [NSDate dateWithTimeIntervalSinceNow:60];
    
    NSError *error;
    if (![[BGTaskScheduler sharedScheduler] submitTaskRequest:request error:&error]) {
        NSLog(@"BGTaskScheduler failed: %@", error);
    }
    

    在您的代码中,如果失败,您将永远不会知道。

  3. 您已将此代码放在applicationDidEnterBackground中。即,您是否完全看到此“正在进入后台”消息?我问的原因是,如果您提供了场景委托(通常只是创建了一个新的iOS 13应用),则不会调用此方法,而会sceneDidEnterBackground调用。

  4. 您说过您尝试了“模拟背景提取”。但是您尚未创建后台提取请求(BGAppRefreshTask)。您创建了一个后台任务(BGProcessingTask),这是另一回事。要测试后台处理请求,请参阅Starting and Terminating Tasks During Development

  5. 关于您如何知道获取请求已得到处理,这是一个有趣的问题。您只是在使用NSLog(假设您将应用程序附加到Xcode调试器)。我建议在不将应用程序附加到Xcode的情况下对此进行测试。有几个选项:

    • 如果您可以查看服务器日志中的请求,则可以。

    • 我个人经常会输入UserNotifications(并确保进入设置并打开持久性通知,这样我就不会错过它们)。

    • 我做过的另一种方法是将这些事件记录到应用程序的持久存储中的某个表中,然后在应用程序中使用一些UI来获取此数据,以便我可以确认发生了什么。

      < / li>
    • 我将经常使用Unified Logging,以便即使Xcode未运行,我也可以从macOS控制台观看设备发出的os_log语句。这在记录应用程序/场景方法中非常有用。参见WWDC 2016 Unified Logging and Activity Tracing

    无论您做什么,对于诸如后台处理,后台应用刷新等操作,我都会编写某种机制,以便即使没有附加到Xcode时也可以检查请求/任务是否发生。在某些情况下,附加到调试器可能会影响应用程序的生命周期,我想确保自己有某种方法可以确认正在发生的事情而没有控制台的好处。

  6. 很明显,但请确保不要“强制退出”应用程序,因为这将阻止后台进程的发生。

有关更多信息,请参阅WWDC 2019视频Advances in App Background Execution