从webserver更新iOS TableView单元格中的数据(ASIHTTPRequest,PHP,mysql)

时间:2013-07-02 08:40:48

标签: ios objective-c apple-push-notifications tableview asihttprequest

我有tableview名称和状态。苹果推送通知(APNS)时状态改变。 但我有这个问题。如果没有通知,我该怎么办?或者,如果用户点击此消息的关闭按钮。 我尝试使用ASIHTTPRequest更新表:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
    static NSString *CellIdentifier = @"Cell";  
    HomePageTableCell *cell = (HomePageTableCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    // Configure the cell...
    NSManagedObject *device = [self.devices objectAtIndex:indexPath.row];
    cell.nameLabel.text = [device valueForKey:@"name"];

    if ([[device valueForKey:@"status"] isEqualToNumber:@1]) 
    {
        cell.status.text = @"Not configured";
        cell.stav.image = [UIImage imageNamed:@"not_configured.png"];
    }
    if ([[device valueForKey:@"status"] isEqualToNumber:@2]) 
    {
        //some other states
    }

    return cell;
}

我尝试在加载单元格之前更改状态...

- (void) getStatus:(NSString *)serialNumber
{
    NSURL *url = [NSURL URLWithString:@"link to my server"];

    __block ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
    __weak ASIHTTPRequest *request_b = request;
    request.delegate = self;

    [request setPostValue:@"updatedevice" forKey:@"cmd"];
    [request setPostValue:serialNumber forKey:@"serial_number"]; //get status of this serial number

    [request setCompletionBlock:^
     {
         if([self isViewLoaded])
         {
             [MBProgressHUD hideHUDForView:self.view animated:YES];
             if([request_b responseStatusCode] != 200)
             {
                 ShowErrorAlert(@"Comunication error", @"There was an error communicating with the server");                     
             }
             else
             {                                      
                 NSString *responseString = [request_b responseString];
                 SBJsonParser *parser = [[SBJsonParser alloc] init];
                 NSDictionary *result = [parser objectWithString:responseString error:nil];

                 status = [result objectForKey:@"status"];
                 NSInteger statusInt = [status intValue]; //change to int value

                 //here I want to change cell status in SQLite, but don't know how
                 //something with indexPath.row? valueForKey:@"status"???                
             }
         }
     }];
    [request setFailedBlock:^
     {
         if ([self isViewLoaded])
         {
             [MBProgressHUD hideHUDForView:self.view animated:YES];
             ShowErrorAlert(@"Error", [[request_b error] localizedDescription]);

         }
     }];

    [request startAsynchronous];
}

或者,如果没有苹果通知或者用户没有点击通知消息,那么更好的方法是在我的表视图中更改状态?感谢

编辑: 我不知道如何将数据存储到NSManagedObject *设备。你能帮帮我吗? 我试试这个,但它不起作用:(在你写的地方)

NSInteger statusInt = [status intValue]; //change to int value
NSManagedObject *device = [self.devices objectAtIndex:indexPath.row];
[device setValue:statusInt forKey:@"status"];

EDIT2: 我明白了,但问题在于重装表数据

 NSString *responseString = [request_b responseString];
 SBJsonParser *parser = [[SBJsonParser alloc] init];
 NSDictionary *result = [parser objectWithString:responseString error:nil];

 NSString *status = [result objectForKey:@"status"];
 NSInteger statusInt = [status intValue]; //change to int value
 NSManagedObject *device = [self.devices objectAtIndex:indexPath.row];
 [device setValue:statusInt forKey:@"status"]; //there is problem in save statusInt

// [device setValue:@ 5 forKey:@“status”]; //如果我这样做,那么状态是integer16类型

和第二个问题在于重载表数据。我把它放在那里

[self.tableView reloadData]

但它在循环中一次又一次地重装,出了什么问题?我的东西有无限循环,如果我没有重新加载表数据更改将在下一个应用程序加载中可见。我认为问题是我打电话

- (void) getStatus:(NSString *)serialNumber atIndexPath:(NSIndexPath *)indexPath
{}

in

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
}

最好是在viewDidLoad或viewDidApper中,但我不知道如何为所有设备制作循环并调用

    [self getStatus:[device valueForKey:@"serialNumber"] atIndexPath:indexPath];

在那个地方。

EDIT3:

如果我这样做会怎么样:

- (void)viewDidLoad
{
    [self updateData];
    [self.tableView reloadData];
}
-(void)updateData 
{
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Device"];
    request.returnsDistinctResults = YES;
    //request.resultType = NSDictionaryResultType;
    request.propertiesToFetch = @[@"serialNumber"];

    NSArray *fetchedObjects = [self.managedObjectContext
                               executeFetchRequest:request error:nil];

    NSArray *result = [fetchedObjects valueForKeyPath:@"serialNumber"]; 

    //there I get all serialNumbers of my devices and than I call method getStatus and get "new" status and than update it in Core Data. 
}

这是解决这个问题的好方法吗?如果我只调用一次getStatus方法并获得一系列状态,我认为会更好。

也许我可以将所有serialNubers设置在一个变量('xxx','yyyy','zzz')中,并在服务器上执行SELECT * FROM Devices WHERE serialNumber in(serialNuber)。

你认为这可行吗?我没有经验如何将数据从数组转换为字符串('array_part1','array_part2'....)

1 个答案:

答案 0 :(得分:2)

您的代码在哪里拨打[UITableView reloadData]

一旦从服务器检索到新数据,就应该在tableview上调用reloadData。由于您的服务器调用是异步的,服务器调用将在主线程继续时在单独的线程上运行,因此我假设您有以下问题...

- (void) ...
{
    [self getStatus:@"SERIAL_NUMBER"];
    [self reloadData]; // This will be called before the async server call above has finished
}

因此,您正在重新加载原始数据,因此可能在几秒钟后加载的新数据将不会显示。

要解决此问题,请调整[getStatus:]方法以在服务器响应上调用[UITableView reloadData]方法。

[request setCompletionBlock:^
 {
     if([self isViewLoaded])
     {
         [MBProgressHUD hideHUDForView:self.view animated:YES];
         if([request_b responseStatusCode] != 200)
         {
             ShowErrorAlert(@"Comunication error", @"There was an error communicating with the server");

         }
         else
         {                 

             NSString *responseString = [request_b responseString];
             SBJsonParser *parser = [[SBJsonParser alloc] init];
             NSDictionary *result = [parser objectWithString:responseString error:nil];

             status = [result objectForKey:@"status"];
             NSInteger statusInt = [status intValue]; //change to int value

             // Store the server response in NSManagedObject *device,
             // which will be used as the data source in the tableView:cellForRowAtIndexPath: method

             // Once stored, check the tableview isn't NULL and therefore can be accessed
             // As this call is async the tableview may have been removed and therefore
             // a call to it will crash
             if(tableView != NULL)
             {
                    [tableView reloadData];
             }           
         }
     }
 }];

开发人员也不再支持ASIHTTPRequest,我建议你研究AFNetworking


<强>更新

针对您在设备statusInt内设置NSManagedObject时遇到的问题

 NSManagedObject *device = [self.devices objectAtIndex:indexPath.row];
 [device setValue:statusInt forKey:@"status"]; //there is problem in save statusInt

这是因为statusIntNSInteger,它是主要数据类型,而不是NSObject所期望的[NSManagedObject setValue:forKey:]。从[NSManagedObject setValue:forKey:]的文档中,方法预期参数如下。

- (void)setValue:(id)value forKey:(NSString *)key

因此,在这种情况下,您需要传递NSNumberNSInteger的问题在于,对于基于当前系统的最大dynamic typedef数据类型,它只是int。从NSInteger的实现中你可以看到抽象。

#if __LP64__
typedef long NSInteger;
#else
typedef int NSInteger;
#endif

如果您当前的系统是64位,它将使用更大的long数据类型。

现在,从技术上讲,服务器返回的status值可以按原样存储,而不作为NSString进行任何转换。当您需要检索并使用int的主数据类型时,您可以使用已使用的[NSString intValue]方法。

虽然最佳做法是使用NSNumberFormatter,这对基于locale的号码调整很有用,并确保不存在无效字符。

NSString *status = [result objectForKey:@"status"];
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
NSNumber * statusNumber = [f numberFromString:status];
NSManagedObject *device = [self.devices objectAtIndex:indexPath.row];
[device setValue:statusNumber forKey:@"status"];

要在代码中使用int时检索主数据类型,只需致电[NSNumber intValue]

NSNumber *statusNumber = [device objectForKey:@"status"];
int statusInt = [statusNumber intValue];

至于你在无限循环中遇到的问题,这是由[... getStatus:atIndexPath:]内的调用reloadData引起的,它包含方法调用[UITableView tableView:cellForRowAtIndexPath:]

这是因为reloadData实际上会调用[UITableView tableView:cellForRowAtIndexPath:]。 因此,您的代码将继续如下...

Initial UITableView data load -> tableView:cellForRowAtIndexPath: -> getStatus:atIndexPath: -> Server Response -> reloadData -> tableView:cellForRowAtIndexPath: -> getStatus:atIndexPath: -> Server Response -> reloadData -> ...

不幸的是,您无法强制更新一个单元格,您必须请求UITableView使用reloadData重新加载所有数据。因此,如果可能,您需要调整服务器以返回设备的唯一ID,以便您只能调整NSManagedObject中的更新设备。

getStatus方法的建议更改可能只是使用serialNumber,如果它作为密钥存储在NSManagedObject中。

- (void) getStatus:(NSString*)serialNumber