MKStoreKit purchase error - ios

I'm still struggling with MKStoreKit.
There is a button called "buyAction" who has to launch the in-app purchase process. I've tried to implement it following tutorial advice but it still doesn't work :
- (IBAction)buyAction:(id)sender {
NSLog(#"buyFeature")
[[MKStoreManager sharedManager] buyFeature:#"PBonnet.TOEIC3.Package1"
onComplete:^(NSString* purchasedFeature, NSData*purchasedReceipt, NSArray* availableDownloads)
{ package1bought=1;
NSLog(#"success");
}
onCancelled:^
{ NSLog(#"failed purchase");
}];
}
On the onCompleteline, I've got 2 error messages : "Parameter name omitted" and "Expected expression".
Thanks for your help.
Cheers
EDIT of the onComplete line as supposed by matt. Now, the log is showing "buyFeature" but nothing after. App Store doesn't launch. Log shows this error message :
NSUbiquitousKeyValueStore error: PBonnet.TOEIC3 has no valid com.apple.developer.ubiquity-kvstore-identifier entitlement.

Look at the header for MKStoreManager. Here is the declaration for buyFeature:onComplete:onCancelled:
- (void) buyFeature:(NSString*) featureId
onComplete:(void (^)(NSString* purchasedFeature, NSData*purchasedReceipt, NSArray* availableDownloads)) completionBlock
onCancelled:(void (^)(void)) cancelBlock;
So, just to start with, we see that onComplete: requires a block that takes three parameters. But you are supplying a block with no parameters. Thus, the compiler rightly complains that you forgot the block parameters.
In effect, the problem has nothing whatever to do with MKStoreKit. The problem is that you don't know C - in particular, you don't know the syntax for blocks. I would suggest reading Apple's explanation of this topic.

Related

Can't read or create UIManagedDocuments anymore

I have a big issue in my app, which prevents creating new documents and reading them, whereas it worked well until now.
I didn't change anything, and it started bugging from a build to another.
This is the code I'm using:
CLProject *project = [[CLProject alloc] initWithFileURL:projectURL];
NSLog (#"Will save project at URL: %#", projectURL);
[project saveToURL:projectURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
NSLog (#"Project saved: %d", success);
[...]
}];
CLProject is a subclass of UIManagedDocument.
The first NSLog is called, but not the second one. Instead I get an error :
2018-02-14 19:21:03.597495+0100 CamList[2247:750786] Will save project
at URL:
file:///var/mobile/Containers/Data/Application/151E38F5-2214-4876-A188-2AB8B5E8CF6A/Documents/Projects/715A0087-F2EF-439B-A2DD-8E878EF8A973.camlist
2018-02-14 19:21:03.783397+0100 CamList[2247:750886] [default] [ERROR]
Could not get attribute values for item
/var/mobile/Containers/Data/Application/151E38F5-2214-4876-A188-2AB8B5E8CF6A/Documents/Projects/715A0087-F2EF-439B-A2DD-8E878EF8A973.camlist
(n). Error: Error Domain=NSFileProviderInternalErrorDomain Code=1 "The
reader is not permitted to access the URL."
UserInfo={NSLocalizedDescription=The reader is not permitted to access
the URL.}
But it doesn't crash, the app keeps running (but nothing happens because the completion block never gets called).
What I don't understand is that everything was working fine and I haven't changed anything...
Can you help me??
Thanks
Well, it seems to work fine again this morning... Nothing to understand. My iPhone had to be tired...

ContentExtensionsDomain error 1

I am trying to use "reloadContentBlockerWithIdentifier" function of SFContentBlockerManager as follows:
[SFContentBlockerManager reloadContentBlockerWithIdentifier:#"appid.extensionid"
completionHandler: ^ (NSError *error) {
NSLog("%#", error.localizedDescription)
}
However, it only returns error message that said as follows:
The operation couldn’t be completed. (ContentExtensionsDomain error 1.)
I set both APP IDs (appid and appid.appextensionid) in "Certificates, Identifiers & Profiles" in developer.apple.com. Also, I made two "target"s for each -- ios app and ios app extension. I also checked open source implementations in Github which are roughly the same as mine, and App Extension manual several times, but it seems hard to find the relevant page for this problem. The most critical problem is: I couldn't find any offcial documents describing this message.
I'd appreciate if you let me know what is the problem.
Looking at the safari services generated swift interface, I could find the following enum for error codes:
public enum SFContentBlockerErrorCode : Int {
case NoExtensionFound
case NoAttachmentFound
case LoadingInterrupted
}
So in your case it's probably the NoAttachmentFound error. Which can be caused by failure to initialise an item provider with given data from a json file.

Is it a good practice to extend NSError

Sorry for asking this question. I know in java we are extending Exception class for custom exceptions. But I don't see any scenarios for that in objective c.
So my question, Is it a good practice to extend NSError and introducing custom errors? If so when we should extend NSError class. I checked documentation for this too. But I can't see overriding notes for NSError.
While I agree that you shouldn't subclass NSError, it is very useful to put categories on it, and I do this regularly. For example, say your system often posts errors that come from some JSON block. I'd find it very convenient to create a category like:
#interface NSError (MyErrors)
// Construct an NSError from data in JSON.
// Include full JSON response in the userInfo
+ (NSError *)myErrorWithJSON:(JSON *)json;
// Parse the text out of the user info
- (NSString *)myErrorMessage;
// The full JSON error block as a string
- (NSString *)myErrorJSON;
// BOOLs can be helpful sometimes, or you could return an enum for use in a switch.
- (BOOL)myIsNetworkError;
- (BOOL)myIsAuthError;
#end
I often write little helpers to construct NSError more simply, construct the userinfo the way I want, and the pull data back out of the userinfo without callers needing to know its internal representation. I find this to be a very good form of data-hiding, and encourages the use of more descriptive messages.
Similarly, even for smaller projects, I often create a +myErrorWithCode:localizedDescription: category method. I know my domain, so I usually don't need to pass that, and this makes it a lot easier to set the NSLocalizedDescription key in the user info. Again, this encourages better errors by making them easier to create, and makes it easier to change the implementation details of your error handling.
I've never seen it done and that's because NSError is already very versatile. It allows the type of error to be defined by setting the domain and code properties and allows arbitrary additional information to be attached within the userInfo dictionary.
So, no, it's not good practice.
In the documentation is written that it is ok to subclass:
Applications may choose to create subclasses of NSError, for example,
to provide better localized error strings by overriding
localizedDescription.
In my case I am working with OSStatus which is Int32. NSError constructor supports only Int. So I need to subclass it to support OSSStatus.
It's not a bad idea to extend NSError.
I also have made a category on NSError for my own use. I would like to share it with you.
(1) Make a strings file to define all the error codes:
/* Following are general error messgaes those we can show to user
regarding to Internet connection and request. You can add more
codes. */
"-1001" = "Connection time out";
"-1003" = "Cannot find Host";
"-1004" = "Cannot connect to Host";
"-1005" = "Server is temporarily down";
"-1009" = "The Internet connection appears to be offline";
"-1012" = "Authentication failed";
"2000" = "This is a custom error message"; // custom created error code
/* Always describe unknow error with whatever you want in
except case (i.e. except error codes). If not mentioned
the following line, still you will get message "Unknown error" */
"Unknown error" = "Network error occured";
(2) Make a category on NSError, let say "NSError+ErrorInfo":
#interface NSError (ErrorInfo)
-(NSString *)userDescription;
#end
(3) Define it:
#define ERROR_KEY(code) [NSString stringWithFormat:#"%d",code]
#define ERROR_LOCALIZED_DESCRIPTION(code) NSLocalizedStringFromTable(ERROR_KEY(code),#"Errors",nil)
#implementation NSError (ErrorInfo)
-(NSString *)userDescription
{
NSString *errorDescrption = NSLocalizedStringFromTable(ERROR_KEY(self.code),#"Errors",nil);
if (!errorDescrption || [errorDescrption isEqual:ERROR_KEY(self.code)]){
return NSLocalizedStringFromTable(#"Unknown error",#"Errors",nil);;
}
else{
return ERROR_LOCALIZED_DESCRIPTION(self.code);
}
return nil;
}
#end
(4) Make use of it:
NSError *yourError; // This can be any NSError object you get
yourError = [NSError errorWithDomain:#"yourDomain" code:2000 userInfo:details]; // Just for test
NSLog(#"%#",[yourError userDescription]);

Getting an audio device with OpenAL

I'm trying to use OpenAL for an IOS game I'm working on, but having an issue opening the audio device. Specifically, when I call the function alcOpenDevice(NULL), I get 'NULL' in return. This is causing issues, of course, but I can't tell what I'm doing wrong.
I'm new to OpenAL, so I've been looking at a couple guides here and here to see what I need to do. If I download their sample projects and test 'em, they both work fine. If I copy their files into my project, and ignore the files I made, they still work fine. I'm assuming something got lost in translation when I started rebuilding the code for use in my project. Asking around and searching online hasn't given me any leads though, so I'm hoping someone here could put me on the right track.
Here's the actual setup code I'm using in my AudioPlayer.m
- (void)setup {
audioSampleBuffers = [NSMutableDictionary new];
audioSampleSources = [NSMutableArray new];
[self setupAudioSession];
[self setupAudioDevice];
[self setupNotifications];
}
- (BOOL)setupAudioSession {
// // This has been depricated.
//
// /* Setup the Audio Session and monitor interruptions */
// AudioSessionInitialize(NULL, NULL, AudioInterruptionListenerCallback, NULL);
//
// /* Set the category for the Audio Session */
// UInt32 session_category = kAudioSessionCategory_MediaPlayback;
// AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(session_category), &session_category);
//
// /* Make the Audio Session active */
// AudioSessionSetActive(true);
BOOL success = NO;
NSError *error = nil;
AVAudioSession *session = [AVAudioSession sharedInstance];
success = [session setCategory:AVAudioSessionCategoryPlayback error:&error];
if (!success) {
NSLog(#"%# Error setting category: %#", NSStringFromSelector(_cmd), [error localizedDescription]);
return success;
}
success = [session setActive:YES error:&error];
if (!success) {
NSLog(#"Error activating session: %#", [error localizedDescription]);
}
return success;
}
- (BOOL)setupAudioDevice {
// 'NULL' uses the default device.
openALDevice = alcOpenDevice(NULL); // Returns 'NULL'
ALenum error = alGetError(); // Returns '0'
NSLog(#"%i", error);
if (!openALDevice) {
NSLog(#"Something went wrong setting up the audio device.");
return NO;
}
// Create a context to use with the device, and make it the current context.
openALContext = alcCreateContext(openALDevice, NULL);
alcMakeContextCurrent(openALContext);
[self createAudioSources];
// Setup was successful
return YES;
}
- (void)createAudioSources {
ALuint sourceID;
for (int i = 0; i < kMaxConcurrentSources; i++) {
// Create a single source.
alGenSources(1, &sourceID);
// Add it to the array.
[audioSampleSources addObject:[NSNumber numberWithUnsignedInt:sourceID]];
}
}
Note: I'm running IOS 7.1.1 on a new iPad air, and using Xcode 5.1.1. This issue has been confirmed on the iPad, my simulator, and an iPod touch.
The Short Answer:
Apple's implementation of alcOpenDevice() only returns the device once. Every subsequent call returns NULL. This function can be called by a lot of Apple audio code, so take out EVERY TRACE of audio code before using OpenAL and manually calling that function yourself.
The Long Answer:
I spent half a day dealing with this problem while using ObjectAL, and ended up doing exactly what you did, re-making the entire project. It worked, until out of curiosity I copied the entire project over, then same problem again, alcOpenDevice(NULL) returned NULL. By chance I stumbled upon the answer. It was this bit of code in my swift game scene:
let jumpSound = SKAction.playSoundFileNamed("WhistleJump.mp3", waitForCompletion: false)
And then I remembered I had this problem before without SKAction involved. That time it turned out I was using ObjectAL in two different ways, I used OALSimpleAudio in one place, and OpenAL objects in another, and it was initializing my audio session twice.
The common thread between these two incidents is both times alcOpenDevice() was called more than once during the life of the application. The first time it was ObjectAL calling it twice due to my misuse of the library. The second SKAction.playSoundFileNamed() must have called alcOpenDevice() before my ObjectAL code did. Upon further research I found this bit in the OpenAL 1.1 Specification:
6.1.1. Connecting to a Device
The alcOpenDevice function allows the application (i.e. the client program) to connect to a device (i.e. the server).
ALCdevice * alcOpenDevice (const ALCchar *deviceSpecifier);
If the function returns NULL, then no sound driver/device has been found. The argument is a null terminated string that requests a certain device or device configuration. If NULL is specified, the implementation will provide an implementation specific default.
My hunch is that Apple's implementation of this function only returns the correct device ONCE for the life of the application. Every time alcOpenDevice is called after that, it returns NULL. So bottom line: Take out every trace of audio code before switching to OpenAL. Even code that seems safe, like SKAction.playSoundFileNamed() still might contain a call to alcOpenDevice() buried deep in its implementation.
For those using ObjectAL, here is the console output of this problem to help them find their way here from google, as I couldn't find a good answer myself:
OAL Error: +[ALWrapper openDevice:]: Could not open device (null)
OAL Error: -[ALDevice initWithDeviceSpecifier:]: <ALDevice: 0x17679b20>: Failed to create OpenAL device (null)
OAL Error: +[ALWrapper closeDevice:]: Invalid Enum (error code 0x0000a003)
OAL Warning: -[OALAudioSession onAudioError:]: Received audio error notification, but last reset was 0.012216 seconds ago. Doing nothing.
fatal error: unexpectedly found nil while unwrapping an Optional value
This SO answer seems to validate my comment about AVAudioSession conflicting with OpenAL. Try removing AVAudioSession, or initializing OpenAL first (tho I imagine this would cause the inverse problem).
Alright, so I ended up starting over in a fresh project with a copy-pasted version of AudioSamplePlayer from the first sample project I linked. -It worked.
I then edited it step by step back to the format I had set up in my project. -It still works!
I still don't know what I did wrong the first time, and I'm not sure it was even in my audio player anymore, but It's running now. I blame gremlins.
...maybe alien surveillance.

Google Analytics: GANTrackerError 195946409 - reason?

I'm using the Google Analytics library in my iPhone/iPad app; one part of the code sometimes throws an error, the [error localizedDescription] shows the following:
The operation couldn’t be completed. (com.google.googleanalytics.GANTrackerError error 195946409.
I didn't find any information about this error code; the app doesn't crash there, but I'm assuming that the tracking also doesn't work in that case. Does someone know the reason?
Thanks a lot!
Edit: the code in question:
[[GANTracker sharedTracker] setCustomVariableAtIndex:0
name:#"article"
value:[model name]
withError:&error];
got it: customVariableAtIndex must not be 0; it seems like it has to start at 1...
It appears this error 195946409 can appear for a number of reasons, not limited to just setting an incorrect index. For example, If your value is too long, you will get the same error.

Resources