How can i add a custom delegate method to my singleton - ios

I am developing a digital magazine reader app and it requires to download magazines first.
While downloading them i want to pass download progress data between viewcontrollers.
That's why i am using singleton design pattern.
I also use NSNotification to update progressBar percentage while its downloading. But i do not think it is quite efficient to send notificiation in every milisecond. So i decided to use delegate design pattern but i don't know to implement a custom delagete method. Any help on custom delegate? And is it the best way to use delegate?
// Header
#interface ZXCSingleton : NSObject
+ (id)sharedInstance;
- (BOOL)isDownloadingProduct:(NSString *)productID;
- (void)addToDownloadListWithProductID:(NSString *)productID;
- (void)removeFromDownloadListWithProductID:(NSString *)productID;
- (NSArray *)getDownloadList;
- (void)setDownloadProgress:(float)progress
withProductID:(NSString *)productID;
- (float)getDownloadProgressWithProductID:(NSString *)productID;
#end
// M
#import "ZXCSingleton.h"
#implementation ZXCSingleton{
NSMutableArray *downloadList;
NSMutableDictionary *downloadProgress;
}
+ (id)sharedInstance
{
static ZXCSingleton *sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
sharedInstance = [[ZXCSingleton alloc] init];
});
return sharedInstance;
}
- (id)init
{
self = [super init];
if (self) {
downloadList = [[NSMutableArray alloc] init];
downloadProgress = [[NSMutableDictionary alloc] init];
}
return self;
}
- (BOOL)isDownloadingProduct:(NSString *)productID
{
for (int i = 0; i < downloadList.count; i++) {
if ([[downloadList objectAtIndex:i] isEqualToString:productID]) return YES;
}
return NO;
}
- (void)addToDownloadListWithProductID:(NSString *)productID
{
[downloadList addObject:productID];
}
- (void)removeFromDownloadListWithProductID:(NSString *)productID
{
[downloadList removeObject:productID];
}
- (NSArray *)getDownloadList
{
return downloadList;
}
- (void)setDownloadProgress:(float)progress
withProductID:(NSString *)productID
{
if (progress != [[downloadProgress objectForKey:productID] floatValue]) [[NSNotificationCenter defaultCenter] postNotificationName:#"downloading" object:nil];
[downloadProgress setObject:[NSString stringWithFormat:#"%0.2f", progress] forKey:productID];
}
- (float)getDownloadProgressWithProductID:(NSString *)productID
{
return [[downloadProgress objectForKey:productID] floatValue];
}

Define a delegate by creating a protocol first for defining the interface required by the delegate :
#protocol ZXCSingletonProtocol
- (void)downloadProgessUpdates:(float)progress;
#end
Create a property for the delegate in your singleton :
#property (nonatomic, weak) id<ZXCSingletonProtocol> delegate;
Let your view controller conform to that protocol :
#interface MyViewController : UIViewController <ZXCSingletonProtocol>
and implement the downloadProgessUpdates in the view controller as required (e.g. update the UI element with the given progress number). REMEMBER to set the delegate when you initialize the view controller (viewDidLoad will do):
[ZXCSingleton sharedInstance].delegate = self; // self is the view controller instance
In your singleton update the setDownloadProgress method to update the delegate as well :
[_delegate downloadProgessUpdates:progress];

Related

iPhone - How to implement delegate between my Static library and the app which the library is used?

I have been creating a cocoa static library in which I have a public nsobject file where I created a custom delegate. In the app I imported the nsobject file and implemented the delegate but the delegate is not getting called... the static library name is glamApi.
the SKUIDPasser.h file of the NSObject in the library
#import <Foundation/Foundation.h>
#protocol SubClassDelegate <NSObject>
#required
- (void)MethodNameToCallBack:(NSString *)s;
#end
#interface SKUIDPasser : NSObject
-(void)getSKUIDsFromCart:(NSString *)SKUIDs;
#property (nonatomic, weak) id <SubClassDelegate> delegatePasser;
#end
and the SKUIDPasser.m file
#import "SKUIDPasser.h"
#implementation SKUIDPasser
#synthesize delegatePasser;
-(void)getSKUIDsFromCart:(NSString *)SKUIDs{
NSLog(#"getSKUIDsFromCart %#",SKUIDs);
[delegatePasser MethodNameToCallBack:SKUIDs];
}
#end
And the method is called from a Viewcontroller in static library
- (IBAction)CartShowEvent:(id)sender {
if (![cartBadge isHidden]) {
buyClicked = TRUE;
[self loadCart];
[self showCartItemsAll];
self.cartView.frame = self.view.bounds;
[self.view addSubview:self.cartView];
SKUIDPasser *pass = [[SKUIDPasser alloc] init];
[pass getSKUIDsFromCart:#"sssss"];
} else {
[Utilities alert:#"No products to display !!!"];
}
}
The Viewcontroller which the custom delegate has to be implemented Viewcontroller.h
#import <glamAPI/SKUIDPasser.h>
#interface ViewController : UIViewController<SubClassDelegate>{
SKUIDPasser *sk;
}
Viewcontroller.m
- (void)viewDidLoad {
[super viewDidLoad];
sk = [[SKUIDPasser alloc] init];
sk.delegatePasser = self;
NSLog(#"sk.delegatePasser %#",sk.delegatePasser);
}
- (void)MethodNameToCallBack:(NSString *)s
{
NSLog(#"MethodNameToCallBack %#",s);
}
I didn't get any error but the method is not calling..Please help me to resolve this
The very first thing you need to understand is that each instance object of a class is entirely different entity and maintains it's state separately.
In you case your have created an object of your static library in viewDidLoad: and set the delegate accordingly, but when you are making the call to method getSKUIDsFromCart, you are using a different instance for which you never set the delegate property. That's why there was no callback.
To solve this, you can set the delegate in method CartShowEvent: before making the call, something like this
SKUIDPasser *pass = [[SKUIDPasser alloc] init];
pass.delegatePasser = self;
[pass getSKUIDsFromCart:#"sssss"];
However i would suggest that you should use the instance variable of library which you already created in viewDidLoad:
- (IBAction)CartShowEvent:(id)sender {
if (![cartBadge isHidden]) {
buyClicked = TRUE;
[self loadCart];
[self showCartItemsAll];
self.cartView.frame = self.view.bounds;
[self.view addSubview:self.cartView];
//No need to create another object.
//SKUIDPasser *pass = [[SKUIDPasser alloc] init];
//Use the previously created instance object
[sk getSKUIDsFromCart:#"sssss"];
}
else {
[Utilities alert:#"No products to display !!!"];
}
}
The SKUIDPasser object that you are calling within (IBAction)CartShowEvent:(id)sender and the SKUIDPasser object that you are setting the delegate are NOT the same.
Just for a test, try calling the method [sk getSKUIDsFromCart:#"sssss"]; just after you set the delegate and you will see that it will be called because this instance has the delegate set correctly:
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
sk = [[SKUIDPasser alloc] init];
sk.delegatePasser = self;
[sk getSKUIDsFromCart:#"sssss"];
NSLog(#"sk.delegatePasser %#",sk.delegatePasser);
}
- (void)MethodNameToCallBack:(NSString *)s
{
NSLog(#"MethodNameToCallBack %#",s);
}
Update
I updated my answer to help you call the trigger from the static library
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
sk = [[SKUIDPasser alloc] init];
sk.delegatePasser = self;
/*
You now can pass this variable to the static library to get called
from there ...
example:
viewControllerOnStaticLibrary.passer = sk;
*/
NSLog(#"sk.delegatePasser %#",sk.delegatePasser);
}
- (void)MethodNameToCallBack:(NSString *)s
{
NSLog(#"MethodNameToCallBack %#",s);
}
Viewcontroller_in_static_library.h
#property (nonatomic, strong) SKUIDPasser *passer;
Viewcontroller_in_static_library.m
- (IBAction)CartShowEvent:(id)sender {
if (![cartBadge isHidden]) {
buyClicked = TRUE;
[self loadCart];
[self showCartItemsAll];
self.cartView.frame = self.view.bounds;
[self.view addSubview:self.cartView];
//now you are calling the same instance
[self.passer getSKUIDsFromCart:#"sssss"];
} else {
[Utilities alert:#"No products to display !!!"];
}
}

Initialize and Allocate a Class that is visible to all methods

Greetings i need to init alloc an instance of a class and have it accessible by any method
Example for using "Whatever *boom = [Wathever alloc]init];"
#interface something : NSObject;
#implementation
-(void) method1{
boom.size = 10;
}
-(void) method2{
boom.color = blue;
}
Where would i alloc and init boom so that i can manipulate it in every method?
for example in whatever.h and whatever.m to call the methods of a class must be declared in whatever.h
-(void) method1;
-(void) method2;
and used
Whatever *boom = [Wathever alloc]init];
[boom method1];
[boom method2];
In a single class? Make it a property of that class.
//.h
#property Whatever *boom;
//.m
- (id)init {
self = [super init];
if (self) {
_boom = [[Whatever alloc] init];
}
return self;
}
Across your whole app? Create an instance of it somewhere, like your app delegate, and then pass it along to the Root View Controller, which in turns passes it to each View Controller.
// AppDelegate .m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// app setup code
Whatever *boom = [[Whatever alloc] init];
FirstViewController vc = self.window.rootViewController;
vc.boom = boom;
}
// FirstViewController.h, NextViewController.h, etc.
#property Whatever *boom;
// FirstViewController.m
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NextViewController *nextVC = sender.destinationViewController;
nextVC.boom = self.boom;
}
You could also go the Singleton route, but then you are tightly coupled to a single instance of the class app-wide.
See when you create a class in that to intialize that class a common method will always be there which calls that class it self:
Something like this :
-(id)init
{
self = [super init];
if (self)
{
}
return self;
}
You can declare the instance in .h file like this :
Whatever *boom;
Than you can initialize that instance in above method as following :
-(id)init
{
self = [super init];
if (self)
{
boom = [Wathever alloc]init];
}
return self;
}
hope this will help you.

Referencing a ViewController in AppDelegate - Objective C

I am trying to get a NSMutableArray from another class. I am trying to reference that ViewController that contains the array. Lets say, a has the array I want. b is the class I am going to get that array in.
a.h:
#property (strong, nonatomic) NSMutableArray *selectedCells;
a.m:
#synthesize selectedCells;
AppDelegate.h:
#property (strong, nonatomic) a *create_challengeDelegate;
AppDelegate.m:
#synthesize create_challengeDelegate;
a *create_challengeDelegate = [[a alloc]init];
Right here when I try to reference that ViewController I get an error saying:
Initializer element is not a compile-time constant
I assume it has something to do with it not being able to see the ViewController.
In my b.m:
AppDelegate *app = (AppDelegate*)[[UIApplication sharedApplication] delegate];
app.create_challengeDelegate.selectedCells
My issue is working with initializing the ViewController in the delegate.
Suggestions and thoughts on that?
My suggestion is that you create the selectedCells array in you AppDelegate and send it to A. E.g. in:
AppDelegate.h
#property (nonatomic, strong) NSArray *selectedCells;
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.selectedCells = [NSMutableArray array];
A *viewA = [[a alloc] initWithNibName:#"a" bundle:nil selectedCells:self.selectedCells];
[...]
}
a.h
#property (nonatomic, strong) NSMutableArray *selectedCells;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil selectedCells:(NSMutableArray*)cells;
a.m
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil selectedCells:(NSMutableArray*)cells
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.selectedCells = cells;
}
return self;
}
b.m
AppDelegate *app = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSMutableArray *selectedCells = app.selectedCells;
This way, whenever you make changes to selectedCells it will keep it in the array you send there since selectedCells is a refference to an object which is created in your AppDelegate.
Otherwise, what you are trying to do is access a view which might no longer be available in the memory since the view might have been deallocated.
Also, you create the instance of a but its not set to the AppDelegate's instance of a, its a seperate instance.
Instead of
#synthesize create_challengeDelegate;
a *create_challengeDelegate = [[a alloc]init];
You should have
#synthesize create_challengeDelegate;
self.create_challengeDelegate = [[a alloc] init];
I still strongly recommend you do not access your view this way though.
P.s. #synthesize is no longer necessary.
Edit
This is a better solution.
Selection.h
/**
* The entity for selections
*/
#interface Selection : NSObject
/**
* The Shared Instance
*
* #return Selection The instance
*/
+ (Selection *)sharedInstance;
/**
* Add an item to the selections
*
* #param object id The object to add
*/
- (void)addSelection:(id)object;
/**
* Remove an item from the selections
*
* #param object id The object to remove
*/
- (void)removeObject:(id)object;
/**
* Get the selections
*
* #return NSArray The array with the current selection objects
*/
- (NSArray *)getSelections;
#end
Selection.m
#import "Selection.h"
#interface Selection ()
#property (nonatomic, strong) NSMutableArray *selections;
#end
#implementation Selection
#pragma mark - Public methods
+ (Selection *)sharedInstance
{
static Selection *sharedInstance = nil;
#synchronized(self) {
if (sharedInstance == nil) {
sharedInstance = [[self alloc] init];
}
}
return sharedInstance;
}
#pragma mark - Private methods
- (id)init
{
if (self = [super init])
{
self.selections = [NSMutableArray array];
}
return self;
}
#pragma mark - Manage selection
- (void)addSelection:(id)object
{
[self.selections addObject:object];
}
- (void)removeObject:(id)object
{
[self.selections removeOjbect:object];
}
- (NSArray *)getSelections
{
return [NSArray arrayWithArray:self.selections];
}
Now you can assess your shared singeton in A and/or B by accessing:
[[Selection sharedInstance] addObject:myObject];
[[Selection sharedInstance] removeObject:myObject];
NSArray *array = [[Selection sharedInstance] getSelections];
Is this line inside a method:
a *create_challengeDelegate = [[a alloc]init];
If not that would probably explain your issue. If that's not the problem it might be helpful to post more complete code.

Accessing class method in objective c. Using self or classname?

I am learning iOS programming and am confused by the following code regarding the use of keyword self.
From my understanding, self is like Java's this. It refers to the current instance. When I want to call a class method, the usual way should be like [PlayingCard validSuits]; But it's also OK to invade a class method on an instance, right? Like [self validSuits]; (I am in the class so self refers to an instance of PlayingCard)
But in the following code, it gives error somewhere but looks ok elsewhere.(Pointed out by 3 comments, this is within Xcode 5.1)
Am I missing anything?
(P.S. I think I am having the similar problem as here, which no one answered yet. He got the same error even using [PlayingCard validSuits]. )
// PlayingCard.m
#import "PlayingCard.h"
#implementation PlayingCard
#synthesize suit = _suit;
+ (NSArray *)validSuits {
return #[#"♠︎", #"♣︎", #"♥︎", #"♦︎"];
}
+ (NSArray *)rankStrings {
return #[#"?", #"A", #"2", #"3", #"4",#"5",#"6",#"7",#"8",#"9",#"10",#"J",#"Q",#"K"];
}
+ (NSUInteger)maxRank {
return [[PlayingCard rankStrings] count] -1;
//1. [self rankStrings] works fine.**
}
//override super class's method
- (NSString *)contents {
NSArray *rankStrings = [PlayingCard rankStrings];
//2. if change rankStrings to self, then error:
//No visible #interface for 'PlayingCard' declares the selector 'rankStrings'
return [rankStrings[self.rank] stringByAppendingString:self.suit];
}
- (void) setSuit:(NSString *)suit {
if ( [[PlayingCard validSuits] containsObject:suit]) {
//3.error when changed to [self validsuits]
//No visible #interface for 'PlayingCard' declares the selector 'validsuits'**
_suit = suit;
}
}
- (NSString *) suit {
return _suit ? _suit : #"?";
}
#end
The header file:
// PlayingCard.h
#import "Card.h"
#interface PlayingCard : Card
#property (nonatomic, strong) NSString *suit;
#property (nonatomic) NSUInteger rank;
+ (NSArray *) validSuits;
+ (NSUInteger) maxRank;
#end
If you are calling another class method from inside a class method (of the same class) you can just use [self classMethod]. If however you are in an instance method and you need to call that classes class method you can use [[self class] classMethod]
As pointed out by #Martin R - if you subclass PlayingCard, calling self in a class method will then be that subclass and not PlayingCard.
EDIT:
For completeness you need to do:
// PlayingCard.m
#import "PlayingCard.h"
#implementation PlayingCard
#synthesize suit = _suit;
+ (NSArray *)validSuits {
return #[#"♠︎", #"♣︎", #"♥︎", #"♦︎"];
}
+ (NSArray *)rankStrings {
return #[#"?", #"A", #"2", #"3", #"4",#"5",#"6",#"7",#"8",#"9",#"10",#"J",#"Q",#"K"];
}
+ (NSUInteger)maxRank {
return [[self rankStrings] count] -1;
}
//override super class's method
- (NSString *)contents {
NSArray *rankStrings = [[self class] rankStrings];
return [rankStrings[self.rank] stringByAppendingString:self.suit];
}
- (void) setSuit:(NSString *)suit {
if ( [[[self class] validSuits] containsObject:suit]) {
_suit = suit;
}
}
- (NSString *) suit {
return _suit ? _suit : #"?";
}
#end

How to pass data from parent view to child upon opening?

I want to load data (an array of strings) from the parent view into a set of UITextFields in the child view upon presenting the modalView.
I know how to pass from child to parent, and I'm sure it's even easier to go the other way, but I don't know how.
UPDATE: Update removed because I found the problem (double releasing of modal view)
Override the init method for the child view controller.
- (id) initWithStrings:(NSArray *)string {
if (self = [super init]) {
// Do stuff....
}
return self;
}
Then in the parent:
MyChildViewController *vc = [[[MyChildViewController alloc] initWithStrings: strings] autorelease];
Two ways you could do it:
1.Override the init method as Matt suggests
2.Create fields in your child class and pass those values to your text field.
#interface ChildViewController : UIViewController{
NSArray *strings;
UITextfield *textField1;
UITextfield *textField2;
}
...
- (void)viewDidLoad {
[super viewDidLoad];
textField1.text = [strings objectAtIndex:0];
textField2.text = [strings objectAtIndex:1];
}
Then in the parent class:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
ChildViewController *childController = [[ChildViewController alloc] init];
childController.strings = your_array_of_strings;
[self.navigationController pushViewController:childController animated:YES];
[childController release];
}
- (id)initWithDataObject:(YourDataObjectClass *)dataObject {
if (self = [super init]) {
self.dataObject = dataObject;
// now you can do stuff like: self.myString = self.dataObject.someString;
// you could do stuff like that here or if it is related to view-stuff in viewDidLoad
}
return self;
}
If you want to get really fancy, you can make a delegate for your child view.
#protocol MyChildViewDelegate
- (NSArray*)getStringsForMyChildView:(MyChildView*)childView;
#end
#interface MyChildView : UIView
{
id <MyChildViewDelegate> delegate;
...
}
#property (nonatomic, assign) id <MyChildViewDelegate> delegate;
...
#end
Then somewhere in your view you would ask for the strings:
- (void)viewDidLoad
{
...
NSArray* strings = [delegate getStringsForMyChildView:self];
...
}
Then in your controller (or where ever) you can do:
myChildView = [[MyChildView alloc] initWith....];
myChildView.delegate = self;
...
- (NSArray*)getStringsForMyChildView:(MyChildView*)childView
{
return [NSArray arrayWithObjects:#"one", #"two", #"three", nil];
}
It's probably a little overkill in this case, but this is how UITableViews do it too: they have a data source delegate to provide them with their contents.

Resources