Objective-c calling a method from class method - ios

I am trying to access an instance method from a class method. I am getting this error
+[ActiveVC goToDashBoard]: unrecognized selector sent to class 0x112010
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[ActiveVC goToDashBoard]:
unrecognized selector sent to class 0x112010'
My code
+ (void) removeClosedVisitor:(NSString *) visitorID{
for (NSInteger i = activelist.count - 1; i >= 0 ; i--) {
ActiveItemObject *item = [activelist objectAtIndex:i];
if ([visitorID isEqualToString:item.VisitorId]) {
NSLog(#"Removing Visitor from Active List -- %#", visitorID);
[activelist removeObjectAtIndex:i];
//[self.incommingTable reloadData];
// NSDictionary *activeDictionary = [[NSDictionary alloc] init];
// activeDictionary = [activelist mutableCopy];
//
// [[NSNotificationCenter defaultCenter]
// postNotificationName:#"PassData"
// object:nil
// userInfo:activeDictionary];
[[self class] goToDashBoard];
}
}
}
- (void) goToDashBoard{
NSLog(#"Segue to Dashboard");
UITabBarController *dvc = [self.storyboard instantiateViewControllerWithIdentifier:#"id_tabView"];
[dvc setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[self presentViewController:dvc animated:YES completion:nil];
}
can some one help me to fix this issue . tnx.

You need to create an instance of your class or convert your class to a singleton. For example: [[ActiveVC sharedInstance] goToDashBoard];
Here's how you create a Singleton Class:
First, create a New file and subclass it from NSObject. Name it anything, we will use CommonClass here. Xcode will now generate CommonClass.h and CommonClass.m files for you.
In your CommonClass.h file:
#import <Foundation/Foundation.h>
#interface CommonClass : NSObject {
}
+ (CommonClass *)sharedObject;
#property NSString *commonString;
#end
In your CommonClass.m File:
#import "CommonClass.h"
#implementation CommonClass
+ (CommonClass *)sharedObject {
static CommonClass *sharedClass = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedClass = [[self alloc] init];
});
return sharedClass;
}
- (id)init {
if (self = [super init]) {
self.commonString = #"this is string";
}
return self;
}
#end

If you want to call instance method then you will need an instance variable so create instance variable of this class and call it.

Make goToDashBoard a class method. Since you are not creating any instance here, if it is not a class method then it can't be executed.
+ (void) goToDashBoard

Do you actually have an instance anywhere? If not you will have to create one:
[self.sharedInstance goToDashBoard]
[[self alloc] init] goToDashBoard]
I assume you do have an instance, because its looks like its a view controller. In which case I suggest you pass the instance into the static method.
+ (void) removeClosedVisitor:(NSString *) visitorID viewController: (xxx) viewController {

Related

How to initialise a VC or Class iOS, ObjectiveC

When a button is clicked at FirstVC, it will pass data and trigger SecondVC using NSNotificationCenter
During initial launch of the app, because SecondVC has not been initialize yet, so data cannot be passed to SecondVC. NSNotificationCenter cannot function properly. Only after SecondVC has been initialize, NSNotificationCenter will function correctly.
So I need to initialise SecondVC somewhere. Will it be at - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions?
Or how do I programatically call the tab of SecondVC.
FirstVC
#import "Search.h"
#import "Classes.h"
#import "MyTabBarController.h"
#interface Search(){
AppDelegate *appDelegate;
CERangeSlider* _rangeSlider;
NSString *sURL, *strResult, *sRemaining, *sStartTime, *sEndTime, *sSelectedLat, *sSelectedLong;
}
#end
#implementation Search
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (IBAction)btnSearch:(UIButton *)sender {
self.tabBarController.selectedIndex = 1;
sURL = #"Testing 123";
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:sURL forKey:#"theURL"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"toClasses" object:nil userInfo:userInfo];
}
#end
Second VC
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(receiveTestNotification:)
name:#"toClasses"
object:nil];
dtDate = [[NSMutableArray alloc] init]; //=== Mutable array to store the dates generated
self.currentPageIndex = 0;
[self setupSegmentButtons];
NSDate *now = [NSDate date];
NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"dd/MM/YYYY"];
sDtDate = [dateFormatter stringFromDate:now];
[self LoadClasses];
}
-(void)viewWillAppear:(BOOL)animated {
//--- Hide the Top Navigation Controller Bar at the current View
[[self navigationController] setNavigationBarHidden:YES animated:YES];
}
//--- Top Navigation Controller reappear on the next VC
-(void)viewDidDisappear:(BOOL)animated{
[[self navigationController] setNavigationBarHidden:NO animated:YES];
}
-(void) receiveTestNotification:(NSNotification*)notification
{
if ([notification.name isEqualToString:#"toClasses"])
{
NSDictionary* userInfo = notification.userInfo;
NSLog (#"Successfully received userInfo! %#", userInfo);
NSString* sFromSearch = [NSString stringWithFormat: #"%#", userInfo];
NSLog (#"Successfully received test notification! %#", sFromSearch);
}
}
In my opinion, you don't need to use notification or singleton on this case.
Simply, get SecondViewController from self.tabBarController and call the method.
First VC
- (IBAction)btnSearch:(UIButton *)sender {
self.tabBarController.selectedIndex = 1;
sURL = #"Testing 123";
UINavigationController* secondNav = (UINavigationController*)self.tabBarController.viewControllers[1];
SecondViewController* secondViewController = [secondNav.viewControllers firstObject];
[secondViewController handleString:sURL];
}
Second VC
- (void)handleString:(NSString*)string {
// Do whatever you want with string passed from First VC
}
You added observer in viewDidLoad, so it will not work even you create it before user tap on button and send notification. because observer will not be registered. I advise you not use observer to send data in this case. you can save this data elsewhere and use it when seconVC will load. for example in singleton object.
your Singleton object looks like this:
Interface:
#interface DataManager : NSObject
#property (nonatomic, strong) NSDictionary *userInfo;
+ (DataManager *) getInstance;
#end
Implementation:
#implementation DataManager
+ (DataManager *) getInstance {
static DataManager *appManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
appManager = [[DataManager alloc] init];
});
return appManager;
}
#end
Now you can access this object where you want and you can assured that only one instance is created.
here is your button click method:
- (IBAction)btnSearch:(UIButton *)sender {
self.tabBarController.selectedIndex = 1;
sURL = #"Testing 123";
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:sURL forKey:#"theURL"];
[DataManager getInstance].userInfo = userInfo;
}
and your viewDidLoad in secondVC
- (void)viewDidLoad {
[super viewDidLoad];
NSDictionary *userInfo = [DataManager getInstance].userInfo;
}

How to call specific function for a global class?

I have some Realm models in my app that all use a base class. In this class I wrote some generic functions like the one below:
- (void)save {
self.updatedAt = [NSDate date];
[self.realm beginWriteTransaction];
[self.realm addOrUpdateObject:self];
[self.realm commitWriteTransaction];
[[SyncEngine sharedInstance] store:self];
}
Now, I also wrote a class called SyncEngine, which checks if some available synchronization methods are enabled and then calls them:
- (void)store:(id)object {
if ([Preferences CloudKitEnabled]) {
[self.cloudKit store:object];
}
}
This is where my problem arises. I have written a base class called CloudKitManager which has some generic functions. I then create a specific CloudKitClass for every model in my app, so I'll end up with CloudKitRestaurant and CloudKitTable. All of these will contain a function (void)store:(id)sender. What would be the best way to call the store function of a specific CloudKit class, based on the class that is being stored in Realm?
Ideally, I'd like for RLMRestaurant to automatically use CloudKitRestaurant and not have to use and if else or switch statement.
For further clarity, this is how SyncEngine works.
#interface SyncEngine()
#property (nonatomic, strong) CloudKitManager *cloudKitManager;
#end
#implementation SyncEngine
static SyncEngine *sharedInstance = nil;
+ (SyncEngine *)sharedInstance {
if (sharedInstance == nil) {
sharedInstance = [[self alloc] init];
}
return sharedInstance;
}
- (instancetype)init {
self = [super init];
if (self) {
self.cloudKitManager = [[CloudKitManager alloc] init];
}
return self;
}
#end
In my opinion, you should keep type of CloudKitManager class inside RLMBase object. And when you need to call [[CloudKitManager sharedInstance] store:object], call [[object.cloudKitClass sharedInstance] store:object].
Try my code below.
#interface RLMBase : NSObject
- (Class)cloudKitClass;
#end
#implementation RLMBase
- (Class)cloudKitClass {
// Must be overridden in subclass.
return CloudKitManager.class;
}
#end
#interface RLMRestaurant : RLMBase
#end
#implementation RLMRestaurant
- (Class)cloudKitClass {
return CloudKitRestaurant.class;
}
#end
- (void)store:(RLMBase *)object {
if ([Preferences CloudKitEnabled]) {
[[object.cloudKitClass sharedInstance] store:object];
}
}
ANOTHER WAY
Put store: method from SyncEngine to RLMBase object.
#interface RLMBase : NSObject
- (Class)cloudKitClass;
- (void)store;
#end
#implementation RLMBase
- (Class)cloudKitClass {
// Must be overridden in subclass.
return CloudKitManager.class;
}
- (void)store {
if ([Preferences CloudKitEnabled]) {
[[self.cloudKitClass sharedInstance] store:self];
}
}
#end
And save method will become
- (void)save {
self.updatedAt = [NSDate date];
[self.realm beginWriteTransaction];
[self.realm addOrUpdateObject:self];
[self.realm commitWriteTransaction];
[self store];
}

How to pass data to viewController without creating its object

I am creating a iOS static library in which user will pass the name of the Viewontroller and some parameters inside the push and I am getting these details in didReceiveRemoteNotification and from here I got a string suppose NSString *vcName = #"ViewController2" and parameter suppose NSString *param1= #"UserName" NSString *param2= #"email" now I want to pass these parameters to the viewController Which name's string is received from push. But I don't want to write #import ViewController2.
I am able to redirect to ViewController2 without importing it but don't know how to pass these parameters to ViewController2
I can redirect to the viewController from the following code.
NSString *vcName = #"ViewController2";
NSString *param1= #"UserName";
NSString *param2= #"user_email";
UIStoryboard * storyboard = [[[UIApplication sharedApplication] keyWindow] rootViewController].storyboard;
UIViewController *vcToOpen = [storyboard instantiateViewControllerWithIdentifier:vcName]];
vcToOpen.modalPresentationStyle =UIModalPresentationFullScreen;
[[[[UIApplication sharedApplication]keyWindow] rootViewController] presentViewController:vcToOpen animated:YES completion:nil];
Now I want to get these two parameter's value in ViewController2. Can anybody help me how to do it. without writing #import ViewController2 because app can has many ViewControllers and vcName can be any of them.
AppDelegate.h
-(NSString *)getEmail;
-(NSString *)getName;
-(void)setEmail:(NSString *)email Name:(NSString *)name;
+(AppDelegate *)sharedAppDelegate;
AppDelegate.m
#interface AppDelegate ()
{
NSString *strEmail, *strName;
}
-(NSString *)getEmail
{
return strEmail;
}
-(NSString *)getName
{
return strName;
}
-(void)setEmail:(NSString *)email Name:(NSString *)name
{
strEmail = email;
strName = name;
}
+(AppDelegate *)sharedAppDelegate
{
return (AppDelegate *)[[UIApplication sharedApplication] delegate];
}
ViewController1.m
#import "AppDelegate.h"
-(void)gotoViewController2
{
[[AppDelegate sharedAppDelegate] setEmail:#"email#gmail.com" Name:#"name1234"];
[self performSegueWithIdentifier:#"segueToViewController2" sender:nil];
}
ViewController2.m
#import "AppDelegate.h"
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *name = [[AppDelegate sharedAppDelegate]getName];
NSString *email = [[AppDelegate sharedAppDelegate]getEmail];
NSLog(#"name = %# and email = %#",name, email); //name = name1234 and email = email#gmail.com
}
Storing values in your app delegate is just messy.
Each one of your UIViewControllers that could be launched from a push notification could conform to a custom 'launch' protocol.
Each UIViewController e.g. 'UIViewController2' would conform to this protocol.
You could write the protocol like this:
#protocol LaunchProtocol <NSObject>
- (void) launchParams:(NSDictionary *)params;
#end
Each UIViewController could conform to this protocol, like so:
#interface ViewController2 : UIViewController <LaunchProtocol>
#end
#implementation ViewController2
- (void) launchParams:(NSDictionary *)params {
}
#end
Your app delegate only needs to know about the protocol, it doesn't care about your UIViewControllers.
When you get a push notification you check if the view controller conforms to the launch protocol.
...
vcToOpen.modalPresentationStyle =UIModalPresentationFullScreen;
if ([vcToOpen conformsToProtocol:#protocol(LaunchProtocol)]) {
UIViewController <LaunchProtocol> *launchController = (UIViewController <LaunchProtocol> *) vcToOpen;
NSDictionary* params = #{ /* create your param dict */ };
[launchController launchParams:params];
}
[[[[UIApplication sharedApplication] keyWindow] rootViewController] presentViewController:vcToOpen animated:YES completion:nil];
...
You would include the information from the push notification in the 'params' dict, and the UIViewController would extract what information it needs from it in launchParams:
- (void) launchParams:(NSDictionary *)params {
NSLog(#"Username: %#", params[#"username"]);
}
Actually you can use Singleton design pattern to achieve this. Create one shared instance class to store the values.
+ (instancetype)sharedInstance
{
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
create properties inside the manager class which needs to be saved, then access the values from the manager class.

App crashes when trying to load data from a singleton

I have ViewControllerA and ViewControllerB. In each I have this property
#property (retain, nonatomic) NSMutableArray *racersArray;
In ViewControllerA I'm filling the racersArray with custom objects. When I press the burger button on my ViewControllerA, I store my filled _racersArray to singleton array and then I send a notification to ViewControllerB that my _racesArray content is yet in singleton array. I use this method:
- (void)burgerMenu
{
[ArraySingleton sharedManager].sharedArray = _racersArray;
// Send a notification to burger menu view controller to reload it's tableview
[[NSNotificationCenter defaultCenter] postNotificationName:#"ReloadTableViewData" object:nil];
// Opens the burger menu
[self.frostedViewController presentMenuViewController];
}
In ViewControllerB when I receive the notification, I call this method:
- (void)reloadTableviewData
{
_racersArray = [ArraySingleton sharedManager].sharedArray;
[self.tableView reloadData];
}
But after I try to load the data from singleton array to _racersArray, my app crashes with error:
[Racer count]: unrecognized selector sent to instance 0x7fe46aef7330
2016-10-03 23:58:44.091 Stopwatch[67948:6727940] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Racer count]: unrecognized selector sent to instance 0x7fe46aef7330'
This is how my singleton looks
#synthesize sharedArray;
+ (ArraySingleton *)sharedManager
{
static ArraySingleton *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[ArraySingleton alloc] init];
});
return sharedMyManager;
}
- (id)init
{
if (self = [super init]) {
sharedArray = [[NSMutableArray alloc] initWithObjects:#"picee", nil];
}
return self;
}
Can anyone tell me what am I doing wrong?
Thanks

OCMock unit test error

I use OCMock to test out singleton methods. I get "no such method exists in the mocked class." error for testSingleton method and infinite loop (the screenshot, the spinning indicator) for testSingletonWithBlock method
EDIT:
download sample project here
https://drive.google.com/file/d/0B-iP0P7UfFj0LVFpWWpPb3RDZFU/edit?usp=sharing
Here is my implementation
manager:
#implementation Manager
+ (Manager *)sharedManager {
static Manager *instance;
dispatch_once_t predicate;
dispatch_once(&predicate, ^{
instance = [Manager new];
});
return instance;
}
- (int)getOne {
return 1;
}
- (void)success:(BOOL)success completion:(void(^)(void))completion failure:(void(^)(void))failure {
success ? completion() : failure();
}
view controller:
- (void)manager_printOne {
int num = [[Manager sharedManager] getOne];
NSLog(#"number is: %d", num);
}
- (void)manager_success:(BOOL)success completion:(void(^)(void))completion failure:(void(^)(void))failure {
[[Manager sharedManager] success:success completion:completion failure:failure];
}
test view controller:
#interface coreDataTestTests : XCTestCase
#property (nonatomic, strong) id mockManager;
#property (nonatomic, strong) ViewController *viewController;
#end
#implementation coreDataTestTests
- (void)setUp
{
[super setUp];
self.viewController = [ViewController new];
//for singleton
self.mockManager = [Manager createNiceMockManager];
}
- (void)tearDown
{
[super tearDown];
self.viewController = nil;
//Note: singleton need both, retain counts = 2
self.mockManager = nil;
[Manager releaseInstance];
}
- (void)testSingleton {
NSLog(#"testSingleton");
OCMStub([self.mockManager getOne]).andReturn(2);
[self.viewController manager_printOne];
}
- (void)testSingletonWithBlock {
NSLog(#"testSingletonWithBlock");
[[[[self.mockHelper stub] ignoringNonObjectArgs] andDo:^(NSInvocation *invocation) {
void(^block)(void);
[invocation getArgument:&block atIndex:3];
block();
}] success:0 completion:[OCMArg any] failure:[OCMArg any]];
[self.viewController manager_success:NO completion:^{
NSLog(#"completion");
} failure:^{
NSLog(#"failure");
}];
}
#end
manager category for unit test:
static Manager *mockManager = nil;
#implementation Manager
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
+ (Manager *)sharedManager {
if (mockManager) {
return mockManager;
}
return invokeSupersequentNoParameters();
}
#pragma clang diagnostic pop
+(id)createMockManager {
mockManager = [OCMockObject mockForClass:[Manager class]];
return mockManager;
}
+(id)createNiceMockManager {
mockManager = [OCMockObject niceMockForClass:[Manager class]];
return mockManager;
}
+(void)releaseInstance {
mockManager = nil;
}
Rather than creating a category, you could just stub sharedManager and return a nice mock.
- (void)setUp
{
[super setUp];
self.viewController = [ViewController new];
//for singleton
id classMockManager = OCClassMock([Manager class]);
OCMStub([classMockManager sharedManager]).andReturn(classMockManager);
self.mockManager = classMockManager;
}
I don't have an environment up to test this just this moment, but this strategy should work. Note that this is OCMock3 syntax. See http://ocmock.org/reference/#mocking-class-methods
In your description above you write "manager category for unit test", but the implementation that follows is not a category, it's an actual implementation of the Manager class, i.e. the code reads
#implementation Manager
and not
#implementation Manager(unitTests)
It seems that the test code uses this second implementation of Manager and that implementation does not have a getOne method. So the mock is right to complain; the implementation of Manager it sees does not have the method and hence it can't stub it.
I believe you can fix your test by making the implementation a category. As far as I know it is possible to override a class method (sharedManager in your case) in a category, but it's a bit dicey to do so. The approach described by Ben Flynn is better.

Resources