connecting 2 or more tableviews to display information from the same nsarray - nsarray

I'm confussed and tried many different things to eliminate the errors im facing.
I have a NSArray of proteins, in my storyboard i have two seperate table views that i wish to both display the protiens array.
i am having errors at -(UITableViewcell *) and #end of which there are two } errors.
If anyone can help please find my ViewController.m code below: (Please disregard the segue coding near the end)
#import "JViewController.h"
#import "OneViewController.h"
#import "TwoViewController.h"
#interface JViewController ()
#end
#implementation JViewController
{
NSArray *proteins;
}
#synthesize tableView1;
#synthesize tableView2;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
proteins = [NSArray arrayWithObjects:#"Chicken", #"Turkey", #"Ham", #"Steak", #"Pork Chop", #"Roast Lamb", #"Salmon", #"Egg", #"Lentils", #"Kidney Beans", nil];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == tableView1) {
return [proteins count];
{
if (tableView == tableView2) {
return [proteins count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ProteinCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if (tableView1 == tableView2)
{
proteins;
}
else
{
proteins;
}
cell.textLabel.text = [proteins objectAtIndex:indexPath.row];
return cell;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"showMealOneDetail"])
{
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
OneViewController *destViewController = segue.destinationViewController;
destViewController.proteinName = [proteins objectAtIndex:indexPath.row];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Big thankyou in advance.

You have two open brackets here, close this one:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == tableView1) {
return [proteins count];
->> {
if (tableView == tableView2) {
return [proteins count];
}
}
And what do you mean with this?
if (tableView1 == tableView2)
{
proteins;
}
else
{
proteins;
}
Try this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ProteinCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [proteins objectAtIndex:indexPath.row];
return cell;
}
Have you been able to show anything in one tableview? Make sure you have connected dataSource and delegate.
Hope it helps =)

Related

AWSSignInProvider for OpenID using Mobile Hub

I have an iOS 7 app that uses AWS Mobile Hub for AWS services. I am using Auth0 as an identity broker. All of my users authenticate using OpenID.
In order to use Mobile Hub I have to define an AWSSignInProvider for Auth0 users. Has anyone written a (possibly generic?) AWSSignInProvider in Objective C for OpenID users? I have written one but it does not work correctly. It works for the initial login but does not refresh the Auth0 login for Auth0 users when they re-login to my app.
My code is below. It does not work right.
Thanks
Auth0SignInProvider.h
#import <Foundation/Foundation.h>
#import <Lock/Lock.h>
#import "AWSSignInProvider.h"
#class FFKeyChain;
#pragma clang assume_nonnull begin
#interface FFAuth0SignInProvider : NSObject <AWSSignInProvider>
#property (readonly, strong) FFKeyChain * keychain;
+ (instancetype)sharedInstance;
- (void)completeLogin;
#end
#pragma clang assume_nonnull end
Auth0SignInProvider.m
#import "Auth0SignInProvider.h"
#import <Lock/Lock.h>
#import "AWSIdentityManager.h"
#import "AWSConfiguration.h"
#import "Cloud.h"
#import "FFCloudController.h"
#import "FFJWT.h"
#import "FFKeyChain.h"
static NSString *const AWSAuth0SignInProviderKey = #"Auth0";
static NSString *const AWSAuth0SignInProviderUserNameKey = #"Auth0.userName";
static NSString *const AWSAuth0SignInProviderImageURLKey = #"Auth0.imageURL";
static NSTimeInterval const AWSAuth0SignInProviderTokenRefreshBuffer = 10 * 60;
#interface AWSIdentityManager()
- (void)completeLogin;
#end
#interface FFAuth0SignInProvider()
#property (atomic, strong) AWSTaskCompletionSource *taskCompletionSource;
#property (nonatomic, strong) dispatch_semaphore_t semaphore;
#end
#implementation FFAuth0SignInProvider
#synthesize keychain=_keychain;
+ (instancetype)sharedInstance {
static FFAuth0SignInProvider *_sharedInstance = nil;
static dispatch_once_t onceToken;
//ok1
dispatch_once(&onceToken, ^{
_sharedInstance = [FFAuth0SignInProvider new];
});
return _sharedInstance;
}
- (instancetype)init {
if (self = [super init])
{
_keychain = [FFKeyChain sharedInstance];
}
return self;
}
#pragma mark - Properties
- (FFKeyChain*)keychain
{
if (!_keychain)
{
_keychain = [FFKeyChain sharedInstance];
}
return _keychain;
}
#pragma mark - AWSIdentityProvider Protocol
- (NSString *)identityProviderName
{
return [[NSBundle mainBundle] infoDictionary][STRAuth0DomainKey];
}
- (AWSTask<NSString *> *)token
{
NSString* tokenString = [[self keychain] stringForKey:UDAuth0IdToken];
NSString* refreshToken = [[self keychain] stringForKey:UDAuth0RefreshToken];
NSDate* idTokenExpirationDate = [[[FFJWT alloc] initWithToken:tokenString] expDate];
if (tokenString
// If the cached token expires within 10 min, tries refreshing a token.
&& [idTokenExpirationDate compare:[NSDate dateWithTimeIntervalSinceNow:AWSAuth0SignInProviderTokenRefreshBuffer]] == NSOrderedDescending) {
return [AWSTask taskWithResult:tokenString];
}
AWSTaskCompletionSource *taskCompletionSource = [AWSTaskCompletionSource taskCompletionSource];
A0Lock* lock = [A0Lock sharedLock];
A0APIClient* client = [lock apiClient];
A0AuthParameters* params = [A0AuthParameters newDefaultParams];
params[A0ParameterConnection] = [[FFCloudController sharedInstance] auth0ConnectionName:[[NSUserDefaults standardUserDefaults] integerForKey:UDLoginType]];
[client fetchNewIdTokenWithRefreshToken:refreshToken parameters:params success:^(A0Token * _Nonnull token)
{
NSLog(#"Success - token");
[[self keychain] setString:token.idToken ForKey:UDAuth0IdToken];
taskCompletionSource.result = token.idToken;
} failure:^(NSError * _Nonnull error)
{
NSLog(#"Error - token: %#", error);
taskCompletionSource.error = error;
}];
return taskCompletionSource.task;
}
- (BOOL)isLoggedIn {
NSData* profile = [[self keychain] dataForKey:UDAuth0Profile];
return [[NSUserDefaults standardUserDefaults] objectForKey:AWSAuth0SignInProviderKey] != nil && (profile != nil);
}
- (NSString *)userName {
return [[NSUserDefaults standardUserDefaults] objectForKey:AWSAuth0SignInProviderUserNameKey];
}
- (void)setUserName:(NSString *)userName {
[[NSUserDefaults standardUserDefaults] setObject:userName
forKey:AWSAuth0SignInProviderUserNameKey];
}
- (NSURL *)imageURL {
return [NSURL URLWithString:[[NSUserDefaults standardUserDefaults] objectForKey:AWSAuth0SignInProviderImageURLKey]];
}
- (void)setImageURL:(NSURL *)imageURL {
[[NSUserDefaults standardUserDefaults] setObject:imageURL.absoluteString
forKey:AWSAuth0SignInProviderImageURLKey];
}
- (void)reloadSession {
if ([[NSUserDefaults standardUserDefaults] objectForKey:AWSAuth0SignInProviderKey]
&& [[self keychain] stringForKey:UDAuth0IdToken])
{
[self completeLogin];
}
}
- (void)completeLogin {
[[NSUserDefaults standardUserDefaults] setObject:#"YES"
forKey:AWSAuth0SignInProviderKey];
[[NSUserDefaults standardUserDefaults] synchronize];
[[AWSIdentityManager sharedInstance] completeLogin];
A0UserProfile *profile = [NSKeyedUnarchiver unarchiveObjectWithData:[[self keychain] dataForKey:UDAuth0Profile]];
self.userName = profile.email;
}
- (void)login
{
if ([[self keychain] stringForKey:UDAuth0IdToken])
{
[self completeLogin];
return;
}
A0AuthParameters* params = [A0AuthParameters newDefaultParams];
params[A0ParameterConnection] = [[FFCloudController sharedInstance] auth0ConnectionName:[[NSUserDefaults standardUserDefaults] integerForKey:UDLoginType]];
[[[A0Lock sharedLock] apiClient] loginWithUsername:[[FFKeyChain sharedInstance] stringForKey:UDEmail] password:[[FFKeyChain sharedInstance] stringForKey:UDPassword] parameters:params success:^(A0UserProfile * _Nonnull profile, A0Token * _Nonnull tokenInfo)
{
[[self keychain] setString:tokenInfo.idToken ForKey:UDAuth0IdToken];
[[self keychain] setString:tokenInfo.refreshToken ForKey:UDAuth0RefreshToken];
[[self keychain] setData:[NSKeyedArchiver archivedDataWithRootObject:profile] ForKey:UDAuth0Profile];
[self completeLogin];
} failure:^(NSError * _Nonnull error)
{
NSLog(#"Auth0 login error: %#", error);
}];
}
- (void)logout
{
[[NSUserDefaults standardUserDefaults] removeObjectForKey:AWSAuth0SignInProviderKey];
[[self keychain] removeObjectForKey:UDAuth0IdToken];
[[self keychain] removeObjectForKey:UDAuth0AccessToken];
[[self keychain] removeObjectForKey:UDAuth0RefreshToken];
[[self keychain] removeObjectForKey:UDAuth0Profile];
[[[A0Lock sharedLock] apiClient] logout];
}
#pragma mark - Application delegates
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
return YES;
}
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
return [[A0Lock sharedLock] handleURL:url sourceApplication:sourceApplication];
}
#end
It will only attempt to get an updated logins map if the credentials associated with the CredentialsProvider have expired. Have you tried calling clearCredentials on the CredentialsProvider when you log out to force it to refresh?

EXC_BAD_ACCESS when using NSKeyedUnarchiver to load saved state

I have subclassed a CCSprite to make an object that can be encoded and decoded. I want to save sprites state and load it again at particular positions. Everything seems to be okay apart from decoding with NSKeyedUnarchiver (see loadIconSpriteState below) which gives an EXC_BAD_ACCESS Below is the code:
HelloWorldLayer.h
#interface CCIconSprite : CCSprite {
NSString *iconName;
float iconXPos;
float iconYPos;
}
#property (nonatomic, retain) NSString *iconName;
#property (nonatomic, assign) float iconXPos;
#property (nonatomic, assign) float iconYPos;
+ (id)iconWithType:(NSString*)imageName;
- (id)initWithIconType:(NSString*)imageName;
#end
#interface HelloWorldLayer : CCLayer < NSCoding>
{
CCIconSprite* testSprite;
BOOL savedState;
CGSize size;
CCMoveTo* moveTo;
NSMutableArray* saveSpriteArray;
NSData* savedSpriteData;
}
+(CCScene *) scene;
#end
HelloWorldLayer.m:
CCIconSprite implementation:
#implementation CCIconSprite
#synthesize iconXPos;
#synthesize iconYPos;
#synthesize iconName;
+ (id)iconWithType:(NSString*)imageName
{
return [[[[self class] alloc] initWithIconType:imageName] autorelease];
}
- (id)initWithIconType:(NSString*)imageName
{
self = [super initWithFile:imageName];
if (!self) return nil;
iconName = imageName;
self.position = ccp(iconXPos, iconYPos);
return self;
}
Encoding and decoding:
- (id)initWithCoder:(NSCoder *)decoder {
NSString* imageFileName = [decoder decodeObjectForKey:#"imageFileName"];
self = [self initWithIconType:imageFileName];
if (!self) return nil;
self.iconXPos = [decoder decodeFloatForKey:#"iconXPos"];
self.iconYPos = [decoder decodeFloatForKey:#"iconYPos"];
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:iconName forKey:#"imageFileName"];
[encoder encodeFloat:self.iconXPos forKey:#"iconXPos"];
[encoder encodeFloat:self.iconYPos forKey:#"iconYPos"];
}
#end
HelloWorldLayer implementation:
#implementation HelloWorldLayer
+(CCScene *) scene
{
CCScene *scene = [CCScene node];
HelloWorldLayer *layer = [HelloWorldLayer node];
[scene addChild: layer];
return scene;
}
-(id) init
{
if( (self=[super init]) ) {
self.scale = 0.5;
savedState = NO;
size = [[CCDirector sharedDirector] winSize];
testSprite = [CCIconSprite spriteWithFile:#"Icon.png"];
testSprite.position = ccp(size.width/4, size.width/4);
testSprite.anchorPoint = ccp(0,0);
[self addChild:testSprite];
moveTo = [CCMoveTo actionWithDuration:3 position:ccp(3*size.width/4, 3*size.width/4)];
[testSprite runAction:moveTo];
[self schedule:#selector(saveAndLoadSpriteState)];
}
return self;
}
Saving and loading state:
-(void)saveIconSpriteState {
saveSpriteArray = [[NSMutableArray alloc] init];
[saveSpriteArray addObject:testSprite];
savedSpriteData = [NSKeyedArchiver archivedDataWithRootObject:saveSpriteArray];
}
-(void)loadIconSpriteState {
[NSKeyedUnarchiver unarchiveObjectWithData:savedSpriteData];
}
-(void)saveAndLoadSpriteState {
if ((testSprite.position.x > size.width/2) && !savedState) {
savedState = YES;
[self saveIconSpriteState];
}
else if ((testSprite.position.x == 3*size.width/4) && savedState) {
savedState = NO;
[self loadIconSpriteState];
[testSprite runAction:moveTo];
}
}
#end
EDIT
After setting an exception break point I got the following error
Assertion failure in -[CCIconSprite initWithFile:]
pointing to the in line:
NSAssert(filename != nil, #"Invalid filename for sprite");
in the
-(id) initWithFile:(NSString*)filename
method of the CCSprite.m class.
Just a hunch but maybe that's it. When decoding a string, you should copy it because you don't own it at this point, nor are you assigning it to a strong or copy property.
- (id)initWithCoder:(NSCoder *)decoder {
NSString* imageFileName = [[decoder decodeObjectForKey:#"imageFileName"] copy];
self = [self initWithIconType:imageFileName];
if (!self) return nil;
...
}
If that's not it, set a breakpoint and verify that the string is indeed correct when encoding and when decoding.

layer in menu singleton not displaying

I have made a singleton to handle menu changes (navigating through menu hierarchy) in my game. I don't know why the menu is not displaying. Please see my MenuManager classes singleton below.
MenuManager.h:
#interface MenuManager : NSObject
{
MenuLayerTypes currentMenuLayer;
CCLayer* runningLayer;
CCLayer* oldLayer;
}
+(MenuManager*)sharedMenuManager;
-(void)runMenuWithID:(MenuLayerTypes)menuLayerID;
#end
MenuManager.m:
#import "MenuManager.h"
#implementation MenuManager
static MenuManager* _sharedMenuManager = nil;
+(MenuManager*)sharedMenuManager
{
#synchronized([MenuManager class])
{
if (!_sharedMenuManager)
[[self alloc] init];
return _sharedMenuManager;
}
return nil;
}
+(id)alloc
{
#synchronized ([MenuManager class])
{
NSAssert(_sharedMenuManager == nil,
#"Attempted to allocated a second instance of the Menu Manager singleton");
_sharedMenuManager = [super alloc];
return _sharedMenuManager;
}
return nil;
}
-(id)init
{
self = [super init];
if (self != nil)
{
Layer = kMainMenuLayer; // default menu type
}
return self;
}
-(void)runMenuWithID:(MenuLayerTypes)menuLayerID {
//MenuLayerTypes oldMenuLayer = currentMenuLayer;
currentMenuLayer = menuLayerID;
oldLayer.tag = 321;
switch (menuLayerID)
{
case kMainMenuLayer:
CCLOG(#"main menu layer");
runningLayer = [MainMenuLayer node];
oldLayer = runningLayer;
break;
case kSettingsLayer:
runningLayer = [SettingsLayer node];
oldLayer = runningLayer;
break;
case kAckLayer:
runningLayer = [AckLayer node];
oldLayer = runningLayer;
break;
default:
CCLOG(#"Unknown ID");
return;
break;
}
MainMenuScene* mainMenuScene = (MainMenuScene *)[[CCDirector sharedDirector] runningScene];
// checks to see if a menu layer is already displayed before replacing it
if (![mainMenuScene getChildByTag:321]) {
[mainMenuScene addChild:runningLayer];
}
else {
[mainMenuScene removeChildByTag:321];
[mainMenuScene addChild:runningLayer];
}
}
#end
Also in the init of the MainMenuScene:
[[MenuManager sharedMenuManager] runMenuWithID:kMainMenuLayer];
How can I solve this display issue?

Cocos2d animation resume

I've added iAP in cocos2d following the ray tutorials (but changed a few things to fit cocos2d) but after I make a transaction the console log says 2013-08-19 16:32:12.626 Game[2483:907] Buying *product* ...
2013-08-19 16:32:13.208 Game[2483:907] cocos2d: animation stopped
2013-08-19 16:32:16.690 Game[2483:907] completeTransaction...
2013-08-19 16:32:16.725 Game[2483:907] User defaults for *product* are YES
So I know the transaction works, but the game never resumes it just freezes. Is there a way to resume the game after the transaction? [[CCDirector sharedDirector]resume] doesn't work so I think it may have to do with UIAlert View. Any help? Here is my iAPHelper.mm:
#import "IAPHelper.h"
#import <StoreKit/StoreKit.h>
NSString *const IAPHelperProductPurchasedNotification
#"IAPHelperProductPurchasedNotification";
#interface IAPHelper () <SKProductsRequestDelegate, SKPaymentTransactionObserver>
#end
#implementation IAPHelper {
SKProductsRequest * _productsRequest;
RequestProductsCompletionHandler _completionHandler;
NSSet * _productIdentifiers;
NSMutableSet * _purchasedProductIdentifiers;
}
- (id)initWithProductIdentifiers:(NSSet *)productIdentifiers {
if ((self = [super init])) {
// Store product identifiers
_productIdentifiers = productIdentifiers;
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
// Check for previously purchased products
_purchasedProductIdentifiers = [NSMutableSet set];
for (NSString * productIdentifier in _productIdentifiers) {
BOOL productPurchased = [[NSUserDefaults standardUserDefaults] boolForKey:productIdentifier];
if (productPurchased) {
[_purchasedProductIdentifiers addObject:productIdentifier];
NSLog(#"Previously purchased: %#", productIdentifier);
} else {
NSLog(#"Not purchased: %#", productIdentifier);
}
}
}
return self;
}
- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler {
_completionHandler = [completionHandler copy];
_productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers];
_productsRequest.delegate = self;
[_productsRequest start];
}
#pragma mark - SKProductsRequestDelegate
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSLog(#"Loaded list of products...");
_productsRequest = nil;
NSArray * skProducts = response.products;
for (SKProduct * skProduct in skProducts) {
NSLog(#"Found product: %# %# %0.2f",
skProduct.productIdentifier,
skProduct.localizedTitle,
skProduct.price.floatValue);
}
_completionHandler(YES, skProducts);
_completionHandler = nil;
}
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
NSLog(#"Failed to load list of products.");
_productsRequest = nil;
_completionHandler(NO, nil);
_completionHandler = nil;
}
- (BOOL)productPurchased:(NSString *)productIdentifier {
return [_purchasedProductIdentifiers containsObject:productIdentifier];
}
- (void)buyProduct:(SKProduct *)product {
NSLog(#"Buying %#...", product.productIdentifier);
SKPayment * payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction * transaction in transactions) {
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
default:
break;
}
};
}
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"completeTransaction...");
[self provideContentForProductIdentifier:transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"restoreTransaction...");
[self provideContentForProductIdentifier:transaction.originalTransaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"failedTransaction...");
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(#"Transaction error: %#", transaction.error.localizedDescription);
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void)provideContentForProductIdentifier:(NSString *)productIdentifier {
[_purchasedProductIdentifiers addObject:productIdentifier];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:productIdentifier];
[[NSUserDefaults standardUserDefaults] synchronize];
[[NSNotificationCenter defaultCenter] postNotificationName:IAPHelperProductPurchasedNotification object:productIdentifier userInfo:nil];
NSLog(#"User defaults for %# are YES", productIdentifier);
}
- (void)restoreCompletedTransactions {
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
#end
Before you popup your alert view try running...
[[CCDirector sharedDirector] stopAnimation];
When you get a response back from the user in the alertview callback or transaction completion run...
[[CCDirector sharedDirector] startAnimation];
Cocos doesn't often play well with UIKit/Cocoa callbacks so you need to pause rendering while they do their thing. We have similar behaviour for when the app goes into background/foreground in the app delegate...
-(void) applicationDidEnterBackground:(UIApplication*)application {
[[CCDirector sharedDirector] stopAnimation];
}
-(void) applicationWillEnterForeground:(UIApplication *)application {
[[CCDirector sharedDirector] startAnimation];
}
-(void)applicationDidBecomeActive:(UIApplication *)application {
[[CCDirector sharedDirector] resume];
}
Seems we use resume inside the applicationDidBecomeActive. Can't recall the full logic there but that has seemed to work for a bunch of our projects.

UITableView is not reloading

here is the situation:
I have a "UITableViewController" which loads objects with RestKits "RKFetchedResultsTableController". After clicking on a cell I switch to a detail UITableViewController also driven by a "RKFetchedResultsTableController" which gives me a corresponding answer text for the selected object.
The problem is now, if I go back to the first "UITableViewController" and select another object in the table the old answer text from the previous selected object is in the detail table. If I use the "pullToRefresh" function the table gets refreshed and the correct answer is loading.
Why is the old answer from the previous object still in the tableView and not the correct answer for the new selected Object even if I tell [tableController loadTable] in the viewWillAppear method.
AppDelegate:
#
interface AppDelegate ()
#property (nonatomic, strong, readwrite) RKObjectManager *objectManager;
#property (nonatomic, strong, readwrite) RKManagedObjectStore *objectStore;
#end;
#implementation AppDelegate
#synthesize window = _window, isAuthenticated;
#synthesize objectManager;
#synthesize objectStore;
- (void)initializeRestKit
{
//self.objectManager = [RKObjectManager managerWithBaseURLString:#"http://falling-ocean-1302.herokuapp.com"];
self.objectManager = [RKObjectManager managerWithBaseURLString:#"http://falling-ocean-1302.herokuapp.com"];
self.objectManager.serializationMIMEType = RKMIMETypeJSON;
self.objectManager.acceptMIMEType = RKMIMETypeJSON;
self.objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:#"MMmtvzme.sqlite"];
self.objectManager.objectStore = self.objectStore;
self.objectManager.mappingProvider = [MMMappingProvider mappingProviderWithObjectStore:self.objectStore];
self.objectManager.client.cachePolicy = RKRequestCachePolicyNone;
RKLogConfigureByName("RestKit", RKLogLevelTrace);
RKLogConfigureByName("RestKit/Network", RKLogLevelTrace);
RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelTrace);
RKLogConfigureByName("RestKit/Network/Queue", RKLogLevelTrace);
// Enable automatic network activity indicator management
objectManager.client.requestQueue.showsNetworkActivityIndicatorWhenBusy = YES;
[objectManager.router routeClass:[MMRequest class] toResourcePath:#"/requests" forMethod:RKRequestMethodPOST];
[objectManager.router routeClass:[MMRequest class] toResourcePath:#"/requests" forMethod:RKRequestMethodDELETE];
[objectManager.router routeClass:[MMAnswer class] toResourcePath:#"/requests/:request_id/answers" forMethod:RKRequestMethodPOST];
}
Ok this is the code from the first UITableViewController
#interface MMMyRequestList ()
#property (nonatomic, strong) RKFetchedResultsTableController *tableController;
#end
#implementation MMMyRequestList
#synthesize tableController;
- (void)viewDidLoad
{
[super viewDidLoad];
[self configureRKTableController];
[self configureCellMapping];
[self useCustomNib];
}
- (void)configureRKTableController{
self.tableController = [[RKObjectManager sharedManager] fetchedResultsTableControllerForTableViewController:self];
self.tableController.autoRefreshFromNetwork = YES;
self.tableController.pullToRefreshEnabled = YES;
self.tableController.resourcePath = #"/requests";
self.tableController.variableHeightRows = YES;
NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:#"request_id" ascending:NO];
self.tableController.sortDescriptors = [NSArray arrayWithObject:descriptor];
tableController.canEditRows = YES;
}
- (void)configureCellMapping{
RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping cellMapping];
cellMapping.cellClassName = #"MMRequestCell";
cellMapping.reuseIdentifier = #"MMRequest";
cellMapping.rowHeight = 100.0;
[cellMapping mapKeyPath:#"title" toAttribute:#"requestLabel.text"];
[cellMapping mapKeyPath:#"user.first_name" toAttribute:#"userLabel.text"];
cellMapping.onSelectCellForObjectAtIndexPath = ^(UITableViewCell *cell, id object, NSIndexPath* indexPath)
{
MMMyRequestListAnswer * uic = [self.storyboard instantiateViewControllerWithIdentifier:#"MMMyRequestListAnswer"];
MMRequest *request = [self.tableController objectForRowAtIndexPath:indexPath];
if ([uic respondsToSelector:#selector(setRequest:)]) {
[uic setRequest:request];
}
[self.navigationController pushViewController:uic animated:YES];
};
[tableController mapObjectsWithClass:[MMRequest class] toTableCellsWithMapping:cellMapping];
}
- (void)useCustomNib{
[self.tableView registerNib:[UINib nibWithNibName:#"MMRequestCell" bundle:nil] forCellReuseIdentifier:#"MMRequest"];
}
- (void)objectLoader:(RKObjectLoader*)objectLoader didFailWithError:(NSError*)error {
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
NSLog(#"Hit error: %#", error);
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
/**
Load the table view!
*/
[tableController loadTable];
}
After clicking on a cell the Detail UIViewController come into action
interface MMMyRequestListAnswer ()
#property (nonatomic, strong) RKFetchedResultsTableController *tableController;
#end
#implementation MMMyRequestListAnswer
#synthesize tableHeaderView, requestTextLabel;
#synthesize request;
#synthesize tableController;
- (void)viewDidLoad
{
[super viewDidLoad];
[self configureRKTableController];
[self configureCellMapping];
[self useCustomNib];
}
- (void)configureRKTableController{
self.tableController = [[RKObjectManager sharedManager] fetchedResultsTableControllerForTableViewController:self];
self.tableController.autoRefreshFromNetwork = YES;
self.tableController.pullToRefreshEnabled = YES;
self.tableController.resourcePath = [NSString stringWithFormat:#"/requests/%i/answers", [self.request.request_id intValue]];
self.tableController.variableHeightRows = YES;
NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:#"answer_id" ascending:NO];
self.tableController.sortDescriptors = [NSArray arrayWithObject:descriptor];
}
- (void)configureCellMapping{
RKTableViewCellMapping *cellMapping = [RKTableViewCellMapping cellMapping];
cellMapping.cellClassName = #"MMRequestAnswerCell";
cellMapping.reuseIdentifier = #"MMAnswer";
cellMapping.rowHeight = 80.0;
[cellMapping mapKeyPath:#"text" toAttribute:#"answerLabel.text"];
[cellMapping mapKeyPath:#"user.first_name" toAttribute:#"userLabel.text"];
[tableController mapObjectsWithClass:[MMAnswer class] toTableCellsWithMapping:cellMapping];
}
- (void)useCustomNib{
[self.tableView registerNib:[UINib nibWithNibName:#"MMRequestAnswerCell" bundle:nil] forCellReuseIdentifier:#"MMAnswer"];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
/**
Load the table view!
*/
[tableController loadTable];
}
The Object Mapping is handled in this Class:
#implementation MMMappingProvider
#synthesize objectStore;
+ (id)mappingProviderWithObjectStore:(RKManagedObjectStore *)objectStore {
return [[self alloc] initWithObjectStore:objectStore];
}
- (id)initWithObjectStore:(RKManagedObjectStore *)theObjectStore {
self = [super init];
if (self) {
self.objectStore = theObjectStore;
[self setObjectMapping:[self requestObjectMapping] forResourcePathPattern:#"/requests" withFetchRequestBlock:^NSFetchRequest *(NSString *resourcePath) {
NSFetchRequest *fetchRequest = [MMRequest fetchRequest];
return fetchRequest;
}];
[self setObjectMapping:[self answerObjectMapping] forResourcePathPattern:#"/requests/:request_id/answers" withFetchRequestBlock:^NSFetchRequest *(NSString *resourcePath) {
NSFetchRequest *fetchRequest = [MMAnswer fetchRequest];
fetchRequest.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"answer_id" ascending:YES]];
return fetchRequest;
}];
[self setSerializationMapping:[self.requestObjectMapping inverseMapping] forClass:[MMRequest class]];
[self setSerializationMapping:[self.answerObjectMapping inverseMapping] forClass:[MMAnswer class]];
[self setSerializationMapping:[self.userObjectMapping inverseMapping] forClass:[MMUser class]];
}
return self;
}
- (RKManagedObjectMapping *)userObjectMapping {
RKManagedObjectMapping *mapping = [RKManagedObjectMapping mappingForEntityWithName:#"MMUser" inManagedObjectStore:self.objectStore];
mapping.primaryKeyAttribute = #"user_id";
[mapping mapAttributes:#"first_name", nil];
[mapping mapKeyPathsToAttributes:
#"id", #"user_id",
nil];
return mapping;
}
- (RKManagedObjectMapping *)answerObjectMapping {
RKManagedObjectMapping *mapping = [RKManagedObjectMapping mappingForEntityWithName:#"MMAnswer" inManagedObjectStore:self.objectStore];
mapping.primaryKeyAttribute = #"answer_id";
[mapping mapAttributes:#"text",#"request_id",#"user_id", nil];
[mapping mapKeyPathsToAttributes:
#"id", #"answer_id",
nil];
[mapping mapKeyPath:#"user" toRelationship:#"user" withMapping:[self userObjectMapping]];
[mapping mapKeyPath:#"request" toRelationship:#"request" withMapping:[self requestObjectMapping]];
return mapping;
}
- (RKManagedObjectMapping *)requestObjectMapping {
RKManagedObjectMapping *mapping = [RKManagedObjectMapping mappingForEntityWithName:#"MMRequest" inManagedObjectStore:self.objectStore];
mapping.primaryKeyAttribute = #"request_id";
[mapping mapAttributes:#"title",#"user_id", nil];
[mapping mapKeyPathsToAttributes:
#"id", #"request_id",
nil];
[mapping mapKeyPath:#"user" toRelationship:#"user" withMapping:[self userObjectMapping]];
return mapping;
}
OK Figured it out!!! Some digging revealed that I was loading my UITableviewcontroller BEFORE the mapping provider.
The fix was, to take [self initialiserestkit] method in - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
and put it before any of the code in this method (i.e. make [self initialiserestkit] the first line of the didfinishlaunchingwithoptions method.
Issue solved. Now the tableview is loaded AFTER the mappings so everything works as it should.