Core-Bluetooth get wrong characters back as answers? - ios

i need to develop an app wich can communticate with a peripheral and i use a BlueGigaModul. Basically i send commands and wait for the answere. Ive the problem that the response is not correct. The wrong character appears randomly. I send each second a command to my device. To bluetooth module get the correct data but the data i get on the iphone contains wrong characters? The data i get is bigger than 20 bytes but the module should split it.
1.
How could this be? Is it because i memcopy to early?
The method didUpdateValueForCharacteristic is called if the whole data arrived?
2.
Is it better to writeValue or updateCharacteristic?
3.
I only find one characteristic? Do i need one to send and one to receive?
Or do i need to unsubscribe after writeValue and resubscribe for receive data?
4.
Does CoreBluetooth check if the data is corrupt??
this is how i writeValue:
NSString * test = #"\r";
NSData *data = [test dataUsingEncoding:NSASCIIStringEncoding];
if(hr_characteristic != nil){
[_discoveredPeripheral writeValue:data forCharacteristic: hr_characteristic type:CBCharacteristicWriteWithResponse];
I check the response with:
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
if(error != nil)
return;
int len=characteristic.value.length;
if(len != 0)
{
memcpy(pbuffer,[characteristic.value bytes],len);
pbuffer += len;
}
}
Atm i fill the buffer with all the data i receive. I send 10 times. After i received all compare the buffer with my expected answer.
I send 600 commands and have 34 wrong characters in it. The buffer is filled with 5230bytes.
Thanks

Related

How to calculate payload size and timeout length for network connectivity test in iOS?

My app offers the option to download 3430 high resolution images from our server, each image of size 50k - 600k bytes.
The original approach was to just download all of them - but we realized that gave a lot of NSURLErrorTimedOut errors and crashed our program. We've now implemented it such that we download all of the images, but in batches of 100 images at a time.
- (void)batchDownloadImagesFromServer:(BOOL)downloadHiResImages
{
[UIApplication sharedApplication].idleTimerDisabled = YES;
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[self generateImageURLList:YES];
[leafletImageLoaderQueue removeAllObjects];
numberOfThumbnailLeft = [uncachedThumbnailArray count];
numberOfHiResImageLeft = [uncachedHiResImageArray count];
NSLog(#"DEBUG: In batchDownloadImagesFromServer numberOfThumbnailLeft %ul , numberOfHiResImageLeft %ul ", numberOfThumbnailLeft, numberOfHiResImageLeft);
numberOfImagesToDownload = numberOfThumbnailLeft;
if (downloadHiResImages)
{
numberOfImagesToDownload += numberOfHiResImageLeft;
}
if (numberTotalToDownload < 0) {
numberTotalToDownload = numberOfHiResImageLeft;
}
int midBatchCt = 0;
// start where we stopped
NSArray *subArray;
NSRange batchRange;
batchRange.location = 0;//uncachedHiResIndex;
NSInteger uncachedNumber = [uncachedHiResImageArray count];
NSLog(#"uncachedHiResIndex and numberTotalToDownload: %d %d", uncachedHiResIndex, numberTotalToDownload);
if (uncachedHiResIndex >= numberTotalToDownload || batchRange.location >= uncachedNumber) {
// we have reached the end of the uncached hires images
NSLog(#" END of download total to download=%ld , uncachedNumber=%ld, # not downloaded is %ld", (long)numberTotalToDownload, uncachedNumber, (long)numberFailedToDownload);
return;
}
if (batchRange.location+100 > uncachedNumber) {
NSInteger imagesUntilEnd = uncachedNumber -1;
batchRange.length = imagesUntilEnd;
NSLog(#"this is uncached number: %d this is uncachedhiresindex:%d and this images until end:%d ", uncachedNumber, uncachedHiResIndex, imagesUntilEnd);
} else {
batchRange.length = 100;
}
NSLog(#" NEW RANGE is from %lul to %lul ", (unsigned long)batchRange.location, batchRange.length);
subArray = [uncachedHiResImageArray subarrayWithRange:batchRange];
if (downloadHiResImages)
{
for ( LeafletURL* aLeafletURL in subArray )
{
LeafletImageLoader* hiResImageLoader = [[LeafletImageLoader alloc] initWithDelegate:self];
[leafletImageLoaderQueue addObject:hiResImageLoader]; // do this before making connection!! //
[hiResImageLoader loadImage:aLeafletURL isThumbnail:NO isBatchDownload:YES];
//// Adding object to array already retains it, so it's safe to release it here. ////
[hiResImageLoader release];
midBatchCt++;
uncachedHiResIndex++;
if (midBatchCt == 10) {
NSLog(#" Waiting for queued images to download...");
NSLog(#" REMOVED from QUEUE %lul , UncachedIndex %lul", numberRemovedFromQueue, uncachedHiResIndex);
break;
}
}
}
if ( [leafletImageLoaderQueue count] == 0 && numberRemovedFromQueue == numberTotalToDownload) {
if([delegate respondsToSelector:#selector(requestDidBatchDownloadImages:)])
{
[delegate requestDidBatchDownloadImages:self];
}
}
}
This has resolved most of our issues. However, we would like to test for network connectivity before even beginning batch downloading. I found a low level ping library that gives accurate round-trip timing results. Using the demo code from GBPing as a reference, I wrote my own code to ping our server before we call batchDownloadImagesFromServer.
- (IBAction)preloadAll:(id)sender
{
self.ping = [GBPing new];
self.ping.host = kDefaultDataServer;
self.ping.delegate = self;
self.ping.timeout = 1;
self.ping.pingPeriod = 0.9;
// setup the ping object (this resolves addresses etc)
[self.ping setupWithBlock:^(BOOL success, NSError *error) {
if (success) {
// start pinging
[self.ping startPinging];
// stop it after 5 seconds
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(#"stop it");
[self.ping stop];
self.ping = nil;
});
} else {
UIAlertController * alert = [UIAlertController alertControllerWithTitle:#"Internet Connection"
message:#"Not strong enough internet connection"
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* OKButton = [UIAlertAction
actionWithTitle:#"Ok"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
[downloadManager batchDownloadImagesFromServer:YES];
}];
[alert addAction:OKButton];
[self presentViewController:alert animated:NO completion:nil];
}
}];
}
I am completely new to networking. How do I determine the payload size and timeout length for my test considering the batch size and the size of the images?
Timeout length is per request. It is just the time the networking code will wait for a reply before it gives up. This shouldn't be too short, but for most system API, timeout length is something around a minute or more, which is probably too long.
Also, note that you will still get time out errors if connectivity is bad, so whatever caused your crashes needs to be fixed. You have to be able to recover from time-outs.
You're not giving much information about your crash (what kind of crash is it? What backtrace do you get?), but I can see three obvious things that may be happening:
You did your downloading in a tight loop without an #autoreleasepool {} block inside it. This means that all your downloaded file data accumulated in RAM and blew your app's memory limit. Be sure to put autorelease pools in long-running loops.
You were doing these downloads on the main thread. The main thread is for the UI and short actions. If your main thread code does anything that takes longer than a few seconds, UIApplication will not get to process touch events from the user (and other occurrences) and the operating system will shoot it down as being unresponsive. Offload longer operations onto a dispatch queue (or use some other way to move the actions off the main thread, e.g. by using asynchronous API).
You are flooding your server with requests, and some DDoS-protection inside it decides to just ignore requests from you for a few minutes as a form of self-protection. Many servers have limits on how many requests they will accept from a client within a given period of time, or how many open connections a client may have at the same time.
I think you would be much better served by showing the code that performs the actual download. You should not need to get accurate ping timing information to download a bunch of image files.
Assuming one or more of the above possibilities are true, I'd suggest you implement your download code like this:
Create a list of all file URLs that need to be downloaded.
Write your code so that it downloads these URLs sequentially. I.e. do not let it start downloading a file until the previous one has finished (or failed and you decided to skip it for now).
Use NSURLSession's support for downloading an individual file to a folder, don't use the code to get an NSData and save the file yourself. That way, your application doesn't need to be running while the download finishes.
Ensure that you can tell whether a file has already been downloaded or not, in case your download gets interrupted, or the phone is restarted in mid-download. You can e.g. do this by comparing their names (if they are unique enough), or saving a note to a plist that lets you match a downloaded file to the URL where it came from, or whatever constitutes an identifying characteristic in your case.
At startup, check whether all files are there. If not, put the missing ones in above download list and download them sequentially, as in #2.
Before you start downloading anything (and that includes downloading the next file after the previous download has finished or failed), do a reachability check using the Reachability API from Apple's SystemConfiguration.framework. That will tell you whether the user has a connection at all, and whether you're on WiFi or cellular (in general, you do not want to download a large number of files via cellular, most cellular connections are metered).
If your images are stored on separate servers, or they are comparatively small and there is more overhead setting up the connection than actually downloading the data, you could modify the code to download several images at once, but usually if you're downloading more than 4 images from a server at the same time, you'll likely not see a performance benefit, as every additional image will just reduce the amount of bandwidth available for the others.

iOS app that collects data from "Accu-Chek Aviva Connect" BG meter. (Bluetooth Low Energy)

I'm trying to create an iOS app that will collect data from Accu-Chek Aviva Connect.
To pair with BG meter I send a write request to Record Access Control Point Characteristic:
- (void)peripheral:(CBPeripheral *)aPeripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
if ([service.UUID isEqual:[CBUUID UUIDWithString:#"1808"]]) {
for (CBCharacteristic *aChar in service.characteristics) {
// Read Glucose Measurement...
// Read Glucose Measurement Context...
// Read Glucose Feature...
// Read Date Time...
// Read Record Access Control Point
if ([aChar.UUID isEqual:[CBUUID UUIDWithString:#"2A52"]]) {
[aPeripheral readValueForCharacteristic:aChar];
[aPeripheral setNotifyValue:YES forCharacteristic:aChar];
uint8_t bytes[] = {0x04, 0x01, 0x00};
NSData *data = [NSData dataWithBytes:(void*)&bytes length:sizeof(bytes)];
[aPeripheral writeValue:data forCharacteristic:aChar type:CBCharacteristicWriteWithResponse];
}
}
}
}
And on iPhone I see a UIAlert with a field to enter the security code. And I'm able to pair iPhone with Accu-Chek Aviva Connect. But the next time I send some write request to Record Access Control Poin Characteristic (after device disconnected) I get "Error: Authentication is insufficient" and no UIAlert on iPhone.
I have a feeling that I'm doing everything wrong.
I want to read records from bluetooth device's log. AFAIU I write bytes to Read Record Access Control Point ([aPeripheral writeValue:data forCharacteristic:aChar type:CBCharacteristicWriteWithResponse];) and get answer in peripheral:didWriteValueForCharacteristic:error:. But I can't do it because "Error: Authentication is insufficient" stands in my way!
You need to be authenticated to issue a write request to the BG meter. Insufficient authentication means that you are trying to issue the request without being authenticated. You can't just connect and then write to a medically relevant characteristic.
Check if you really issue an authentication request (which would also require a PIN entry). And if yes, check if the PIN is accepted or rejected.
The way to work with Accu-Chek Aviva Connect is to pair it the first time by going to Settings → Wireless → Pairing → Pair Device.
Then you get a screen with number code and a message: "Enter code on device".
On iPhone you discover Accu-Chek device and write a value to Read Record Access Control Point characteristic. For example request a Number of records:
- (void)peripheral:(CBPeripheral *)aPeripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
if ([service.UUID isEqual:[CBUUID UUIDWithString:#"1808"]]) {
for (CBCharacteristic *aChar in service.characteristics) {
// Read Record Access Control Point
if ([aChar.UUID isEqual:[CBUUID UUIDWithString:#"2A52"]]) {
[aPeripheral readValueForCharacteristic:aChar];
[aPeripheral setNotifyValue:YES forCharacteristic:aChar];
self.readAccessControlPointCharacteristic = aChar;
NSMutableData *mutableData = [NSMutableData data];
uint8_t opCode = 0x04; // Report number of stored records
uint8_t operator = 0x01; // All records
[mutableData appendData:[NSData dataWithBytes:(void*)&opCode length:sizeof(opCode)]];
[mutableData appendData:[NSData dataWithBytes:(void*)&operator length:sizeof(operator)]];
[aPeripheral writeValue:mutableData forCharacteristic:self.readAccessControlPointCharacteristic type:CBCharacteristicWriteWithResponse];
}
}
}
This leads to an UIAlertView on iPhone where it asks you to enter a code which is displayed on Accu-Chek screen. When you do it, your iPhone will be successfully paired with Accu-Chek Aviva Connect.
Now. When you want to read all records from Bluetooth device - you will have to go to My Data → Data Transfer → Wireless → and if your Accu-Chek device has a lot of pairings, select your iPhone name. Note: iPhone must be scaning for BT devices and connect to a discovered device automatically.
Bluetooth connection will be established and you can send any requests from your iPhone to Bluetooth device without "Error: Authentication is insufficient"!

Knowing that data from an NSStream is complete

I'am sending chunks of UIImage data over MCSession with an NSStream.
When I get bytes
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
if (eventCode == NSStreamEventHasBytesAvailable) {
// read data and append to self.data
// how to know that self.data can be used to create UIImage
}
}
I append them to a mutable data instance. The problem is how to know that the accumulated data represents a full image, so I can use -[UIImage initWithData:] to create it?
You should watch for NSStreamEventEndEncountered
The stream has no knowledge of its contents. If you can't rely on the stream ending to tell you that the data is complete, then you either need to use/create some protocol for the transmission that includes a "finished" signal, or just try to create the image and take appropriate action if that fails.

Error using semaphore token to run code after a group of asynchronous web service calls

I am in a situation where I need to call multiple web service requests at a time to call to my server to delete messages on the server and I am having a difficult time trying to figure out the best way to trigger some methods to refresh my data at the completion of these group of web service calls.
From what I have researched using a semaphore counter should work for what I am wanting to do, but I am running into a problem where upon calling dispatch_release() on my semaphore token my application crashes with this -
libdispatch.dylib`_dispatch_semaphore_dispose$VARIANT$mp:
0x3c03ed70: push {r7, lr}
0x3c03ed72: ldr r1, [r0, #40]
0x3c03ed74: mov r7, sp
0x3c03ed76: ldr r2, [r0, #36]
0x3c03ed78: cmp r1, r2
0x3c03ed7a: bge 0x3c03ed7e ; _dispatch_semaphore_dispose$VARIANT$mp + 14
0x3c03ed7c: trap
Everything i've found on this problem points to the semaphore token being referenced by something but I can't see what would have a reference to the token.
- (void)deleteMultipleThreadsForMessages:(NSArray *)messages withCompletion:(void(^)(BOOL allThreadsDeleted))completion
{
NSDictionary *userDictionary = [[MSMKeychainAccess sharedKeychain] returnUserDictionary];
long messagesCount = [messages count] - 1;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(messagesCount);
BOOL (^isLastRequest)(void) = ^BOOL (void) {
long result = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW);
if (0 == result) {
return false;
}
dispatch_release(semaphore);
return true;
};
for (MSMMessageDataModel *message in messages) {
NSDictionary *dictionary = #{#"license": userDictionary[#"license"],
#"messageID" : message.msgID};
NSLog(#"Called this many times! %#", message.msgID);
[[MSMClient sharedClient] soapPostsWithCompletion:^(NSDictionary *response, NSError *error) {
if (error) {
isLastRequest();
completion(NO);
} else {
if (isLastRequest()) {
completion(YES);
}
}
} andRequest:[[[MSMRequestsController alloc] init] createGetMessagesSOAPCallAndSendRequest:#"DeleteThread"
withParameters:dictionary]];
}
}
EDIT
Thanks for the great answers. As Dustin said I was attempting to use dispatch_semaphore for something that it should not be used for. I accepted his answer because it was simple to implement and didn't need any re-structure of what i'm doing currently to send my web services. I now have some good reading material though about dispatch_groups in general though!
Thanks for all your help!
I'm not sure this is the most efficient way to solve your problem, but just looking through your code, one problem you might be running into is that you're creating the semaphore with a count and trying to release it when that count is less than the initial count. In GCD, always create a semaphore with 0 and then signal the semaphore to the correct number of "resources" you need to count. The reason is because semaphores can't be destroyed/released if their count is less than the initial count. It kinda makes sense if you think of it as a resource counter. Having a semaphore number less than the initial count means that you have a worker still using one of the resources.
You can see this code here http://opensource.apple.com/source/libdispatch/libdispatch-187.7/src/semaphore.c. The code that will throw the exception in _dispatch_semaphore_dispose is:
if (dsema->dsema_value < dsema->dsema_orig) {
DISPATCH_CLIENT_CRASH(
"Semaphore/group object deallocated while in use");
}
This is exactly what dispatch_group is designed to address. You dispatch several blocks to a group, and when they have all completed, another block will be executed (or you can wait on them if you need a synchronous behavior).
First see Waiting on Groups of Queued Tasks in the Concurrency Programming Guide, and see the dispatch_group functions in the GCD reference. To see them in action, see the JuliaCell example from Chapter 13 of iOS:PTL. Cocoa Samurai also has some examples.
Even if you can't actually dispatch the blocks to a group (it may not work with how MSMClient operates), you can still use dispatch groups manually by calling dispatch_group_enter() and dispatch_group_leave() to get the same behavior you're trying to get from the semaphore.
As a side note, BOOL is not the same as bool. BOOL return YES and NO, which are different than true and false (which I assume means you're compiling this as ObjC++, which always makes me shudder, but that's a different issue). Mixing them can matter because they can be (and sometimes are) different sizes. I've had to crashes due to that personally.
This is an incorrect usage for dispatch_semaphore -- it isn't meant to do what you are attempting. What you need is a counting variable that is thread safe. You can get this using __sync_sub_and_fetch.
- (void)deleteMultipleThreadsForMessages:(NSArray *)messages withCompletion:(void(^)(BOOL allThreadsDeleted))completion
{
NSDictionary *userDictionary = [[MSMKeychainAccess sharedKeychain] returnUserDictionary];
__block long messagesCount = [messages count];
for (MSMMessageDataModel *message in messages) {
NSDictionary *dictionary = #{#"license": userDictionary[#"license"],
#"messageID" : message.msgID};
NSLog(#"Called this many times! %#", message.msgID);
[[MSMClient sharedClient] soapPostsWithCompletion:^(NSDictionary *response, NSError *error) {
long which = __sync_sub_and_fetch(&messageCount, 1);
if(which == 0)
completion(error == nil);
} andRequest:[[[MSMRequestsController alloc] init] createGetMessagesSOAPCallAndSendRequest:#"DeleteThread"
withParameters:dictionary]];
}
}
__sync_sub_and_fetch tells the CPU that you want it to take latest version of 'messageCount' from all threads (and cores), subtract 1, and give you the result.

How can I catch EPIPE in my NSFIleHandle handling?

I'm having a problem with EPIPE in my iOS app, and it's not being caught in the #try/#catch/#finally block. How can I catch this signal (SIGPIPE, likely)...
I've built a "web proxy" into my app that will handle certain kinds of URLs - in this error case, it seems that the remote end (also in my app, but hiding in the iOS libraries) closes its end of the socket. I don't get a notification (should I? Is there something I should register for with the NSFileHandle that might help here?).
I've based this proxy on HTTPServer that Matt Gallagher put together (available here), and the problem is in a subclass of the HTTPRequestHandler class he put together. Here's the code (this code is the equivalent of the startResponse method in the base class):
-(void)proxyTS:(SSProxyTSResource *)proxyTS didReceiveResource:(NSData *)resource
{
NSLog(#"[%# %#]", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
CFHTTPMessageRef response =
CFHTTPMessageCreateResponse(kCFAllocatorDefault, 200, NULL, kCFHTTPVersion1_1);
CFHTTPMessageSetHeaderFieldValue(response,
(CFStringRef)#"Content-Type",
(__bridge CFStringRef)s_MIMEtype);
CFHTTPMessageSetHeaderFieldValue(response,
(CFStringRef)#"Connection",
(CFStringRef)#"close");
CFHTTPMessageSetBody(response,
(__bridge CFDataRef)resource);
CFDataRef headerData = CFHTTPMessageCopySerializedMessage(response);
#try
{
NSLog(#" -> writing %u bytes to filehandle...",[((__bridge NSData *)headerData) length]);
[self.fileHandle writeData:(__bridge NSData *)headerData];
}
#catch (NSException *exception)
{
// Ignore the exception, it normally just means the client
// closed the connection from the other end.
}
#finally
{
NSLog(#" *ding*");
CFRelease(headerData);
CFRelease(response);
[self.server closeHandler:self];
}
}
And here's what shows up in the console log when it crashes:
Jan 15 14:55:10 AWT-NoTouch-iPhone-1 Streamer[1788] <Warning>: [SSProxyTSResponseHandler proxyTS:didReceiveResource:]
Jan 15 14:55:10 iPhone-1 Streamer[1788] <Warning>: -> writing 261760 bytes to filehandle...
Jan 15 14:55:11 iPhone-1 com.apple.launchd[1] (UIKitApplication:com.XXX.Streamer[0xf58][1788]) <Warning>: (UIKitApplication:com.XXX.Streamer[0xf58]) Exited abnormally: Broken pipe: 13
It seems that because the other end closed the pipe the write() fails, so if someone can point me at how I can either discover that it's already closed and not try to write data to it OR whatever will make it not crash my program that would be very helpful.
The immediate problem of crashing with SIGPIPE is solved. I'm not entirely giggly about this solution, but at least the app doesn't crash. It's not clear that it's working 100% correctly, but it does seem to be behaving quite a bit better.
I've resolved this issue by examining further what's going on. In doing some research, I found that perhaps I should be using NSFileHandle's writeabilityHandler property to install a block to do the writing. I'm not fully sold on that approach (it felt convoluted to me), but it might help.
Writability-handler solution:
In doing some web searching on writeabilityHandler, I stumbled on Bert Leung's blog entry on some issues he was having in a similar area. I took his code and modified it as follows, replacing the #try/#catch/#finally block above with this code:
self.pendingData = [NSMutableData dataWithData:(__bridge NSData *)(headerData)];
CFRelease(headerData);
CFRelease(response);
self.fileHandle.writeabilityHandler = ^(NSFileHandle* thisFileHandle)
{
int amountSent = send([thisFileHandle fileDescriptor],
[self.pendingData bytes],
[self.pendingData length],
MSG_DONTWAIT);
if (amountSent < 0) {
// errno is provided by system
NSLog(#"[%# %#] Error while sending response: %d", NSStringFromClass([self class]), NSStringFromSelector(_cmd), errno);
// Setting the length to 0 will cause this handler to complete processing.
self.pendingData.length = 0;
} else {
[self.pendingData replaceBytesInRange:NSMakeRange(0, amountSent)
withBytes:NULL
length:0];
}
if ([self.pendingData length] == 0) {
thisFileHandle.writeabilityHandler = nil;
// Hack to avoid ARC cycle with self. I don't like this, but...
[[NSNotificationCenter defaultCenter] postNotification:self.myNotification];
}
};
That worked fine but it didn't solve the problem. I was still getting SIGPIPE/EPIPE.
SIGPIPE be gone!
This wasn't a surprise, exactly, as this does pretty much the same thing as the former writeData: did but does it using send() instead. The key difference though is that using send() allows errno to be set. This was quite helpful, actually - I was getting a couple of error codes (in errno), such as 54 (Connection reset by peer) and 32 (Broken pipe). The 54's were fine, but the 32's resulted in the SIGPIPE/EPIPE. Then it dawned on me - perhaps I should just ignore SIGPIPE.
Given that thought, I added a couple of hooks into my UIApplicationDelegate in application:didFinishLaunchingWithOptions:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
[self installSignalHandlers];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
...
and applicationWillTerminate::
- (void)applicationWillTerminate:(UIApplication *)application
{
// Saves changes in the application's managed object context before the application terminates.
[self removeSignalHandlers];
[self saveContext];
}
-(void)installSignalHandlers
{
signal(SIGPIPE,SIG_IGN);
}
-(void)removeSignalHandlers
{
signal(SIGPIPE, SIG_DFL);
}
Now at least the app doesn't crash. It's not clear that it's working 100% correctly, but it does seem to be behaving.
I also switched back to the #try/#catch/#finally structure because it's more direct. Further, after ignoring SIGPIPE, the #catch block does get triggered. Right now, I'm logging the exception, but only so I can see that it's working. In the released code, that log will be disabled.

Resources