I am building an iOS & Android application for a contractor.
Basically it's a client that should show a list of notifications from a server.
The contractor plan to give this app to multiple client companies of his. for each company he will ship a server (dedicated to that client), and the mobile app will be installed on ANY device, and for each client fleet the drivers will only have to enter the IP of the client server.
My question is - can I use Parse (or any other remote push aggregator) with a single app but have multiple servers send push notifications to devices (these will NOT be 'broadcast' pushes to all devices - they will be aimed to specific devices) ?
I'm not sure what the "server" in this case it, but Parse should be able to handle what you want.
I would create standard User and Installation classes in Parse, plus a custom class called "company" (or "client" or whatever). You can add whatever fields you like for this class, but it seems like the important one for you would be "IP Address".
You can associate an Installation with a User and a User with a company. The data structure would be like this:
Installation -> (Pointer)User
User -> (Pointer)company
company -> (String)ipAddress
Then, when you want to send a PUSH for a particular IP address, you could do the following:
Query for the company that matches the "IP address".
Query for all User objects that have the result from #1 as their company property.
Query for all Installation objects that have their User object in the array returned by #2.
Send the PUSH using the query in #3.
Here's a way to do it in Objective-C:
- (void)sendPushMessage:(NSString *)message
toClientWithIpAddress:(NSString *)ipAddress {
PFQuery *companyQuery = [PFQuery queryWithClassName:#"company"];
[companyQuery whereKey:#"ipAddress" equalTo:ipAddress];
[companyQuery getFirstObjectInBackgroundWithBlock:^(PFObject *company, NSError *error) {
// Do real error handling
PFQuery *userQuery = [PFUser query];
[userQuery whereKey:#"company" equalTo:company];
[userQuery findObjectsInBackgroundWithBlock:^(NSArray *users, NSError *error) {
// Do real error handling
PFQuery *installationQuery = [PFInstallation query];
[installationQuery whereKey:#"user" containedIn:users];
PFPush *push = [PFPush push];
[push setQuery:installationQuery];
[push setMessage:message];
// You could also have a completion block for this, if desired
[push sendPushInBackground];
}
];
}
];
}
Caveat: Although I wrote the code in Objective-C, and although Parse allows Client PUSH, it's recommended that you do this from a Cloud Code function.
Related
I am working on an OS X (Yosemite) app which downloads two types of csv data (call them type A and type B) from the internet asynchronously using the NSURLSession API .There are multiple requests for each type of csv. Each request is it's own dedicated session wrapped in a custom class. There is one base request class with a subclass for each type. (In hindsight maybe not an ideal design but irrelevant for my issue I think).
The app is constructed such that each type of csv data is downloaded in a sequential queue. Only one request of each type can be active at a time but both types can occur simultaneously and both use the main thread for delegate callbacks. All of this works fine usually.
The issue I am seeing is that sometimes with heavy traffic I get "cross hearing", i.e. I sometimes get a response back to a type B request that is reported as completed successfully but it contains a number of type B cvs lines and then some type A lines tagged on after - so I sometimes (rarely) get type A data in my type B requests. (or the other way around).
Basically it look like the "switching" logic in Apples API gets confused about which incoming packet belongs to what request/session. The two different request types goes to different URLs but they are related and it may be that they both in the end resolve to the same IP, I am not sure about that. I wonder if there may be something related to the packet headers if they come from the same server that makes it difficult to determine what request they belong to (I'm not good enough at the internet protocols to know if this is a sensible guess). If that is the case then the solution must be to ensure all requests are in one queue so that they cannot be active simultaneously, but I do not want to do that large architecture change before I am confident there is no other workaround.
I looked for similar questions and found this old question (Why is my data getting corrupted when I send requests asynchronously in objective c for iOS?) which appears to describe the exact same issue but unfortunately it has no answer. Other than that I found nothing similar so I guess I am doing something stupid here but it would be good to know why this issue occurs before I start changing the architecture to fix it.
Has anyone seen this before and know what the cause and workaround is?
I did not include any code as I felt there was no point given it appears to be an architecture issue and if I added code it would need to be a lot. However I will be happy to add whatever you suggest if that helps understand the question.
Edit:
The relevant (I hope) code added below. Note objects are one shot only. The parameters for the request are injected by the init method and the NSURLSession is used for a single task only. Hence the session is invalidated after launch and the NSMutableData array released after parsing of the data.
-(BOOL)executeRequest {
NSURLSessionConfiguration *theConfig = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession *theSession = [NSURLSession sessionWithConfiguration:theConfig delegate:self delegateQueue:[NSOperationQueue mainQueue]];
NSURLRequest *theRequest = [NSURLRequest requestWithURL:self.queryURL cachePolicy: NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:BSTTIMEOUT];
NSURLSessionDataTask *theTask = [theSession dataTaskWithRequest:theRequest];
if(!theTask) {
return NO;
}
[theTask resume];
[theSession finishTasksAndInvalidate];
self.internetData = [NSMutableData dataWithCapacity:0];
return YES;
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
[self.internetData appendData:data];
return;
}
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
if((error)||(![self parseData]))
{
self.internetData = nil;
if(!error) {
NSDictionary *errorDictionary = #{ NSLocalizedDescriptionKey : #"Parsing of internet data failed", NSLocalizedFailureReasonErrorKey : #"Bad data was found in received buffer"};
error = [NSError errorWithDomain:NSCocoaErrorDomain code:EIO userInfo:errorDictionary];
}
NSDictionary* ui = [NSDictionary dictionaryWithObject:error forKey:#"Error"];
[[NSNotificationCenter defaultCenter] postNotificationName:[self failNotification] object:self userInfo:ui];
return;
}
[[NSNotificationCenter defaultCenter] postNotificationName:[self successNotification] object:self];
return;
}
First of all: You should not create a new session for each request. This are no sessions anymore. From the docs:
With the NSURLSession API, your app creates one or more sessions, each of which coordinates a group of related data transfer tasks. For example, if you are writing a web browser, your app might create one session per tab or window, or one session for interactive use and another session for background downloads. Within each session, your app adds a series of tasks, each of which represents a request for a specific URL (following HTTP redirects if necessary).
Second: Where do you store the session et al., so it is not deallocated?
Your main problem: Obviously you start new requests while requests are potentially running. But you have only one NSMutableData instance that receives the data in -URLSession:task:didReceiveData:: Many requests, one storage … Of course that mixes up.
I finally managed to track down my (stupid) error. For future reference the issue was caused by a failure to realise that the data coming back was not zero terminated.
Most of the data requested in my case is XML and the NSXMLParserclass wants a NSDatawithout extra trailing zeros so that works well.
But the requests which occasionally failed uses a CSV format where the data passes over a NSStringwhich is created by [NSString stringWithUTF8String] which expects a zero terminated c style string as input. This was the main culprit. Often it worked as it should. Sometimes it failed outright and sometimes it just did a buffer overrun and got some of the previous request data that was in the same memory area. These were the cases I noticed when posting the question.
Thus the solution is to switch to the use of [[NSString alloc] initWithData: encoding:NSUTF8StringEncoding] which works with non null-terminated NSDatabuffers.
It would like to know if I obtain to differentiate which user this informing the fingerprint, as well as Apple obtains!
[self evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:#"" reply:^(BOOL success, NSError *authenticationError){
if (success) {
//Code Here
else {
//Code Here
}
}];
It's impossible.
The SDK only give access to validation success or failure status, nothing more.
https://developer.apple.com/library/prerelease/ios/documentation/LocalAuthentication/Reference/LocalAuthentication_Framework/index.html
The reason behind that is that the Fingerprint is stored in a safe location of the A7/8 chip, not even accessible to the iOS kernel, which only give access to this very high success/failure information.
I want to know if its possible or not to send different message to different people at a time. Using MFMessageComposeViewController, i can send one message to various people but cant send different messages to different people.
Thank you
Not possible as there are no methods for doing these task within the SDK, However you could use some open source tools such as Mailcore available on GitHub.
For example with MailCore you send easily send a email in the background like:
CTCoreMessage *msg = [[CTCoreMessage alloc] init];
CTCoreAddress *toAddress = [CTCoreAddress addressWithName:#"Monkey"
email:#"monkey#monkey.com"];
[msg setTo:[NSSet setWithObject:toAddress]];
[msg setBody:#"This is a test message!"];
NSError *error;
BOOL success = [CTSMTPConnection sendMessage:testMsg
server:#"mail.test.com"
username:#"test"
password:#"test"
port:587
connectionType:CTSMTPConnectionTypeStartTLS
useAuth:YES
error:&error];
I'm trying to implement the push functionality in Sinch.
I've set up my since client as such:
[_client setSupportMessaging:YES];
[_client setSupportPushNotifications:YES];
_client.delegate = self;
[_client start];
[_client startListeningOnActiveConnection];
Then on
- (void)clientDidStart:(id<SINClient>)client {
client.messageClient.delegate = self;
}
And in AppDelegate.h
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
// get previously initiated Sinch client
id<SINClient> client = [SinchClient sharedClient].client;
[client registerPushNotificationData:deviceToken];
}
The other delegate methods work fine (messageSent, messageDelivered, etc), however, I can't seem to invoke
(void)message:(id<SINMessage>)message shouldSendPushNotifications:(NSArray *)pushPairs
for testing, I've setup two phones each running a SinchClient, and force quit the app on the one of the phones (assuming that should trigger the offline state) - still no dice though. I'm watching a breakpoint, but the delegate doesn't seem to be firing.
Any thoughts?
Thanks,
Charlie
Make sure that both phones have registered push data (won't work on a simulator for example). Otherwise, putting one of these devices in flight mode and try to send a message to that device should suffice to get the shouldSendPushNotification callback (make sure you are signed in as different user on the two devices).
If you don't receive the messageSent callback you won't receive the shouldSendPushNotification callback. Likewise, if you receive the messageDelivered event, you won't get the shouldSendPushNotification.
I'm investigating using NSURLSessionUploadTasks to manage the background uploading of a few files. The session is created using:
_urlsession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration backgroundSessionConfiguration:identifier] delegate:self delegateQueue:nil];
This is created within a class that conforms to URLSessionDataTaskDelegate, and specifically defines:
– URLSession:dataTask:didReceiveResponse:completionHandler:
– URLSession:dataTask:didBecomeDownloadTask:
– URLSession:dataTask:didReceiveData:
And logs to the console each time one of these delegates is called.
Then, an upload task is created with the following code:
NSString *urlString = [NSString stringWithFormat:#"%#%#?filename=%#", HOST, UPLOAD_PATH, filename];
NSMutableURLRequest *attachmentUploadRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];
attachmentUploadRequest.HTTPMethod = #"POST";
[attachmentUploadRequest addValue:#"application/binary" forHTTPHeaderField:#"Content-Type"];
NSURLSessionTask* task = [_urlsession uploadTaskWithRequest:attachmentUploadRequest fromFile:filePath];
task.taskDescription = 'upload';
However, the sequence of delegate callbacks that I get is not as expected:
URLSession:didReceiveChallenge:completionHandler:]:196: Respond with <NSURLCredential: 0x1cf4fe00>:
URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:]:282: Task 'upload' sent 32768 bytes
URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:]:282: Task 'upload' sent 48150 bytes
URLSession:dataTask:didReceiveData:]:222: Task 'upload' got some data:
Notably, the body data is sent, as expected, but then it switches immediately to didReceiveData delegate callbacks, with no didReceiveResponse callback beforehand. This is an unexpected behavior for me: I'd expected to receive information about the response so that I can properly set up data, or better yet, convert the task to a download task to save the response to a file.
If the upload task is submitted in a default URL session, then didReceiveResponse is called, and I can successfully convert the task to a background download task.
I can't find any indications in Apple's documentation for whether or not didReceiveResponse should be called for NSURLSessionUploadTasks that are in the background. It seems that they should: the documentation for NSURLSessionUploadTask indicates that it is a subclass of NSURLSessionDataTask with small modifications in behavior, but neither of the listed differences involves not sending the didReceiveResponse callback. None of the background-session-specific docs mention this limitation.
Is this a bug, or have a missed/misinterpreted some piece of the documentation that explain that upload tasks in the background do not call didReceiveResponse?
I asked Apple engineers about this during recent Tech Talks. They followed up and gave the following response - not entirely satisfactory, and I feel like they should document this behavior if it is different than any other HTTP handling flow. Especially since the foreground behavior does get the didReceiveData, but doesn't get the didReceiveResponse. At the very least they need to document this non-obvious behavior.
"The way things work today is that we don’t send the didReceiveResponse callback for background uploads to avoid waking the app if it’s not already running. The drawback is that the app cannot choose to convert the background upload into a download task when the response is received. Our decision was based on expecting the response data for a file upload would be small and therefore delivering the response data to the client as NSData instead of a downloaded file would be fine."