Add:
What you have to do here is to subclass NSObject to represent your package, and then accept NSCoding to serialize it to NSData the way you want. Doing this with a framework does not buy anything, but makes things even more complicated. It is also fragile, since packaging the structure in NSData does not take into account things like endian-ness, etc.
The hard part of the batching process using NSCoding is that you really donโt know what the overhead of the coding process is, therefore, to be as large as possible, but still under the maximum packet size is difficult ...
I imagine this without testing or guarantee, but if you want to participate in this approach, it may be so. Be careful, I did not check if my arbitrary 100 bytes for the overhead were realistic. You will have to play a little with numbers.
Packet.h:
@interface Packet : NSObject <NSCoding> { NSString* fileName; NSInteger fileType; NSUInteger totalPackets; NSUInteger packetIndex; NSData* packetContents; } @property (readonly, copy) NSString* fileName; @property (readonly, assign) NSInteger fileType; @property (readonly, assign) NSUInteger totalPackets; @property (readonly, assign) NSUInteger packetIndex; @property (readonly, retain) NSData* packetContents; + (NSArray*)packetsForFile: (NSString*)name ofType: (NSInteger)type withData: (NSData*)fileContents; @end
Packet.m:
#import "Packet.h" @interface Packet () @property (readwrite, assign) NSUInteger totalPackets; @property (readwrite, retain) NSData* packetContents; @end @implementation Packet - (id)initWithFileName: (NSString*)pFileName ofType: (NSInteger)pFileType index: (NSUInteger)pPacketIndex { if (self = [super init]) { fileName = [pFileName copy]; fileType = pFileType; packetIndex = pPacketIndex; totalPackets = NSUIntegerMax; packetContents = [[NSData alloc] init]; } return self; } - (void)dealloc { [fileName release]; [packetContents release]; [super dealloc]; } @synthesize fileName; @synthesize fileType; @synthesize totalPackets; @synthesize packetIndex; @synthesize packetContents; - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject: self.fileName forKey: @"fileName"]; [aCoder encodeInt64: self.fileType forKey:@"fileType"]; [aCoder encodeInt64: self.totalPackets forKey:@"totalPackets"]; [aCoder encodeInt64: self.packetIndex forKey:@"packetIndex"]; [aCoder encodeObject: self.packetContents forKey:@"totalPackets"]; } - (id)initWithCoder:(NSCoder *)aDecoder { if (self = [super init]) { fileName = [[aDecoder decodeObjectForKey: @"fileName"] copy]; fileType = [aDecoder decodeInt64ForKey:@"fileType"]; totalPackets = [aDecoder decodeInt64ForKey:@"totalPackets"]; packetIndex = [aDecoder decodeInt64ForKey:@"packetIndex"]; packetContents = [[aDecoder decodeObjectForKey:@"totalPackets"] retain]; } return self; } + (NSArray*)packetsForFile: (NSString*)name ofType: (NSInteger)type withData: (NSData*)fileContents { const NSUInteger quanta = 8192; Packet* first = [[[Packet alloc] initWithFileName:name ofType:type index: 0] autorelease]; // Find out how big the NON-packet payload is... NSMutableData* data = [NSMutableData data]; NSKeyedArchiver* coder = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:data] autorelease]; [first encodeWithCoder: coder]; [coder finishEncoding]; const NSUInteger nonPayloadSize = [data length]; NSMutableArray* packets = [NSMutableArray array]; NSUInteger bytesArchived = 0; while (bytesArchived < [fileContents length]) { Packet* nextPacket = [[[Packet alloc] initWithFileName: name ofType: type index: packets.count] autorelease]; NSRange subRange = NSMakeRange(bytesArchived, MIN(quanta - nonPayloadSize - 100, fileContents.length - bytesArchived)); NSData* payload = [fileContents subdataWithRange: subRange]; nextPacket.packetContents = payload; bytesArchived += [payload length]; [packets addObject: nextPacket]; } for (Packet* packet in packets) { packet.totalPackets = packets.count; } return packets; } - (NSData*)dataForSending { NSMutableData* data = [NSMutableData data]; NSKeyedArchiver* coder = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:data] autorelease]; [self encodeWithCoder: coder]; [coder finishEncoding]; return [NSData dataWithData:data]; } + (Packet*)packetObjectFromRxdData:(NSData*)data { NSKeyedUnarchiver* decoder = [[[NSKeyedUnarchiver alloc] initForReadingWithData:data] autorelease]; return [[[Packet alloc] initWithCoder:decoder] autorelease]; } @end
Reassembling the source file from these packages can be done using the same approach as splitting it ... Iterating over packages, copying from a separate NSDatas package load to a large NSMutableData.
In conclusion, I feel compelled to say that when you do something like this, it comes down to introducing a primitive TCP stack, usually you have to stop there and ask if there are any better ways to do this. In other words, if GameKit was the best way to transfer files between devices via Bluetooth, you would expect the API to have a method for this, but instead it has this 8K limit.
I'm not intentionally cryptic - I donโt know what the correct API will be for your situation, but the exercise of preparing this Packet class left me in my head: "There must be a better way."
Hope this helps.