I have the following piece of code:
switch (self.status) {
case refused:
message=NSLocalizedString(#"booking refused", nil);
break;
case accepted:
message=NSLocalizedString(#"booking accepted", nil);
break;
case taxiAborted:
message=NSLocalizedString(#"booking aborted", nil);
break;
case near:
message=NSLocalizedString(#"booking aborted", nil);
break;
case completed:
message=NSLocalizedString(#"booking completed", nil);
break;
default:
break;
}
that never enters the interior of the switch: neither hitting the default case nor the correct value. In my case self.status had value 'active' that is part of the following enumeration:
typedef enum {
requested = 0,
refused = 1,
accepted =2,
taxiAborted=3,
near = 4,
running= 5,
completed = 6,
userAborted=7
} statuses;
What did I do wrong?
Related
I'm trying to modify this code. It's a logging script that shows keypresses.
Currently the log only shows the ABCs, numbers, and a combination of modifier+(key). Is it possible to show every keypress like "command", "option", "F1" and so on?
I don't know Objective-C and I'm blindly trying to change this code for a project of mine. Any help will take me a step further in this project and is much appreciated.
The repo:
https://github.com/chbrown/osx-tap
My version that shows the actual keys names:
#import <Foundation/Foundation.h>
#include <ApplicationServices/ApplicationServices.h>
#import "key-tap.h"
#implementation LoggerConfig
#synthesize output;
#synthesize epoch;
#end
/* CGEventTapCallBack function signature:
proxy
A proxy for the event tap. See CGEventTapProxy. This callback function may pass this proxy to other functions such as the event-posting routines.
type
The event type of this event. See “Event Types.”
event
The incoming event. This event is owned by the caller, and you do not need to release it.
refcon
A pointer to user-defined data. You specify this pointer when you create the event tap. Several different event taps could use the same callback function, each tap with its own user-defined data.
*/
CGEventRef myCGEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
LoggerConfig *config = (__bridge LoggerConfig *)refcon;
#autoreleasepool {
NSMutableArray *modifiers = [NSMutableArray arrayWithCapacity:10];
CGEventFlags flags = CGEventGetFlags(event);
if ((flags & kCGEventFlagMaskShift) == kCGEventFlagMaskShift)
[modifiers addObject:#"shift"];
if ((flags & kCGEventFlagMaskControl) == kCGEventFlagMaskControl)
[modifiers addObject:#"control"];
if ((flags & kCGEventFlagMaskAlternate) == kCGEventFlagMaskAlternate)
[modifiers addObject:#"alt"];
if ((flags & kCGEventFlagMaskCommand) == kCGEventFlagMaskCommand)
[modifiers addObject:#"command"];
if ((flags & kCGEventFlagMaskSecondaryFn) == kCGEventFlagMaskSecondaryFn)
[modifiers addObject:#"fn"];
// Ignoring the following flags:
// kCGEventFlagMaskAlphaShift = NX_ALPHASHIFTMASK,
// kCGEventFlagMaskHelp = NX_HELPMASK,
// kCGEventFlagMaskNumericPad = NX_NUMERICPADMASK,
// kCGEventFlagMaskNonCoalesced = NX_NONCOALSESCEDMASK
NSString *modifierString = [modifiers componentsJoinedByString:#"+"];
// The incoming keycode. CGKeyCode is just a typedef of uint16_t, so we treat it like an int
CGKeyCode keycode = (CGKeyCode)CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
// CGEventKeyboardGetUnicodeString
// Keypress code goes here.
NSString *action;
if (type == kCGEventKeyDown)
action = #"down";
else if (type == kCGEventKeyUp)
action = #"up";
else
action = #"other";
NSString *keytype;
switch (keycode) {
case 0: keytype = #"a"; break;
case 1: keytype = #"s"; break;
case 2: keytype = #"d"; break;
case 3: keytype = #"f"; break;
case 4: keytype = #"h"; break;
case 5: keytype = #"g"; break;
case 6: keytype = #"z"; break;
case 7: keytype = #"x"; break;
case 8: keytype = #"c"; break;
case 9: keytype = #"v"; break;
case 11: keytype = #"b"; break;
case 12: keytype = #"q"; break;
case 13: keytype = #"w"; break;
case 14: keytype = #"e"; break;
case 15: keytype = #"r"; break;
case 16: keytype = #"y"; break;
case 17: keytype = #"t"; break;
case 18: keytype = #"1"; break;
case 19: keytype = #"2"; break;
case 20: keytype = #"3"; break;
case 21: keytype = #"4"; break;
case 22: keytype = #"6"; break;
case 23: keytype = #"5"; break;
case 24: keytype = #"#"; break;
case 25: keytype = #"9"; break;
case 26: keytype = #"7"; break;
case 27: keytype = #"-"; break;
case 28: keytype = #"8"; break;
case 29: keytype = #"0"; break;
case 30: keytype = #"]"; break;
case 31: keytype = #"o"; break;
case 32: keytype = #"u"; break;
case 33: keytype = #"["; break;
case 34: keytype = #"i"; break;
case 35: keytype = #"p"; break;
case 37: keytype = #"l"; break;
case 38: keytype = #"j"; break;
case 39: keytype = #"'"; break;
case 40: keytype = #"k"; break;
case 41: keytype = #";"; break;
case 42: keytype = #"\\"; break;
case 43: keytype = #","; break;
case 44: keytype = #"/"; break;
case 45: keytype = #"n"; break;
case 46: keytype = #"m"; break;
case 47: keytype = #"."; break;
case 50: keytype = #"`"; break;
case 65: keytype = #"[decimal]"; break;
case 67: keytype = #"[asterisk]"; break;
case 69: keytype = #"[plus]"; break;
case 71: keytype = #"[clear]"; break;
case 75: keytype = #"[divide]"; break;
case 76: keytype = #"[enter]"; break;
case 78: keytype = #"[hyphen]"; break;
case 81: keytype = #"[equals]"; break;
case 82: keytype = #"0"; break;
case 83: keytype = #"1"; break;
case 84: keytype = #"2"; break;
case 85: keytype = #"3"; break;
case 86: keytype = #"4"; break;
case 87: keytype = #"5"; break;
case 88: keytype = #"6"; break;
case 89: keytype = #"7"; break;
case 91: keytype = #"8"; break;
case 92: keytype = #"9"; break;
case 36: keytype = #"[return]"; break;
case 48: keytype = #"[tab]"; break;
case 49: keytype = #" "; break;
case 51: keytype = #"[del]"; break;
case 53: keytype = #"[esc]"; break;
case 54: keytype = #"[right-cmd]"; break;
case 55: keytype = #"[left-cmd]"; break;
case 56: keytype = #"[left-shift]"; break;
case 57: keytype = #"[caps]"; break;
case 58: keytype = #"[left-option]"; break;
case 59: keytype = #"[left-ctrl]"; break;
case 60: keytype = #"[right-shift]"; break;
case 61: keytype = #"[right-option]"; break;
case 62: keytype = #"[right-ctrl]"; break;
case 63: keytype = #"[fn]"; break;
case 64: keytype = #"[f17]"; break;
case 72: keytype = #"[volup]"; break;
case 73: keytype = #"[voldown]"; break;
case 74: keytype = #"[mute]"; break;
case 79: keytype = #"[f18]"; break;
case 80: keytype = #"[f19]"; break;
case 90: keytype = #"[f20]"; break;
case 96: keytype = #"[f5]"; break;
case 97: keytype = #"[f6]"; break;
case 98: keytype = #"[f7]"; break;
case 99: keytype = #"[f3]"; break;
case 100: keytype = #"[f8]"; break;
case 101: keytype = #"[f9]"; break;
case 103: keytype = #"[f11]"; break;
case 105: keytype = #"[f13]"; break;
case 106: keytype = #"[f16]"; break;
case 107: keytype = #"[f14]"; break;
case 109: keytype = #"[f10]"; break;
case 111: keytype = #"[f12]"; break;
case 113: keytype = #"[f15]"; break;
case 114: keytype = #"[help]"; break;
case 115: keytype = #"[home]"; break;
case 116: keytype = #"[pgup]"; break;
case 117: keytype = #"[fwddel]"; break;
case 118: keytype = #"[f4]"; break;
case 119: keytype = #"[end]"; break;
case 120: keytype = #"[f2]"; break;
case 121: keytype = #"[pgdown]"; break;
case 122: keytype = #"[f1]"; break;
case 123: keytype = #"[left]"; break;
case 124: keytype = #"[right]"; break;
case 125: keytype = #"[down]"; break;
case 126: keytype = #"[up]"; break;
default: keytype = #"none"; break;
}
NSTimeInterval offset = [[NSDate date] timeIntervalSince1970] - config.epoch;
// logLine format:
// ticks since started <TAB> key code <TAB> action <TAB> modifiers
// so it'll look something like "13073 45 up shift+command"
NSString *logLine = [NSString stringWithFormat:#"%d\t%d\t%#\t%#\t%#\n",
(int)offset, keycode ,keytype, action, modifierString];
NSLog(#"> %#", logLine);
[config.output writeData:[logLine dataUsingEncoding:NSUTF8StringEncoding]];
}
// We must return the event for it to be useful.
return event;
}
int main(void) {
// set up the file that we'll be logging into
// NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
LoggerConfig *config = [[LoggerConfig alloc] init];
NSUserDefaults *args = [NSUserDefaults standardUserDefaults];
// grabs command line arguments --directory
NSString *directory = [args stringForKey:#"directory"];
if (!directory) {
// default to /var/log/osx-tap
directory = #"/var/log/osx-tap";
}
NSFileManager *fileManager = [NSFileManager defaultManager];
// create directory if needed (since withIntermediateDirectories:YES,
// this shouldn't fail if the directory already exists)
bool directory_success = [fileManager createDirectoryAtPath:directory
withIntermediateDirectories:YES attributes:nil error:nil];
if (!directory_success) {
NSLog(#"Could not create directory: %#", directory);
return 1;
}
config.epoch = [[NSDate date] timeIntervalSince1970];
NSString *filename = [NSString stringWithFormat:#"%d.log", (int)config.epoch];
NSString *filepath = [NSString pathWithComponents:#[directory, filename]];
bool create_success = [fileManager createFileAtPath:filepath contents:nil attributes:nil];
if (!create_success) {
NSLog(#"Could not create file: %#", filepath);
return 1;
}
// now that it's been created, we can open the file
config.output = [NSFileHandle fileHandleForWritingAtPath:filepath];
// [config.output seekToEndOfFile];
// Create an event tap. We are interested in key ups and downs.
CGEventMask eventMask = ((1 << kCGEventKeyDown) | (1 << kCGEventKeyUp));
/*
CGEventTapCreate(CGEventTapLocation tap, CGEventTapPlacement place,
CGEventTapOptions options, CGEventMask eventsOfInterest,
CGEventTapCallBack callback, void *refcon
CGEventTapLocation tap:
kCGHIDEventTap
Specifies that an event tap is placed at the point where HID system events enter the window server.
kCGSessionEventTap
Specifies that an event tap is placed at the point where HID system and remote control events enter a login session.
kCGAnnotatedSessionEventTap
Specifies that an event tap is placed at the point where session events have been annotated to flow to an application.
CGEventTapPlacement place:
kCGHeadInsertEventTap
Specifies that a new event tap should be inserted before any pre-existing event taps at the same location.
kCGTailAppendEventTap
Specifies that a new event tap should be inserted after any pre-existing event taps at the same location.
CGEventTapOptions options:
kCGEventTapOptionDefault = 0x00000000
kCGEventTapOptionListenOnly = 0x00000001
...
CGEventTapCallBack has arguments:
(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
*/
// we don't want to discard config
// CFBridgingRetain(config);
CFMachPortRef eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, 0,
eventMask, myCGEventCallback, (__bridge void *)config);
if (!eventTap) {
NSLog(#"failed to create event tap");
return 1;
}
if (!CGEventTapIsEnabled(eventTap)) {
NSLog(#"event tap is not enabled");
return 1;
}
/* Create a run loop source.
allocator
The allocator to use to allocate memory for the new object. Pass NULL or kCFAllocatorDefault to use the current default allocator.
port
The Mach port for which to create a CFRunLoopSource object.
order
A priority index indicating the order in which run loop sources are processed. order is currently ignored by CFMachPort run loop sources. Pass 0 for this value.
*/
CFRunLoopSourceRef runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
/* Adds a CFRunLoopSource object to a run loop mode.
CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef mode);
rl
The run loop to modify.
source
The run loop source to add. The source is retained by the run loop.
mode
The run loop mode to which to add source. Use the constant kCFRunLoopCommonModes to add source to the set of objects monitored by all the common modes.
*/
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
// Enable the event tap.
// CGEventTapEnable(eventTap, true);
// Runs the current thread’s CFRunLoop object in its default mode indefinitely:
CFRunLoopRun();
return 0;
}
I've tried to understand this function but failed to do so after about 3 hours of fiddling.
I want to use TouchID authenticate my own app.
1.I want user can click 'Enter passcode' to invoke system build-in passcode screen to authenticate,if success then enter my own app.
But I don't how know to make it forward to passcode authenticate view like the following screen in 'case LAErrorUserFallback'
Here is my code :
LAContext *context = [[LAContext alloc] init];
__block NSString *msg;
__block BOOL bAuth;
// show the authentication UI with our reason string
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:NSLocalizedString(#"Unlock APP With FingerPrint", nil) reply:
^(BOOL success, NSError *authenticationError) {
if (success) {
bAuth = YES;
msg =[NSString stringWithFormat:NSLocalizedString(#"EVALUATE_POLICY_SUCCESS", nil)];
dispatch_async(dispatch_get_main_queue(), ^{
[[MYAppDelegate theDelegate] initializeAppAfterKeyVaultUnlocked];
});
NSLog(#"%#",msg);
} else {
bAuth = NO;
switch (authenticationError.code) {
case LAErrorAuthenticationFailed:
msg = [NSString stringWithFormat:NSLocalizedString(#"Authentication Failed", nil)];
// ...
break;
case LAErrorUserCancel:
msg = [NSString stringWithFormat:NSLocalizedString(#"User pressed Cancel button", nil)];
dispatch_async(dispatch_get_main_queue(), ^{
[[MYAppDelegate theDelegate] exitAndLock];
});
break;
case LAErrorUserFallback:
msg = [NSString stringWithFormat:NSLocalizedString(#"User pressed \"Enter Password\"", nil)];
//Here I want to fallback to iOS build-in passcode authenticate view, and get the auth result.
break;
default:
msg = [NSString stringWithFormat:NSLocalizedString(#"Touch ID is not configured", nil)];
// ...
break;
}
NSLog(#"%#",authenticationError.localizedDescription);
}
}];
Now in iOS 9 it is pretty simple actually - you just have to use LAPolicyDeviceOwnerAuthentication instead of LAPolicyDeviceOwnerAuthenticationWithBiometrics
So in your code you just replace this:
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:NSLocalizedString(#"Unlock APP With FingerPrint", nil) reply:
^(BOOL success, NSError *authenticationError) {
With this:
[context evaluatePolicy:LAPolicyDeviceOwnerAuthentication localizedReason:NSLocalizedString(#"Unlock APP With FingerPrint", nil) reply:
^(BOOL success, NSError *authenticationError) {
Thus, when user fails to authenticate with fingerprint, there'd be "enter passcode" option which would invoke system passcode input screen.
In my understanding, you will have to build the passcode screen yourself if you want to use evaluatePolicy.
If you use keychain, the SecItemCopyMatching function automatically falls back to passcode if fingering fails. Here's a reference on how to get it going (see section 3): https://www.secsign.com/fingerprint-validation-as-an-alternative-to-passcodes/
Didn't try that, but this post claim you can use the system as follow here (This works only with iOS 8 or later).
Or (which is what I did) you can build your passcode entry screen (to support older iOS versions), my controller have a passcode entry view, which will get exposed when the user select use passcode. At that point the call back from evaluatePolicy will return with LAErrorUserFallback, which can be the time to open your custom passcode screen.
something like that:
-(void)maybeShowTouchIDMessage {
if (![SettingsManager sharedManager].isUseTouchID || self.createPassCodeMode) {
self.shieldView.hidden = YES;
return;
}
self.shieldView.hidden = NO;
self.shieldView.alpha = 1.0;
LAContext *context = [[LAContext alloc] init];
NSError *evalError = nil;
if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&evalError] ) {
__weak SecurityWindowViewController *weakSelf = self;
[context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:#"Use touch id \n or hit \"Cancel\" to enter passcode"
reply:^(BOOL success, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
SecurityWindowViewController *strongSelf = weakSelf;
if (success) {
[strongSelf hideWithSuccess:YES];
} else if (error){
NSString *errorMessage;
BOOL showError = NO;
switch (error.code) {
case LAErrorAuthenticationFailed:
errorMessage = #"Sorry couldn't autheticate";
showError = YES;
break;
case LAErrorPasscodeNotSet:
errorMessage = #"Sorry couldn't autheticate";
showError = YES;
break;
case LAErrorTouchIDNotEnrolled:
errorMessage = #"Touch ID has no enrolled fingers";
showError = YES;
break;
default:
showError = NO;
break;
}
[UIView animateWithDuration:0.5 animations:^{
strongSelf.shieldView.alpha = 0.0;
} completion:^(BOOL finished) {
strongSelf.shieldView.hidden = YES;
}];
if (showError) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:errorMessage
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alert show];
}
}
});
}];
}
For your case :
case LAErrorUserFallback:
[context evaluatePolicy:LAPolicyDeviceOwnerAuthentication localizedReason:NSLocalizedString(#"Unlock APP With PassCode", nil) reply: ^(BOOL success, NSError *authenticationError) {
if(success){
NSLog(#"PassCode Login successful");
}else{
NSLog(#"%#",authenticationError);
}
}
For device passcode verification you need to use LAPolicyDeviceOwnerAuthentication instead of LAPolicyDeviceOwnerAuthenticationWithBiometrics. Hope this helps !!
You can add another case and call your passcode screen from that.
Here is my code:
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
NSString *myLocalizedReasonString = strMessage;
objFlockr.pVerificationBlock = objResponse;
if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
if (!isShow) {
myContext.localizedFallbackTitle = #"";
}
else
{
myContext.localizedFallbackTitle = #"Set Up Passcode";
}
[myContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:myLocalizedReasonString
reply:^(BOOL succes, NSError *error) {
if (!AppDel.firstAttampt && succes && !isShow)
{
if (objFlockr.pVerificationBlock)
objFlockr.pVerificationBlock(1);
}
else if (succes) {
if (objFlockr.pVerificationBlock)
objFlockr.pVerificationBlock(0);
NSLog(#"User authenticated");
} else {
switch (error.code) {
case LAErrorAuthenticationFailed:
NSLog(#"Authentication Failed");
if (objFlockr.pVerificationBlock)
objFlockr.pVerificationBlock(1);
break;
case LAErrorUserCancel:
NSLog(#"User pressed Cancel button");
if (objFlockr.pVerificationBlock)
objFlockr.pVerificationBlock(3);
break;
case LAErrorUserFallback:
NSLog(#"User pressed \"Enter Password\"");
if (objFlockr.pVerificationBlock)
objFlockr.pVerificationBlock(4);
break;
default:
[self showMessage:#"Touch ID is not configured" withTitle:#"Error"];
if (objFlockr.pVerificationBlock)
objFlockr.pVerificationBlock(2);
NSLog(#"Touch ID is not configured");
break;
}
NSLog(#"Authentication Fails");
}
}];
} else {
NSLog(#"Can not evaluate Touch ID");
[self showMessage:#"Can not evaluate TouchID" withTitle:#"Error"];
}
Replace LAPolicy policy enum value deviceOwnerAuthenticationWithBiometrics with deviceOwnerAuthentication
Note: If user has enable biometric (face id or touch id) authentication, then device will ask first for biometric authentication and if user choose fall back authentication, then only deviceOwnerAuthentication will show passcode screen.
Try this and see (swift 4):
func touchIDAuthentication() {
let context = LAContext()
var error:NSError?
// edit line - deviceOwnerAuthentication
guard context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) else {
//showAlertViewIfNoBiometricSensorHasBeenDetected()
return
}
// edit line - deviceOwnerAuthentication
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &errorPointer) {
// edit line - deviceOwnerAuthentication
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason, reply: { (success, error) in
if success {
DispatchQueue.main.async {
print("Authentication was successful")
}
}else {
DispatchQueue.main.async {
//self.displayErrorMessage(error: error as! LAError )
print("Authentication was error")
}
}
})
}else {
// self.showAlertWith(title: "Error", message: (errorPointer?.localizedDescription)!)
}
}
I have been trying to figure this out for days now but I cannot figure out what is going on. In my app I want to be able to delete an item, and related items from a tableView. So when the delete button in the TableView is pushed the following method is called.
- (BOOL) deleteDuik:(MOODuik *) duik
{
int diveid = [duik duikId];
NSLog(#"Begin Delete");
sqlite3 *db = [self openDatabase];
sqlite3_stmt * deleteStmt = nil;
BOOL succes = TRUE;
#try{
if (db != NULL) {
sqlite3_exec(db, "BEGIN TRANSACTION", 0, 0, 0);
//NSString *sqlString = [NSString stringWithFormat:#"delete from duik where duikid = %d", diveid];
const char *sql = "delete from duik where duikid = ?";
if(sqlite3_prepare_v2(db, sql, -1, &deleteStmt, NULL) == SQLITE_OK)
{
sqlite3_bind_int(deleteStmt, 1, diveid);
sqlite3_step(deleteStmt);
sqlite3_finalize(deleteStmt);
deleteStmt = nil;
sql = "delete from waarneming where duikid = ?";
if(sqlite3_prepare_v2(db, sql, -1, &deleteStmt, NULL) == SQLITE_OK)
{
sqlite3_bind_int(deleteStmt, 1, diveid);
sqlite3_step(deleteStmt);
sqlite3_finalize(deleteStmt);
}
}
} else
{
NSLog(#"Failed to connect to db");
succes = FALSE;
}
if(succes && ( [duik backendid] == 0 || [self deleteDuikServerSide:[duik backendid]]))
{
NSLog(#"End Delete");
if(sqlite3_exec(db, "COMMIT TRANSACTION", 0, 0, 0) !=SQLITE_OK) NSLog(#"SQL Error: %s",sqlite3_errmsg(db));
} else{
NSLog(#"Failed to delete remote copy");
sqlite3_exec(db, "ROLLBACK TRANSACTION", 0, 0, 0);
succes = false;
}
} #catch (NSException *e) {
NSLog(#"Failed to delete: %#", [e description]);
sqlite3_exec(db, "ROLLBACK TRANSACTION", 0, 0, 0);
} #finally {
#try {
sqlite3_finalize(deleteStmt);
sqlite3_close(db);
}
#catch (NSException *exception) {
//REally nothing to do here
}
sqlite3_close(db);
}
return succes;
}
When I try to run this, it keeps telling me the database is locked, yet I have no idea why it is locked. I have checked to see if other actions require the DB at the same time but I cannot find any. Also, if I add a row from the same tableView, I use the same construction with a transaction and that one DOES work. I have tried everything I could come up with, rewritten this chunk of code at least 10 times but so far no luck.
Is there anyone who can tell me what I am doing wrong here?
I have switch. One of the cases must be a count of array:
int count = [array count];
switch (someValue) {
case 0:
[self foo];
break;
case count:
[self bar];
break;
default:
break;
}
But compiler tolds:
Expression is not an integer constant expression
How to make const int from [array count]?
As the error suggests, the cases must all be constants. You'll need an if statement to check the dynamic case:
int count = [array count];
switch (someValue) {
case 0:
[self foo];
break;
default:
if (someValue == count)
[self bar];
break;
}
if(some value == 0) {
[self foo];
} else if (someValue == [array count]) {
[self bar]
}
i set my in app purchase creating two bundle id's .i created a test account and checked my in app purchase..it was working perfectly fine...after a week later when i tested i m getting this error *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 0 beyond bounds for empty array'
*** First throw call stack: and its not returning the products...could u guys help me out.what possibly could be wrong with this
-(void)Declarations
{
if ([SKPaymentQueue canMakePayments]) {
NSLog(#"parental functions are disabled");
SKProductsRequest *productRequest = [[SKProductsRequest alloc]initWithProductIdentifiers:[NSSet setWithObjects:#"com.techzone.9xo.onedollar",#"com.techzone.9xo.twodollar",nil]];
productRequest.delegate=self;
[productRequest start];
}else {
NSLog(#"parental functions are enabled");
}
}
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse: (SKProductsResponse *)response
{
SKProduct *validProduct=nil;
int count = [response.products count];
NSLog(#"number of prouducts present:%d",count);
if(_HintValue)
{
validProduct = [response.products objectAtIndex:0];
}
if (!_HintValue) {
validProduct = [response.products objectAtIndex:1];
}
NSLog(#"the product is :%#",validProduct.localizedTitle);
SKPayment *skpayment = [SKPayment paymentWithProduct:validProduct];
[[SKPaymentQueue defaultQueue] addPayment:skpayment];
[[SKPaymentQueue defaultQueue]addTransactionObserver:self];
}
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error
{
NSLog(#"Failed to connect with error: %#", [error localizedDescription]);
}
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
NSLog(#"stuff is getting purchased");
break;
case SKPaymentTransactionStatePurchased:
NSLog(#"purchased properly");
NSInteger currentValue=[Util getIntegerValueForKey:#"hintValue"];
if(_HintValue)
[Util setIntegerValue:10+currentValue forKey:#"hintValue"];
if (!_HintValue)
[Util setIntegerValue:25+currentValue forKey:#"hintValue"];
[[SKPaymentQueue defaultQueue]finishTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
[[SKPaymentQueue defaultQueue]finishTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
if (transaction.error.code != SKErrorPaymentCancelled) {
NSLog(#"error happened");
}
[[SKPaymentQueue defaultQueue]finishTransaction:transaction];
break;
default:
break;
}
}
}
The problem is that you are getting an empty list of valid product IDs. You are accessing objectAtIndex:0 of an empty array which creates that error.
if you check response.invalidProductIdentifiers you will find your product IDs there.
Unfortunately that can have many reasons. Have a look at this checklist of reasons that can lead to invalid product IDs.