This question is about reducing the size of files saved to disk in an infinite world 2D mining game. I finally added code to my game to save and load distant chunks instead of keeping all of them in memory. Up to this point, I had only used the default NSCoding Apple library to save and load files from disk. In my initial testing, each `Chunk` consisted of 900 Blocks, and when saved to disk the file was `160kb`. This would mean that worlds with just a few million blocks could be bigger than a gigabyte. I tried a number of different methods to decrease the size of the files. I found some code on Stack Overflow to implement some gzip compression, and that reduced the size down to about `74kb`. This was good, but not nearly good enough. The next thing I tried was to reduce the amount of information stored in each block. I removed all the `NSStrings`, and derived those from other values instead of saving them. I converted all the `CGPoints` to `ints`, and saved those instead. This got the file size down to about `52kb`. This was almost small enough to work, but I knew I could do better. The `Blocks` themselves only had a very small amount of information stored in them. I decided to manually compose the bytes needed to initialize a block, and then manually parse those bytes when the file was loaded. I succeeded in writing some code to do this, and it got the file size down to `4kb`. Each `Block` is `28 bytes`, so this is much closer to a reasonable number. I should note that when I tried without the gzip compression, the file size rose to `25kb`. I decided to use the Apple libraries to do this, so I am reading and writing to `NSData`. I believe there are better ways to achieve this, and I would love to hear about them. **DWBlock.m** #pragma mark - Custom encode and decode -(instancetype) initWithData:(NSData *)data { self = [super init]; if (self) { int theBytesRecovered[7]; [data getBytes:theBytesRecovered]; _type = (int)theBytesRecovered[0]; _damageLevel = (int)theBytesRecovered[1]; _chunkSize = (int)theBytesRecovered[2]; int posX = (int)theBytesRecovered[3]; int posY = (int)theBytesRecovered[4]; int chunkPosX = (int)theBytesRecovered[5]; int chunkPosY = (int)theBytesRecovered[6]; _position = CGPointMake(posX, posY); _chunkPosition = CGPointMake(chunkPosX, chunkPosY); _blockName = NSStringFromCGPoint(_position); _chunkName = NSStringFromCGPoint(_chunkPosition); _calculatedPosition = CGPointMake((_chunkPosition.x * _chunkSize * kBlockSpriteWidth) + (_position.x * kBlockSpriteWidth), (_chunkPosition.y * _chunkSize * kBlockSpriteWidth) + (_position.y * kBlockSpriteWidth)); } return self; } -(NSData *) customEncode { int type = _type; int damageLevel = _damageLevel; int chunkSize = _chunkSize; int posX = _position.x; int posY = _position.y; int chunkPosX = _chunkPosition.x; int chunkPosY = _chunkPosition.y; int theBytes[] = {type, damageLevel, chunkSize, posX, posY, chunkPosX, chunkPosY}; return [NSData dataWithBytes:&theBytes length:sizeof(theBytes)]; } **DWChunk.m** #pragma mark - Custom encode and decode -(instancetype) initWithData:(NSData *)data { self = [super init]; if (self) { _memConstants = [[DWMemoryConstants alloc]init]; //Iterate through the chunk data //Grab the correct number of bytes for each block //Compose a new NSData out of those bytes, and add them to an array NSMutableArray *blockDatas = [[NSMutableArray alloc]init]; uint8_t *bytePtr = (uint8_t *)[data bytes]; NSInteger totalData = [data length] / sizeof(uint8_t); for (int i = 0; i < totalData; i+=kChunkSizeInBytes) { NSMutableData *blockData = [[NSMutableData alloc]init]; for (int j = 0; j < kChunkSizeInBytes; j++) { [blockData appendBytes:&bytePtr[i + j] length:1]; } [blockDatas addObject:blockData]; } //Create the Block objects _blocks = [[NSMutableDictionary alloc]init]; BOOL firstRun = YES; for (NSData *data in blockDatas) { DWBlock *block = [[DWBlock alloc]initWithData:data]; //Get some data about this Chunk from the first block if (firstRun) { _position = CGPointFromString(block.chunkName); firstRun = NO; } [_blocks setObject:block forKey:[NSValue valueWithCGPoint:CGPointFromString(block.blockName)]]; } _chunkName = NSStringFromCGPoint(_position); _bounds = CGRectMake(_position.x * _memConstants.chunkSize * kBlockSpriteWidth, _position.y * _memConstants.chunkSize * kBlockSpriteWidth, _memConstants.chunkSize * kBlockSpriteWidth, _memConstants.chunkSize * kBlockSpriteWidth); } return self; } -(NSData *) customEncode { NSMutableData *data = [[NSMutableData alloc]init]; for (DWBlock *block in _blocks.allValues) { //It is very important to get the bytes of the NSData and not just the pointer [data appendBytes:[[block customEncode]bytes] length:[[block customEncode]length]]; } return data; } Chunks are saved like this: for (DWChunk *chunk in chunks) { NSString *path = [[self applicationDocumentsPath] stringByAppendingPathComponent:[NSString stringWithFormat:@"worldtest%@", NSStringFromCGPoint(chunk.position)]]; NSData *data = [chunk customEncode]; data = [DWDataZipper gzipDeflate:data]; [data writeToFile:path atomically:YES]; } And loaded like this: NSString *path = [[self applicationDocumentsPath] stringByAppendingPathComponent:[NSString stringWithFormat:@"worldtest%@", NSStringFromCGPoint(position.CGPointValue)]]; NSData *chunkData = [NSData dataWithContentsOfFile:path]; if (chunkData) { chunkData = [DWDataZipper gzipInflate:chunkData]; DWChunk *chunk = [[DWChunk alloc]initWithData:chunkData]; }