shlist

share and manage lists between multiple people
Log | Files | Refs

commit 8aece53978c85fd61a1cbd47d0a583626430e37a
parent c8a158d096c7a9b1ca16593735d8c45846a2f336
Author: Kyle Milz <kyle@green.krwm.net>
Date:   Mon, 15 Feb 2016 11:37:47 -0700

ios: factor out serialize/deserialize of lists

- also kill the last remaining callback junk
- use notification center for everything now

Diffstat:
Mios/shlist/AppDelegate.m | 18+++++++++++++++---
Mios/shlist/MainTableViewController.m | 205++++++++++++++++++++++++++++++++++++-------------------------------------------
Mios/shlist/Network.h | 9---------
Mios/shlist/Network.m | 28++++++++++++++++++----------
Mios/shlist/SettingsTableViewController.h | 2--
Mios/shlist/SettingsTableViewController.m | 24+++++++++++++++++++-----
6 files changed, 146 insertions(+), 140 deletions(-)

diff --git a/ios/shlist/AppDelegate.m b/ios/shlist/AppDelegate.m @@ -45,10 +45,22 @@ - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { NSLog(@"notify: got remote notification"); - for (id key in userInfo) { - NSLog(@"notify: '%@' => '%@'", key, userInfo[key]); + + NSString *msg_type = userInfo[@"msg_type"]; + if (msg_type == nil) { + NSLog(@"didReceiveRemoteNotification: did not have 'msg_type' key"); + return; } - [[NSNotificationCenter defaultCenter] postNotificationName:@"MessageReceivedNotification" object:nil userInfo:userInfo]; + + // Create unique notification name based on the incoming message type + NSString *notification_name = [NSString stringWithFormat:@"PushNotification_%@", userInfo[@"msg_type"]]; + + if (![userInfo[@"payload"] isKindOfClass:[NSDictionary class]]) { + NSLog(@"didReceiveRemoteNotification: payload wasn't a dictionary"); + return; + } + + [[NSNotificationCenter defaultCenter] postNotificationName:notification_name object:nil userInfo:userInfo[@"payload"]]; } - (void) applicationWillResignActive:(UIApplication *)application diff --git a/ios/shlist/MainTableViewController.m b/ios/shlist/MainTableViewController.m @@ -26,9 +26,6 @@ - (void) dealloc { - // If you don't remove yourself as an observer, the Notification Center - // will continue to try and send notification objects to the deallocated - // object. [[NSNotificationCenter defaultCenter] removeObserver:self]; } @@ -36,37 +33,37 @@ { [super viewDidLoad]; + NSNotificationCenter *default_center = [NSNotificationCenter defaultCenter]; + NSString *notification_name; + // Listen for push notifications - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(receiveNotification:) - name:@"MessageReceivedNotification" - object:nil]; + [default_center addObserver:self selector:@selector(push_friend_added_list:) + name:@"PushNotification_friend_added_list" object:nil]; + + [default_center addObserver:self selector:@selector(push_updated_list:) + name:@"PushNotification_updated_list" object:nil]; // Hook up generic message handlers - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(lists_get_finished:) - name:[NSString stringWithFormat:@"NetworkResponseForMsgType%i", lists_get] - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(lists_get_other_finished:) - name:[NSString stringWithFormat:@"NetworkResponseForMsgType%i", lists_get_other] - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(finished_new_list_request:) - name:[NSString stringWithFormat:@"NetworkResponseForMsgType%i", list_add] - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(finished_join_list_request:) - name:[NSString stringWithFormat:@"NetworkResponseForMsgType%i", list_join] - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(finished_leave_list_request:) - name:[NSString stringWithFormat:@"NetworkResponseForMsgType%i", list_leave] - object:nil]; + notification_name = [NSString stringWithFormat:@"NetworkResponseForMsgType%i", lists_get]; + [default_center addObserver:self selector:@selector(lists_get_finished:) + name:notification_name object:nil]; + + notification_name = [NSString stringWithFormat:@"NetworkResponseForMsgType%i", lists_get_other]; + [default_center addObserver:self selector:@selector(lists_get_other_finished:) + name:notification_name object:nil]; + + notification_name = [NSString stringWithFormat:@"NetworkResponseForMsgType%i", list_add]; + [default_center addObserver:self selector:@selector(finished_new_list_request:) + name:notification_name object:nil]; + + notification_name = [NSString stringWithFormat:@"NetworkResponseForMsgType%i", list_join]; + [default_center addObserver:self selector:@selector(finished_join_list_request:) + name:notification_name object:nil]; + + notification_name = [NSString stringWithFormat:@"NetworkResponseForMsgType%i", list_leave]; + [default_center addObserver:self selector:@selector(finished_leave_list_request:) + name:notification_name object:nil]; + // display an Edit button in the navigation bar for this view controller self.navigationItem.leftBarButtonItem = self.editButtonItem; @@ -107,31 +104,19 @@ _address_book.main_tvc = self; } -- (void) receiveNotification:(NSNotification *) notification +// Handle 'friend_added_list' message from notification service +- (void) push_friend_added_list:(NSNotification *) notification { - NSDictionary *userinfo = notification.userInfo; - - // [notification name] should always be @"TestNotification" - // unless you use this method for observation of other notifications - // as well. + NSDictionary *json_list = notification.userInfo; - if ([[notification name] isEqualToString:@"MessageReceivedNotification"]) - NSLog (@"Successfully received the test notification!"); + // Server will only send back partial list information because this will + // always be put in the other lists section + SharedList *tmp = [self deserialize_light_list:json_list]; NSMutableArray *other_lists = [_lists objectAtIndex:1]; - - SharedList *tmp = [[SharedList alloc] init]; - // tmp.num = list[@"num"]; - tmp.num = [NSNumber numberWithInt:99]; - - // NSData *name_data = [list[@"name"] dataUsingEncoding:NSISOLatin1StringEncoding]; - // tmp.name = [[NSString alloc] initWithData:name_data encoding:NSUTF8StringEncoding]; - tmp.name = @"Some new list from the outthere"; - - // tmp.members_phone_nums = list[@"members"]; [other_lists addObject:tmp]; - NSLog(@"notify: adding other list '%@', num '%@'", tmp.name, tmp.num); + NSLog(@"notify: new other list '%@', num '%@'", tmp.name, tmp.num); NSIndexPath *new_path = [NSIndexPath indexPathForRow:[other_lists count] - 1 inSection:1]; [self.tableView insertRowsAtIndexPaths:@[new_path] withRowAnimation:UITableViewRowAnimationAutomatic]; @@ -253,22 +238,7 @@ clickedButtonAtIndex:(NSInteger)buttonIndex [lists removeAllObjects]; for (NSDictionary *list in json_lists) { - SharedList *tmp = [[SharedList alloc] init]; - tmp.num = list[@"num"]; - - // Convert incoming encoded UTF-8 into real UTF-8 - NSData *name_data = [list[@"name"] dataUsingEncoding:NSISOLatin1StringEncoding]; - tmp.name = [[NSString alloc] initWithData:name_data encoding:NSUTF8StringEncoding]; - - NSNumber *date = list[@"date"]; - if ([date intValue] != 0) - tmp.date = [NSDate dateWithTimeIntervalSince1970:[date floatValue]]; - else - tmp.date = nil; - - tmp.members_phone_nums = list[@"members"]; - tmp.items_ready = list[@"items_complete"]; - tmp.items_total = list[@"items_total"]; + SharedList *tmp = [self deserialize_full_list:list]; [lists addObject:tmp]; NSLog(@"adding list '%@', num '%@'", tmp.name, tmp.num); @@ -288,13 +258,7 @@ clickedButtonAtIndex:(NSInteger)buttonIndex [other_lists removeAllObjects]; for (NSDictionary *list in other_json_lists) { - SharedList *tmp = [[SharedList alloc] init]; - tmp.num = list[@"num"]; - - NSData *name_data = [list[@"name"] dataUsingEncoding:NSISOLatin1StringEncoding]; - tmp.name = [[NSString alloc] initWithData:name_data encoding:NSUTF8StringEncoding]; - - tmp.members_phone_nums = list[@"members"]; + SharedList *tmp = [self deserialize_light_list:list]; [other_lists addObject:tmp]; NSLog(@"adding other list '%@', num '%@'", tmp.name, tmp.num); @@ -309,14 +273,7 @@ clickedButtonAtIndex:(NSInteger)buttonIndex NSDictionary *response = notification.userInfo; NSDictionary *list = [response objectForKey:@"list"]; - SharedList *shlist = [[SharedList alloc] init]; - shlist.num = [list objectForKey:@"num"]; - shlist.name = [list objectForKey:@"name"]; - - NSMutableArray *members = [list objectForKey:@"members"]; - shlist.members_phone_nums = members; - shlist.items_ready = [list objectForKey:@"items_complete"]; - shlist.items_total = [list objectForKey:@"items_total"]; + SharedList *shlist = [self deserialize_full_list:list]; NSMutableArray *lists = [_lists objectAtIndex:0]; [lists addObject:shlist]; @@ -329,6 +286,44 @@ clickedButtonAtIndex:(NSInteger)buttonIndex [self update_section_headers]; } +- (SharedList *) deserialize_full_list:(NSDictionary *)json_list +{ + SharedList *shlist = [[SharedList alloc] init]; + shlist.num = [json_list objectForKey:@"num"]; + + // We need some careful decoding to get a usable Unicode string + NSData *name_data = [json_list[@"name"] dataUsingEncoding:NSISOLatin1StringEncoding]; + shlist.name = [[NSString alloc] initWithData:name_data encoding:NSUTF8StringEncoding]; + + NSNumber *date = json_list[@"date"]; + if ([date intValue] != 0) { + shlist.date = [NSDate dateWithTimeIntervalSince1970:[date floatValue]]; + } + else { + shlist.date = nil; + } + + shlist.members_phone_nums = [json_list objectForKey:@"members"]; + shlist.items_ready = [json_list objectForKey:@"items_complete"]; + shlist.items_total = [json_list objectForKey:@"items_total"]; + + return shlist; +} + +- (SharedList *) deserialize_light_list:(NSDictionary *)json_list +{ + SharedList *shlist = [[SharedList alloc] init]; + shlist.num = [json_list objectForKey:@"num"]; + + // We need some careful decoding to get a usable Unicode string + NSData *name_data = [json_list[@"name"] dataUsingEncoding:NSISOLatin1StringEncoding]; + shlist.name = [[NSString alloc] initWithData:name_data encoding:NSUTF8StringEncoding]; + + shlist.members_phone_nums = [json_list objectForKey:@"members"]; + + return shlist; +} + // major thing here is join list requests - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath @@ -352,8 +347,8 @@ clickedButtonAtIndex:(NSInteger)buttonIndex - (void) finished_join_list_request:(NSNotification *) notification { NSDictionary *response = notification.userInfo; - NSDictionary *shlist = response[@"list"]; - NSLog(@"network: joined list %@", shlist[@"num"]); + NSDictionary *json_list = response[@"list"]; + NSLog(@"network: joined list %@", json_list[@"num"]); NSMutableArray *lists = [_lists objectAtIndex:0]; NSMutableArray *other_lists = [_lists objectAtIndex:1]; @@ -361,7 +356,7 @@ clickedButtonAtIndex:(NSInteger)buttonIndex // Find the list number we received a response for SharedList *needle = nil; for (SharedList *temp in other_lists) { - if (temp.num == shlist[@"num"]) { + if (temp.num == json_list[@"num"]) { needle = temp; break; } @@ -371,12 +366,16 @@ clickedButtonAtIndex:(NSInteger)buttonIndex if (needle == nil) return; - // Swap between data structures first before moving rows - [lists addObject:needle]; + // The server sent us a full list object, make sure to copy cell reference + SharedList *joined_list = [self deserialize_full_list:json_list]; + joined_list.cell = needle.cell; + + // Add completely new object to lists section and remove the old list + [lists addObject:joined_list]; [other_lists removeObject:needle]; // Get the cell index path from the matched list cell - NSIndexPath *orig_index_path = [self.tableView indexPathForCell:needle.cell]; + NSIndexPath *orig_index_path = [self.tableView indexPathForCell:joined_list.cell]; // Compute new position and start moving row as soon as possible // XXX: sorting @@ -386,36 +385,20 @@ clickedButtonAtIndex:(NSInteger)buttonIndex [self update_section_headers]; - // Put any new values into data structs - NSData *name_data = [shlist[@"name"] dataUsingEncoding:NSISOLatin1StringEncoding]; - needle.name = [[NSString alloc] initWithData:name_data encoding:NSUTF8StringEncoding]; - - NSNumber *date = shlist[@"date"]; - if ([date intValue] != 0) { - needle.date = [NSDate dateWithTimeIntervalSince1970:[date floatValue]]; - } - else { - needle.date = nil; - } - - needle.items_ready = shlist[@"items_complete"]; - needle.items_total = shlist[@"items_total"]; - needle.num_members = shlist[@"num_members"]; - - needle.members_phone_nums = shlist[@"members"]; - [self process_members_array:shlist[@"members"] cell:needle.cell]; + // Update members in list row + [self process_members_array:joined_list.members_phone_nums cell:joined_list.cell]; // Add > accessory indicator - needle.cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + joined_list.cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; // Find fraction UILAbel, populate it and then show it - UILabel *fraction = (UILabel *)[needle.cell viewWithTag:4]; - fraction.text = [self fraction:needle.items_ready denominator:needle.items_total]; + UILabel *fraction = (UILabel *)[joined_list.cell viewWithTag:4]; + fraction.text = [self fraction:joined_list.items_ready denominator:joined_list.items_total]; fraction.hidden = NO; // Show date label if date has been set to something - if (needle.date != nil) { - UILabel *deadline_label = (UILabel *)[needle.cell viewWithTag:3]; + if (joined_list.date != nil) { + UILabel *deadline_label = (UILabel *)[joined_list.cell viewWithTag:3]; deadline_label.hidden = NO; } } @@ -690,7 +673,7 @@ titleForHeaderInSection:(NSInteger)section [segue.destinationViewController setMetadata:list]; // send update list items message - network_connection->shlist_ldvc = segue.destinationViewController; + // network_connection->shlist_ldvc = segue.destinationViewController; //[network_connection send_message:6 contents:list.id]; } diff --git a/ios/shlist/Network.h b/ios/shlist/Network.h @@ -1,18 +1,10 @@ #import <UIKit/UIKit.h> -#import "MainTableViewController.h" -#import "ListTableViewController.h" -#import "SettingsTableViewController.h" #import "MsgTypes.h" @interface Network : NSObject <NSStreamDelegate> { NSInputStream *inputShlistStream; NSOutputStream *outputShlistStream; - int *bytesRead; - - @public - ListTableViewController *shlist_ldvc; - SettingsTableViewController *settings_tvc; } - (void) connect; @@ -24,7 +16,6 @@ - (bool) connected; - (bool) send_message:(uint16_t)msg_type contents:(NSMutableDictionary *)data; - // returns singleton instance + (id) shared_network_connection; diff --git a/ios/shlist/Network.m b/ios/shlist/Network.m @@ -51,10 +51,6 @@ - (void) connect { - NSLog(@"network: connect()"); - connected = 1; - [settings_tvc update_network_text:@"Connected"]; - CFReadStreamRef readStream; CFWriteStreamRef writeStream; @@ -76,13 +72,17 @@ [inputShlistStream open]; [outputShlistStream open]; + + NSLog(@"network: connect()"); + connected = 1; + [[NSNotificationCenter defaultCenter] postNotificationName:@"NetworkConnectedNotification" object:nil userInfo:nil]; } - (void) disconnect { NSLog(@"network: disconnect()"); connected = 0; - [settings_tvc update_network_text:@"Disconnected"]; + [[NSNotificationCenter defaultCenter] postNotificationName:@"NetworkDisconnectedNotification" object:nil userInfo:nil]; [inputShlistStream close]; [outputShlistStream close]; @@ -103,11 +103,12 @@ // read device id from filesystem into memory NSError *error = nil; device_id = [NSString stringWithContentsOfFile:device_id_file encoding:NSUTF8StringEncoding error:&error]; - if (error != nil) + if (error != nil) { NSLog(@"%@", [error userInfo]); + return false; + } NSLog(@"network: device id loaded"); - return true; } @@ -120,7 +121,7 @@ nil]; NSError *error = nil; - NSData *json = [NSJSONSerialization dataWithJSONObject:request options:NSJSONWritingPrettyPrinted error:&error]; + NSData *json = [NSJSONSerialization dataWithJSONObject:request options:0 error:&error]; if (error != nil) { NSLog(@"%@", [error userInfo]); return false; @@ -148,19 +149,23 @@ if (!connected) [self connect]; + // Append 'device_id' to all message requests sent through this function [request setObject:device_id forKey:@"device_id"]; NSError *error = nil; - NSData *json = [NSJSONSerialization dataWithJSONObject:request options:NSJSONWritingPrettyPrinted error:&error]; + // Try to serialize request, bail if errors + NSData *json = [NSJSONSerialization dataWithJSONObject:request options:0 error:&error]; if (error != nil) { NSLog(@"%@", [error userInfo]); return false; } + // Convert header values into network byte order uint16_t version = htons(0); uint16_t msg_type_network = htons(send_msg_type); uint16_t length = htons([json length]); + // Construct message header by concatenating network byte order fields NSMutableData *msg = [NSMutableData data]; [msg appendBytes:&version length:2]; [msg appendBytes:&msg_type_network length:2]; @@ -286,6 +291,7 @@ NSData *data = [NSData dataWithBytesNoCopy:payload length:payload_size]; NSError *error = nil; + // Try to parse payload and check for errors NSDictionary *response = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; if (error) { @@ -293,18 +299,20 @@ return; } + // Make sure server sent 'status' key in response NSString *status = response[@"status"]; if (status == nil) { NSLog(@"read: response did not contain 'status' key"); return; } + // Make sure 'status' key is not 'err' if ([status compare:@"err"] == 0) { NSLog(@"read: response error, reason = '%@'", response[@"reason"]); return; } - // device_add responses don't trigger any gui updates if (msg_type == device_add) { + // device_add responses don't trigger any gui updates device_id = [response objectForKey:@"device_id"]; NSLog(@"device_add: writing new key '%@' to file", device_id); diff --git a/ios/shlist/SettingsTableViewController.h b/ios/shlist/SettingsTableViewController.h @@ -2,6 +2,4 @@ @interface SettingsTableViewController : UITableViewController -- (void) update_network_text:(NSString *)new_text; - @end diff --git a/ios/shlist/SettingsTableViewController.m b/ios/shlist/SettingsTableViewController.m @@ -18,20 +18,34 @@ { [super viewDidLoad]; + NSNotificationCenter *default_center = [NSNotificationCenter defaultCenter]; + // Listen for network connect/disconnect events and set the text field accordingly + [default_center addObserver:self selector:@selector(set_network_text_connected) + name:@"NetworkConnectedNotification" object:nil]; + + [default_center addObserver:self selector:@selector(set_network_text_disconnected) + name:@"NetworkDisconnectedNotification" object:nil]; + netconn = [Network shared_network_connection]; NSString *device_id = [netconn get_device_id]; + + // Just show the first 8 characters of the device id (aka device fingerprint) _device_id_label.text = [device_id substringToIndex:8]; if ([netconn connected]) - _network_label.text = @"Connected"; + [self set_network_text_connected]; else - _network_label.text = @"Disconnected"; - netconn->settings_tvc = self; + [self set_network_text_disconnected]; +} + +- (void) set_network_text_connected +{ + _network_label.text = @"Connected"; } -- (void) update_network_text:(NSString *)new_text +- (void) set_network_text_disconnected { - _network_label.text = new_text; + _network_label.text = @"Disconnected"; } - (void)didReceiveMemoryWarning