How to solve it ( EXC_BAD_ADDRESS in Objective-C )? - ios

sometimes, fatal error occurs in my app with EXC_BAD_ADDRESS error log.
What is the cause of my code?
crash log
0. Crashed: GGMutableDictionary Isolation Queue
0 libobjc.A.dylib 0x184e2c150 objc_msgSend + 16
1 CoreFoundation 0x1862e0d04 -[NSDictionary descriptionWithLocale:indent:] + 916
2 CoreFoundation 0x1862e0dac -[NSDictionary descriptionWithLocale:indent:] + 1084
3 Foundation 0x186da35a4 _NSDescriptionWithLocaleFunc + 76
4 CoreFoundation 0x1863788bc __CFStringAppendFormatCore + 8440
5 CoreFoundation 0x18637678c _CFStringCreateWithFormatAndArgumentsAux2 + 244
6 Foundation 0x186da3418 +[NSString stringWithFormat:] + 68
7 app 0x10019f4b8 -[TinyDB saveAsync] + 4296963256
8 app 0x10019c86c __26-[TinyDB putString:value:]_block_invoke + 4296951916
9 libdispatch.dylib 0x18526e9a0 _dispatch_client_callout + 16
10 libdispatch.dylib 0x18527bee0 _dispatch_barrier_sync_f_invoke + 84
11 app 0x10019c7e0 -[TinyDB putString:value:] + 4296951776
source file
This class is used asynchronously from many other class.
This class should have thread safe. But EXC_BAD_ADDRESS fatal error occurs in saveAsync method.
I think weakDictionaryRef or isolationQueue variables are deallocated. I want to fix this problem. What should I fix in this code?
Thank you for your advice.
TinyDB.h file
#interface TinyDB : NSObject
#property (nonatomic, retain) NSString * docPath;
// #property (nonatomic, retain) NSMutableDictionary * dictionary;
#property (nonatomic, retain) NSFileManager * fileManager;
#property (nonatomic, retain) NSString * dir;
#property (nonatomic, assign) BOOL flagWrite;
- (instancetype)initWithFile:(NSString *)file;
- (NSString *)getDocPath;
- (void)putDouble:(NSString *)key value:(double)value;
- (void)putInt:(NSString *)key value:(NSInteger)value;
- (void)putMutableArray:(NSString *)key value:(NSMutableArray *)value;
- (void)putString:(NSString *)key value:(NSString *)value;
- (void)putTinyDB:(NSString *)key value:(TinyDB *)value;
- (void)putLong:(NSString *)key value:(NSInteger)value;
- (void)putBool:(NSString *)key value:(BOOL)value;
- (void)putDictionary:(NSString *)key value:(NSDictionary *)value;
- (id)get:(NSString *)key;
- (NSMutableArray *)getMutableArray:(NSString *)key;
- (BOOL)has:(NSString *)key;
- (void)saveAsync;
- (void)save;
- (NSString *)jsonString;
- (NSString *)stringify:(id)obj;
- (NSString *)getSet:(id)value;
- (NSString *)getPairSet:(NSString *)key value:(id)value;
- (NSMutableDictionary*)getMutableDictionary:(NSString*)key;
- (NSString *)getString:(NSString *)key;
- (BOOL)getBool:(NSString*)key;
- (NSArray *)allKeys;
- (void)removeObjectForKey:(NSString*)key;
#end
TinyDB.m file
#implementation TinyDB
{
#private dispatch_queue_t isolationQueue_;
#private __strong NSMutableDictionary * _myDictionary;
}
#synthesize docPath=docPath,fileManager=fileManager,dir=dir,flagWrite=flagWrite;
BOOL flagOnSave = false;
BOOL flagWrite = true;
NSString* dir;
NSString* docPath = #"";
NSLock* _dicLock=nil;
NSFileManager* fileManager;
__weak id _weakDictionaryRef;
-(id)initWithFile:(NSString *)file{
self = [super init];
docPath = file;
// ##########################################
isolationQueue_ = dispatch_queue_create([#"GGMutableDictionary Isolation Queue" UTF8String], DISPATCH_QUEUE_CONCURRENT);
// ##########################################
fileManager = [NSFileManager defaultManager];
dir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true) firstObject];
dir = [NSString stringWithFormat:#"%#/myapp/",dir];
BOOL flagFileExist = [fileManager fileExistsAtPath:[[NSString alloc] initWithFormat:#"%#/%#.myapp",dir,docPath]];
_myDictionary = [[NSMutableDictionary alloc] init];
if(flagFileExist){
[BSDebugger log:[NSString stringWithFormat:#"DEBUG_myapp_CONFIG : File Found!!! %# --> ",file]];
#try{
NSMutableDictionary* dic = [[NSMutableDictionary alloc] initWithContentsOfFile:[[NSString alloc] initWithFormat:#"%#/%#.myapp",dir,docPath]];
_myDictionary = [[NSMutableDictionary alloc] initWithDictionary:dic];
}#catch(NSException * e){
_myDictionary = [[NSMutableDictionary alloc] init];
}#finally{
}
}else{
[BSDebugger log:[NSString stringWithFormat:#"DEBUG_myapp_CONFIG : File Not Found!!!--> %# ",file]];
_myDictionary = [[NSMutableDictionary alloc] init];
}
_weakDictionaryRef = _myDictionary;
[self saveAsync];
return self;
}
-(void)putString:(NSString*)key value:(NSString*)value{
dispatch_barrier_sync(isolationQueue_, ^{
if(value == nil){
return;
}
#try{
[_myDictionary setObject:value forKey:key];
}#catch(NSException* e){
NSMutableDictionary* buff = [[NSMutableDictionary alloc] initWithDictionary:_myDictionary];
_myDictionary = buff;
_weakDictionaryRef = _myDictionary;
dispatch_barrier_sync(isolationQueue_, ^{
[_myDictionary setObject:value forKey:key];
});
}#finally{
}
[self saveAsync];
});
}
- (void)putDouble:(NSString *)key value:(double)value{
dispatch_barrier_sync(isolationQueue_, ^{
[_myDictionary setObject:[[NSNumber alloc] initWithDouble:value] forKey:key];
[self saveAsync];
});
}
- (void)putInt:(NSString *)key value:(NSInteger)value{
dispatch_barrier_sync(isolationQueue_, ^{
#try{
[_myDictionary setObject:[[NSNumber alloc] initWithInteger:value] forKey:key];
}#catch(NSException* e){
NSMutableDictionary* buff = [[NSMutableDictionary alloc] initWithDictionary:_myDictionary];
_myDictionary = buff;
_weakDictionaryRef = _myDictionary;
[_myDictionary setObject:[[NSNumber alloc] initWithInteger:value] forKey:key];
}#finally{
}
[self saveAsync];
});
}
- (void)putMutableArray:(NSString *)key value:(NSMutableArray *)value{
dispatch_barrier_sync(isolationQueue_, ^{
if( key != nil && value != nil ){
[_myDictionary setObject:value forKey:key];
[self saveAsync];
}
});
}
- (void)putTinyDB:(NSString *)key value:(TinyDB *)value{
dispatch_barrier_sync(isolationQueue_, ^{
TinyDB* db = value;
NSString* docuPath = [db getDocPath];
#try{
[_myDictionary setObject:docuPath forKey:key];
}#catch(NSException* e){
NSMutableDictionary* buff = [[NSMutableDictionary alloc] initWithDictionary:_myDictionary];
_myDictionary = buff;
_weakDictionaryRef = _myDictionary;
dispatch_barrier_sync(isolationQueue_, ^{
[_myDictionary setObject:docuPath forKey:key];
});
}#finally{
}
[self saveAsync];
});
}
- (void)putLong:(NSString *)key value:(NSInteger)value{
dispatch_barrier_sync(isolationQueue_, ^{
[_myDictionary setObject:[[NSNumber alloc] initWithInteger:value] forKey:key];
[self saveAsync];
});
}
- (void)putBool:(NSString *)key value:(BOOL)value{
dispatch_barrier_sync(isolationQueue_, ^{
[_myDictionary setObject:[[NSNumber alloc] initWithBool:value] forKey:key];
[self saveAsync];
});
}
- (void)putDictionary:(NSString *)key value:(NSDictionary *)value{
dispatch_barrier_sync(isolationQueue_, ^{
[_myDictionary setObject:value forKey:key];
[self saveAsync];
});
}
-(void)removeObjectForKey:(NSString*)key{
dispatch_barrier_sync(isolationQueue_, ^{
if( _myDictionary != nil && [_myDictionary objectForKey:key] != nil ){
[_myDictionary removeObjectForKey:key];
}
});
}
-(void) save{
dispatch_barrier_sync(isolationQueue_, ^{
#try {
// NSLog([NSString stringWithFormat:#"writeToFile Error : orgPath = %#/%#.myapp / %#",dir,docPath, self.dictionary ]);
// NSMutableDictionary* nsDic = self.dictionary;
if( _myDictionary != nil ){
NSDictionary * _dictionary =
(__bridge NSDictionary *)(CFPropertyListCreateDeepCopy(kCFAllocatorDefault,
(__bridge CFPropertyListRef)(_myDictionary),
kCFPropertyListImmutable));
if( _dictionary != nil ){
[_dictionary writeToFile:[[NSString alloc] initWithFormat:#"%#/%#.myapp",dir,docPath] atomically: false];
}
}
}#catch (NSException *exception) {
_myDictionary = [[NSMutableDictionary alloc] initWithDictionary:_myDictionary];
_weakDictionaryRef = _myDictionary;
}#finally {
}
});
}
// ################################################################
// normal function
- (NSString *)jsonString{
__block NSString* buff = #"";
dispatch_barrier_sync(isolationQueue_, ^{
if( _myDictionary != nil ){
NSDictionary * _dictionary = nil;
// dispatch_barrier_sync(isolationQueue_, ^{
_dictionary = (__bridge NSDictionary *)(CFPropertyListCreateDeepCopy(kCFAllocatorDefault,
(__bridge CFPropertyListRef)(_myDictionary),
kCFPropertyListImmutable));
if( _dictionary != nil ){
buff = [self stringify:_dictionary];
}
}else{
NSLog(#"buff = [self stringify:_myDictionary]; is nil ");
}
});
return buff;
}
- (NSString *)stringify:(id)obj{
if([obj isKindOfClass:[NSDictionary class]]){
int idx = 0;
NSString* buff = #"{";
if( obj != nil ){
NSDictionary* dic = [NSDictionary dictionaryWithDictionary:obj]; //obj;
for(NSString* key in dic){
id value = [dic objectForKey:key];
if(idx != 0){
buff = [[NSString alloc] initWithFormat:#"%#%#",buff,#","];
}
buff = [[NSString alloc] initWithFormat:#"%#%#",buff,[self getPairSet:key value:value]];
idx++;
}
}
buff = [[NSString alloc] initWithFormat:#"%#%#",buff,#"}"];
return buff;
}else if([obj isKindOfClass:[NSArray class]]){
int idx = 0;
NSString* buff = #"[";
if( obj != nil ){
NSMutableArray* _a = [[NSMutableArray alloc] init];
for( int ai = 0; ai < [obj count]; ai++){
if( [obj objectAtIndex:ai] != nil ){
[_a addObject:[obj objectAtIndex:ai]];
}
}
NSArray* arr = [NSArray arrayWithArray:_a]; //obj;
for(id value in arr){
if(idx != 0){
buff = [[NSString alloc] initWithFormat:#"%#%#",buff,#","];
}
buff = [[NSString alloc] initWithFormat:#"%#%#",buff,[self getSet:value]];
idx++;
}
}
buff = [[NSString alloc] initWithFormat:#"%#%#",buff,#"]"];
return buff;
}else{
return [self getSet:obj];
}
}
- (NSString *)getSet:(id)value{
NSString* buff = #"";
if([value isKindOfClass:[NSString class]]){
buff = [[NSString alloc] initWithFormat:#"%#\"%#\"",buff,value];
}else if([value isKindOfClass:[NSNumber class]]){
NSNumber* val = value;
buff = [[NSString alloc] initWithFormat:#"%#%#",buff,[val stringValue]];
}else{
buff = [[NSString alloc] initWithFormat:#"%#%#",buff,[self stringify:value]];
}
return buff;
}
- (NSString *)getPairSet:(NSString *)key value:(id)value{
NSString* buff = #"";
if([value isKindOfClass:[NSString class]]){
buff = [[NSString alloc] initWithFormat:#"%#\"%#\":\"%#\"",buff,key,value];
}else if([value isKindOfClass:[NSNumber class]]){
buff = [[NSString alloc] initWithFormat:#"%#\"%#\":%#",buff,key,[value stringValue]];
}else{
buff = [[NSString alloc] initWithFormat:#"%#\"%#\":%#",buff,key,[self stringify:value]];
}
return buff;
}
-(NSMutableDictionary*)getMutableDictionary:(NSString*)key{
NSMutableDictionary* dic = [[NSMutableDictionary alloc] init];
id obj = [_myDictionary objectForKey:key];
if(obj != nil){
dic = [[NSMutableDictionary alloc] initWithDictionary:obj];
}
return dic;
}
-(NSArray *)allKeys{
__block NSArray* arr = nil;
dispatch_barrier_sync(isolationQueue_, ^{
if( _myDictionary != nil ){
arr = [_myDictionary allKeys];
}
});
return arr;
}
- (NSString *)getDocPath{
return docPath;
}
- (id)get:(NSString *)key{
__block id _obj = nil;
dispatch_barrier_sync(isolationQueue_, ^{
_obj = [_myDictionary objectForKey:key];
});
return _obj;
}
- (NSString *)getString:(NSString *)key{
__block NSString* returnStr = #"";
dispatch_barrier_sync(isolationQueue_, ^{
if([_myDictionary objectForKey:key]==nil){
returnStr = #"";
}
if([[_myDictionary objectForKey:key] isKindOfClass:[NSString class]]){
returnStr = [_myDictionary objectForKey:key];
}else{
returnStr = #"";
}
});
return returnStr;
}
- (BOOL)getBool:(NSString*)key{
__block BOOL flag = false;
dispatch_barrier_sync(isolationQueue_, ^{
if([_myDictionary objectForKey:key]==nil){
flag = NO;
}
if([[_myDictionary objectForKey:key] isKindOfClass:[NSNumber class]]){
NSNumber* boolValue = [_myDictionary objectForKey:key];
if([boolValue boolValue] == YES){
flag = YES;
}else{
flag = NO;
}
}else{
flag = NO;
}
});
return flag;
}
- (NSMutableArray *)getMutableArray:(NSString *)key{
__block NSMutableArray* _arr = nil;
dispatch_barrier_sync(isolationQueue_, ^{
_arr = [_myDictionary objectForKey:key];
});
return _arr;
}
- (BOOL)has:(NSString *)key{
__block BOOL flag = false;
dispatch_barrier_sync(isolationQueue_, ^{
if([_myDictionary objectForKey:key] != nil){
flag = true;
}else{
flag = false;
}
});
return flag;
}
// #########################
// save function
int flagTest = 0;
bool mark = 0;
NSTimer* saveTimer = nil;
-(void)saveAsync{
#try {
[BSDebugger log:[NSString stringWithFormat:#"_weakDictionaryRef : %# ",_weakDictionaryRef ]];
if( _myDictionary != nil ){
if( _weakDictionaryRef != nil ){
NSDictionary * _dictionary = nil;
_dictionary = (__bridge NSDictionary *)(CFPropertyListCreateDeepCopy(kCFAllocatorDefault,
(__bridge CFPropertyListRef)(_myDictionary),
kCFPropertyListImmutable));
#try{
flagOnSave = true;
UIBackgroundTaskIdentifier taskID = [myappCore beginBackgroundUpdateTask];
flagWrite = false;
NSString* orgPath = [[NSString alloc] initWithFormat:#"%#/%#.myapp",dir,docPath ];
#try {
if( _dictionary != nil ){
[_dictionary writeToFile:orgPath atomically:YES];
}
}#catch (NSException *exception) {
[BSDebugger log:[NSString stringWithFormat:#"DEBUG_myapp_TINYDB : %#",[exception callStackSymbols]]];
}#finally {
}
flagWrite = true;
flagOnSave = false;
[myappCore endBackgroundUpdateTask:taskID];
}#catch (NSException *exceptionMain) {
[BSDebugger log:[NSString stringWithFormat:#"DEBUG_myapp_TINYDB : %#",[exceptionMain callStackSymbols]]];
}#finally {
_dictionary = nil;
}
return;
// });
}
}
}#catch (NSException *exception) {
}#finally {
// [_dicLock unlock];
}
}
#end

Some ideas:
Turn on zombies (How to enable NSZombie in Xcode). You'll get more information about the object causing the crash when you do that.
If you can make the error happen at will, set a breakpoint at the top of saveAsync and step through it.
Change the stringWithFormat calls somehow so they don't reference anything that might be deallocated and see if the problem goes away. If it does, you'll know what was causing it.
I find it sometimes helps to override retain and release in objects I'm suspicious of and either print something out or set a breakpoint so I can see who might be over-releasing an object.

Related

Trying to sort a NSString using NSMutableArray

I have a NSString which holds info like this:
%hook APMIntro
- (bool)isIntroductoryOffer {
return NO;
}
%end
%hook APMIntro
- (void)setIntroductoryOffer:(bool)arg1 {
arg1 = NO;
%orig;
}
%end
%hook ChallengeProgressHolder
- (void)setHasItem:(bool)arg1 {
arg1 = NO;
%orig;
}
%end
%hook ChallengeProgressHolder
- (bool)hasItem {
return NO;
}
%end
The end result I'm trying to achieve is this
%hook APMIntro
- (bool)isIntroductoryOffer {
return NO;
}
- (void)setIntroductoryOffer:(bool)arg1 {
arg1 = NO;
%orig;
}
%end
%hook ChallengeProgressHolder
- (void)setHasItem:(bool)arg1 {
arg1 = NO;
%orig;
}
- (bool)hasItem {
return NO;
}
%end
So far, I've tried using NSMutableArray to separate the different lines to try and organize them - but the result is an infinite loop. I've tried this code so far
-(NSString *)cleanUp:(NSString *)cleanUp{
NSMutableArray *content = [[cleanUp componentsSeparatedByString:#"%hook "] mutableCopy];
// NSMutableArray *content = [[NSMutableArray alloc] init];
NSMutableArray *cleaned = [[NSMutableArray alloc] init];
NSMutableArray *hooks = [[NSMutableArray alloc] init];
// NSArray *messy = [[cleanUp componentsSeparatedByString:#"\n"] mutableCopy];
cleanUp = #"";
NSString *line, *line1;
//NSString *fixMe = #"";
for (unsigned h = 1; h < content.count; h++){
line = [content objectAtIndex:h-1];
if ([line hasPrefix:#"#import"]){
[cleaned addObject:[NSString stringWithFormat:#"%#\n", line]];
continue;
} else {
cleanUp = [NSString stringWithFormat:#"%#%%hook %#", cleanUp, line];
//[content addObject:[NSString stringWithFormat:#"%hook %#", line]];
}
}
//NSLog(#"\n\n\n%#\n\n\n", cleanUp);
BOOL lookingForEnd = NO;
BOOL hookFound = NO;
hooks = [[cleanUp componentsSeparatedByString:#"\n"] mutableCopy];
// NSString *hook;
//NSLog(#"\n\n\n%#\n\n\n", hooks);
for (unsigned i = 1; i < hooks.count; i++){
line = [hooks objectAtIndex:i-1];
//NSLog(#"%i: %#\n\n", i, line);
if (lookingForEnd) {
[cleaned addObject:[NSString stringWithFormat:#"%#\n", line]];
if ([line isEqualToString:#"%end"]){
lookingForEnd = NO;
//i = 1;
continue;
} else if ([line1 isEqualToString:line]){
lookingForEnd = YES;
}
}
if ([line hasPrefix:#"%hook"]){
line1 = line;
for (unsigned j = 1; j < hooks.count; j++){
printf("\n\n\nHOOK\n\n\n");
if ([[hooks objectAtIndex:j-1] isEqual:line1]){
printf("\n\n\nFOUND\n\n\n");
[hooks addObject:line];
[cleaned addObject:[NSString stringWithFormat:#"%#\n", line]];
lookingForEnd = YES;
hookFound = YES;
//break;
}
}
}
}
NSLog(#"%#\n", cleaned);
return cleaned;
}
After setting this up and reading further into how to sort arrays - I found that I'm going about this all wrong - I'm just not sure how to go about doing it right.
Thanks in advance - any help is greatly appreciated
You possibly could do this all with a complex RegEx -- but, here's a bit of a "step by step" approach.
Create a NSObject class with two string properties - blockName and blockBody.
We'll use this regular expression:
(^%hook )(.*?)(?=%end$)
to split the string into "pieces" of text contained between "%hook " and "%end".
Then, we'll loop through the matches creating an array of "block" objects, each with the name and body.
Next, we sort the array by blockName.
Finally, we loop through the sorted array, formatting an output string with the "bodies" grouped into the named blocks.
So, if we have:
MyBlock.h
#interface MyBlock : NSObject
#property (strong, nonatomic) NSString *blockName;
#property (strong, nonatomic) NSString *blockBody;
#end
MyBlock.m
#import "MyBlock.h"
#implementation MyBlock
#end
and we start with your sample NSString:
%hook APMIntro
- (bool)isIntroductoryOffer {
return NO;
}
%end
%hook APMIntro
- (void)setIntroductoryOffer:(bool)arg1 {
arg1 = NO;
%orig;
}
%end
%hook ChallengeProgressHolder
- (void)setHasItem:(bool)arg1 {
arg1 = NO;
%orig;
}
%end
%hook ChallengeProgressHolder
- (bool)hasItem {
return NO;
}
%end
We can do this:
NSString *input = [self sampleString];
NSMutableArray <MyBlock *> *blocks = [NSMutableArray new];
NSString *pattern = #"(^%hook )(.*?)(?=%end$)";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionDotMatchesLineSeparators | NSRegularExpressionAnchorsMatchLines error:NULL];
NSArray *myArray = [regex matchesInString:input options:0 range:NSMakeRange(0, [input length])] ;
for (NSTextCheckingResult *match in myArray) {
// get the range starting after "%hook "
NSRange matchRange = [match rangeAtIndex:2];
// get the "block" string
NSString *s = [input substringWithRange:matchRange];
// split it by newLines
NSArray <NSString *> *a = [s componentsSeparatedByString:#"\n"];
// we want the "body" to skip the first line
NSRange r;
r.location = 1;
r.length = [a count] - 1;
// get the lines excluding the first line
NSArray <NSString *> *a2 = [a subarrayWithRange:r];
// new MyBlock object
MyBlock *b = [MyBlock new];
b.blockName = [a firstObject];
b.blockBody = [a2 componentsJoinedByString:#"\n"];
[blocks addObject:b];
}
// sort the array of blocks by blockName
NSArray <MyBlock *> *sortedArray;
sortedArray = [blocks sortedArrayUsingComparator:^NSComparisonResult(MyBlock *a, MyBlock *b) {
return [a.blockName compare:b.blockName];
}];
NSMutableString *output = [NSMutableString new];
NSString *curName = #"";
// loop through the array of blocks
for (int i = 0; i < [sortedArray count]; i++) {
MyBlock *b = sortedArray[i];
// if we're at a "new" blockName
if (![curName isEqualToString:b.blockName]) {
curName = b.blockName;
// add %end if output is not ""
if ([output length] != 0) {
[output appendString:#"%end\n"];
}
// add a new line of %hook blockName
[output appendFormat:#"\n%%hook %#\n", curName];
}
// add the block body
[output appendFormat:#"%#", b.blockBody];
}
// "close" the last block
[output appendString:#"%end\n"];
// log the resulting string
NSLog(#"%#", output);
and the output from that sample is:
%hook APMIntro
- (bool)isIntroductoryOffer {
return NO;
}
- (void)setIntroductoryOffer:(bool)arg1 {
arg1 = NO;
%orig;
}
%end
%hook ChallengeProgressHolder
- (void)setHasItem:(bool)arg1 {
arg1 = NO;
%orig;
}
- (bool)hasItem {
return NO;
}
%end
Play with the output formatting (line breaks, spacing, etc) as desired :)

I'm not able to resume download task in NSURLSession

I have create a demo for downloading a file from the server it is working fine with foreground and background, but when I'm going to resume it, It will giving below Error
I have stcuk here could help me so solved it, I search in internet but I couldn't find anything helpful.
Download file and Pause Downlaod is working fine I have issue to resume it.
Task <3AE5BF24-3A4E-4713-8FC2-A0032022C913>.<6> finished with error [-3003] Error Domain=NSURLErrorDomain Code=-3003 "(null)" UserInfo={_NSURLErrorRelatedURLSessionTaskErrorKey=(
"BackgroundDownloadTask <3AE5BF24-3A4E-4713-8FC2-A0032022C913>.<6>"
), _NSURLErrorFailingURLSessionTaskErrorKey=BackgroundDownloadTask <3AE5BF24-3A4E-4713-8FC2-A0032022C913>.<6>}
Here is the final Xcode project
Downlaod xcode project
#import "NSURLSession+ResumeData.h"
#import <UIKit/UIKit.h>
#define IS_IOS10ORLATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 10)
#pragma mark- private
static NSData * correctRequestData(NSData *data) {
if (!data) {
return nil;
}
// return the same data if it's correct
if ([NSKeyedUnarchiver unarchiveObjectWithData:data] != nil) {
return data;
}
NSMutableDictionary *archive = [[NSPropertyListSerialization propertyListWithData:data options:NSPropertyListMutableContainersAndLeaves format:nil error:nil] mutableCopy];
if (!archive) {
return nil;
}
NSInteger k = 0;
id objectss = archive[#"$objects"];
while ([objectss[1] objectForKey:[NSString stringWithFormat:#"$%ld",k]] != nil) {
k += 1;
}
NSInteger i = 0;
while ([archive[#"$objects"][1] objectForKey:[NSString stringWithFormat:#"__nsurlrequest_proto_prop_obj_%ld",i]] != nil) {
NSMutableArray *arr = archive[#"$objects"];
NSMutableDictionary *dic = arr[1];
id obj = [dic objectForKey:[NSString stringWithFormat:#"__nsurlrequest_proto_prop_obj_%ld",i]];
if (obj) {
[dic setValue:obj forKey:[NSString stringWithFormat:#"$%ld",i+k]];
[dic removeObjectForKey:[NSString stringWithFormat:#"__nsurlrequest_proto_prop_obj_%ld",i]];
[arr replaceObjectAtIndex:1 withObject:dic];
archive[#"$objects"] = arr;
}
i++;
}
if ([archive[#"$objects"][1] objectForKey:#"__nsurlrequest_proto_props"] != nil) {
NSMutableArray *arr = archive[#"$objects"];
NSMutableDictionary *dic = arr[1];
id obj = [dic objectForKey:#"__nsurlrequest_proto_props"];
if (obj) {
[dic setValue:obj forKey:[NSString stringWithFormat:#"$%ld",i+k]];
[dic removeObjectForKey:#"__nsurlrequest_proto_props"];
[arr replaceObjectAtIndex:1 withObject:dic];
archive[#"$objects"] = arr;
}
}
// Rectify weird "NSKeyedArchiveRootObjectKey" top key to NSKeyedArchiveRootObjectKey = "root"
if ([archive[#"$top"] objectForKey:#"NSKeyedArchiveRootObjectKey"] != nil) {
[archive[#"$top"] setObject:archive[#"$top"][#"NSKeyedArchiveRootObjectKey"] forKey: NSKeyedArchiveRootObjectKey];
[archive[#"$top"] removeObjectForKey:#"NSKeyedArchiveRootObjectKey"];
}
// Reencode archived object
NSData *result = [NSPropertyListSerialization dataWithPropertyList:archive format:NSPropertyListBinaryFormat_v1_0 options:0 error:nil];
return result;
}
static NSMutableDictionary *getResumeDictionary(NSData *data) {
NSMutableDictionary *iresumeDictionary = nil;
if (IS_IOS10ORLATER) {
id root = nil;
id keyedUnarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
#try {
root = [keyedUnarchiver decodeTopLevelObjectForKey:#"NSKeyedArchiveRootObjectKey" error:nil];
if (root == nil) {
root = [keyedUnarchiver decodeTopLevelObjectForKey:NSKeyedArchiveRootObjectKey error:nil];
}
} #catch(NSException *exception) {
}
[keyedUnarchiver finishDecoding];
iresumeDictionary = [root mutableCopy];
}
if (iresumeDictionary == nil) {
iresumeDictionary = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListMutableContainersAndLeaves format:nil error:nil];
}
return iresumeDictionary;
}
static NSData *correctResumeData(NSData *data) {
NSString *kResumeCurrentRequest = #"NSURLSessionResumeCurrentRequest";
NSString *kResumeOriginalRequest = #"NSURLSessionResumeOriginalRequest";
if (data == nil) {
return nil;
}
NSMutableDictionary *resumeDictionary = getResumeDictionary(data);
if (resumeDictionary == nil) {
return nil;
}
resumeDictionary[kResumeCurrentRequest] = correctRequestData(resumeDictionary[kResumeCurrentRequest]);
resumeDictionary[kResumeOriginalRequest] = correctRequestData(resumeDictionary[kResumeOriginalRequest]);
NSData *result = [NSPropertyListSerialization dataWithPropertyList:resumeDictionary format:NSPropertyListXMLFormat_v1_0 options:0 error:nil];
return result;
}
#implementation NSURLSession (ResumeData)
- (NSURLSessionDownloadTask *)downloadTaskWithCorrectResumeData:(NSData *)resumeData {
NSString *kResumeCurrentRequest = #"NSURLSessionResumeCurrentRequest";
NSString *kResumeOriginalRequest = #"NSURLSessionResumeOriginalRequest";
NSData *cData = correctResumeData(resumeData);
cData = cData ? cData:resumeData;
NSURLSessionDownloadTask *task = [self downloadTaskWithResumeData:cData];
NSMutableDictionary *resumeDic = getResumeDictionary(cData);
if (resumeDic) {
if (task.originalRequest == nil) {
NSData *originalReqData = resumeDic[kResumeOriginalRequest];
NSURLRequest *originalRequest = [NSKeyedUnarchiver unarchiveObjectWithData:originalReqData ];
if (originalRequest) {
[task setValue:originalRequest forKey:#"originalRequest"];
}
}
if (task.currentRequest == nil) {
NSData *currentReqData = resumeDic[kResumeCurrentRequest];
NSURLRequest *currentRequest = [NSKeyedUnarchiver unarchiveObjectWithData:currentReqData];
if (currentRequest) {
[task setValue:currentRequest forKey:#"currentRequest"];
}
}
}
return task;
}
#end

EXC_BAD_ACCESS. Completion block

ANSWER
I heve a mistake when i call the fetchDailyForecast it should looks like
[self.fetchController fetchDailyForecast:self.currentLocation.coordinate completionBlock:^(DailyModel *newModel) {
_dailyCondition = newModel;
NSLog(#"newModel = %#", _dailyCondition);
}];
END
I try to use bloc's and wait but have a bad_access exaction. Firstly i call method from MangeDataController.m
[self.fetchController
fetchDailyForecast:self.currentLocation.coordinate
completionBlock:(void(^)(DailyModel *))_dailyCondition];
where the dailyCondition is instance of DailyModel.
Secondly here is fetchDailyForecast method realization in FetchController.m.
-(void)fetchDailyForecast:(CLLocationCoordinate2D)coordinate completionBlock:(void(^)(DailyModel *))completionBlock {
NSString *urlString = [NSString stringWithFormat:#"http://api.openweathermap.org/data/2.5/forecast/daily?lat=%f&lon=%f&units=imperial&cnt=7%#", coordinate.latitude, coordinate.longitude, _key];
urlString = [urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]];
[self fetchJSONfromURL:urlString completionBlock:^(NSDictionary *weatherData) {
completionBlock([[DailyModel alloc] dataFromDictionaryDaily:weatherData]); --- (here the bad access);
}];
}
and thirdly in fatchDailyForecast i call fetchJSONfromURL here is realization:
-(void)fetchJSONfromURL:(NSString *)urlString completionBlock:(void (^)(NSDictionary *))completionBlock {
NSLog(#"url = %#", urlString);
[_manager GET:urlString parameters:nil progress:nil success:^(NSURLSessionTask *task, id responceObject) {
if (responceObject) {
completionBlock(responceObject);
} else {
NSLog(#"responce object error");
}
}
failure:^(NSURLSessionTask *operation, NSError *error){
NSLog(#"error %#", error);
}];
}
P.S. I think my mistake is that i try to pass day _dailyCondition but should pass something different. Thanks for any help!
**EDIT ------- DailyModel.m **
#import "DailyModel.h"
#implementation DailyModel
-(id)dataFromDictionaryDaily:(NSDictionary *)dic {
NSArray *arrayWithDictionarys = [dic objectForKey:#"list"];
NSDictionary *dictionaryWithWeekDays = [self indexKeyedDictionaryFromArray:arrayWithDictionarys];
// NSLog(#"dictionary with dayweeks = %#", dictionaryWithWeekDays);
_arrayWithTemp = [NSMutableArray new];
_arrayWithDate = [NSMutableArray new];
_arrayWithIcon = [NSMutableArray new];
for (int i = 0; i <= 6; i++) {
NSNumber* key = [NSNumber numberWithInt:i];
[self addValuesToArrayWithTemp:dictionaryWithWeekDays key:key index:i];
[self addValuesToArrayWithDate:dictionaryWithWeekDays key:key index:i];
[self addValueToArrayWithImage:dictionaryWithWeekDays key:key index:i];
}
return self;
}
- (NSDictionary *) indexKeyedDictionaryFromArray:(NSArray *)array
{
NSMutableDictionary *mutableDictionary = [[NSMutableDictionary alloc] init];
[array enumerateObjectsUsingBlock:
^(id obj, NSUInteger idx, BOOL *stop){
NSNumber *index = [NSNumber numberWithInteger:idx];
[mutableDictionary setObject:obj forKey:index];
}];
NSDictionary *result = [NSDictionary.alloc initWithDictionary:mutableDictionary];
return result;
}
-(void) addValuesToArrayWithTemp:(NSDictionary *)inputDic key:(NSNumber *)key index:(int)ind {
NSDictionary *currentDic = [inputDic objectForKey:key];
NSDictionary *temp = [currentDic objectForKey:#"temp"];
_tempLow = [temp objectForKey:#"min"];
_tempHigh = [temp objectForKey:#"max"];
_tempLow = [self convertToCelsius:_tempLow];
_tempHigh = [self convertToCelsius:_tempHigh];
NSString *tempString = [NSString stringWithFormat:#"%.02f / %.02f", _tempLow.floatValue , _tempHigh.floatValue];
[_arrayWithTemp insertObject:tempString atIndex:ind];
}
-(NSNumber *)convertToCelsius:(NSNumber *)far {
double f = (far.doubleValue - 32) / 1.8;
NSNumber *celsius = [NSNumber numberWithDouble:f];
return celsius;
}
-(void) addValuesToArrayWithDate:(NSDictionary *)inputDic key:(NSNumber *)key index:(int)ind {
NSDictionary *currenDic = [inputDic objectForKey:key];
NSNumber *tempNumber = [currenDic objectForKey:#"dt"];
double unixTimeStamp = tempNumber.doubleValue;
NSString* weekDay = [self convertToWeekDay:unixTimeStamp];
[_arrayWithDate insertObject:weekDay atIndex:ind];
}
-(NSString *)convertToWeekDay:(double) unixtime {
NSTimeInterval _interval = unixtime;
NSDate *date = [NSDate dateWithTimeIntervalSince1970:_interval];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setLocale:[NSLocale currentLocale]];
[formatter setDateFormat:#"EEEE"];
NSString *dateString = [formatter stringFromDate:date];
return dateString;
}
-(void) addValueToArrayWithImage:(NSDictionary *)inputDic key:(NSNumber *)key index:(int)ind {
NSDictionary *currenDic = [inputDic objectForKey:key];
NSArray *weatherArray = [currenDic objectForKey:#"weather"];
NSDictionary *weather = weatherArray[0];
_icon = [weather objectForKey:#"icon"];
_icon = [self currentImageString:_icon];
[_arrayWithIcon insertObject:_icon atIndex:ind];
}
-(NSString *)currentImageString:(NSString *)ico {
NSDictionary *dic = #{
#"01d" : #"weather-clear",
#"02d" : #"weather-few",
#"03d" : #"weather-few",
#"04d" : #"weather-broken",
#"09d" : #"weather-shower",
#"10d" : #"weather-rain",
#"11d" : #"weather-tstorm",
#"13d" : #"weather-snow",
#"50d" : #"weather-mist",
#"01n" : #"weather-moon",
#"02n" : #"weather-few-night",
#"03n" : #"weather-few-night",
#"04n" : #"weather-broken",
#"09n" : #"weather-shower",
#"10n" : #"weather-rain-night",
#"11n" : #"weather-tstorm",
#"13n" : #"weather-snow",
#"50n" : #"weather-mist",
};
ico = [dic objectForKey:ico];
return ico;
}
#end
EDIT 2
I add the condition into (void)fetchDailyForecast:(CLLocationCoordinate2D)coordinate completionBlock:(void(^)(DailyModel *))completionBlock and now app don't crash but it's still not work and nslog say #"nothing"
[self fetchJSONfromURL:urlString completionBlock:^(NSDictionary
*weatherData) { if (completionBlock) { completionBlock(model = [[DailyModel alloc] dataFromDictionaryDaily:weatherData]); } else {
NSLog(#"nothing"); } }];

Need to share songs with different devices using multipeer connectivity.

Requirement:
We are able to make connection from host device to multiple slave devices. For example, if device A initiate connection to device B and C, the contributor devices can accept peer connection and connected to device A. Here A is master device and B and C are contributors device. Now if B share their songs to A, A can play songs and see songs information. In meantime C will be idle but should connected. When B will finish to play songs, then C can also able to share their songs to device A.
Here are the problem that we have faced to achieve above tasks:
1. As soon as B started sharing songs to A, C got crashed. But A still able to play song shared by B.
array = [[NSMutableArray alloc] initWithObjects:[self.session connectedPeers], nil];
[_session sendData:[NSKeyedArchiver archivedDataWithRootObject:[info mutableCopy]] toPeers:[array objectAtIndex:0] withMode:MCSessionSendDataUnreliable error: &error];
NSLog(#"localizedDescription %#",error);
To overcome from this problem directly pass array without index here toPeers:array. It is working and we are able to share and play songs with device A, but song information not receive to device A.
Here are full code that we are using:
contributor Controller :
- (void)mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection
{
[self dismissViewControllerAnimated:YES completion:nil];
someMutableArray = [mediaItemCollection items];
counter = 0;
if(someMutableArray.count>1){
BOOL isselectsongone=YES;
[[NSUserDefaults standardUserDefaults] setBool:isselectsongone forKey:#"isselectsongone"];
}
[self showSpinner];
[self someSelector:nil];
}
- (void)someSelector:(NSNotification *)notification {
if(notification && someMutableArray.count>1 && counter <someMutableArray.count-1){
NSDate *start = [NSDate date];
counter=counter+1;
[self.outputStreamer stop];
self.outputStreamer = nil;
self.outputStream = nil;
}
song=[someMutableArray objectAtIndex:counter];
NSMutableDictionary *info = [NSMutableDictionary dictionary];
info=[[NSMutableDictionary alloc] init];
info[#"title"] = [song valueForProperty:MPMediaItemPropertyTitle] ? [song valueForProperty:MPMediaItemPropertyTitle] : #"";
info[#"artist"] = [song valueForProperty:MPMediaItemPropertyArtist] ? [song valueForProperty:MPMediaItemPropertyArtist] : #"";
//NSNumber *duration=[song valueForProperty:MPMediaItemPropertyPlaybackDuration];
int fullminutes = floor([timeinterval floatValue] / 60); // fullminutes is an int
int fullseconds = trunc([duration floatValue] - fullminutes * 60); // fullseconds is an int
[NSTimer scheduledTimerWithTimeInterval:[duration doubleValue]target:self selector:#selector(getdata) userInfo:nil repeats:YES];
}
-(void)getdata {
NSMutableDictionary *info = [NSMutableDictionary dictionary];
info=[[NSMutableDictionary alloc] init];
info[#"title"] = [song valueForProperty:MPMediaItemPropertyTitle] ? [song valueForProperty:MPMediaItemPropertyTitle] : #"";
info[#"artist"] = [song valueForProperty:MPMediaItemPropertyArtist] ? [song valueForProperty:MPMediaItemPropertyArtist] : #"";
NSNumber *duration=[song valueForProperty:MPMediaItemPropertyPlaybackDuration];
int fullminutes = floor([duration floatValue] / 60); // fullminutes is an int
int fullseconds = trunc([duration floatValue] - fullminutes * 60); // fullseconds is an int
info[#"duration"] = [NSString stringWithFormat:#"%d:%d", fullminutes, fullseconds];
MPMediaItemArtwork *artwork = [song valueForProperty:MPMediaItemPropertyArtwork];
UIImage *image = [artwork imageWithSize:CGSizeMake(150, 150)];
NSData * data = UIImageJPEGRepresentation(image, 0.0);
image = [UIImage imageWithData:data];
array = [[NSMutableArray alloc] initWithObjects:[self.session connectedPeers], nil];
MCPeerID* peerID11 = self.session.myPeerID;
NSMutableArray *arr=[[NSMutableArray alloc] initWithObjects:peerID11, nil];
NSLog(#"%#",arr);
if (image)
self.songArtWorkImageView.image = image;
else
self.songArtWorkImageView.image = nil;
self.songTitleLbl.text = [NSString stringWithFormat:#"%# \n[Artist : %#]", info[#"title"], info[#"artist"]];
NSError *error;
[_session sendData:[NSKeyedArchiver archivedDataWithRootObject:[info mutableCopy]] toPeers:[array objectAtIndex:0] withMode:MCSessionSendDataUnreliable error: &error];
NSLog(#"localizedDescription %#",error);
#try {
if(_session && _session.connectedPeers && [_session.connectedPeers count] > 0) {
NSLog(#"%#",[song valueForProperty:MPMediaItemPropertyAssetURL]);
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:[song valueForProperty:MPMediaItemPropertyAssetURL] options:nil];
[self convertAsset: asset complition:^(BOOL Success, NSString *filePath) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
if(Success) {
if(image) {
[self saveImage: image withComplition:^(BOOL status, NSString *imageName, NSURL *imageURL) {
if(status) {
#try {
[_session sendResourceAtURL:imageURL withName:imageName toPeer:[_session.connectedPeers objectAtIndex:0]withCompletionHandler:^(NSError *error) {
if (error) {
NSLog(#"Failed to send picture to %#", error.localizedDescription);
return;
}
//Clean up the temp file
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtURL:imageURL error:nil];
}];
}
#catch (NSException *exception) {
}
}
}];
}
#try {
[self hideSpinner];
if(!self.outputStream) {
NSArray * connnectedPeers = [_session connectedPeers];
if([connnectedPeers count] != 0) {
[self outputStreamForPeer:[_session.connectedPeers objectAtIndex:0]];
}
}
}
#catch (NSException *exception) {
}
if(self.outputStream) {
self.outputStreamer = [[TDAudioOutputStreamer alloc] initWithOutputStream:self.outputStream];
//
[self.outputStreamer initStream:filePath];
NSLog(#"%#",filePath);
if(self.outputStreamer) {
[self.outputStreamer start];
}
else{
NSLog(#"Error: output streamer not found");
}
}
else{
//self.outputStream=[[NSOutputStream alloc] init];
self.outputStreamer = [[TDAudioOutputStreamer alloc] initWithOutputStream:self.outputStream];
[self.outputStreamer initStream:filePath];
NSLog(#"%#",filePath);
if(self.outputStreamer) {
[self.outputStreamer start];
}
}
}
else {
[UIView showMessageWithTitle:#"Error!" message:#"Error occured!" showInterval:1.5];
}
});
}];
// }
}
}
#catch (NSException *exception) {
NSLog(#"Expection: %#", [exception debugDescription]);
}
//}
}
HostViewcontroller :
- (void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID
{
NSLog(#"%#",peerID);
NSLog(#"sessions%#",session);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
#try {
// NSData *myData = [NSKeyedArchiver archivedDataWithRootObject:data];
info = [NSKeyedUnarchiver unarchiveObjectWithData:data];
self.songTitleLbl.text = [NSString stringWithFormat:#"%# \n[Duration: %#] [Artist : %#] ", info[#"title"], info[#"duration"], info[#"artist"]];
NSLog(#"eeret%#",self.songTitleLbl.text);
self.songArtWorkImageView.image = nil;
[self showSpinner];
}
#catch (NSException *exception) {
self.songTitleLbl.text = #"Some error occured...\nPlease try again";
self.songArtWorkImageView.image = nil;
}
});
}
Please let us know if I have missed anything here or the better way to achieve the above requirement. Any help really appreciated.

Parsing http-multipart response

I need receive http-multipart responses from web service. The multipart response with pairs of JSON and image.
How to handle response in two parts (NSDictionary for JSON and NSData for image)?
Thanks in advance!
[UPDATE]
I wrote a category for NSData. Code below:
NSData+MultipartResponses.h
#import <Foundation/Foundation.h>
#interface NSData (MultipartResponses)
- (NSArray *)multipartArray;
- (NSDictionary *)multipartDictionary;
#end
NSData+MultipartResponses.m
#import "NSData+MultipartResponses.h"
#implementation NSData (MultipartResponses)
static NSMutableDictionary *parseHeaders(const char *headers)
{
NSMutableDictionary *dict=[NSMutableDictionary dictionary];
int max=strlen(headers);
int start=0;
int cursor=0;
while(cursor<max)
{
while((headers[cursor]!=':')&&(headers[cursor]!='='))
{
cursor++;
}
NSString *key=[[NSString alloc] initWithBytes:(headers+start) length:(cursor-start) encoding:NSASCIIStringEncoding];
cursor++;
while(headers[cursor]==' ')
{
cursor++;
}
start=cursor;
while(headers[cursor]&&(headers[cursor]!=';')&&((headers[cursor]!=13)||(headers[cursor+1]!=10)))
{
cursor++;
}
NSString *value;
if((headers[start]=='"')&&(headers[cursor-1]=='"'))
{
value=[[NSString alloc] initWithBytes:(headers+start+1) length:(cursor-start-2) encoding:NSASCIIStringEncoding];
}
else
{
value=[[NSString alloc] initWithBytes:(headers+start) length:(cursor-start) encoding:NSASCIIStringEncoding];
}
[dict setObject:value forKey:key];
if(headers[cursor]==';')
{
cursor++;
}
else
{
cursor+=2;
}
while(headers[cursor]==' ')
{
cursor++;
}
start=cursor;
}
return dict;
}
- (NSDictionary *)multipartDictionaryWithBoundary:(NSString *)boundary
{
NSMutableDictionary *dict=[NSMutableDictionary dictionary];
const char *bytes=(const char *)[self bytes];
const char *pattern=[boundary cStringUsingEncoding:NSUTF8StringEncoding];
int cursor=0;
int start=0;
int max=[self length];
int keyNo=0;
while(cursor<max)
{
if(bytes[cursor]==pattern[0])
{
int i;
int patternLength=strlen(pattern);
BOOL match=YES;
for(i=0; i<patternLength; i++)
{
if(bytes[cursor+i]!=pattern[i])
{
match=NO;
break;
}
}
if(match)
{
if(start!=0)
{
int startOfHeaders=start+2;
int cursor2=startOfHeaders;
while((bytes[cursor2]!=(char)0x0d)||(bytes[cursor2+1]!=(char)0x0a)||(bytes[cursor2+2]!=(char)0x0d)||(bytes[cursor2+3]!=(char)0x0a))
{
cursor2++;
if(cursor2+4==max)
{
break;
}
}
if(cursor2+4==max)
{
break;
}
else
{
int lengthOfHeaders=cursor2-startOfHeaders;
char *headers=(char *)malloc((lengthOfHeaders+1)*sizeof(char));
strncpy(headers, bytes+startOfHeaders, lengthOfHeaders);
headers[lengthOfHeaders]=0;
NSMutableDictionary *item=parseHeaders(headers);
int startOfData=cursor2+4;
int lengthOfData=cursor-startOfData-2;
if(([item valueForKey:#"Content-Type"]==nil)&&([item valueForKey:#"filename"]==nil))
{
NSString *string=[[NSString alloc] initWithBytes:(bytes+startOfData) length:lengthOfData encoding:NSUTF8StringEncoding];
keyNo++;
[dict setObject:string forKey:[NSString stringWithFormat:#"%d", keyNo]];
}
else
{
NSData *data=[NSData dataWithBytes:(bytes+startOfData) length:lengthOfData];
[item setObject:data forKey:#"data"];
keyNo++;
[dict setObject:item forKey:[NSString stringWithFormat:#"%d", keyNo]];
}
}
}
cursor=cursor+patternLength-1;
start=cursor+1;
}
}
cursor++;
}
return dict;
}
- (NSArray *)multipartArray
{
NSDictionary *dict=[self multipartDictionary];
NSArray *keys=[[dict allKeys] sortedArrayUsingSelector:#selector(localizedStandardCompare:)];
NSMutableArray *array=[NSMutableArray array];
for(NSString *key in keys)
{
[array addObject:dict[key]];
}
return array;
}
- (NSDictionary *)multipartDictionary
{
const char *bytes=(const char *)[self bytes];
int cursor=0;
int max=[self length];
while(cursor<max)
{
if(bytes[cursor]==0x0d)
{
break;
}
else
{
cursor++;
}
}
char *pattern=(char *)malloc((cursor+1)*sizeof(char));
strncpy(pattern, bytes, cursor);
pattern[cursor]=0x00;
NSString *boundary=[[NSString alloc] initWithCString:pattern encoding:NSUTF8StringEncoding];
free(pattern);
return [self multipartDictionaryWithBoundary:boundary];
}
#end
For me, your code didn't work. Instead, I rewrote that code in pure objective-c:
Take care that (my) boundaries in this code always have additional -- in front of the next boundary and a final boundary - those are stripped.
An NSArray is returned with a NSDictionary for each part, containing the keys "headers" as NSDictionary and "body" as NSData
- (NSArray *)multipartArrayWithBoundary:(NSString *)boundary
{
NSString *data = [[[NSString alloc] initWithData:self encoding:NSUTF8StringEncoding] autorelease];
NSArray *multiparts = [data componentsSeparatedByString:[#"--" stringByAppendingString:boundary]]; // remove boundaries
multiparts = [multiparts subarrayWithRange:NSMakeRange(1, [multiparts count]-2)]; // continued removing of boundaries
NSMutableArray *toReturn = [NSMutableArray arrayWithCapacity:2];
for(NSString *part in multiparts)
{
part = [part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSArray *separated = [part componentsSeparatedByString:#"\n\n"];
NSMutableDictionary *headers = [NSMutableDictionary dictionaryWithCapacity:3];
for(NSString *headerLine in [[separated objectAtIndex:0] componentsSeparatedByString:#"\n"])
{
NSArray *keyVal = [headerLine componentsSeparatedByString:#":"];
[headers setObject:[[keyVal objectAtIndex:1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] forKey:[[keyVal objectAtIndex:0] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
}
[toReturn addObject:[NSDictionary dictionaryWithObjectsAndKeys:[[separated objectAtIndex:1] dataUsingEncoding:NSUTF8StringEncoding], #"body", headers, #"headers", nil]];
}
return toReturn;
}

Resources