Set viewController shared public object - c++

I've got ViewController that contains tableView and an array of items to be displayed on that table.
In order to change table content, I could initialise the array and calling reloadData method from within ViewController button callback (see buttonTap method below).
However, I also need to change the table contents from external code outside the scope of viewController, but it's inaccessible.
In the minimal example below I'm using external C/C++ based thread that unsuccessfully attempts to access a viewController method but it fails on compilation due to No known class method for selector 'changeArr'
Any idea how to make viewController public ?
viewController.m
#import "ViewController.h"
#interface ViewController() <NSTableViewDelegate, NSTableViewDataSource>
#property (weak) IBOutlet NSTableView *tableView;
#property NSMutableArray<NSString*> * arr;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.delegate = self;
self.tableView.dataSource = self;
[self.tableView reloadData];
}
- (void)viewWillAppear {
[super viewWillAppear];
[self.tableView reloadData];
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
return self.arr.count;
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
NSTableCellView* cellView =(NSTableCellView*)[tableView makeViewWithIdentifier:#"cell1" owner:nil];
cellView.textField.stringValue = self.arr[row];
return cellView;
}
-(void)changeArr {
self.arr = [NSMutableArray arrayWithObjects:#"ccc", #"ddd", nil];
[self.tableView reloadData];
NSLog(#"fwefwe");
}
- (IBAction)buttonTap:(id)sender {
self.arr = [NSMutableArray arrayWithObjects:#"AAAA", #"BBB", nil];
[self.tableView reloadData];
}
main.m
#import <Cocoa/Cocoa.h>
#include <pthread.h>
#import "ViewController.h"
void updateTable() {
sleep(10);
dispatch_sync(dispatch_get_main_queue(), ^{ [ViewController changeArr]; }); // ERROR : No known class method for selector 'changeArr'
}
int main(int argc, const char * argv[]) {
#autoreleasepool {
// Setup code that might create autoreleased objects goes here.
}
pthread_t threadHandler;
pthread_create(&threadHandler, NULL, &updateTable, NULL);
return NSApplicationMain(argc, argv);
}

changeArr is a private function(-). So you can't call [ViewController changeArr].
If you change changeArr to public fuction(+), it's impossible to access internal array.
So I think the easy way to do this task is using a instance of ViewController by singletone.
...
#implementation ViewController
+ (id) sharedViewController {
static ViewController *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
sharedInstance = [storyboard instantiateViewControllerWithIdentifier:#"MyViewControllerIdentifier"];
});
return sharedInstance ;
}
- (void)viewDidLoad {
[super viewDidLoad];
...
and in updateTable:
dispatch_sync(dispatch_get_main_queue(), ^{ [[ViewController sharedViewController] changeArr]; });

Related

how to get result from .m function to .mm class Objective-C mixed with Qt

I have Qt application, I'm calling UIImagePickerController to get file path for movie the user selected. So I have static functions in Objective-C which call functions from .m file, when user selects movie I can read selected path in another .m function. Question is, how to get this filePath into some Objective-C class where I can call another Qt classes (like Singleton) and pass this result to proper class ?
Objective-C CL_image_call.mm
#include "cl_image_call.h"
#include "cl_image_func.h"
myCLImageClass* CL_image_obj=NULL;
int CL_ImageCCall::CL_objectiveC_Call() {
//Objective C code calling.....
if( CL_image_obj==NULL ) {
//Allocating the new object for the objective C class we created
CL_image_obj=[[myCLImageClass alloc]init];
}
return 1;
}
void CL_ImageCCall::CL_openMedia() {
[CL_image_obj openMedia];
}
CL_image_call.h
#include <QImage>
#include <QString>
#include <QDebug>
#include <stdio.h>
#include "my_singleton.h"
class CL_ImageCCall
{
public:
static int CL_objectiveC_Call();
static void CL_openMedia();
};
CL_image_func.h
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#import <CoreGraphics/CoreGraphics.h>
#import <UIKit/UIKit.h>
#import <MobileCoreServices/MobileCoreServices.h>
#interface myCLImageClass:NSObject
-(void)openMedia;
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info;
#end
CL_image_func.m
#import "cl_image_func.h"
#interface QIOSViewController : UIViewController
#end
#implementation myCLImageClass
UIImagePickerController *picker=NULL;
UIViewController *viewController=NULL;
NSString *f_path;
-(void)openMedia {
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
viewController = (UIViewController *)([keyWindow rootViewController]);
if (!viewController)
return;
if([UIImagePickerController isSourceTypeAvailable:
UIImagePickerControllerSourceTypePhotoLibrary]) {
picker= [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
picker.mediaTypes = [NSArray arrayWithObjects:(NSString *)kUTTypeMovie, nil];
[viewController presentViewController:picker animated:YES completion:NULL];
}
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSString *docDirPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [docDirPath stringByAppendingPathComponent:#"movie.mov"];
NSLog (#"File Path = %#", filePath);
//filePath is what I need to pass to the rest of my app
[viewController dismissViewControllerAnimated:YES completion:nil];
}
So in CL_image_call.h I have my "my_singleton" where I can reach my whole app, but how to get filePath to this class ?
Best Regards
Marek
Here is the code to export media:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSString *docDirPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [docDirPath stringByAppendingPathComponent:#"movie.mov"];
NSLog (#"File Path = %#", filePath);
//filePath is what I need to pass to the rest of my app
NSURL *inputURL = [info objectForKey:UIImagePickerControllerMediaURL];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:inputURL options:nil];
// config the session, maybe some option is not what you need, just config by what you need
AVAssetExportSession *session = [[AVAssetExportSession alloc] initWithAsset:asset
presetName:AVAssetExportPresetMediumQuality];
session.outputURL = [NSURL fileURLWithPath:filePath];
session.outputFileType = AVFileTypeMPEG4;
session.shouldOptimizeForNetworkUse = YES;
[session exportAsynchronouslyWithCompletionHandler:^(void)
{
// do a completion handler
}];
[viewController dismissViewControllerAnimated:YES completion:nil];
}
Then you can visit the output by the filePath, and use when you need.
I have modified code with your suggestion and it does what it should. I'm settings moviePath inside my Qt classes and then I call Objective-C code with this path
NSString *f_path;
//Your objective c code here....
-(void)openMedia:(NSString*)moviePath {
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
f_path=moviePath.copy;
viewController = (UIViewController *)([keyWindow rootViewController]);
if (!viewController)
return;
if([UIImagePickerController isSourceTypeAvailable:
UIImagePickerControllerSourceTypePhotoLibrary]) {
picker= [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
picker.mediaTypes = [NSArray arrayWithObjects:(NSString *)kUTTypeMovie, nil];
[viewController presentViewController:picker animated:YES completion:NULL];
}
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSURL *inputURL = [info objectForKey:UIImagePickerControllerMediaURL];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:inputURL options:nil];
// config the session, maybe some option is not what you need, just config by what you need
AVAssetExportSession *session = [[AVAssetExportSession alloc] initWithAsset:asset
presetName:AVAssetExportPresetMediumQuality];
session.outputURL = [NSURL fileURLWithPath:f_path];
session.outputFileType = AVFileTypeMPEG4;
session.shouldOptimizeForNetworkUse = YES;
[session exportAsynchronouslyWithCompletionHandler:^(void)
{
// do a completion handler
NSLog (#"export complete = %#", f_path);
}];
[viewController dismissViewControllerAnimated:YES completion:nil];
}
with some trick I can view this movie inside Qt app. One thing that is missing:
I need to know somehow that session has been completed, to refresh file path for MediaPlayer. How can I notify my base class CL_ImageCCall about that. Or actually maybe another Objective-C++ class where I can actually mix Qt and Objective-C ?
Best Regards and thanks for Your help
Marek

why I get error when call [myclass alloc] misx c++ to object-c++ to object-c?

i try use call c++ from object c
use B4I
this code but i get error
i already use file.mm
this file Greeting.cpp
#include "Greeting.hpp"
Greeting::Greeting() {
greeting = "Hello C++!";
}
std::string Greeting::greet() {
return greeting;
}
this file Greeting.hpp
#ifndef Greeting_hpp
#define Greeting_hpp
#include <stdio.h>
#include <string>
class Greeting {
std::string greeting;
public:
Greeting();
std::string greet();
};
#endif /* Greeting_hpp */
this file ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
- (void)showGreeting;
#end
this file ViewController.mm
#import "ViewController.h"
#import "Greeting.hpp"
#interface ViewController ()
{
Greeting greeting;
IBOutlet UIButton *helloButton;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)showGreeting {
NSLog(#"test %#");
}
#end
i try call it
ViewController* ViewController1= [[ViewController alloc] init];
[ ViewController1 showGreeting ];
i get error unknown method
-------------------------------
Undefined symbols for architecture arm64:
"_OBJC_CLASS_$_ViewController", referenced from:
objc-class-ref in b4i_main.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
---------------------------------
when i use
ViewController* ViewController1;
[ ViewController1 showGreeting ];
return null
note this example HelloCpp-master
thanks
The following example shows how to call a c++ class from an objc app using a method in AppDelegate. It may be run in Terminal by first saving the source code in a file named 'greet.mm' and then using this command: clang greet.mm -framework Cocoa -o greet && ./greet
//clang greet.mm -framework Cocoa -o greet && ./greet
#import <Cocoa/Cocoa.h>
class Greeting {
public:
void greet();
};
void Greeting::greet() {
NSLog(#"Hello C++!");
}
#interface AppDelegate : NSObject <NSApplicationDelegate>
{
NSWindow *window;
}
- (void) myBtnAction: (id)sender;
- (void) buildMenu;
- (void) buildWnd;
#end
#implementation AppDelegate
- (void) myBtnAction: (id)sender {
Greeting g;
g.greet();
}
- (void) buildMenu {
NSMenu *menubar = [NSMenu new];
NSMenuItem *menuBarItem = [NSMenuItem new];
[menubar addItem:menuBarItem];
[NSApp setMainMenu:menubar];
NSMenu *appMenu = [NSMenu new];
NSMenuItem *quitMenuItem = [[NSMenuItem alloc] initWithTitle:#"Quit"
action:#selector(terminate:) keyEquivalent:#"q"];
[appMenu addItem:quitMenuItem];
[menuBarItem setSubmenu:appMenu];
}
- (void) buildWnd {
#define _wndW 200
#define _wndH 150
window = [ [ NSWindow alloc ] initWithContentRect:NSMakeRect( 0, 0, _wndW, _wndH )
styleMask: NSWindowStyleMaskClosable | NSWindowStyleMaskTitled
backing:NSBackingStoreBuffered defer:YES];
[window center];
[window setTitle: #"Test window" ];
[window makeKeyAndOrderFront:nil];
// **** My Button **** //
NSButton *myBtn =[[NSButton alloc]initWithFrame:NSMakeRect( 40, 80, 125, 24 )];
[myBtn setBezelStyle:NSBezelStyleRounded ];
[myBtn setTitle: #"Run CPP Class"];
[myBtn setAction: #selector (myBtnAction:)];
[[window contentView] addSubview: myBtn];
// **** Quit btn **** //
NSButton *quitBtn = [[NSButton alloc]initWithFrame:NSMakeRect( _wndW - 50, 10, 40, 40 )];
[quitBtn setBezelStyle:NSBezelStyleCircular ];
[quitBtn setTitle: #"Q" ];
[quitBtn setAction:#selector(terminate:)];
[[window contentView] addSubview: quitBtn];
}
- (void) applicationWillFinishLaunching: (NSNotification *)notification {
[self buildMenu];
[self buildWnd];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
}
#end
int main () {
NSApplication *app = [NSApplication sharedApplication];
AppDelegate *appDelegate = [[AppDelegate alloc] init];
[app setDelegate:appDelegate];
[NSApp run];
return 0;
}
Should run in iOS. Change extension of ViewController from .m to .mm
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#end
#import "ViewController.h"
class Greeting {
public:
void greet();
};
void Greeting::greet() {
NSLog(#"Hello C++!");
}
#interface ViewController ()
#property (strong, nonatomic) IBOutlet UIButton *LaunchBtn;
#end
#implementation ViewController
- (IBAction)showGreeting:(id)sender {
Greeting g;
g.greet();
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
#end

Cocos2d: Controller calling method of a CCNode

My GameController.m calls a method of CCNode class. It seems to call it as I can see NSLog but the method is not either operate or not result in view. In other words, screen does not change when calling the method from GameController. (method operates itself in its own class)
How should I change my code to fix it?
Summary:
GameController -> calls -> GoalPost.GoalKeeper.method.
Code:
GameController.m, GoalPost.m and GoalKeeper.m
GameController.m
#import "GameController.h"
#import "cocos2d-ui.h"
#import "GoalPost.h"
#import "Ball.h"
#import "Field.h"
#interface GameController()
#property Field* field;
//#property Ball* ball;
#property GoalPost* post;
#end
#implementation GameController
- (instancetype)init
{
self = [super init];
if (self) {
self.field = [Field node];
// self.ball = [Ball node];
self.post = [GoalPost node];
}
return self;
}
//control computer.
-(void)kickerTurn{
NSLog(#"Player kick");
//random location generation
//call keeper.
int randomKeeperX = arc4random() % (int)self.post.contentSize.width;
int randomKeeperY = arc4random() % (int)self.post.contentSize.height;
CGPoint keeperLoc = ccp(randomKeeperX, randomKeeperY);
[self.post.keeper keeperLocation:keeperLoc];
}
#end
GoalPost.h
#import "GoalKeeper.h"
#interface GoalPost : CCNodeColor { }
#property GoalKeeper* keeper;
#end
GoalPost.m
#import "GoalPost.h"
#import "GoalKeeper.h"
#interface GoalPost ()
#property NSArray* post;
#end
#implementation GoalPost
- (instancetype)init
{
self = [super initWithColor:[CCColor lightGrayColor]];
if (self) {
....
[self addChild:aPost];
}
//Call goal keeper and add.
//for touch to run goal keeper.
[self setUserInteractionEnabled:true];
self.keeper = [GoalKeeper node];
//Put it in front of goal post.
self.keeper.position = ccp(self.contentSize.width/2, 0);
[self addChild:self.keeper];
}
return self;
}
-(void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event{
CGPoint touchLoc = [touch locationInNode:self];
NSLog(#"Goal touched: %f %f", touchLoc.x, touchLoc.y);
//Goal keeper call
[self.keeper keeperLocation:touchLoc];
}
#end
Finally, GoalKeeper.m
.h
#import "cocos2d.h"
#interface GoalKeeper : CCNodeColor { }
-(void)keeperLocation:(CGPoint)location;
#end
.m
#import "GoalKeeper.h"
#interface GoalKeeper()
#property CGPoint touchLoc;
#end
#implementation GoalKeeper
- (instancetype)init
{
self = [super initWithColor:[CCColor blueColor]];
if (self) {
[self setUserInteractionEnabled:TRUE];
[self setContentSize:CGSizeMake(30, 70)];
}
return self;
}
-(void)keeperLocation:(CGPoint)location {
CGPoint offset = ccpSub(location, self.position);
int targetX = self.position.x + offset.x;
int targetY = self.position.y + offset.y;
CGPoint targetPosition = ccp(targetX, targetY);
CCActionMoveBy *actionMoveBy = [CCActionMoveBy actionWithDuration:0.5f position:offset];
[self runAction:actionMoveBy];
}
#end

why my admob doesn't show?

There is no errors in console, and seems everything work. But the bannerView_ just didn't show.
here is my AdMobObject.h
#import "AdMobObject.h"
#import "RootViewController.h"
#import "GADBannerView.h"
#class RootViewController;
#class GADBannerView;
#interface AdMobObject : UIViewController{
RootViewController * viewController;
GADBannerView * bannerView_;
}
+ (AdMobObject *) shared;
- (void) addAdMob;
- ( void) hideAdMob;
#end
here is my AdMobObject.mm
#import "AdMobObject.h"
#import "AppController.h"
#import "RootViewController.h"
#import "EAGLView.h"
#import "cocos2d.h"
#implementation AdMobObject
static AdMobObject* instance;
+(AdMobObject *) shared{
#synchronized(self){
if( instance == nil ){
instance = [[self alloc] init];
}
}
return instance;
}
- (void) addAdMob{
NSLog(#"----------addAdMob");
bannerView_ = [[GADBannerView alloc] initWithAdSize:kGADAdSizeSmartBannerPortrait];
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenHeight = screenRect.size.height;
CGFloat screenWidth = screenRect.size.width;
viewController.view.frame = CGRectMake(0,0,screenWidth,screenHeight);
[bannerView_ setFrame:CGRectMake(0,
screenHeight-bannerView_.bounds.size.height,
//0,
bannerView_.bounds.size.width,
bannerView_.bounds.size.height)];
bannerView_.adUnitID = #"myadmobid";
bannerView_.rootViewController = viewController;
[viewController.view addSubview:bannerView_];
GADRequest *request = [GADRequest request];
// For testing
request.testDevices = [NSArray arrayWithObjects:#"mydeviceid", nil];
[bannerView_ loadRequest:request];
[viewController.view addSubview:bannerView_];
}
- (void) showAdMob{
}
- (void) hideAdMob{
[bannerView_ setHidden:YES];
}
#end
Then i have a class to manage it:
#interface MyGameCenterManager : NSObject
{
}
+(MyGameCenterManager *) shared;
+(void) addAdMob;
+(void) hideAdMob;
#end
here is the class implementation:
#implementation MyGameCenterManager
static MyGameCenterManager *instance;
+(MyGameCenterManager*)shared{
#synchronized(self){
if(instance == nil){
instance = [[self alloc] init];
}
}
return instance;
}
+ (void) addAdMob
{
[[AdMobObject shared] addAdMob];
}
+ (void) hideAdMob{
[[AdMobObject shared] hideAdMob];
}
Finally, I have cpp class:
include "cocos2d.h"
#include "ScriptingCore.h"
namespace ls{
class GameCenterBridge: public cocos2d::CCObject{
public:
static cocos2d::CCScene* scene();
virtual bool init();
CREATE_FUNC(GameCenterBridge);
void addAdMob();
void hideAdMob();
};
}
I called my class:
ls::GameCenterBridge * class = new ls::GameCenterBridge();
class->addAdMob();
I can see the console log:
----------addAdMob
which means it enter into the addAdMob function. And there has no other errors.
But the banner view just didn't show.
the only way I make it show is to add codes into AppController.mm in didFinishLaunchingWithOptions. But I am just wondering why it won't work for my self created class.
When i was implementing AdMob i showed the ad only when it got loaded from the server. You can achieve this by implementing GADBannerViewDelegate, especially interesting methods are
- (void)adViewDidReceiveAd:(GADBannerView *)bannerView;
- (void)adView:(GADBannerView *)bannerView didFailToReceiveAdWithError:(GADRequestError *)error
This way you can be sure that you received the ad.

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.