Getting and setting NSArrays in an NSMutableDictionary - nsarray

I have a global file that stores values before another class is deallocated that created the values. My method looks like this for storing:
`- (void)set_plantKind:(NSArray *)plantKindArr forMapName:(NSString *)mapName {
[plantKinds setObject:plantKindArr forKey:mapName];
NSLog(#"key: %#, value: %#", mapName, [plantKinds objectForKey:mapName]);
}
`
I can log plantKindArr and mapName just fine, but it does not seem to storing plantKindArr in the dictionary, or maybe I just don't know how to retrieve it correctly. I tried logging as you can see in the NSLog, yet the value returns null. Any clues to what I could be doing wrong here?

I never allocated it. Oops!
plantKinds = [[NSMutableDictionary alloc] init];
It's monday, time to wake up.

Related

Increment Number Property in AWS DynamoDB

How do I increment a number in AWS Dynamodb?
The guide says when saving an item to simply resave it:
http://docs.aws.amazon.com/mobile/sdkforios/developerguide/dynamodb_om.html
However I am trying to use a counter where many users may be updating at the same time.
Other documentation has told me to use and UpdateItem operation but I cannot find a good example to do so.
http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.Modifying.html
However, I cannot find a method to implement the expression. In the future I will be adding values to arrays and maps. Will this be the same? My code is in Obj C
Currently my code looks like:
AWSDynamoDBUpdateItemInput *updateItemInput = [AWSDynamoDBUpdateItemInput new];
updateItemInput.tableName = #"TableName";
updateItemInput.key = #{
UniqueItemKey:#"KeyValue"
};
updateItemInput.updateExpression = #"SET counter = counter + :val";
updateItemInput.expressionAttributeValues =#{
#":val":#1
};
It looks like you're missing the last bit of code that actually makes the update item request:
AWSDynamoDB *dynamoDB = [AWSDynamoDB defaultDynamoDB];
[[dynamoDB updateItem:updateItemInput]
continueWithBlock:^id(AWSTask *task) {
if (task.error) {
NSLog(#"The request failed. Error: [%#]", task.error);
}
if (task.exception) {
NSLog(#"The request failed. Exception: [%#]", task.exception);
}
if (task.result) {
//Do something with result.
}
return nil;
}];
In DynamoDB if you want to increment the value of the any propertie/field you can use the UpdateItemRequest with action option ADD. I used in android this method would update the existing value of the field. Let me share the code snippet. You can use any actions such like add,delete,put etc.
.....
AttributeValue viewcount = new AttributeValue().withS("100");
AttributeValueUpdate attributeValueUpdate = new AttributeValueUpdate().withAction(AttributeAction.ADD).withValue(viewcount);
updateItems.put(UploadVideoData.FIELD_VIEW_COUNT, attributeValueUpdate);
UpdateItemRequest updateItemRequest = new UpdateItemRequest().withTableName(UploadVideoData.TABLE_NAME)
.withKey(primaryKey).withAttributeUpdates(updateItems);
UpdateItemResult updateItemResult = amazonDynamoDBClient.updateItem(updateItemRequest);
....
You can see the above code will add 100 count into the existing value of that field.
This code is for android but the technique would remain the same.
Thank you.

does NSMetadataQueryDidUpdateNotification can work with NSFileManager setUbiquitous

i am trying to track my icloud upload progress using NSMetadataQueryDidUpdateNotification..but it can't work... i don't know what the problem is..
here is my code for upload to icloud
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
NSFileCoordinator* fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
[fileCoordinator coordinateReadingItemAtURL:backupUrl options:NSFileCoordinatorReadingWithoutChanges error:nil byAccessor:^(NSURL *newURL) {
NSFileManager* fm = [NSFileManager defaultManager];
NSError *theError = nil;
BOOL success =[fm setUbiquitous:YES itemAtURL:backupUrl destinationURL:[[ubiq URLByAppendingPathComponent:#"Documents" isDirectory:true] URLByAppendingPathComponent:bName] error:&theError];
if (!(success)) {
[progView dismiss];
UIAlertView* alertFail=[[UIAlertView alloc]initWithTitle:#"Backup Error" message:#"Could not backup to iCloud." delegate:Nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alertFail show];
NSLog(#"iCloud error: %#", [theError localizedDescription]);
}
else{
[self loadNotes:bName];
}
}];
});
and this code for tracing my upload progress
- (void)loadNotes:(NSString *)bname {
self.alertQuery = [[NSMetadataQuery alloc] init];
[self.alertQuery setPredicate:[NSPredicate predicateWithFormat:#"%K LIKE %#", NSMetadataItemFSNameKey, bname]];
[self.alertQuery setSearchScopes:#[NSMetadataQueryUbiquitousDataScope]];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(liveupdate:) name:NSMetadataQueryDidUpdateNotification object:self.alertQuery];
[self.alertQuery startQuery];
}
-(void)liveupdate:(NSNotification *)note {
NSMetadataQuery* query=[note object];
if ([query resultCount]==0){
return;
}
NSMetadataItem* item=[query resultAtIndex:0];
float progress=[[item valueForAttribute:NSMetadataUbiquitousItemPercentUploadedKey]floatValue];
[progView.progBar setProgress:progress animated:NO];
if ([[item valueForAttribute:NSMetadataUbiquitousItemIsUploadedKey] boolValue]){
[query stopQuery];
[query disableUpdates];
_alertQuery=nil;
[progView dismiss];
}
}
what is the wrong with code...
can somebody tell me what is the best way to track icloud upload progress in NSFileManager setUbiquitous....
thank you...
You will probably want to observe the NSMetadataQueryDidFinishGatheringNotification notification, which fires first, with the initial set of results.
But even then, you may not get what you want, because the update notification will only fire if the set of results changes. You are searching for a particular file, and since that file is not being deleted or anything like that, your set of results will remain the same, even if the file uploads or downloads.
In my experience, NSMetadataQuery is not very effective for monitoring upload and download progress. You can hack it to almost work, but it is never exactly what you want.
Probably the best you can do is fire the metadata query, observe the finished-gathering notification, stop the query, and start the query again. Do this at regular intervals of a second or so, and you should be able to track the progress.
You should also consider whether you really want to track progress of an individual file. It will depend how large your files are. In many cases, you may be better to track the number of files to upload/download, or the total bytes remaining.
If this is the case, you can try to setup a metadata that includes a predicate with the uploaded/downloaded status included. This will continually fire notifications when a file finishes uploading/downloading. You can find an example of this here. Look for the method startMonitoringMetadata.

iOS query update not working

Can someone point me in the right direction to find out how to correctly monitor when files change in the app iCloud container? I've based my code on Apple and other iCloud tutorials I've reviewed, but none of them deal with updates to the iCloud container, just using initial queries. I've been working on this for three weeks now with no success. I use UIDocument in an app saving to the app iCloud container. Since UIDocument sends no notice when a document is added, I can't update the app on another iOS device when the app is running on multiple devices. Changing and deleting a document works fine by monitoring the UIDocument UIDocumentStateChangedNotification.
I use a query to initially check the iCloud container when the app starts or resumes from the background which works fine to get all files in the iCloud container on the device, including any documents added while the app was not active. I disable updates to process the query results when NSMetadataQueryDidFinishGatheringNotification is posted, then enable updates on the query. Sometimes I get one or two update notices shortly after enabling updates from NSMetadataQueryDidUpdateNotification being posted, but that is all. Never any further update notices and never from a document being added to the iCloud container.
I understand the code for iCloud use is somewhat complex, I don't expect anyone to examine my code (I've provided an excerpt for reference) to correct it. I'd appreciate it if someone can point me to more information on the specifics of tracking iCloud container changes during app execution.
Thanks,
Fred
Code excerpt for starting query:
-(void)loadDocument {
// set iCloud URL to nil for local storage to start
NSURL *ubiq = nil;
// if iCloud is selected get the iCloud container URL
if ([_useiCloud isEqualToString:#"YES"]) {
// get the app iCloud container URL
ubiq = DefaultMemoDataController.iCloudContainerURL;
}
// if iCloud URL is available and user chooses to use iCloud, set the query for app memo file names
if (ubiq) {
// adding to see if not creating another query prevents crash resuming from background
if (!self.query) {
self.query = [[NSMetadataQuery alloc] init];
}
// set the scope of the query to look in iCloud documents
[self.query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]];
// set search to look for a group of file names by setting up a predicate
// use the note file name format for the app
NSPredicate *pred = [NSPredicate predicateWithFormat:#"%K like 'FOLMemo_*'", NSMetadataItemFSNameKey];
// set the query to search with the predicate.
[self.query setPredicate:pred];
// set up a notification when the query is complete because the query is an asynchronous call (off the main queue)
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(queryDidFinishGathering:)
name:NSMetadataQueryDidFinishGatheringNotification
object:self.query];
// start the query.
[self.query startQuery];
// not sure this is needed, but want to make sure same query is started again for updates.
DefaultMemoDataController.query = self.query;
}
}
code when query completes
-(void)queryDidFinishGathering:(NSNotification *)notification {
// stop the query while processing the query results to prevent changes while processing
NSMetadataQuery *query = [notification object];
[query disableUpdates];
// not sure is needed but want to make sure resume updates on same query
DefaultMemoDataController.query = query;
// stop looking for query did finish notifications since the query was completed.
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSMetadataQueryDidFinishGatheringNotification
object:query];
// start looking for query updates
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(processQueryUpdate:)
name:NSMetadataQueryDidUpdateNotification
object:query];
// load the data from the query
[self loadData:query];
}
code to process query:
-(void)loadData:(NSMetadataQuery *)query {
// add all the memos from the query results to the app memos dictionary
for (NSMetadataItem *item in [query results]) {
// get the URL for the memo
NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];
// load the memo text from the url
FOLMemoDoc *doc = [[FOLMemoDoc alloc] initWithFileURL:url];
// open the note
[doc openWithCompletionHandler:^(BOOL success) {
if (success) {
// add the memo UIDocument object to the memo dictionary
// need temp dictionary since can't change a property dictionary for some reason
NSMutableDictionary * tempDict = [NSMutableDictionary dictionaryWithDictionary:DefaultMemoDataController.masterMemoDictionary];
[tempDict setObject:doc forKey:doc.memoDictionaryKey];
DefaultMemoDataController.masterMemoDictionary = [NSMutableDictionary dictionaryWithDictionary:tempDict];
NSNotification *notice = [NSNotification notificationWithName:kFlashofLightUpdateMemoNotice
object:doc];
[[NSNotificationCenter defaultCenter] postNotification:notice];
} else {
// failed to open document, should probably alert the user
}
}];
}
// enable query updates
[query enableUpdates];
}
After another week's experimentation I got query updates for the iCloud container working by adding a property to my persistent dataController object for the query object. By replacing each query reference in my previous code with the persistent dataController property, keeping the observer for the finished query (NSMetadataQueryDidFinishGatheringNotification) and never stopping the query, query updates now works (NSMetadataQueryDidUpdateNotification). The app receives a NSMetadataQueryDidUpdateNotification notification for every change to the app iCloud container. Multiple notices are received at times, but I have not come across a time when a notice is not posted, so I can now catch all real time updates on all devices running the app.
Here are the revised code extracts from above. This code requires other methods and set-up that is not included, so it will not run stand alone, but shows the changes I had to make to get NSMetadataQueryDidUpdateNotification notifications working in my app.
Code excerpt for starting query:
-(void)loadDocument {
// set iCloud URL to nil for local storage to start
NSURL *ubiq = nil;
// if iCloud is selected get the iCloud container URL
if ([_useiCloud isEqualToString:#"YES"]) {
// get the app iCloud container URL
ubiq = DefaultMemoDataController.iCloudContainerURL;
}
// if iCloud URL is available and user chooses to use iCloud, set the query for app memo file names
if (ubiq) {
// adding to see if not creating another query prevents crash resuming from background
if (!DefaultMemoDataController.query) {
DefaultMemoDataController.query = [[NSMetadataQuery alloc] init];
}
// set the scope of the query to look in iCloud documents
[DefaultMemoDataController.query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]];
// set search to look for a group of file names by setting up a predicate
// use the note file name format for the app
NSPredicate *pred = [NSPredicate predicateWithFormat:#"%K like 'FOLMemo_*'", NSMetadataItemFSNameKey];
// set the query to search with the predicate.
[DefaultMemoDataController.query setPredicate:pred];
//remove observer to make sure no duplicate observers
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSMetadataQueryDidFinishGatheringNotification
object:DefaultMemoDataController.query];
// set up a notification when the query is complete because the query is an asynchronous call (off the main queue)
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(queryDidFinishGathering:)
name:NSMetadataQueryDidFinishGatheringNotification
object:DefaultMemoDataController.query];
// remove observer to make sure no duplicate observers
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSMetadataQueryDidUpdateNotification
object:DefaultMemoDataController.query];
// set observer for query update
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(processQueryUpdate:)
name:NSMetadataQueryDidUpdateNotification
object:DefaultMemoDataController.query];
// start the query.
[DefaultMemoDataController.query startQuery];
}
code when query initially completes:
-(void)queryDidFinishGathering:(NSNotification *)notification {
// disable the query while processing the query results to prevent changes while processing
DefaultMemoDataController.query
NSMetadataQuery *query = [notification object];
[DefaultMemoDataController.query disableUpdates];
// call loadData with the query results
[self loadData:DefaultMemoDataController.query];
}
code to process query
-(void)loadData:(NSMetadataQuery *)query {
// add all the memos from the query results to the app memos dictionary
for (NSMetadataItem *item in [query results]) {
// get the URL for the memo
NSURL *url = [item valueForAttribute:NSMetadataItemURLKey];
// load the memo text from the url
FOLMemoDoc *doc = [[FOLMemoDoc alloc] initWithFileURL:url];
// open the memo
[doc openWithCompletionHandler:^(BOOL success) {
if (success) {
// add the memo UIDocument object to the memo dictionary
// need temp dictionary since can't change a property dictionary for some reason
NSMutableDictionary * tempDict = [NSMutableDictionary dictionaryWithDictionary:DefaultMemoDataController.masterMemoDictionary];
[tempDict setObject:doc forKey:doc.memoDictionaryKey];
DefaultMemoDataController.masterMemoDictionary = [NSMutableDictionary dictionaryWithDictionary:tempDict];
// save the memo dictionary
[DefaultMemoDataController saveMemoDictionary];
NSNotification *notice = [NSNotification notificationWithName:kFlashofLightUpdateMemoNotice
object:doc];
[[NSNotificationCenter defaultCenter] postNotification:notice];
} else {
// failed to open document
// if there is a memo dictionary key available, delete the memo from master memo dictionary
if (doc.memoDictionaryKey) {
// delete memo from master memo dictionary
[DefaultMemoDataController.masterMemoDictionary removeObjectForKey:doc.memoDictionaryKey];
}
// get the dictionary key from the file name and try to delete it that way
else {
NSString * filename = [doc.fileURL lastPathComponent];
if (filename) {
[DefaultMemoDataController.masterMemoDictionary removeObjectForKey:filename];
}
}
}
}];
}
// enable query updates
[DefaultMemoDataController.query enableUpdates];
}
I hope this helps someone else.
Fred

Testing HTTP using Kiwi/Nocilla

I just started developing an app that connects to this URL and retrieves the rate exchange for a given pair of currencies.
I need to test the HTTP request and I ended up learning about Kiwi and Nocilla. However, I'm completely new to any kind of testing and there's not a lot of information about Nocilla that can help me to get started.
I added all the NSURLConnectionDataDelegate and NSURLConnectionDelegate methods to the ViewController of my single view application, and the data retrieved from the URL is stored in #property (strong, nonatomic) NSMutableData *receivedData;. When I run the program everything works as expected but I haven't been able to pass the test I wrote:
SPEC_BEGIN(URLConnectionSpec)
__block URLConnectionAppDelegate *app_delegate;
__block URLConnectionViewController *view_controller;
describe(#"URLConnection", ^{
beforeAll(^{
[[LSNocilla sharedInstance] start];
app_delegate = [[UIApplication sharedApplication] delegate];
[[app_delegate shouldNot] beNil];
view_controller = app_delegate.viewController;
});
afterAll(^{
[[LSNocilla sharedInstance] stop];
});
afterEach(^{
[[LSNocilla sharedInstance] clearStubs];
});
context(#"When testing", ^{
it(#"should do something", ^{
stubRequest(#"GET", #"http://rate-exchange.appspot.com/currency?from=USD&to=EUR&q=1");
[view_controller beginCommunication];
[[expectFutureValue([NSString stringWithUTF8String:[view_controller.receivedData bytes]]) shouldEventuallyBeforeTimingOutAfter(2)] equal:#"{\"to\": \"EUR\", \"rate\": 0.76610740799999999, \"from\": \"USD\", \"v\": 0.76610740799999999}"];
});
});
});
SPEC_END
I'm sorry for the long snippet of code.
The test always failed with this message
URLConnection_WhenTesting_ShouldDoSomething] : 'URLConnection, When testing, should do something' [FAILED], expected subject to equal "{"to": "EUR", "rate": 0.76610740799999999, "from": "USD", "v": 0.76610740799999999}", got ""
I tried changing the time to even 10 seconds hoping that the test finished too early but I got the same results. I don't know why 'receivedData' is empty.
I would really appreciate any help
See discussion in comments: the overall structure of the Kiwi test looks good, the Nocilla stubRequest function call doesn't seem to result in the response that the test is expecting.
Perhaps you could use andReturnRawResponse to set up the expected response data. Something like this (assuming I got the Nocilla syntax correct):
NSData *rawData = ...
stubRequest(...).andReturnRawResponse(rawData);
[view_controller beginCommunication];
[expectFutureValue([view_controller.receivedData bytes])
shouldEventuallyBeforeTimingOutAfter(2)] equal:rawData.bytes];

NSDictionary from P-list with UIPickerView

I'm having a problem with my picker in one of my apps. I have an NSDictionary obtained from a property list that contains a bunch of keys, which in turn contain a bunch of strings. I have two components, each one should have the same list of strings within. I also have a slider that I want to use to allow the user to change keys. So when the slider's value goes from 0 to 1 the key at index 1 in the dictionary should load its contents into the pickerview's components.
It's working as far as loading the new contents into the picker based on the slider. I've been using the slider's tag as the variable to dictate which contents get loaded. The problem is that after loading a new list of items the program crashes, I'm thinking that the number of rows needed isn't getting update or something but I'm just not experienced enough with UIPickerView to isolate the problem myself without spending more hours than I've already used trying to figure this out myself.
Here are my delegate/data methods for the picker:
#pragma mark -
#pragma mark Picker Delegate/Data Methods
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 2;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
//aryNames is an NSArray instance variable that contains the values for the components of the picker
if (component == 0)
return [self.aryNames count];
return [self.aryNames count];
}
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row
forComponent:(NSInteger)component
{
//I think this is where my problem is
//I'm using a string to select the object
// at the index of the slider's location to
// fill up the instance variable with new data.
//Anyway, it works fine if I have two different arrays hardcoded
//but I'd really like to have this load dynamically because
//there are a lot of keys and this way I could add and remove keys without
//worrying about changing code
NSString *selectedType = [self.aryKeys objectAtIndex:slideUnitTypes.tag];
NSArray *newNames = [dictAllNames objectForKey:selectedType];
self.aryNames = newNames;
return [aryNames objectAtIndex:row];
}
//I'm pretty sure that the method below is not the problem
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:
(NSInteger)component
{
if (component == 0)
{
[firstValueHeading setText:[aryNames objectAtIndex:row]];
}
else
{
[secondValueHeading setText:[aryNames objectAtIndex:row]];
}
}
If it wasn't descriptive enough or you need to see more of my code please tell me. This problem has been a real bugger in an otherwise smooth project. Thanks.
I am still fairly new to this myself, but in Troy Brant's book (chapter 9) he does this. You should grab the book from the library/bookstore and review the source code at http://troybrant.net/iphonebook/chapter9/Ruralfork-done.zip
It should help.
i've actually solved this long since. here is the code for that delegate if it helps:
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
NSUInteger index = slideUnitTypes.value;
NSString *placeString = [self.aryKeys objectAtIndex:index];
NSArray *returnThisArray = [dictAllNames objectForKey:placeString];
return [returnThisArray objectAtIndex:row];
}
if anyone out there needs to see any of my other delegates just comment on this answer and hopefully SO should send me an email.