PropertyList

Discussion in 'iOS Programming' started by GhostDZ9, Jun 9, 2011.

  1. GhostDZ9 macrumors regular

    Joined:
    Sep 13, 2010
    #1
    Hey guys

    So I am working on a app that has a MutableArray in a static class, when the user does an action it saves on to a tableview which is then written into the getters and setters of the mutableArray.

    I followed a Tutorial on developer.apple.com for this and this is what i've got

    Code:
    - (id) init {
    	
        self = [super init];
        if (self) {
            NSString *errorDesc = nil;
            NSPropertyListFormat format;
            NSString *plistPath;
            NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
    																  NSUserDomainMask, YES) objectAtIndex:0];
            plistPath = [rootPath stringByAppendingPathComponent:@"Data.plist"];
            if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath]) {
                plistPath = [[NSBundle mainBundle] pathForResource:@"Data" ofType:@"plist"];
            }
            NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
            NSDictionary *temp = (NSDictionary *)[NSPropertyListSerialization
    											  propertyListFromData:plistXML
    											  mutabilityOption:NSPropertyListMutableContainersAndLeaves
    											  format:&format
    											  errorDescription:&errorDesc];
            if (!temp) {
                NSLog(@"Error reading plist: %@, format: %d", errorDesc, format);
            }
            self.parkingHistory = [StaticClass get_logObjectsArray:[temp objectForKey:@"history"]];
    		
        }
        return self;
    }
    
    - (void)applicationWillResignActive:(UIApplication *)application {
    	NSString *error;
        NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
        NSString *plistPath = [rootPath stringByAppendingPathComponent:@"Data.plist"];
        NSDictionary *plistDict = [NSDictionary dictionaryWithObjects:
    							   [NSArray arrayWithObjects: parkingHistory, nil]
    														  forKeys:[NSArray arrayWithObjects: @"history", nil]];
        NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:plistDict
    																   format:NSPropertyListXMLFormat_v1_0
    														 errorDescription:&error];
        if(plistData) {
            [plistData writeToFile:plistPath atomically:YES];
        }
        else {
            NSLog(@"%@", error);
    		
            [error release];
        }
    }
    
    I put [self init] in the method that writes to the array. Now every time click the history tab in the app it crashes and it doesn't write to the plist.... how can I fix this code?
     
  2. robbieduncan Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #2
    Don't do that. init should only ever be called once on an object when it is created to initialise it.
     
  3. GhostDZ9 thread starter macrumors regular

    Joined:
    Sep 13, 2010
    #3
    Ok ive removed that code but it still not writing to the plist?
     
  4. robbieduncan Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #4
    This is bordering on basic program design: if you want to call a piece of code from two different places what do you do? You create a new method that's what.
     
  5. GhostDZ9 thread starter macrumors regular

    Joined:
    Sep 13, 2010
    #5
    well this is what my class looks like

    Code:
    //
    //  LogViewController.m
    //  ParkingThing
    //
    //  Created by Wesam Haj-ali on 11-05-13.
    //  Copyright 2011 __MyCompanyName__. All rights reserved.
    //
    
    #import "LogViewController.h"
    #import "StaticClass.h"
    
    @implementation LogViewController
    
    @synthesize tableView_log, parkingHistory;
    
    - (id) init {
    	
        self = [super init];
        if (self) {
            NSString *errorDesc = nil;
            NSPropertyListFormat format;
            NSString *plistPath;
            NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
    																  NSUserDomainMask, YES) objectAtIndex:0];
            plistPath = [rootPath stringByAppendingPathComponent:@"Data.plist"];
            if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath]) {
                plistPath = [[NSBundle mainBundle] pathForResource:@"Data" ofType:@"plist"];
            }
            NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
            NSDictionary *temp = (NSDictionary *)[NSPropertyListSerialization
    											  propertyListFromData:plistXML
    											  mutabilityOption:NSPropertyListMutableContainersAndLeaves
    											  format:&format
    											  errorDescription:&errorDesc];
            if (!temp) {
                NSLog(@"Error reading plist: %@, format: %d", errorDesc, format);
            }
            self.parkingHistory = [StaticClass get_logObjectsArray:[temp objectForKey:@"history"]];
    		
        }
        return self;
    }
    
    - (void)applicationWillResignActive:(UIApplication *)application {
    	NSString *error;
        NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
        NSString *plistPath = [rootPath stringByAppendingPathComponent:@"Data.plist"];
        NSDictionary *plistDict = [NSDictionary dictionaryWithObjects:
    							   [NSArray arrayWithObjects: parkingHistory, nil]
    														  forKeys:[NSArray arrayWithObjects: @"history", nil]];
        NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:plistDict
    																   format:NSPropertyListXMLFormat_v1_0
    														 errorDescription:&error];
        if(plistData) {
            [plistData writeToFile:plistPath atomically:YES];
        }
        else {
            NSLog(@"%@", error);
    		
            [error release];
        }
    }
    
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
    {
        return 1;
    }
    
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
    {
    	return [[StaticClass get_logObjectsArray] count] / 2;
    }
    
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
        if (cell == nil)
            cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"Cell"] autorelease];
        
    	cell.textLabel.text = [[StaticClass get_logObjectsArray] objectAtIndex:indexPath.row*2];
    	cell.detailTextLabel.text = [[StaticClass get_logObjectsArray] objectAtIndex:(indexPath.row*2) + 1];
    	
    	return cell;
    }
    
    
    -(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
    {
        [[StaticClass get_logObjectsArray] removeObjectAtIndex:indexPath.row+1];
    	[[StaticClass get_logObjectsArray] removeObjectAtIndex:indexPath.row];
    	[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationTop];
    }
    
    - (void)viewWillAppear:(BOOL)animated 
    {
        [tableView_log reloadData];
        [gAD_bannerView loadRequest:[GADRequest request]];
    }
    
    
    - (void)viewDidLoad 
    {
        gAD_bannerView = [[[GADBannerView alloc]
                           initWithFrame:CGRectMake(0.0,
                                                    412.0 -
                                                    GAD_SIZE_320x50.height,
                                                    GAD_SIZE_320x50.width,
                                                    GAD_SIZE_320x50.height)] autorelease];
        gAD_bannerView.adUnitID = [StaticClass get_adKey];
    	gAD_bannerView.rootViewController = self;
    	[self.view addSubview:gAD_bannerView];
        [super viewDidLoad];
    }
    
    
    - (void)didReceiveMemoryWarning
    {
        [super didReceiveMemoryWarning];
    }
    
    
    - (void)viewDidUnload 
    {
    	tableView_log = nil;
        
        [super viewDidUnload];
    }
    
    
    - (void)dealloc 
    {
    	[tableView_log release];
        
        [super dealloc];
    }
    
    
    @end
    
    so how would I get it so I can use the two method I wrote to actually collect the information?
     
  6. robbieduncan Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #6
    I have no idea what you are asking. You need to remember we do not know what you were thinking when you wrote your code. You must phrase your questions in such a way that someone who knows nothing about your code or it's purposes can understand. Saying things like "the two method[s" is not helpful. Which two methods? Which information are you "collecting"? From where?

    If there is code in your init method you wish to re-use in another method then you need to create a new, third, method that you call from both other methods.
     
  7. GhostDZ9 thread starter macrumors regular

    Joined:
    Sep 13, 2010
    #7
    Code:
    - (void)saveParkingHistory
    {
            NSString *errorDesc = nil;
            NSPropertyListFormat format;
            NSString *plistPath;
            NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
    																  NSUserDomainMask, YES) objectAtIndex:0];
            plistPath = [rootPath stringByAppendingPathComponent:@"Data.plist"];
            if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath]) {
                plistPath = [[NSBundle mainBundle] pathForResource:@"Data" ofType:@"plist"];
            }
            NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
            NSDictionary *temp = (NSDictionary *)[NSPropertyListSerialization
    											  propertyListFromData:plistXML
    											  mutabilityOption:NSPropertyListMutableContainersAndLeaves
    											  format:&format
    											  errorDescription:&errorDesc];
            if (!temp) {
                NSLog(@"Error reading plist: %@, format: %d", errorDesc, format);
            }
            self.parkingHistory = [StaticClass get_logObjectsArray:[temp objectForKey:@"history"]];		
    }
    I so i re-wrote the method definition and i put it in the numberOfRowsInSection after it writes to the array and it crashes
     
  8. robbieduncan Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #8
    This is going to sound harsh but have you ever written any software before? You seem to be lacking the basic, core, skills you need. If it crashes you run it in the debugger and find out where it crashes. Then you use that information to either add debugging statements to give you more information or work out why.

    Have you run it in the debugger? If so where does the crash occur? When it crashes what is the message?
     
  9. GhostDZ9 thread starter macrumors regular

    Joined:
    Sep 13, 2010
    #9
    i have ran the debugger and this is the information that shows in the console when click the history tab on the tab bar on the bottom

     
  10. robbieduncan Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #10
    So it's told you exactly what the problem is. StaticClass does not have a method get_logObjectsArray:. Honestly you need to read the message and think about it.
     
  11. chown33, Jun 9, 2011
    Last edited: Jun 9, 2011

    chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #11
    Post the code for the StaticClass get_logObjectsArray: method.

    EDIT:
    I think there's a typo here:
    Code:
            self.parkingHistory = [StaticClass get_logObjectsArray:[temp objectForKey:@"history"]];
    In all the other posted code, you refer to get_logObjectsArray like these excerpts show:
    Code:
    	return [[StaticClass get_logObjectsArray] count] / 2;
    
    	cell.textLabel.text = [[StaticClass get_logObjectsArray] objectAtIndex:indexPath.row*2];
    	cell.detailTextLabel.text = [[StaticClass get_logObjectsArray] objectAtIndex:(indexPath.row*2) + 1];
    
        [[StaticClass get_logObjectsArray] removeObjectAtIndex:indexPath.row+1];
    	[[StaticClass get_logObjectsArray] removeObjectAtIndex:indexPath.row];
    
    In other words, the actual method doesn't have a colon. So the most likely problem is simply the colon shouldn't be there. Remove it.

    Colons in method names are significant. Learn to look carefully for them, even if you have to copy and paste the error into TextEdit and look at it using a 24-point font.


    By the way, you should have received a compiler warning for get_logObjectsArray: .
    Do not ignore warnings.
     
  12. GhostDZ9 thread starter macrumors regular

    Joined:
    Sep 13, 2010
    #12
    Code:
    + (NSMutableArray *)get_xml3Arrays { return _xml3Arrays; }
     
  13. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #13
    If this is supposed to be the code for the get_logObjectsArray method, then you have a fairly obvious spelling difference.

    If this is the code for something else, please explain what.
     
  14. GhostDZ9 thread starter macrumors regular

    Joined:
    Sep 13, 2010
    #14
    Sorry I posted the wrong method definition

    Code:
    + (NSMutableArray *)get_logObjectsArray { return _logObjectsArray; }
    I was tweaking my code, I got it now to show no errors however it isn't writing to the property list that I have created

    Here is what i have changed

    Code:
    // the function below is suposed to retain the information obtain for the user when they click the done button and it supposed to save it.
    - (void)saveParkingHistory
    {
            NSString *errorDesc = nil;
            NSPropertyListFormat format;
            NSString *plistPath;
            NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
    																  NSUserDomainMask, YES) objectAtIndex:0];
            plistPath = [rootPath stringByAppendingPathComponent:@"Data.plist"];
            if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath]) {
                plistPath = [[NSBundle mainBundle] pathForResource:@"../Data" ofType:@"plist"];
            }
            NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
            NSDictionary *temp = (NSDictionary *)[NSPropertyListSerialization
    											  propertyListFromData:plistXML
    											  mutabilityOption:NSPropertyListMutableContainersAndLeaves
    											  format:&format
    											  errorDescription:&errorDesc];
            if (!temp) {
                NSLog(@"Error reading plist: %@, format: %d", errorDesc, format);
            }
    //        self.parkingHistory = [StaticClass get_logObjectsArray[temp objectForKey:@"history"]];		
    }
    
    //the function below is supposed to then grab the information saved and write it to the Data.plist file in XML format, however it doesn't do that. WHY?
    - (void)writeParkingHistory{
    	NSString *error;
        NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
        NSString *plistPath = [rootPath stringByAppendingPathComponent:@"Data.plist"];
        NSDictionary *plistDict = [NSDictionary dictionaryWithObjects:
    							   [NSArray arrayWithObjects: parkingHistory, nil]
    														  forKeys:[NSArray arrayWithObjects: parkingHistory, nil]];
        NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:plistDict
    																   format: kCFPropertyListXMLFormat_v1_0
    														 errorDescription:&error];
        if(plistData) {
            [plistData writeToFile:plistPath atomically:YES];
        }
        else {
            NSLog(@"%@", error);
    		
            [error release];
        }
    }
    
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
    {
        return 1;
    }
    
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
    {
    	return [[StaticClass get_logObjectsArray] count] / 2;
    }
    
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
        if (cell == nil)
            cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"Cell"] autorelease];
        
    	cell.textLabel.text = [[StaticClass get_logObjectsArray] objectAtIndex:indexPath.row*2];
    	[self saveParkingHistory];
    	[self writeParkingHistory];
    	cell.detailTextLabel.text = [[StaticClass get_logObjectsArray] objectAtIndex:(indexPath.row*2) + 1];
    	
    	return cell;
    }
     
  15. chown33, Jun 10, 2011
    Last edited: Jun 10, 2011

    chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #15
    What have you tried to debug the code? Are you using the debugger at all? If not, you should be.

    What is the actual path it's reading from? What is the actual path it's writing to? You should be able to add NSLog() calls or use the debugger to tell you this. Post the actual paths.


    How do you know it isn't writing? Describe what you expect to happen, and how you'd be able to tell "it's writing" from "it's not writing".

    You're calling writeParkingHistory for every cell that's shown. Think about what that means if 8 cells are shown.

    You also call writeParkingHistory immediately after [self saveParkingHistory], which reads the data. If you read the data and immediately write it back, what do you expect to happen?

    After you've described what you expect to happen, describe what actually happens.


    Finally, you said you followed a tutorial. Which one? Be specific: post its URL.


    EDIT
    Code:
    - (void)writeParkingHistory{
    	NSString *error;
        NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
        NSString *plistPath = [rootPath stringByAppendingPathComponent:@"Data.plist"];
    [COLOR="Red"]    NSDictionary *plistDict = [NSDictionary dictionaryWithObjects:
      [NSArray arrayWithObjects: parkingHistory, nil]
      forKeys:[NSArray arrayWithObjects: parkingHistory, nil]];
    [/COLOR]
    The red-hilited code is nonsense. Keys in a plist dictionary must be strings (NSString *). You're using the parkingHistory object as the key.

    http://developer.apple.com/library/...troduction.html#//apple_ref/doc/uid/10000048i

    You might want to look at simpler methods for writing data, such as the writeToXXX:atomically: methods in NSArray and NSDictionary. Unless you specifically need XML in the stored file, they are much simpler. You must still use strings as keys, but the code for writing is smaller.
     
  16. GhostDZ9 thread starter macrumors regular

    Joined:
    Sep 13, 2010
    #16
    Thanks for that, Ok so i have gotten it to write to the property list, however i was wondering if there was a way to automatically create a new record each time the user hits submit?

    Code:
    - (void)writeHistoryToFile 
    {
    		NSString *test = [NSString stringWithFormat:@"%@", [StaticClass extractAddress:[StaticClass get_pinLatitude] :[StaticClass get_pinLongitude]]];
    	NSString *filePath = @"/Users/rlakhani/Desktop/Parking Pro/ParkingPro 28/ParkingPro/Data.plist";
    	NSMutableDictionary* plistDict = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];
    	
    	[plistDict setValue:test forKey:@"key"];
    	[plistDict writeToFile:filePath atomically: YES];
    }
    thats what I currently have, now each time the user clicks done it overwrites the one string record in the plist file, i want to be that each time the user clicks done it creates a new string record in the plist. how do i go about doing that?
     
  17. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #17
    Break the problem down.

    You need to add a new entry. If you're adding something that has the same key as an existing entry, it will replace the existing entry. So think about how to make a unique key that doesn't already exist. Do you already have some information that's unique for the new entry? Perhaps a sequence number, a date, something else?

    If you can't think of a way to make unique keys, maybe you have the wrong data structure. Maybe an NSMutableArray, which you addObject: to, is a better choice than an NSMutableDictionary.

    This is very elementary design, so you should be able to do it by yourself. If you can't think of a way to do it, you should go back to your book or tutorial and review the fundamentals. You're trying to solve a problem that's beyond your skill level. You need to improve your skills before you can solve the problem.
     
  18. GhostDZ9 thread starter macrumors regular

    Joined:
    Sep 13, 2010
    #18
    Ok so I got passed all that other stuff above. Now my only issue is

    after the information is written in the plist i have:

    Code:
    	NSString *value;
    	value = [plistDict objectForKey:@"key"];
    which reads the plist and retrieves that values in the plist, now im trying to set it to a NSMutableArray so that I can display whatever has been retrieved into the UITableView, is there anyway to append the string to the array? keep in mind i don't want to break down the string
     
  19. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #19
    You don't append strings to arrays, you add objects to them.
     
  20. GhostDZ9, Jun 14, 2011
    Last edited: Jun 14, 2011

    GhostDZ9 thread starter macrumors regular

    Joined:
    Sep 13, 2010
    #20
    Ok Well what im trying to do is there history is written to a plist, when they click the history tab at the bottom of the app, on viewDidLoad i want there history to load up. The history is currently being held in a string I just need to get it into the array to display the information. how do i go about doing that
     
  21. GhostDZ9 thread starter macrumors regular

    Joined:
    Sep 13, 2010
    #21
    Code:
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
        if (cell == nil)
            cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"Cell"] autorelease];
    	
    	NSString *filePath = @"/Users/rlakhani/Desktop/Parking Pro/ParkingPro 28/ParkingPro/Data.plist";
    	NSMutableDictionary* plistDict = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];
    	NSString *value;
    	value = [plistDict objectForKey:@"key"];
    	
    	[StaticClass add_logObj:value];
    	cell.textLabel.text = [[StaticClass get_logObjectsArray] objectAtIndex:indexPath.row];
    	NSLog(@"%@", [StaticClass get_logObjectsArray]);
    	cell.detailTextLabel.text = [[StaticClass get_logObjectsArray] objectAtIndex:indexPath.row + 1];
    	NSLog(@"%@", [StaticClass get_logObjectsArray]);
    
    	return cell;
    }

    Thats what i have written, what am i missing so that i can get it to show onload?
     
  22. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #22
    First, are you sure you want to be reading in your plist for every displayed cell in your table?

    Second, so currently you have some kind of "history" that seemingly contains multiple elements that you are storing in a string and not an array? Doesn't seem like a great approach. Perhaps elaborate more on the details of this history.
     
  23. GhostDZ9 thread starter macrumors regular

    Joined:
    Sep 13, 2010
    #23
    Well I am building a parking app. So each time the person parks there car and clicks done in a screen on the app it writes that information to the plist.

    I have a separate tab in the tab bar called History.

    On launch of the app if the person is using the app and taps the history button I want the plist information to be read and displayed in the UITableView.

    I'm not too sure which is the best possible way to go about doing this. If you could suggest some ways I would be more then happy to look into them.

    As for the plist data it has two lines, the first line shows the address of where they parked and the second line displays what city an the time they parked at.

    If you need any more information I would be happy to supply it :)
     
  24. dejo, Jun 14, 2011
    Last edited: Jun 14, 2011

    dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #24
    Well, since your History contains multiple elements of an object, to me, that lends itself to modeling it as an array. Then, when you need to add a new parking occurrence, you can add a new object to that array. As for how to model each object in the array, seems to me that a custom ParkingOccurrence class, that contains two or three properties (address, city, time) would work well.

    EDIT:

    Or perhaps a dictionary to store the property values since you want it to end up in a plist. Yeah, that would be better.
     
  25. GhostDZ9 thread starter macrumors regular

    Joined:
    Sep 13, 2010
    #25
    Well it all goes into a dictionary, it goes

    Dictionary -> Array ->String

    String being the last line of data. Can you please help me to get this thing working?
     

Share This Page