How to detect total available/free disk space on the iPhone/iPad device?
I assume that clicking that "done" button would invoke cancelling the picker; in which case you can try did you try implementing the delegate method:
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[self dismissViewControllerAnimated:YES completion:^(void) {
NSLog(#"Cancelled pick");
};
}
Also it looks like you're calling release -- any reason you're not using ARC?
If I had to guess, I'd say perhaps the alert dismiss is in fact calling something like
[imagePicker.delegate imagePickerControllerDidCancel:imagePicker];
but it's already released so you're seeing this "lock-up" issue. Maybe step through in the debugger and make sure those objects still exist.
Edit:
While it doesn't solve your original issue, you could do a check for available space prior to launching the image picker, possibly with something like this so post suggests:
- (uint64_t)freeDiskspace
{
uint64_t totalSpace = 0;
uint64_t totalFreeSpace = 0;
__autoreleasing NSError *error = nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSDictionary *dictionary = [[NSFileManager defaultManager] attributesOfFileSystemForPath:[paths lastObject] error: &error];
if (dictionary) {
NSNumber *fileSystemSizeInBytes = [dictionary objectForKey: NSFileSystemSize];
NSNumber *freeFileSystemSizeInBytes = [dictionary objectForKey:NSFileSystemFreeSize];
totalSpace = [fileSystemSizeInBytes unsignedLongLongValue];
totalFreeSpace = [freeFileSystemSizeInBytes unsignedLongLongValue];
NSLog(#"Memory Capacity of %llu MiB with %llu MiB Free memory available.", ((totalSpace/1024ll)/1024ll), ((totalFreeSpace/1024ll)/1024ll));
} else {
NSLog(#"Error Obtaining System Memory Info: Domain = %#, Code = %d", [error domain], [error code]);
}
return totalFreeSpace;
}
Related
I am new to iOS development. I am using MailCore to get emails in my application. My xcode version is 4.6.3 and iOS:6.1 and 5.1. Can anyone help me to sort out my problem. I get the mails from my server with attachment count and description, but i didn't find a way to download this file or how can i fetch the attached file.
Please help me.
Thanks!
it's very simple:
first of all - get messages uid's
MCOIndexSet *uidSet = [MCOIndexSet indexSetWithRange:MCORangeMake(1,UINT64_MAX)];//for all msgs
MCOIMAPSession *session = <new or reuse IMAP session, i reuse this>
MCOIMAPFetchMessagesOperation *fetchOp =
[session fetchMessagesByUIDOperationWithFolder:#"INBOX"
requestKind:MCOIMAPMessagesRequestKindFullHeaders
uids:uidSet];
[fetchOp start:^(NSError *err, NSArray *messagesList, MCOIndexSet *vanished) {
if (!err) {
NSLog(#"Receive %i messages",(int)[messagesList count]);
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"header.date" ascending:NO];
//upd UI
[[NSNotificationCenter defaultCenter] postNotificationName:kNotif_postCollector_ReceiveMessages object:nil];
//store msgs
[[DataSingleton sharedSingleton] updateMessageListWithMessages:[messagesList sortedArrayUsingDescriptors:#[sort]]];
}else{
//oh no
}
}];
second - get structure of messages (NOT all, just needed - array messagesList)
MCOIndexSet *uidSet = [MCOIndexSet indexSet];
for (Message *message in messagesList)// i use this class to store msgs. mcUid = MailCore Uid
[uidSet addIndex:[[message mcUid] integerValue]];
MCOIMAPSession *session = <new or reuse IMAP session, i reuse this>
MCOIMAPFetchMessagesOperation *fetchOp =
[session fetchMessagesByUIDOperationWithFolder:#"INBOX"//or another folder
requestKind:MCOIMAPMessagesRequestKindStructure
uids:uidSet];
[fetchOp start:^(NSError *err, NSArray *messagesList, MCOIndexSet *vanished) {
if (!err) {
NSLog(#"Receive %i messages with structures",(int)[messagesList count]);
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"header.date" ascending:NO];
//store attachment precence
[[DataSingleton sharedSingleton] updateAttachmentsPresenceForMessages:[messagesList sortedArrayUsingDescriptors:#[sort]]
forAccount:acc];
/* You get array of this objects:
MCOIMAPPart : MCOAbstractPart
//A part identifier looks like 1.2.1
#property (nonatomic, copy) NSString * partID;
//The size of the single part in bytes
#property (nonatomic, nonatomic) unsigned int size;
//It's the encoding of the single part
#property (nonatomic, nonatomic) MCOEncoding encoding;
*/
}
}];
and step number 3 and final step:
MCOIMAPSession *session = <new or reuse IMAP session, i reuse this>
int uid = [[message mcUid] intValue];
NSString *partID = [attachment mcPartID];//we stored it on step 2
MCOIMAPFetchContentOperation * op = [session fetchMessageAttachmentByUIDOperationWithFolder:#"INBOX"
uid:uid
partID:partID
encoding:(MCOEncoding)[[attachment mcEncoding] integerValue]];
NSLog(#"download att %i part %#",uid,partID);
[op start:^(NSError * error, NSData * messageData) {
if (error) {
}else{
TRACE(#"receive att %i part %#",uid,partID);
//save attachment fo local disc
[[DataSingleton sharedSingleton] updateDownloadedAttachment:attachment
withData:messageData];
}
}];
more info on GitHub and official page libmailcore.com
MCOMessageParser * msg = [MCOMessageParser messageParserWithData:data];
if ([_message.attachments count] > 0)
{
for (int i=0; i<[_message.attachments count]; ++i)
{
MCOIMAPPart *part = [_message.attachments objectAtIndex:i];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDir = [paths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/%#",
docDir, [part filename]];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
if (NO == fileExists) {
[data writeToFile:filePath atomically:YES];
}
}
}
I am getting available memory from below method:
static void print_free_memory () {
float totalSpace = 0.0f;
NSError *error = nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSDictionary *dictionary = [[NSFileManager defaultManager] attributesOfFileSystemForPath:[paths lastObject] error: &error];
if (dictionary) {
NSNumber *fileSystemSizeInBytes = [dictionary objectForKey: NSFileSystemSize];
totalSpace = [fileSystemSizeInBytes floatValue];
NSLog(#"Total memory:%f",totalSpace);
freeMemAvailable = [[dictionary objectForKey:NSFileSystemFreeSize] floatValue];
NSLog(#"Free Memory:%f",freeMemAvailable);
} else {
NSLog(#"Error Obtaining File System Info: Domain = %#, Code = %#", [error domain], [error code]);
}
}
Above method returns = 9139.650391 mb ~ 9.14 GB
AND on Device memory displayed on the Device = 8.4 GB
Approximately, 700 MB difference. Why is it so? Any suggestions.
The path that I used to view the device memory on device is as follows.
Go to :- Settings -> General -> About -> Available
Your calculations are not precise the function return the size in bytes so assume the returned value was 9139650391 you will need to divide it bu (1024*1024*1024) = 1073741824
so 9139650391/1073741824 equals approximately 8.5 GB and not 9.14 GB
about the 100 MB difference, there is still a difference but not as big as 700MB
I am attempting to cache a web request. Basically I have an app that uses a facebook user's friend list but I don't want to grab it every single time they log in. Maybe refresh once per month. Caching the friend list in a plist in the documents directory seems to make sense for this functionality. I do this as follows:
- (void)writeToDisk {
NSLog(#"writing cache to disk, where cache = %#", cache);
BOOL res = [cache writeToFile:[FriendCache persistentPath] atomically:YES];
NSLog(#"reading cache from disk immediately after writing, res = %d", res);
NSMutableArray *temp = [[NSMutableArray alloc] initWithContentsOfFile:[FriendCache persistentPath]];
NSLog(#"cache read in = %#", temp);
}
+ (NSString *)persistentPath {
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:#"FriendCache.plist"];
}
These are members of a FriendCache singleton I am using which basically wraps an NSMutableArray. I have verified that the peristentPath method is returning a valid path. As you you can see in the writeToDisk method, I verify there is data in the cache and then I print the result of the write and check if any data could be read back in. There is never data read back in, because the result of the file write is 0.
The output of the cache print is very long, but here is the abbreviated version:
2010-12-28 13:35:23.006 AppName[51607:207] writing cache to disk, where cache = (
{
birthday = "<null>";
name = "Some Name1";
pic = "http://profile.ak.fbcdn.net/hprofile-ak-snc4/hs1324.snc4/7846385648654.jpg";
"pic_big" = "http://profile.ak.fbcdn.net/hprofile-ak-snc4/hs442.snc4/784365789465746.jpg";
"pic_square" = "http://profile.ak.fbcdn.net/hprofile-ak-snc4/hs1324.snc4/7846357896547.jpg";
sex = female;
status = "<null>";
uid = 892374897165;
},
{
birthday = "<null>";
name = "Some Name2";
pic = "http://profile.ak.fbcdn.net/hprofile-ak-snc4/hs625.ash1/54636536547_s.jpg";
"pic_big" = "http://profile.ak.fbcdn.net/hprofile-ak-snc4/hs170.ash2/65465656365666_n.jpg";
"pic_square" = "http://profile.ak.fbcdn.net/hprofile-ak-snc4/hs625.ash1/654635656547_q.jpg";
sex = female;
status = "<null>";
uid = 7658436;
},
...
One thing I checked out is when using writeToFile, I must make sure the object I am writing has valid plist objects. I did check this and here is how I construct the cache object:
- (void)request:(FBRequest*)request didLoad:(id)result{
NSMutableArray *friendsInfo = [[[NSMutableArray alloc] init] autorelease];
for (NSDictionary *info in result) {
NSString *friend_id = [NSString stringWithString:[[info objectForKey:#"uid"] stringValue]];
NSString *friend_name = nil;
NSString *friend_sex = nil;
NSString *friend_relationship_status = nil;
NSString *friend_current_location = nil;
if ([info objectForKey:#"name"] != [NSNull null]) {
friend_name = [NSString stringWithString:[info objectForKey:#"name"]];
}
if ([info objectForKey:#"relationship_status"] != [NSNull null]) {
friend_relationship_status = [NSString stringWithString:[info objectForKey:#"relationship_status"]];
}
if ([info objectForKey:#"sex"] != [NSNull null]) {
friend_sex = [NSString stringWithString:[info objectForKey:#"sex"]];
}
if ([info objectForKey:#"current_location"] != [NSNull null]) {
friend_current_location = [[info objectForKey:#"current_location"] objectForKey:#"name"];
}
NSString *friend_pic_square = [info objectForKey:#"pic_square"];
NSString *friend_status = [info objectForKey:#"status"];
NSString *friend_pic = [info objectForKey:#"pic"];
NSString *friend_pic_big = [info objectForKey:#"pic_big"];
NSString *friend_birthday = [info objectForKey:#"birthday"];
NSDictionary *friend_info = [NSDictionary dictionaryWithObjectsAndKeys:
friend_id,#"uid",
friend_name, #"name",
friend_pic_square, #"pic_square",
friend_status, #"status",
friend_sex, #"sex",
friend_pic, #"pic",
friend_pic_big, #"pic_big",
friend_birthday, #"birthday",
friend_relationship_status, #"relationship_status",
friend_current_location, #"current_location",
nil];
// If the friend qualifies as a single of your gender, add to the friend cache
if ( [AppHelpers friendQualifies:friend_info] == YES) {
[[FriendCache sharedInstance] push:friend_info];
}
}
[[FriendCache sharedInstance] writeToDisk];
}
My push method just wraps the NSMutableArray push:
- (void)push:(id)o {
[cache addObject:o];
}
Can you think of any reason why the write would fail?
Thanks!
So as we already pointed out, it's because of the usage of the NSNull objects.
The best way to avoid this is to create an object Friend, with all of the needed properties. Then you can easily set nil values, something not possible with NSDictionary objects (well, you'd have to remove the key, which is not very good practice).
Then, by implementing the NSCoding protocol, you can easily archive (serialize) your custom object.
This is a much better way of handling your data, and it will become MUCH easier in the future. You'll be able to call messages on the Friend objects, something not possible with NSDictionary.
Use NSError-aware API for NSPropertyListSerialization to get the data and the NSData NSError aware write API so you get a meaningful error helping you understand what your problem might be.
I have written a class to help save and load data for the sake of persistence for my iPhone application but I have a problem with some NSUIntegers that I'm passing across.
Basically, I have the code to use pointers, but eventually it has to start out being an actual value right? So I get this error
warning: passing argument 1 of 'getSaveWithCampaign:andLevel:' makes pointer from integer without a cast
My code is laid out like so.
(Persistence is the name of the class)
NSDictionary *saveData = [Persistence getSaveWithCampaign:currentCampaign andLevel:[indexPath row]];
Here's Persistence.m
#import "Persistence.h"
#implementation Persistence
+ (NSString *)dataFilePath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:kSaveFilename];
}
+ (NSDictionary *)getSaveWithCampaign:(NSUInteger *)campaign andLevel:(NSUInteger *)level
{
NSString *filePath = [self dataFilePath];
if([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
NSDictionary *saveData = [[NSDictionary alloc] initWithContentsOfFile:filePath];
NSString *campaignAndLevelKey = [self makeCampaign:campaign andLevelKey:level];
NSDictionary *campaignAndLevelData = [saveData objectForKey:campaignAndLevelKey];
[saveData release];
return campaignAndLevelData;
}
else
{
return nil;
}
}
+ (NSString *)makeCampaign:(NSUInteger *)campaign andLevelKey:(NSUInteger *)level
{
return [[NSString stringWithFormat:#"%d - ", campaign+1] stringByAppendingString:[NSString stringWithFormat:#"%d", level+1]];
}
#end
You should get rid of the * in ( NSUInteger *) in the getSaveWithCampaign method.
If you read the error message closely, it states that you are making a pointer (*) from an integer without a cast.
Your getSaveWithCampaign method should now have the following signature:
+ (NSDictionary *)getSaveWithCampaign:(NSUInteger)campaign andLevel:(NSUInteger)level
If, on the other hand, for some reason you do want to use pointers, you can pass in the NSUInteger prefixed with an ampersand (&) to pass in the address of the NSUInteger in memory.
I'm saving some data using a series of NSDictionaries, stored in an NSMutableArray and archived using NSKeyedArchiver.
I'm basically trying to save the states of several instances the class 'Brick', so I've implemented a getBlueprint method like this (slimmed down version)
-(id)getBlueprint
{
// NOTE: brickColor is a string
NSDictionary *blueprint = [NSDictionary dictionaryWithObjectsAndKeys:
brickColor, #"color",
[NSNumber numberWithInt:rotation], #"rotation",
nil];
return blueprint;
}
And so I have another method that creates a new Brick instance when provided with a blueprint.
-(id)initWithBlueprint:(NSDictionary *)blueprint spriteSheet:(NSString *)ssheet
{
if((self == [super init])){
brickColor = [blueprint objectForKey:#"color"];
[self setColorOffset:brickColor];
while(rotation != [[blueprint objectForKey:#"rotation"] intValue]){
[self setRotation:90];
}
}
return self;
}
Which works when I pass it a 'fresh' blueprint, but not when I read a blueprint from a saved file... sort of. For example, the rotation will work, but changing the color wont. So while I can read the value of brickColor using
NSLog(#"brick color %#", [blueprint objectForKey:#"color"]);
if I try something like
if(brickColor == #"purple"){
colorOffset = CGPointMake(72,36);
NSLog(#"Changed offset for -- %# -- to %#", color, NSStringFromCGPoint(colorOffset));
}
And I know that color is purple, the condition doesn't return true. I thought it might be that somehow NSKeyedUnarchiver changed a string into something else, but the following test returns true.
if([color isKindOfClass:[NSString class]]){
NSLog(#"%# IS A STRING", color);
}else{
NSLog(#"!!!!! COLOR IS A NOT STRING !!!!!");
}
As I said, this isn't a problem if I try to use a freshly created NSDictionary as a blueprint, only when a blueprint is archived and then read back in.
So, as usual, I'm wondering if anyone has any ideas why this might be happening.
incase it's relevant, here's how the data is being stored and recieved.
// Saving
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-(void)buildLevelData{
levelData = [[NSMutableArray alloc] initWithCapacity:100];
for(brickSprite *brick in spriteHolder.children){
[levelData addObject:[brick getBlueprint]];
}
}
-(void)saveLevel
{
[self buildLevelData];
NSData *rawDat = [NSKeyedArchiver archivedDataWithRootObject:levelData];
if([self writeApplicationData:rawDat toFile:saveFileName]){
NSLog(#"Data Saved");
}else{
NSLog(#"ERROR SAVING LEVEL DATA!");
}
[[Director sharedDirector] replaceScene:[MainMenu scene]];
}
- (BOOL)writeApplicationData:(NSData *)data toFile:(NSString *)fileName {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
if (!documentsDirectory) {
NSLog(#"Documents directory not found!");
return NO;
}
NSString *appFile = [saveDir stringByAppendingPathComponent:fileName];
return ([data writeToFile:appFile atomically:YES]);
}
// Loading
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- (void) loadRandomMapFrom:(NSString *)dir
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [paths objectAtIndex:0];
if(!docsDir){
NSLog(#"Cound Not Find Documents Directory When trying To Load Random Map");
return;
}
dir = [docsDir stringByAppendingPathComponent:[NSString stringWithFormat:#"/%#", dir]];
// we'll also set the file name here.
NSArray *existingFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dir error:nil];
// get last file for this test
NSString *filePath = [dir stringByAppendingPathComponent:[existingFiles objectAtIndex:([existingFiles count] - 1)]];
NSMutableArray *levelData = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
[self buildMapWithData:levelData];
}
-(void)buildMapWithData:(NSMutableArray *)lData
{
for(NSDictionary *blueprint in lData){
brickSprite *brick = [[brickSprite alloc] initWithBlueprint:blueprint spriteSheet:#"blocks.png"];
[spriteHolder addChild:brick];
}
}
Sorry about the mess of a question. There's a lot going on that I'm struggling to fully understand myself so it's hard to break it down to the bare minimum.
You should always compare strings with [firstString isEqualToString:secondString], because firstString == secondString only checks for pointer equality, e.g. if both strings are stored at the same location (which they'll never be when comparing dynamically created objects and string constants).