I have an app which uses Cocoa Pods to use MQTTClient in connecting to io.adafruit. I use the app to get the various data point stored at io.adafruit and to even send data to adafruit to control a connected unit. I am using .xcworkspace to upload the app on my phone and everything works fine. I am hoping to wake my app every so ofter to get some data points from io.adafruit and if certain conditions exist, I want to send a notification to the user. I have added Background Modes to my app's Signing & Capabilities (Background fetch) and in my INFO I have Permitted background task scheduler identifiers with the String identifier LEVELIT.Refresh. The following is my code I am using in my AppDelegate.h
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#import <MQTTClient/MQTTClient.h>
#import <UserNotifications/UserNotifications.h>
@interface AppDelegate: UIResponder <UIApplicationDelegate, UNUserNotificationCenterDelegate,UIAlertViewDelegate,MQTTSessionDelegate>{
MQTTSession *session3;
}
@property (nonatomic, retain) MQTTSession *session3;
@property (nonatomic, strong) NSPersistentContainer *persistentContainer;
@end
And this is the code I use in my AppDelegate.m
#import "AppDelegate.h"
#import <BackgroundTasks/BackgroundTasks.h>
#import <CloudKit/CloudKit.h>
#import <UserNotifications/UserNotifications.h>
static NSString* TaskID = @"LEVELIT.refresh";
@interface AppDelegate()
@property (nonatomic, retain) NSString *myUserName;
@property (nonatomic, retain) NSString *myUserKey;
@property (nonatomic, retain) NSString *myTrips;
@property (nonatomic, retain) NSString *atrip;
@property (nonatomic, retain) NSString *trip;
@property (nonatomic, retain) NSString *gettrip;
@property(strong) void (^expirationHandler)(void);
@end
@implementation AppDelegate
@synthesize session3,window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if (@available(iOS 13.0, *)) {
[[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:TaskID
usingQueue:nil
launchHandler:^(BGAppRefreshTask *task) {
[self handleAppRefreshTask:task];
}];
} else {
// Fallback on earlier versions
}
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound)
completionHandler:^(BOOL granted, NSError * _Nullable error) {
}];
UNNotificationCategory* generalCategory = [UNNotificationCategory
categoryWithIdentifier:@"GENERAL"
actions:@[]
intentIdentifiers:@[]
options:UNNotificationCategoryOptionCustomDismissAction];
NSUserDefaults *defaults2 = [NSUserDefaults standardUserDefaults];
if([[[defaults2 dictionaryRepresentation] allKeys] containsObject:@"myUserNameFile"]){
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
self.myUserName = [defaults objectForKey:@"myUserNameFile"];
NSLog(@"myUserName1:%@",self.myUserName);
self.myUserKey = [defaults objectForKey:@"myUserKeyFile"];
NSLog(@"myUserKey1:%@",self.myUserKey);
}
return YES;
}
}
-(void)schedualLocalNotifications{
}
- (void)setTaskCompletedWithSuccess:(BOOL)success;{
NSLog(@"Completed");
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
NSLog(@"Entering background");
if (@available(iOS 13.0, *)) {
BGAppRefreshTaskRequest *request = [[BGAppRefreshTaskRequest alloc] initWithIdentifier:TaskID];
request.earliestBeginDate = [NSDate dateWithTimeIntervalSinceNow:15*60];
NSError *error;
BOOL success = [[BGTaskScheduler sharedScheduler] submitTaskRequest:request error:&error];
if (success == TRUE) {
}
}
//BREAKPOINT
}
-(void)handleAppRefreshTask:(BGAppRefreshTask *)task API_AVAILABLE(ios(13.0)){
//do things with task
NSLog(@"Process started!");
task.expirationHandler = ^{
NSLog(@"WARNING: expired before finish was executed.");
};
MQTTCFSocketTransport *transport = [[MQTTCFSocketTransport alloc] init];
transport.host = @"io.adafruit.com";
transport.port = 1883;
self.session3 = [[MQTTSession alloc] init];
self.session3.userName = self.myUserName;
self.session3.password = self.myUserKey;
self.session3.transport = transport;
self.session3.delegate = self;
self.session3.keepAliveInterval = 30;
// new stuff
NSString *feeds = [self.myUserName stringByAppendingString:@"/feeds/"];
self.atrip = [feeds stringByAppendingString:@"trip"];
self.gettrip = [self.atrip stringByAppendingString:@"/get"];
[session3 connectWithConnectHandler:^(NSError *error) {
if(!error){
[self.session3 subscribeToTopic:self.atrip atLevel:1 subscribeHandler:^(NSError *error, NSArray *gQoss){
if (error) {
NSLog(@"Subscription failed %@", error.localizedDescription);
} else {
NSLog(@"Subscription sucessfull! Granted Qos: %@", gQoss);
NSData* data = [@"0" dataUsingEncoding:NSUTF8StringEncoding];
[self.session3 publishData:data onTopic:self.gettrip retain:NO qos:0 publishHandler:nil];
}
}];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(45* NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[task setTaskCompletedWithSuccess:YES];
});
self.session3.keepAliveInterval =30;
}
else {NSLog(@"[connectWithConnectHandler]Error Connect %@", error.localizedDescription);}
}];
task.expirationHandler = ^{
NSLog(@"WARNING: expired before finish was executed.");
};
} }
}
//More code here but too much.
@end
I am not sure about the local notification because the BGAppRefreshTask is not working.
I have placed a breakpoint just after the app enters background and the TASK is submitted. Then I type in e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"LEVELIT.refresh"]
The app launches in the background but I get an error
error: Header search couldn't locate module MQTTClient
error: couldn't install checkers, unknown error
Message from debugger: Terminated due to signal 9
When I tried running the app without .xcworkspace the project said it couldn't find MQTTClient. But with the workspace everything works fine.
Why would launching the app in the background cause the app not to locate the MQTTClient libraries? And how do I work around this problem?
Any and all help would be appreciated.