You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS 13后台服务挂起:remote silent notification唤醒传位置方案咨询

iOS 13 后台静默推送唤醒发送位置的解决方案

我之前也碰到过一模一样的问题,iOS 13 确实收紧了后台唤醒的限制,但不用 BackgroundTasks API 也还是有办法实现需求的——核心是靠后台任务延长执行时间配合正确的静默推送配置,下面是具体的步骤和注意事项:

一、确保静默推送的 Payload 配置正确

首先你的推送 payload 必须严格符合静默推送的要求,否则系统根本不会唤醒后台的 App:

  • 必须包含 aps 字典里的 content-available: 1
  • 不能包含 alert、badge、sound 这些会触发用户通知的字段(如果一定要加 sound,得设为空字符串 "",但非常不建议这么做)

示例 payload:

{
  "aps": {
    "content-available": 1
  },
  "action": "send_location" // 自定义字段,用来标识这是触发位置发送的推送
}

二、用后台任务延长执行窗口

iOS 13 之后,didReceiveRemoteNotification:fetchCompletionHandler: 方法的后台执行时间被大幅压缩(大概只有几秒),直接在里面做定位和网络请求很容易被系统终止。所以必须通过 beginBackgroundTaskWithName:expirationHandler: 来申请额外的后台执行时间(最多约30秒)。

代码实现示例(Objective-C)

首先在 AppDelegate 里处理推送回调:

// 声明属性保存后台任务ID和completionHandler
@property (nonatomic, assign) UIBackgroundTaskIdentifier bgTask;
@property (nonatomic, copy) void (^fetchCompletionHandler)(UIBackgroundFetchResult);

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    // 保存completionHandler,后续在定位/网络请求完成后调用
    self.fetchCompletionHandler = completionHandler;

    // 启动后台任务
    self.bgTask = [application beginBackgroundTaskWithName:@"SendLocationTask" expirationHandler:^{
        // 任务超时,必须结束后台任务,否则App会被系统杀死
        [application endBackgroundTask:self.bgTask];
        self.bgTask = UIBackgroundTaskInvalid;
        if (self.fetchCompletionHandler) {
            self.fetchCompletionHandler(UIBackgroundFetchResultFailed);
            self.fetchCompletionHandler = nil;
        }
    }];

    // 触发单次定位请求(比持续定位更高效,节省电量和时间)
    CLLocationManager *locationManager = [[CLLocationManager alloc] init];
    locationManager.delegate = self;
    // 确保已经获取了后台定位权限
    if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedAlways) {
        [locationManager requestLocation];
    } else {
        // 没有权限的情况,结束任务并返回失败
        [application endBackgroundTask:self.bgTask];
        self.bgTask = UIBackgroundTaskInvalid;
        completionHandler(UIBackgroundFetchResultFailed);
    }
}

然后实现 CLLocationManager 的代理方法,处理定位结果并发送请求:

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
    if (locations.count == 0) {
        [self finishBackgroundTaskWithResult:UIBackgroundFetchResultFailed];
        return;
    }

    CLLocation *currentLoc = locations.lastObject;
    // 构造位置数据并发送到服务器
    NSDictionary *postData = @{
        @"lat": @(currentLoc.coordinate.latitude),
        @"lon": @(currentLoc.coordinate.longitude),
        @"device_id": [[UIDevice currentDevice] identifierForVendor].UUIDString
    };

    NSURL *url = [NSURL URLWithString:@"https://your-server-endpoint.com/upload-location"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"POST";
    request.HTTPBody = [NSJSONSerialization dataWithJSONObject:postData options:0 error:nil];
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];

    NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        UIBackgroundFetchResult result = error ? UIBackgroundFetchResultFailed : UIBackgroundFetchResultNewData;
        [self finishBackgroundTaskWithResult:result];
    }];
    [task resume];

    [manager stopUpdatingLocation];
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
    [self finishBackgroundTaskWithResult:UIBackgroundFetchResultFailed];
}

// 封装结束后台任务的方法
- (void)finishBackgroundTaskWithResult:(UIBackgroundFetchResult)result {
    if (self.bgTask != UIBackgroundTaskInvalid) {
        [[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
        self.bgTask = UIBackgroundTaskInvalid;
    }
    if (self.fetchCompletionHandler) {
        self.fetchCompletionHandler(result);
        self.fetchCompletionHandler = nil;
    }
}

三、关键权限配置

  1. 定位权限:必须申请始终允许的定位权限,在 Info.plist 中添加:

    • NSLocationAlwaysAndWhenInUseUsageDescription:描述为什么需要始终定位
    • NSLocationWhenInUseUsageDescription:备用描述(适配旧系统)
      代码中要调用 [locationManager requestAlwaysAuthorization] 来请求权限。
  2. 后台模式:在 Xcode 的项目配置 -> Signing & Capabilities -> Background Modes 中,勾选 Remote notifications 和 Location updates(因为需要后台定位)。

注意事项

  • 系统给的后台任务时间有限(约30秒),所以要尽量优化操作:用单次定位requestLocation代替持续定位,网络请求尽量简洁,避免不必要的计算。
  • 如果 App 被用户手动杀死(从多任务划掉),静默推送无法唤醒 App,这是 iOS 的系统限制,无法绕过。
  • 虽然这种方式比 BackgroundTasks 更可控,但系统仍可能因资源紧张终止后台任务,所以要做好失败后的重试逻辑(比如下次启动时补发)。

内容的提问来源于stack exchange,提问作者Preema

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 08:22:40