I am working on passbook feature for creating Boarding pass in iOS, I am able to create a single boarding pass and it is working well, but while tying to create multiple passes, I am getting the following issues:
I am able to get the counts but all passes are being replaced with last one while REVIEW
After selecting SAVE ALL, there is only one pass in passbook app, i.e the last one.
I am using Following code to show passes
-(void) initializeWithMultiPassbookArray:(NSMutableArray *)arrPasses
{
NSMutableArray *arrPKPass = [[NSMutableArray alloc] init];
for(NSString *path in arrPasses)
{
NSData *data;
if(path && path.length)
{
data = [[NSData alloc] initWithContentsOfFile:path];
NSError *error;
PKPass *passSample = [[PKPass alloc] initWithData:data error:&error];
if(passSample)
{
[arrPKPass addObject:passSample];
passSample = nil;
}
else
JALog(#"passSample is nil");
}
else
{
JALog(#"Error occured while fetching pkpass data from cache path");
}
}
if(arrPKPass && arrPKPass.count > 1)
{
if(!self.passLibrary)
self.passLibrary = [[PKPassLibrary alloc] init];
__weak BookingSuccessfulViewController *self_ = self;
if(viewPopUpBg)
{
[viewPopUpBg removeFromSuperview];
viewPopUpBg = nil;
}
[self.passLibrary addPasses:arrPKPass withCompletionHandler:^(PKPassLibraryAddPassesStatus status) {
switch (status) {
case PKPassLibraryDidAddPasses:
{
[App_Delegate performSelectorOnMainThread:#selector(RemoveLoader) withObject:nil waitUntilDone:YES];
if([self_ respondsToSelector:#selector(showAlertFor:withMsg:withTag:)])
[self_ showAlertFor:SAVED withMsg:#"Your booking details have been added to Passbook." withTag:0];
break;
}
case PKPassLibraryDidCancelAddPasses:
{
[App_Delegate performSelectorOnMainThread:#selector(RemoveLoader) withObject:nil waitUntilDone:YES];
break;
}
case PKPassLibraryShouldReviewPasses:
{
//[self_ performSelectorOnMainThread:#selector(RemoveLoader) withObject:nil waitUntilDone:YES];
[App_Delegate performSelectorOnMainThread:#selector(RemoveLoader) withObject:nil waitUntilDone:YES];
PKAddPassesViewController *vcPKAddPass = [[PKAddPassesViewController alloc] initWithPasses:arrPKPass];
[vcPKAddPass setDelegate:(id)self_];
[self_ presentViewController:vcPKAddPass animated:YES completion:nil];
vcPKAddPass = nil;
break;
}
default:
break;
}
}];
}
else if(arrPKPass.count == 1)
{
if(viewPopUpBg)
{
[viewPopUpBg removeFromSuperview];
viewPopUpBg = nil;
}
[App_Delegate performSelectorOnMainThread:#selector(RemoveLoader) withObject:nil waitUntilDone:YES];
PKPass *passLocal = [arrPKPass objectAtIndex:0];
if(passLocal)
{
self.currentPass = passLocal;
//present view controller to add the pass to the library
PKAddPassesViewController *vcPKAddPass = [[PKAddPassesViewController alloc] initWithPass:passLocal];
[vcPKAddPass setDelegate:(id)self];
[self presentViewController:vcPKAddPass animated:YES completion:nil];
vcPKAddPass = nil;
}
}
else
{
JALog(#"arrPKPass is nil");
}
}
Got my ans:
Every Pass should contain different serial No in the key "serialNumber" while creating JSON
Also Check the Capabilities in Project>Target>Capabilities>Passbook
Allow subset of pass types is selected with specific identifiers.
These steps worked for me.
Hopefully help to others also.
Thanks
Related
when I launch App for first time (App is not installed perviously) on simulator, the instance of CBLQueryEnumerator count returns nil.
I found it by placing breakpoints and executing
po enumeratorResult
but the enumerator object seems to be allocated.
When I launch App for second time (in this case App is already installed on simulator) the instance of CBLQueryEnumerator count returns 5 (I have 5 documents in DB).
Am I making mistake in using CBLQuery or CBLQueryEnumerator please guide me correct.
viewContoller.m
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.progressView.progressViewStyle = UIProgressViewStyleBar;
[self.progressView setProgress:2.5 animated:YES];
self.cbObject = [CouchBaseObject sharedInstance];
self.syncURLLabel.text = [NSString stringWithFormat:#"%#",self.cbObject.syncURL];
self.documentList = [NSMutableArray array];
[self addDocumentToTicketModel:self.cbObject.database];
if(self.cbObject.pull.status == kCBLReplicationOffline || self.cbObject.pull.status != kCBLReplicationActive){
[self performSelector:#selector(pushTicketDetailViewController) withObject:self afterDelay:2.5];
}
}
-(void)addDocumentToTicketModel:(CBLDatabase*)database{
CBLView* view = [database viewNamed:#"_temp_view"];
if (!view.mapBlock) {
[view setMapBlock:MAPBLOCK({
if (doc) {
emit(doc,#"1");
}
})version:#"1.0"];
}
CBLQuery* query = [view createQuery];
[query runAsync:^(CBLQueryEnumerator * enumeratorResult, NSError * error) {
if(!error){
for (CBLQueryRow* row in enumeratorResult) {
CBLDocument* document = [database documentWithID:row.documentID];
Ticket* ticket = [Ticket modelForDocument:document];
NSLog(#"Ticket ; %#",ticket.propertiesToSave);
[self.documentList addObject:ticket];
}
}
}];
}
#end
CouchBaseObject.m
#implementation CouchBaseObject
-(instancetype)init{
self = [super init];
if(self){
NSError* error;
self.manager = [CBLManager sharedInstance];
if(!self.manager){
NSLog(#"manager fail");
return nil;
}
self.database = [self.manager databaseNamed:#"couch_sample_one" error:&error];
if(!self.database){
NSLog(#"database fail");
return nil;
}
[self initReplication];
}
return self;
}
+(CouchBaseObject*)sharedInstance{
static CouchBaseObject* sharedInstance = nil;
if(sharedInstance == nil){
sharedInstance = [[CouchBaseObject alloc]init];
}
return sharedInstance;
}
-(void)initReplication{
self.syncURL = [[NSURL alloc]initWithString:#"http://***.***.***.***:5984/couch_sample_one"];
self.pull = [self.database createPullReplication:self.syncURL];
self.push = [self.database createPushReplication:self.syncURL];
self.pull.continuous = NO;
self.push.continuous = NO;
[self startReplication];
}
-(void)startReplication{
[self.push start];
[self.pull start];
}
-(void)stopReplication{
[self.push stop];
[self.pull stop];
}
#end
For first time the control will not enter into for in loop
for (CBLQueryRow* row in result)
and for second time control will be in that loop.
This also happens when on just on launch.
Scenario.
Run App (Freshly installed app)
Navigate from initialViewController to viewController.
First time control does not enter into for loop, CBLQueryEnumerator Object.count returns nil.
pop to initialViewController
Push viewController, but this time controls enter into for in loop and CBLQueryEnumerator object.count returns 5
Hierarchy (Push).
initialViewContoller -> ViewContoller -> ListViewController
Hello i'm working on this app which has a login section. What i want is when i click the login button that button has to be unclickable until the login succeeds or fails. I already tried adding this line doLogin.enabled = NO;
But that wasn't useful. Please help. This is my code:
- (IBAction)doLogin:(id)sender {
[self loginUser];
}
- (void)loginUser
{
if (![self.usernameBox.text isEqualToString:#""] && ![self.passwordBox.text isEqualToString:#""])
{
//TODO: check id email pattern is correct
[self showLoginProcess:true];
[[AuthSingleton getInstance] attemptLoginWithUsername:self.usernameBox.text andPassword:self.passwordBox.text withSuccesBlock:^(AFHTTPRequestOperation *operation, id responseObject)
{
[self showLoginProcess:false];
UIViewController *newFrontController = nil;
PaMapViewController * vc = [[PaMapViewController alloc] init];
newFrontController = [[UINavigationController alloc] initWithRootViewController:vc];
SWRevealViewController *revealController = self.revealViewController;
[revealController pushFrontViewController:newFrontController animated:YES];
} andFailureBlock:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSDictionary *dic = [error.userInfo objectForKey:#"JSONResponseSerializerWithDataKey"];
#ifdef DEBUG
NSLog(#"dic = %#", dic);
#endif
if ([[dic objectForKey:#"error_uri"] isEqual:#"phone"])
{
NSError *jsonError;
NSData *objectData = [[dic objectForKey:#"error_description"] dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:objectData
options:NSJSONReadingMutableContainers
error:&jsonError];
[self loginFailed:json];
}
else
{
[self loginFailed:dic];
}
}];
}
else
{
//TODO: show proper message Test
NSLog(#"username or password is empty %#", kBaseURL);
}
}
- (void)showLoginProcess:(BOOL) show
{
[self.spinner setColor:[UIColor whiteColor]];
self.spinner.hidden = !show;
self.usernameBox.hidden = show;
self.passwordBox.hidden = show;
if (show)
{
[self.spinner startAnimating];
} else
{
[self.spinner stopAnimating];
}
}
Hope you have declared a property for the login button. Let it be "doLogin".
What you need to do is
- (IBAction)doLogin:(id)sender
{
[self loginUser];
doLogin.userInteractionEnabled = NO;
}
and when login succeeds or fails write
doLogin.userInteractionEnabled = YES;
within the corresponding block.
Instead of
doLogin.enabled = NO
write
doLogin.userInteractionEnabled = NO
- (IBAction)doLogin:(id)sender
{
doLogin.userInteractionEnabled = NO;
[self loginUser];
doLogin.userInteractionEnabled = YES;
}
Given below is the code snipe that should help
- (IBAction)loginUser:(id)sender
{
//before login disable the user interaction of the button
loginButton.userInteractionEnabled = NO;
/*Your login logic here*/
// after successful login enable the login button once again
loginButton.userInteractionEnabled = YES;
}
I am new to Gimbal framework and I am trying to use this framework at basic level.
Here is my code:
- (void)viewDidLoad
{
....
self.contextCoreConnector = [[QLContextCoreConnector alloc] init];
self.contextCoreConnector.permissionsDelegate = self;
self.contextPlaceConnector = [[QLContextPlaceConnector alloc] init];
self.contextPlaceConnector.delegate = self;
self.contextInterestsConnector = [[PRContextInterestsConnector alloc] init];
self.contextInterestsConnector.delegate = self;
self.contentConnector = [[QLContentConnector alloc] init];
self.contentConnector.delegate = self;
[self.contextCoreConnector checkStatusAndOnEnabled:^(QLContextConnectorPermissions *contextConnectorPermissions) {
if (contextConnectorPermissions.subscriptionPermission)
{
if (self.contextPlaceConnector.isPlacesEnabled)
{
[self displayLastKnownPlaceEvent];
}
}
}
disabled:^(NSError *error) {
NSLog(#"%#", error);
if (error.code == QLContextCoreNonCompatibleOSVersion)
{
NSLog(#"%#", #"SDK Requires iOS 5.0 or higher");
}
else
{
enableSDKButton.enabled = YES;
placeNameLabel.text = nil;
contentInfoLabel.text = nil;
}
}];
}
#pragma mark - QLContextPlaceConnectorDelegate methods
- (void)didGetPlaceEvent:(QLPlaceEvent *)placeEvent
{
NSLog(#"Got place event");
[self savePlaceEvent:placeEvent];
[self displayLastKnownPlaceEvent];
}
- (void)didGetContentDescriptors:(NSArray *)contentDescriptors
{
contentInfoLabel.text = [[contentDescriptors lastObject] title];
NSLog(#"didGetContentDescriptors : %#",contentInfoLabel.text);
[self displaceLastKnownContentDescriptor];
for (QLContentDescriptor *contentDescriptor in contentDescriptors)
{
[self postOneContentDescriptorLocalNotification:contentDescriptor];
}
}
But the method
- (void)didGetContentDescriptors:(NSArray *)contentDescriptors
is not called. I have no idea why this is happening. In the demo app provided with the SDK this method is get called but not in my app. I have matched the code for both the apps but I am unable to find the issue in my app.
I will be very thankful if anyone can help.
is your didGetPlaceEvent delegate method being called?
I have a class called APICalls that manages the calls to the API. Every View Controller calls the appropriate method (createUsername, getStates...) and pass the parameters required. When the data is received and parsed, it calls back the viewcontroller to update the UI with the info downloaded. The following code is working but I would like to know if there is an easier or more flexible/appropriate way of doing this, specially when I update the UI in the viewcontroller. Perhaps with protocols and delegates? Any suggestion is welcomed.
-(void) getObjects:(id)returnObject ofClass:(Class)returnClass fromUrl:(NSString *)urlString withPost:(NSString *)post orPut:(NSString *)put token:(NSString *)token callName:(NSString *)call andAlertTitle:(NSString *)alertTitle
{
// NSString *className = NSStringFromClass([object class]);
__block NSObject *object = returnObject;
__block Class class = returnClass;
__block NSMutableArray *array = [[NSMutableArray alloc]init] ;
__block BOOL dataReceived = NO;
[SVProgressHUD showWithStatus:#"Connecting to the server"];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
//Background Thread
ServerConnection *sc = [[ServerConnection alloc] init]; //post/get/put
NSDictionary *jsonDict;
if ( ([post isEqualToString:#""] || !post ) && ([put isEqualToString:#""] || !put ) )
jsonDict = [sc getFromUrl:urlString withToken:token];
else if ([put isEqualToString:#""] || !put)
jsonDict = [sc postToUrl:urlString withPost:post andToken:token];
else
jsonDict = [sc putToUrl:urlString withPut:put andToken:token];
if (jsonDict)
{
NSLog(#"API: json received");
//parse the received json
NSObject *data = [self parseJson:jsonDict alertTitle:alertTitle];
if ([data isKindOfClass:[NSArray class]]) {
NSLog(#"API: Array");
dataReceived = YES;
// Iterate through the array of dictionaries
for(NSDictionary *dict in (NSArray *) data) {
object = [[class alloc] initWithJSONDictionary:dict];
[array addObject:object];
}
}
else if ([data isKindOfClass:[NSDictionary class]]){
NSLog(#"API: Dictionary");
dataReceived = YES;
object = [[class alloc] initWithJSONDictionary:(NSDictionary *)data];
if ([array count]> 0)
[array addObject:object];
}
else
NSLog(#"API: Error from API"); //alertview is shown from HandleError class
}
else{
NSLog(#"no json received");
dispatch_async(dispatch_get_main_queue(), ^(void){
[self alertStatus:#"Error when connecting to the server, please try it again" :alertTitle];
});
}
if (dataReceived)
{
dispatch_async(dispatch_get_main_queue(), ^(void){
//Run UI Updates
[SVProgressHUD dismiss];
if ([call isEqualToString:#"getStates"])
{
if ([self.currentViewController isKindOfClass:[SignUpViewController class]])
{
SignUpViewController *signup = (SignUpViewController *) self.currentViewController;
[signup updateStatesList:array];
}
else if ([self.currentViewController isKindOfClass:[MyProfileViewController class]])
{
MyProfileViewController *profileVC = (MyProfileViewController *) self.currentViewController;
[profileVC updateStatesList:array];
}
}
else if ([call isEqualToString:#"getPoints"])
{
PromotionSelectionViewController *promotionVC = (PromotionSelectionViewController *) self.currentViewController;
[promotionVC updatePoints:object];
}
else if ([call isEqualToString:#"getPromotions"])
{
PromotionSelectionViewController *promotionVC = (PromotionSelectionViewController *) self.currentViewController;
[promotionVC updatePromotionsList:array];
}
});
}
});
}
//CreateUser: creates an user when this sign up
-(void)createUserWithUsername:(NSString *)username name:(NSString *)name surname:(NSString *)surname birthdate:(NSString *)birthdate address:(NSString*) address city:(NSString *)city state:(int)state country:(int)country zipCode:(int)zipCode email:(NSString *)email password:(NSString *)password fromViewController:(UIViewController *)currentViewController
{
self.currentViewController = currentViewController;
//Create the post with the username and password
NSString *post =[[NSString alloc] initWithFormat:#"username=%#&name=%#&surname=%#&address=%#&city=%#&state=%d&country=%d&zipcode=%d&birthdate=%#&email=%#&password=%#&",username, name, surname, address, city, state, country, zipCode, birthdate,email,password];
NSLog(#"post: %#", post);
User *user;
[self getObjects:user ofClass:NSClassFromString(#"User") fromUrl:signupURL withPost:post orPut:nil token:nil callName:#"createUser" andAlertTitle:#"SignUp Failed"];
}
-(void) getPointsWithToken:(NSString *)token fromViewController:(UIViewController *)currentViewController{
self.currentViewController = currentViewController;
[self getObjects:nil ofClass:nil fromUrl:getPointsURL withPost:nil orPut:nil token:token callName:#"getPoints" andAlertTitle:#"Get Proints Number Failed"];
}
-(void)getStatesforCounry:(int)idCountry fromViewController:(UIViewController *) currentViewController
{
self.currentViewController = currentViewController;
NSString *url = [NSString stringWithFormat:#"%#%d", getStatesURL, idCountry];
// NSLog(#"url: %#", url);
State *state;
[self getObjects:state ofClass:NSClassFromString(#"State") fromUrl:url withPost:nil orPut:nil token:nil callName:#"getStates" andAlertTitle:#"States not loaded"];
}
...
Using a delegate protocol pattern might help, but in this situation, I think my preference would be to pass a completion-handling block into the method, then call that completion handler block on the main thread to handle the results of the API call—it feels like there's a bit too much view-controller logic going on in the API method and using a completion-handling block (or a delegate callback method) would help move that logic back to the view controller.
Also, though it doesn't really change anything, you can replace the calls
dispatch_async(dispatch_get_main_queue(), ^(void){
...
});
with
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
...
}];
(It is generally preferable to use higher-level APIs, such as NSOperationQueue, over lower-level APIs, like dispatch_async, when they are equivalent.)
Below is the method with the code, where I am getting a memory leak using manual memory management. The memory leak is detected by using Xcode instruments and specifically points to the line where I am using NSJSONSerialization. I am running the target app (on a device with iOS 6.1).
The first time that i tap on the refreshButton there is no leak. Any subsequent tap generates the leak(and more leaks on top of that if i continue tapping the button). Below is the code - This is basic stuff for consuming JSON web services(the web service link is bogus but the real one that I am using works). You will notice that I am using Grand Central Dispatch so that I can update the UI without waiting for the parsing of the JSON to finish.
The line detected by instruments is surrounded by the asterisks. I would like to get some help to anyone who might have an idea of what is going on here. The full stack trace(as mentioned in the below comments i will put here:)
+(NSJSONSerialization JSONObjectWithData:option:error:] -> -[_NSJSONReader parseData:options:] -> -[_NSJSONReader parseUTF8JSONData:skipBytes:options]->newJSONValue->newJSONString->[NSPlaceholderString
initWithBytes:length:encoding:]
-(void)parseDictionary:(NSDictionary *)dictonary{
self.transactions = [dictonary objectForKey:#"transactions"];
if(!self.transactions){
NSLog(#"Expected 'transactions' array");
return;
}
for (int arrayIndex = 0; arrayIndex < [self.transactions count]; arrayIndex++) {
TransactionResult *result = [[[TransactionResult alloc] init] autorelease];
result.transactionID = [[self.transactions objectAtIndex:arrayIndex] objectForKey:#"ID"];
result.transactionDescription = [[self.transactions objectAtIndex:arrayIndex] objectForKey:#"description"];
result.transactionPrice = [[self.transactions objectAtIndex:arrayIndex] objectForKey:#"price"];
self.totalPrice += [result.transactionPrice doubleValue];
NSLog(#"total price: %f", self.totalPrice);
[self.transactionResults addObject:result];
result = nil;
}
}
- (IBAction)refreshButtonPressed:(UIBarButtonItem *)sender {
__block id resultObject;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
NSURL *url = [NSURL URLWithString:#"http://mywebservice.php"];
NSData *data = [NSData dataWithContentsOfURL:url];
NSError *error;
***resultObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];***
if(!error){
if([resultObject isKindOfClass:[NSDictionary class]]){
NSDictionary *dictonary = resultObject;
[self parseDictionary:dictonary];
NSLog(#"Done parsing!");
dispatch_async(dispatch_get_main_queue(), ^{
self.isLoading = NO;
[self.transactionsTableView reloadData];
});
}
else{
NSLog(#"JSON Error: Expected Dictionary");
resultObject = nil;
return;
}
}
else{
NSLog(#"JSON Error: %#", [error localizedDescription]);
dispatch_async(dispatch_get_main_queue(), ^{
resultObject = nil;
[self.transactionsTableView reloadData];
[self showError];
});
return;
}
});
}
I used a ARC as soon as it came out with 4.3, and put an app in the store with it - point being you could switch to ARC. That said, I tried to reproduce your problem by creating a class/file that has the no-arc flag applied to it, but cannot reproduce the problem. This makes me believe your problem is elsewhere. In the code below, I create a Test object in another file, retain it, and send it the test message. No matter what I set "i" to, it always deallocs the object:
#import "Tester.h"
#interface Obj : NSObject <NSObject>
#end
#implementation Obj
- (id)retain
{
NSLog(#"retain");
id i = [super retain];
return i;
}
- (oneway void)release
{
NSLog(#"release");
[super release];
}
- (void)foo
{
}
- (void)dealloc
{
NSLog(#"Obj dealloced");
[super dealloc];
}
#end
#implementation Tester
- (void)test
{
int i = 2;
__block Obj *obj;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
obj = [[Obj new] autorelease];
if(i == 0) {
Obj *o = obj;
dispatch_async(dispatch_get_main_queue(), ^
{
[o foo];
} );
} else if(i == 1) {
obj = nil;
} else if(i == 2) {
dispatch_async(dispatch_get_main_queue(), ^
{
obj = nil;
} );
}
} );
}
#end