Monday, October 31, 2011

iOS and In app purchases - what's wrong with this picture?

Well, I'm in the middle of my next submitting stuff to apple struggle. For some reason, it always seems to be this titanic, time-consuming effort. What is the matter with those guys?

Anyway, here's where I'm at. I have an paid app on the app store. So, I've gotten through that hurdle. I even submitted an update to it. So, I've managed to successfully bash my way through that whole process.

I've also added in-app purchase code to my app. I've added a product in iTunes connect which can be purchased by my app. It's got the same bundle-id, everything.

The problems start of course with testing. You knew this wasn't going to be easy, didn't you?

When I run the code to test download the available product ids (I only have one), I don't get anything back from the download. Nor error messages or anything - just an empty list.

So, what could be the cause of this? I decided to look at the in-app products page, and saw this:

The first In-App Purchase for an app must be submitted for review at the same time that you submit an app version. You must do this on the Version Details page. Once your binary has been uploaded and your first In-App Purchase has been submitted for review, additional In-App Purchases can be submitted using the table below.



So, I hadn't submitted a new version yet. So, I did, and think this maybe let me do something like resbumit the app product, or save it, or something. So, at this point I've submitted both the app and the product. And the product now has a status of "wating for review". However, the app itself is showing the infomous "invalid binary" error.


So, I'll have to resolve the invalid binary error. But, what I don't know is if this affects the status of the product. The product is currently "waiting for review". Will it be waiting for review until I get a valid binary? Could the fact that I don't have the product approved be *causing* the invalid binary? Somehow, I doubt that. I hope not.

Actually, it's the same problem I had before - you just have to reselect your app distribution certificate when submitting it via organizer.

Ok, then according to this tutorial,

http://troybrant.net/blog/2010/01/in-app-purchases-a-full-walkthrough/

if you've added your product, you also have to submit an app-binary. Then cancel the app binary (you can do this in binary details, button on the upper right corner).

So, now you will basically have you app in "developer rejected" mode, and your in-app purchase in "waiting for review" status.

This is confirmed by this posting in SO:

http://stackoverflow.com/questions/6570163/invalid-product-id-in-inapp-purchase

So, my next step is to retry running the app.

Ok, wall the reason it wasn't printing "invalid product ID" is that my program wasn't checking for it. It turns out, if I run this piece of code (as suggested in the first url listed):

for (NSString *invalidProductId in response.invalidProductIdentifiers)
{
NSLog(@"Invalid product id: %@" , invalidProductId);
}

I get this output:

2011-11-01 14:42:36.274 JlptVocabularyQuiz[2751:707] IAHelper, received products results...
2011-11-01 14:42:36.282 JlptVocabularyQuiz[2751:707] Invalid product id: com.kanjisoft.JlptVocabularyQuiz.AdditionalVocab1

So, there you go. I've been getting this all along, probably, just didn't know. It's tuesday, and I originally cleared that message on Sunday night, I think. So, it's been nearly 48 hours, and still I'm getting the problem.

So, then next thing to do is go through this checklist:

Have you enabled In-App Purchases for your App ID?
Have you checked Cleared for Sale for your product?
Have you submitted (and optionally rejected) your application binary?
Does your project’s .plist Bundle ID match your App ID?
Have you generated and installed a new provisioning profile for the new App ID?
Have you configured your project to code sign using this new provisioning profile?
Are you building for iPhone OS 3.0 or above?
Are you using the full product ID when when making an SKProductRequest?
Have you waited several hours since adding your product to iTunes Connect?
Are your bank details active on iTunes Connect? (via Mark)
Have you tried deleting the app from your device and reinstalling? (via Hector, S3B, Alex O, Joe, and Alberto)
Is your device jailbroken? If so, you need to revert the jailbreak for IAP to work. (via oh my god, Roman, and xfze)

We'll tackle that in the next post.

Tuesday, October 25, 2011

Putting adds into an iOS app

Well, after taking a couple of days off after successfully *submitting* version 1.1 of the app for development, let's take a look at what involved in putting advertising into our app. I'm excited about this because I understand advertising on the iPhone (via iAd) is significantly better than that offered by Android. Either way, I'm not expecting to get rich.

Ok, let's google iAd iphone or something like that.

Ok, here we go:

http://developer.apple.com/iad/

Rich, Captivating Ads that Keep Users in Your Apps

iAd rich media ads combine the emotion of television with the interactivity of the web to deliver a unique, compelling experience like no other on a mobile device. Users can dive into immersive ads without ever leaving your app. When they tap the ad, your app pauses and with a single tap they close the ad and resume exactly where they left off in your app. And users have been delighted, spending an average of one minute interacting with iAd rich media ads.
Watch the highlight video
Generate More Revenue from Your Apps

iAd is the best way to generate revenue from your apps when compared to other mobile advertising platforms. Hundreds of iOS developers are already making more than $50,000 per quarter serving iAd rich media ads.

iAd is also easy to implement. Since it’s built into iOS, you don’t have to worry about integrating another SDK. Just decide where you want ads to appear in your app and with a minimal amount of code your app is ready to serve iAd rich media ads — Apple does the rest. We sell the ads and serve them to your app while you collect 60 percent of the advertising revenue generated.



Great. I especially like the easy to implement, built into iOS part.

Ok, let's see.

Arghh! High school kids everywhere. I've already moved once. As soon as I get set up, a new pack takes up the table next to me.

Ok, I did this:

Click through the Developer Advertising Services Agreement in iTunes Connect
The first step in joining the iAd Network is to click through the Developer Advertising Services Agreement, which can be found in the Contracts, Tax, and Banking Information module. In addition, you will be asked to set up your banking and tax information if you don't have a paid app on the App Store.




Next:

Enable your app for iAd rich media ads
Once you have clicked through the Developer Advertising Services Agreement in iTunes Connect, you can enable your app for iAd rich media ads in the Manage Your Applications module within iTunes Connect.



Ok, let's look at "Manage your appications".

Ok, before I enable them, I think I need to do something about actually creating the adds.

Here we go:

Creating banner views

Use the ADBannerView class to dedicate a portion of the screen to display banner advertisements in your user interface.

Once created, a banner view automatically downloads new iAd rich media ads to display to your users. View the following resources to learn how to create banner views in your app.



Ok, so it's the AddBannerView class I'm looking for.


Wow - if I click through to the video, there are like 105 of them. Oh, there's one for in-app purchases. Sweet. It's all at this url:

http://developer.apple.com/videos/wwdc/2011/?id=505

Well, I don't have the bandwidth here at the cafe to view them, so, let's look for text links.

Here's one:

http://answers.oreilly.com/topic/1747-how-to-implement-iad-on-your-iphone-applications/

With the introduction if iOS 4, Apple have also introduced iAds, a mobile advertising platform that aims to convey a new concept of interactive marketing on your smart-device. Through CSS3 and multi-touching, you will get a WebKit-powered framework to deliver optimal user experience right within your app. This layer in fact sits on top of your application, so it won’t use your app-reserved memory, as well as not leaving the app when the user interacts with the ad, but rather displayed on top of it.


Putting Ads in your app

The ADBannerView is the core class within this framework. which is simply a view and should be part of the view controller, that you have in your app.

// Ok, this is important (above)

This class manages and retrieves and displays the ads from the iAd network, managing the user interaction. The only thing you need to worry about is:

placement of the banner within your view controller; // ok
respond to networking issues (this will be discussed later); // ok

So in Interface Builder, you drag onto your view controller, the Ad BannerView, the same way you would add any other view in IB. In XCode, you make sure to add the iAd framework to your list of frameworks.

// Good, makes sense. I've been doing stuff in iB.

Working with the Banner view lifecycle

OK, so you have the banner, the next thing to concern yourself with is the managing of connectivity and various inventory changes that might occur throughout the user experience.

That is, the banner queries the iAd network for a new ad (inventory content), and this depends on connectivity. ADBannerView may not always have content in it, because you may not have any network signal on your iPhone, you may have your phone in Airplane mode.

// ok

So the two lifecycle states you have is managed through the ADBannerViewDelegate callbacks:

bannerViewDidLoadAd - Has ad content.
// ok

bannerView:didFailToReceiveAdWithError - When you have network issues and it failed to load the ad during it’s cycle. You also may not have any inventory because of how set up your ad targeting, where there is no ads based on your filter settings.

// ok

So, if we do get a failure, which is not a real error, but just something to let you know that you perhaps would not want to have an empty box, allowing you to hide (place offscreen) and back on screen when you do have content. Space is prime real-estate in the mobile world. An example of how to do this:

#pragma mark -
#pragma mark ADBannerViewDelegate methods

(void)bannerView:(ADBannerView *)bannerdidFailToReceiveAdWithError: (NSError *)error
{
//hide banner
[self moveBannerFromScreen];
}


(void)bannerViewbannerdidLoadAd: (AdBannerView *)banner
{
//show when we have content
[self moveBannerBackOn];
}

(void)moveBannerBackOn
{
//bring back to frame
CGRect theBannerFrame = self.bannerView.frame;
theBannerFrame.origin.y = self.view.frame.size.height - theBannerFrame.size.height;

//shrink existing table
CGRect originalTableFrame = self.tableView.frame;
CGFloat newTableHeight = self.view.frame.size.height - theBannerFrame.size.height;
CGRect newTableFrame = originalTableFrame;
newTableFrame.size.height = newTableHeight;

self.tableView.frame = newTableFrame; //you may want to animate this
self.bannerView.frame = theBannerFrame;

}

(void)viewDidLoadAd {
...
//hide at start until you know you get ad
[self moveBannerFromScreen];
...
}

// I guess viewDidLoadAd is different than bannerViewdidLoadAdd

Responding to actions

OK now you are seeing the ad. The ideal thing to do is reduce activity in your app (pause action when the ad is showing), and to ensure the best experience for the user when viewing the ad. Also you should save minimal state (i.e the tab you are on, rather than content), and when the ad is complete, resume the ap.

In ADBannerViewDelegate, when a user action begins on the ad (you pausing your app) you get the following callback:


- (BOOL) bannerViewActionShouldBegin: (ADBannerView *)banner willLeaveApplication: (BOOL)willLeave
{
//pause your app here
...
return yes;
}

//When the user comes back you get the callback:
- (BOOL) bannerViewActionDidFinish: (ADBannerView *)banner
{
//resume your app here
...
return yes;
}



And there you have it. For more on iAds and the iAd framework, please visit the Apple Developer Centre for documentation and sample code.




Ok. Sounds good. It's based on the concept of the whole ViewController / Delegate thing.

Here's another one:

http://jamesjennin.gs/post/759225411/the-5-minute-guide-to-implementing-iad-correctly


“But James,” my lone reader asks themselves, “is it even possible in implement iAd incorrectly?” Certainly, dear reader. In fact, Apple made it very easy to do so. See, the iAd banner class (ADBannerView) is right there in the Interface Builder library, so a naive programmer (like me) might just drag that into the interface, rebuild, code sign, deploy."

Ok, this is good. It's not going to be that easy, but it looks pretty easy at this point.

Ah, here's the problem. You might get this if you submit your app:

“We noticed that your app, Privacy for Facebook, is displaying an empty iAd banner when ad content is not available. The banner within the app should be hidden whenever ad content is not being served by iAd.”

So, hiding the window isn't optional.

Basically, displaying a blank banner is a no-no. There are a couple reasons why banners may not be displayed: they haven’t rolled out or aren’t available in a particular locale, there is no ad inventory available for your specific app, or there could just be a network issue. Apple wants you to make sure you never show blank banners.



So here’s a quick and dirty way of doing it correctly (assuming a portrait orientation, landscape is left as an exercise for the reader):



1. Add an ADBannerView to your interface, but just off screen. In this example, the ad should be at the bottom of the interface, so the origin of the ADBannerView is 0,460.

Ok. So, the height is presumably 460.

2. Set the view controller as the ADBannerView’s delegate.

Ok, that's good. I can just use whatever the regular view controller is as the app delegate.

Make sure you’ve included the iAd framework in the view controller’s .h file:
3. #import

4. Have the view controller implement ADBannerViewDelegate, and add a bool bannerIsVisible; to the interface


- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
if (!bannerIsVisible)
{
NSLog(@"bannerViewDidLoadAd");
[UIView beginAnimations:@"animateAdBannerOn" context:NULL];
banner.frame = CGRectOffset(banner.frame, 0, -50);
buttonFrame.frame = CGRectOffset(buttonFrame.frame, 0, -50);
web.frame = CGRectMake(web.frame.origin.x,
web.frame.origin.y,
web.frame.size.width,
web.frame.size.height-50);
[UIView commitAnimations];
bannerIsVisible = YES;
}
}
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
if (bannerIsVisible)
{
NSLog(@"bannerView:didFailToReceiveAdWithError:");
[UIView beginAnimations:@"animateAdBannerOff" context:NULL];
// assumes the banner view is at the top of the screen.
banner.frame = CGRectOffset(banner.frame, 0, 50);
buttonFrame.frame = CGRectOffset(buttonFrame.frame, 0, 50);
web.frame = CGRectMake(web.frame.origin.x,
web.frame.origin.y,
web.frame.size.width,
web.frame.size.height+50);
[UIView commitAnimations];
bannerIsVisible = NO;
}

}

Ok. This is great. I'm just going to patch this code right in, and see what happens.

Ok, I just added the banner view to the start screen. Perfect. It's off the visual display - out of sight, out of mind.

Wait, I don't get step 2 - how to set the view controller as the delegate. I did it for the UIPicker, but I don't get how the other part works.

I just found and example that does this:

adView.delegate=self;

Ok, so if I do the import (next step) first, I can then do this:

IBOutlet ADBannerView *bannerView;

Here's the previous step for adding the import:

#import

Ok, now just connect it.

And then add this line on the viewDidLoad:

bannerView.delegate=self;

Ok, well whatever pieces I'm reducing to fit the ad in aren't the same ones in the post. I just need to figure out if I need to reduce the UIView and the image, or maybe just the image. I'll pick this up later.

Ok, here's what I ended up doing. Most of the recommendations seemed to have using CGIRectOffset, to move the banner up or down , like so:

// This moves the banner up 50 pixels
banner.frame = CGRectOffset(banner.frame, 0, -50);

// This moves the banner down 50 pixels
banner.frame = CGRectOffset(banner.frame, 0, 50);

The problem I had with it was using IBuilder to place the original banner where I wanted it. Since the background image is longer than the 480 (or 460) due to the fact that doing it like that was the only way I could get it to not display the top of the background image, just putting it below that wasn't any good - moving it up 50 wouldn't move it up all the way.

So I decided to just start it out at the *viewable* position. This way I could fit it precisely where I wanted to on the image. To move it to the correct position every time, I just saved the original frame on the viewDidLoad for the delegate, and then did the following on - (void)bannerViewDidLoadAd:(ADBannerView *)banner

{
NSLog(@"StartController, bannerViewDidLoadAd called");
if (!self.bannerIsVisible)
{
[UIView beginAnimations:@"animateAdBannerOn" context:NULL];
// banner is invisible now and moved onto the screen on 365 px
banner.frame = originalAdFrame;

[UIView commitAnimations];
self.bannerIsVisible = YES;
}
}


This assured that it would be placed in the original position on a viewable add.

On the

- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error

method, it seemed like no matter what I did, it was invisible anyway. So, just play it safe, I move it down 50, using the original frame as reference:


- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
NSLog(@"StartController, didFailToReceiveAdWithError called");

if (self.bannerIsVisible)
{
[UIView beginAnimations:@"animateAdBannerOff" context:NULL];

CGRect newBannerFrame = originalAdFrame;

int xVal = newBannerFrame.origin.x;
int yVal = newBannerFrame.origin.y;

newBannerFrame.origin.y = yVal + 50;

banner.frame = newBannerFrame;

[UIView commitAnimations];
self.bannerIsVisible = NO;
}
}

Friday, October 21, 2011

Radio Buttons on the iPhone

I've decided to clean up the logic associated with selecting the mode of my Japanese quiz game. There are 3 modes - English to Japanese, Japanese to English, and Japanese Kanji to Japanese kana (Kanji to Kana). I had previous implemented it as a combination of a couple of UISwitches, but I find this to be difficult to understand.

So, as it turns out, radio button per se aren't supported on the iPhone. Among the options possible is a UIPicker and using a table view, but the best option looks to be a UISegmentedControl. Essentially you change the text in an associated label to correspond to which piece of the horizontal UISegmentedControl was chosen. So, I'll have three segments, and the label (and mode) will change based on which segment is selected.

So, I found a good example of this at

http://www.mobisoftinfotech.com/blog/iphone/iphone-segmented-controluisegmentedcontrol-tutorial/

So, state the variables:


UILabel *segmentLabel;
UISegmentedControl *segmentedControl;


The properties and the actions:


@property (nonatomic,retain) IBOutlet UILabel *segmentLabel;
@property (nonatomic,retain) IBOutlet UISegmentedControl *segmentedControl;

-(IBAction) segmentedControlIndexChanged;


In the viewDidLoad:

self.segmentLabel.text =@"Segment 1 selected.";

In the dealloc:

[segmentLabel release];
[segmentedControl release];

And the action method:

#import "SegmentedControlDemoViewController.h"

@implementation SegmentedControlDemoViewController
@synthesize segmentLabel;
@synthesize segmentedControl;

// Implement viewDidLoad to do additional setup after loading the view.
- (void)viewDidLoad {
self.segmentLabel.text =@"Segment 1 selected.";
[super viewDidLoad];
}

- (void)dealloc {
[segmentLabel release];
[segmentedControl release];
[super dealloc];
}

-(IBAction) segmentedControlIndexChanged{
switch (self.segmentedControl.selectedSegmentIndex) {
case 0:
self.segmentLabel.text =@"Segment 1 selected.";
break;
case 1:
self.segmentLabel.text =@"Segment 2 selected.";
break;

default:
break;
}

}

@end


Here's a key note which we'll implement later to account for the 3 nodes:


Note: If you want more than two segments in the segmented control, go to Attributes Inspector for segmented control and change the value for Segments field.



Connect segmented control and label to their respective on the interface builder.

Ok, it's displaying the text change. Now, let's add that third segment. Easy enough, just like the man said, click on the bullhorns and change the number of segments (in this case from "2" to "3" and modify the logic:

-(IBAction) segmentedControlIndexChanged{
switch (self.segmentedControl.selectedSegmentIndex) {
case 0:
self.segmentLabel.text =@"Segment 1 selected.";
break;
case 1:
self.segmentLabel.text =@"Segment 2 selected.";
break;
case 2:
self.segmentLabel.text =@"Segment 3 selected.";
break;

default:
break;
}

}


Ok, we're labeling this "QuizType" so we'll create Utils method to read and write this:

+ (NSInteger) getQuizType {

NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];

return [prefs integerForKey:@"quizType"];

}

+ (void) setQuizType:(int)quizType {

NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];

[prefs setInteger:quizType forKey:@"quizType"];

[prefs synchronize];

}


Ok, we're making progress. Let's set the default to, say, EnglishToJapanese.


Here's how we'll set the value once it's been selected.



-(IBAction) segmentedControlIndexChanged{
switch (self.segmentedControl.selectedSegmentIndex) {
case 0:
self.segmentLabel.text =@"English To Japanese";

break;
case 1:
self.segmentLabel.text =@"Japanese To English";
break;
case 2:
self.segmentLabel.text =@"Kanji to kana";
break;

default:
break;
}

[Utils setQuizType:self.segmentedControl.selectedSegmentIndex];
}




And in the viewWillAppear:

self.segmentedControl.selectedSegmentIndex = [Utils getQuizType];

[self changeQuizTypeLabel];


Where the method is:

-
(void) changeQuizTypeLabel {
switch (self.segmentedControl.selectedSegmentIndex) {
case 0:
self.segmentLabel.text =@"English To Japanese";

break;
case 1:
self.segmentLabel.text =@"Japanese To English";
break;
case 2:
self.segmentLabel.text =@"Kanji to kana";
break;

default:
break;
}

}


Ok, good. That's all working. Now, the next step is to use those setting in code. But that's how you set up a UISegmentControl.

Tuesday, October 18, 2011

Generating a random, matching answer

Ok, well I've been slacking off on the blogging lately. Sometimes it's just quicker to go ahead and code, you know?

But, I'm back because I'm trying to figure out what to tackle next. I've put some new capabilities into the app based on recent feedback, including:

1) If it's kana only, then instead of giving an answer in multiple choice that's only identical to the question, I've swapped off to English. It works really well, and seems natural, so I'm very pleased with it.

2) I've added functionality which allows English to Japanese

3) I've added Japanese to English (before it was Japanese kanji to Japanese hiragana only).

4) I've added setting to support the above functionality.

Ah, I just remembered, I need to change the settings display to hide the "multipleChoiceInEnglish" if "English to Japanese" is selected.

Ok, that was easy - just connect a method to the switch and code it like this:


-(IBAction)switchEnglishToJapanese:(id)sender {

if(englishToJapanese.on){

self.multipleChoiceInEnglishLabel.hidden = YES;
self.multipleChoiceInEnglish.hidden = YES;

}
else{

self.multipleChoiceInEnglishLabel.hidden = NO;
self.multipleChoiceInEnglish.hidden = NO;

}
}


Ok, so, I have a couple of options for this program. My daughter actually had the idea of truncating the hirgana suffixes/prefixes of the question, then having the multiple choice be kana, but with the kanji root separated from the hiragana suffix by parens or better yet a dash or a dot.

If it's a compound kanji, then just choose compound kanji for the possible answers.

The first thing I'll need to do is loop through the word, identifying if it has a hiragana prefix or suffix. If so, extract the root, and display that.

Then, when searching for answers, look for *any* words that have a suffix or prefix. The suffixes no long have to match.

Then just display the kana with an intervening dot or whatever.

If there is no suffix, then look for words that *don't* have a suffix. And just display the whole suffix.

Don't bother doing that for anything where the vocabulary is only kana.

This is great. It will be much cleaner code, and also a better test.

Ok, this is interesting. Here's how you loop through a string:

NSMutableArray *characters = [[NSMutableArray alloc]
initWithCapacity:[myString length]];
for (int i=0; i < [myString length]; i++)
{
NSString *ichar = [NSString stringWithFormat:@"%c", [myString characterAtIndex:i ]];

[characters addObject:ichar];
}



Now, what this algorithm need to do is, when it's getting potential answers, it has to keep looping until it finds one. This won't be difficult, because most words will fit the solution. But, I'll probably call this as a method which has the infinite loop internal to itself, and just returns the word when it finds one. And I'll call it four times.

The method will just loop, and check to make sure that

1. It's not kana only
2. It's not katakana
2. If the question has trailing hiragana, it has trailing hiragana and
3. If the question doesn't have trailing hiragana, it doesn't have trailing hiragana

The check for trailing hiragana can be a utils method. It will ignore the first character, and then check if the any of the subsequent characters are < ”あ" and greater than "ん". If so they aren't in the hiragana range.

Also, to check for katakana, do the same thing for the corresponding katakana values.

For safekeeping, this is the hiragana range in Unicode:

Hiragana. Range: 3040–309F.

Unicode range · U+30A0–U+30FF

-- to be continued ---

The secret to app submission in XCode 4

This will only be a short post. After 2+ days of intensive googling and debugging, I finally got past the "Invalid Binary" error message. All it was had to do with the the submission of the app in Organizer. You choose the app, but you also have to choose a profile. I was letting it default to the developer's profile, when I should have chosen the distribution profile. That was it, I believe. Now, how difficult would it have been to send out an error message stating "app does not match profile selected in app submission process." It would have save me a ton of grief.

Friday, October 14, 2011

Redoing app submission with a different project

Ok, I'm throwing in the towel on the app submission with the current project. Instead, I'm going to create a new project from scratch, copying in the source files. I'm also going to create a new itunes connect record.

Ok, right at the start, we'll mark this info:

Product name: JlptVocabularyQuiz

Company Indentifier:

com.kanjisoft

BundleIdentifier:


com.kanjisoft.JlptVocabularyQuiz

Ok, drag and drop all your program and xib files. I was concerned about replacing the main.xib, but since I just had to redo the connections I think I should've done it.

Also, add the sql dyamic library:

http://www.ajaxapp.com/2011/08/30/how-to-add-sqlite-framework-into-xcode-4-for-your-ios-app/

On Xcode 4, you’ll have to do the followings:

1. Click on your project name in your left pane of Xcode 4. You’ll be shown with the above screen.

// image

2. Then click on “Build Phases”, then get down to “Link Binary With Libraries”, and click the + button and add your SQlite framework (libsqlite3.0.dylib) as shown in the next screen. Voila, then you’re good to go to compile some code using SQLite functions.



Run...not bad. Better than I expected. I'm missing a background, but I can just copy that it. Plus the font is exceeding the available space on the quiz display, and the next button is too low, in what may be a relate problem. A surprisingly good turnout.

Let's change the font size which is set in the cell for row at index path:

// layout the table rows
- (UITableViewCell *)tableView:(UITableView *)tv
cellForRowAtIndexPath:(NSIndexPath *)indexPath


Not, that's into the problem. The problem is the view holding the rows isn't long enough, truncating the last row.

Ok, I lost some of this post when my browser crashed. Anyway, I got the app working, and created a new app record. It turns out that if you want to do anything like in app purchase or click notification, you can't use an app id with an * at the end.

Here's some info:

App IDs are an integral part of the iOS Development and Provisioning Process that allows an application to communicate with the Apple Push Notification service and/or an external hardware accessory. In addition, an App ID can also be used to share keychain data (such as passwords) between a suite of applications and share document and configuration data between your applications using iCloud.

An App ID is the combination of a unique ten character string called the "Bundle Seed ID" and a traditional CF Bundle ID (or Bundle Identifier). The Bundle Seed ID portion of your App ID can be utilized to share keychain access between multiple applications you build with a single App ID. In addition, it can be incorporated into any external hardware accessories you wish to pair your iOS application with. Registration of your App ID is required to utilize the Apple Push Notification service (APNs) and to register an application to incorporate In App Purchases.

The Bundle Identifier portion of an App ID can be substituted with a wild-card character (asterisk '*') so that a single App ID may be used to build and install multiple applications. If the wild-card character is not used, the Bundle Identifier portion of your App ID must be input as your CF Bundle ID in Xcode to allow the application to install on your device. The Bundle Seed ID portion of your App ID does not need to be input into Xcode. Wild-card App IDs cannot be used with the Apple Push Notification service or for In App Purchase.



I selected one without the *:

98X643NLB6.com.kanjisoft....
JLPT Vocabulary Quiz App

But Itunes only shows the ones it's not set to. But I know I chose that one.

Ok, let's see - I think we need to create a certificate signing request.

No, I don't think I need one - i'd have to delete the old one, and I think those are the identity only - it's the distribution profile that's needs the app. Well, the developer profile does to, but I'm talking about the provisioning, not the certificate.


Ok, I've updated the debug and release to all point at the new distribution profile.

Whoah - here's something I totally missed yesterday:

Submitting Your Application for Publication on the App Store
To publish your application on the App Store, you need to enter information about it in iTunes Connect. For more information about iTunes Connect, visit https://itunesconnect.apple.com.

Important: To validate your application or to submit it for publication, you must be registered in iTunes Connect.
To validate your application or submit it for publication on the App Store:

Create an archive of your application using the AppStore scheme.

========================== look at this! =========================================
To learn how to configure the AppStore scheme, see “Configuring Your Project for App Store Submission.”
===================================================================================

Ensure that an application record for the application exists in iTunes Connect. The information in this record must match the application you want to validate or submit for publication.
These are the main matching criteria:

Application Name
Bundle ID
Version
Ensure that the application record status in iTunes Connect is at least Waining for Upload.
Validate or submit the application.
Before submitting your application for publication, you should validate it to ensure that is passes essential iTunes Connect validation tests.




Ok, here's what happens when we click on that link:

Configuring Your Project for App Store Submission

To submit your application for publication on the App Store, you need to build your application using your team’s distribution code signing identity.

To configure your project to facilitate the building of distribution-identity–signed archives of your application:

In your project, duplicate the Release build configuration and name the duplicate “AppStore”.


// AppStore! wow.

// Ok, that's done.

Set the Code Signing Identity build setting to your team’s distribution code signing identity through the application’s distribution provisioning profile for the AppStore build configuration.


// Ugh. I don't see how this works.

Ok, I'm going to punt for now, and just try to upload this guy and see what happens.

In the meantime, I'm going to gather urls for how to submit an app via XCode 4.

http://www.iphonedevsdk.com/forum/iphone-sdk-development/74950-submitting-app-itunes-xcode-4-a.html

Guy discusses a couple of possible ways to submit. Ends up not being able to find in organizer.

http://www.iphonedevsdk.com/forum/iphone-sdk-development/75780-xcode-4-distribute-itunes-connect-organizer.html

The guy had problems because he was pulling in from other libraries. Found some includes in finder, and figured out why they were generated.

http://www.iphonedevsdk.com/forum/business-legal-app-store/75640-how-build-final-version-submit-app-store.html

This guy said he succeed by using the schemes to attach to the distribution profile, but I'm not seeing how that works.

Well, this one looks good:

http://www.youtube.com/watch?v=VxMSLzwHeCk

Google search:

xcode 4 submit app

Ok, here's a comment from the youtube video:

@Charbeltoumieh Ok. When in the drop down menu "Configurations" select the + and press "Duplicate Release Configuration" and then rename it "Distribution". Voila! you have got a distribution in code signing.

Hope this helps and answers your question. If not don't hesitate to ask for more help :D.

MilmersXcode 1 month ago
Reply
@Charbeltoumieh I am thinking of doing this tutorial again as I just realised (based on your comment) I missed out a very big part of the process. I will explain and fix your problem. If you select your entire project (It's the blue icon at the top of the file list) and the select your project again in the next sidebar to the left. In the main screen select the info tab and go to the drop down "Configurations"

This message will have to be split into 2 parts. See you in the next 1

MilmersXcode 1 month ago



So, you have to go to the project, info, configurations and duplicate.

Ok. Let's see what it says to do next.

In the target, delete the Validate Build Product build setting specification for the Release build configuration.


Well, I can't delete it. I can just change it to no.

Duplicate the scheme that builds your application and name the duplicate “-AppStore”.

Ok, I did that.

In the AppStore scheme, set the build configuration for the Archive action to AppStore.


com.kanjisoft.JlptVocabularyApp
com.kanjisoft.JlptVocabularyQuiz

Hmmm...this looks good:

had the same INVALID BINARY error from iTunes Connect even if Application Loader accepted my binary. The solution was very simple...

Open your info.plist, right-click and check Show Raw Key/Values:

CFBundleIconFile = Icon.png (my iPhone 57x57 PNG icon)
CFBundleIconFile~ipad = Icon-72.png (my ipad 72x72 PNG icon)
CFBundleIconFiles = array
Item 0 = Icon.png
Item 1 = Icon@2x.png (my iPhone 4 114x114 PNG icon)
Item 2 = Icon-72.png


Ok, I'm going to pick this up later.

Wednesday, October 12, 2011

Arrggh! Invalid Binary on app submission

Remember when I was so pleased I was able to submit my app the other day? Well, I happened to check into iTunes connect today out of pure luck, when I saw that I had an "invalid binary". There wasn't any email, nor any error message in the submission process. It could have been a week or two before I went to in to check. Why is apple so frikking unhelpful??

Anyway, so, the first thing to do is change the status back to prepare for upload, which is something you have to do by pressing a button on your app in the itunes connect version details.

Then, as soon as your app finishes uploaded, refresh the the browser, and you'll see immediately if it's an invalid binary.

They seem not to send out emails to most people, but some do get them. It's a form email that give some ideas. Someone posted an image on SO:




So, this seems like a good post to check try:



Your not using the correct certificates when building your app. Delete your certificates in Provisioning Portal and create new ones and update them in Xcode.

// which certificates? Development? Distribution?

Goto Organizer / Provisioning Profiles / Refresh and allow Xcode to fetch the latest ones.

Again, clean all of your targets. You can even go to /Users/%USERNAME%/Library/Developer/Xcode/DerivedData and delete all of the directories in there (mainly your app in question).

Then Under TARGET in Xcode, goto Build Settings and choose the NEW certificates.

Also, use Archive *(under Product in Xcode 4.0+)* in Xcode. When the build is complete, Organizer should open to your Application Archives, if not, go there and click on Archives. Select your app and choose Validate..., once your app is validated, click Submit....

Another thing to note is that Entitlements.plist is no longer needed, so that is not an issue for you.



I wish I had a better understanding of the *model* of the certificates. When do you need a developer vs. a distribution certificate? What key am I supposed to sign them with? Why would recreating the certificates make a difference? What's the difference between a certificate signing request, a certificate, and a signed certificate? Where and what is the key they get signed with? Are they stored in XCode? What's the difference between a provisioning profile and a certificate? What is the ".mobileprofile" for? Are the profile and the certificate the same thing? How does Xcode know what profile it's using? Where does it store it?

With these question in mind, let's again go here:

http://developer.apple.com/library/mac/#documentation/ToolsLanguages/Conceptual/Xcode4UserGuide/DistApps/DistApps.htm

This is XCode 4's version of how to submit an app.


Before submitting an application to the App Store or share it with others, you create an application archive.


You can then share your application archive with application testers and other application developers or distribute it to users.

To have the application considered for inclusion on the App store, you submit the archive to iTunes Connect.

To ensure your application archive passes essential iTunes Connect validation tests, you can have Xcode validate it before you submit it.



Archive Your Application

Before archiving your application, ensure that the binary is self contained. That is, if it relies on static libraries, ensure that those libraries are part of the application binary by setting the Skip Install build setting to Yes in the Release build configuration of the target that builds and archives the application.



Why is this called "Skip Install"? This varies with other advice to set that setting to "no", and set the actual static libraries to "yes". But how do you set the actual static librararies to yes? I just created a folder for them - there doesn't seem to be anything I can set the actual static library to.

This is one possible issue in submitting my binary.

To archive your application . . .

From the Scheme pop-up menu, choose a scheme.

From the same menu, choose Edit Active Scheme to display the scheme dialog.

In the left column, select Archive.

Choose a build configuration.

Specify a name for the archive and click OK.

Choose Product > Archive.


Ok, this isn't a problem. I can do this, although the helpful (snicker) documentation doesn't bother telling you where the scheme pop-up menu is (click the bar on the upper left where you specify the app name and where it's going to run).



An archive is a bundle that includes your product along with symbol information. You can build an archive to seed an application for testing or to validate and submit an application to iTunes Connect.

Your new archive appears in the Archives list in the Organizer window, unless you turn off this option. Each archive is identified in the archives organizer with the date and time it was created. For more information, see the related article on the archives organizer.


Ok, to look at the organizer, you can click on the organizer icon. I did have a problem with getting it to show up there, but that had something to do with the Skip something build setting mentioned above.

Validate Your Application and Submit It to the App Store

When you’re ready to publish your application to users on the App Store, you need to enter information about it in iTunes Connect. You should also validate your application on your computer to ensure it passes essential iTunes Connect validation tests. Before you can validate your application or submit it to the App Store, you must archive it, as described in “Archive Your Application.”

Before validating your application or submitting it to iTunes Connect:

Ensure an application record for the application exists in iTunes Connect. The information in this record must match the application you want to validate or submit to the App Store.

These are the main matching criteria:

Application Name

Bundle ID

Version


Ok, this bundle id is a bit confusing. There is the iTunes record. Which originally required a bundle id, plus the bundle id suffix when entering it, and the bundle id suffix was actually com.kanjisoft.jlptvocabquiz, although I managed to screw that up.

But now, when I look at the itunes application information, the Bundle ID com.kanjisoft.jlptvocabquiz, so presumably that's when the bundle ID has to be. I'm also assuming that the bundle id in the applications plist needs to match that. I change the bundle id in my application's plist, and got through that problem - I think.


Ensure the application record status in iTunes Connect is at least Waiting for Upload.


Ok, this works. All I need to do for this is press the button, something like "prepare for upload" on the version details page in iTunes.

For more information about iTunes Connect, visit https://itunesconnect.apple.com.

Submit your application to iTunes Connect for publication on the App Store. Before submitting your application for publication, you should validate it to ensure that it passes standard iTunes Connect checks


Yeah, this isn't going to be very helpful. It validated my app, but I got invalid binary anyway. Well, at least it caught a couple of issues.

To validate and submit your application to the App Store . . .

In the archives organizer, select the application archive, and click Validate or Submit.

Enter your iTunes Connect credentials, and click Next.

Choose the application to share and the appropriate signing identity, and click Next.

Review validation issues found, if any, and click Finish.



Ok. So, this lame documentation doesn't really give much info as to what you really need to do. It doesn't even refer you to the iOS developer's portal, and its horrifically complex process for getting developer certificates, profiles, signing requests, and incorporating them into the submission process.

Ok, so, let's go to the developer's portal now review all that mess.

The link we want to review there is the provisioning portal, on the upper right.

What? Now that I'm taking my time, instead of rushing desperately to get it done like I usually do, I see a few videos off to the right.

"Obtaining Certificates" (nice), "Assigning Devices" (I think I've already done this), "Creating your App ID's" (I already have one), and "Creating Provisioning Profiles" (nice).

Ok, so let's check out obtaining certificates.

Well, it's not exactly You-Tube.

Ok, here it is. Ok, so, they're talking about developer certificates for running on a device. A certificate has information - a digital identity - which has information like your company name.

This digital identity consists of a secret private key and a public key.

XCode4 use this to sign your app.

So, this is a connect from XCode 4 to your certificate.

The first step is to create a certificate signing request (why not a certificate creation request? then a csr).

You first need to launch keychain access. (This is all for the developer certificate to run on your iPhone)

Got to preferences, certificates, set protocols off.

Then, go to Certificate Assistant and select Request a certificate from a certificate authority.

Ok, so this make more sense - requesting the actual certificate.

Give your email address and name - maybe company name? No, they say team leader. Whatever.

You don't need to enter the CA authority email - it's being uploaded directly.

The old specify save to disk, and let me specify key pair information. This is to make sure you are requesting 2048 key size and RSA.

Ok, now save to disk. The suffix is actually .CertificateSigningRequest.

This immediately creates your private key, which can be viewed under "keys" in the KeyChain access.

The CSR needs to be uploaded to the program portal - Certificates (left column) and the "Development" tab, then click "Add Certificate" (why not name up submit CSR? anyway, this video is pretty good).

Ah, ok, next you click on the upload CSR button. Or, choose file and submit.

Then, you watch this to see if the CSR is approved. If it's approved, you click download.

It's called "developer_identity.cer".

When you double click on that, it installs it into keychain.

When you click on ok, the certificate gets installed into keychain.

And that's it. What a nice, crystal clear video. Btw, if you click on "myCertificates" you'll just see the ones you created personally.

Ok. Let's try the provision profile.

First of all, it's something that ties your app and development team and something else together so you can run your app on other machines? I'll have to re-listen to that. I don't really get why they don't give a rewind button. Every video player ever invented provides that. What's their issue? At least they give a pause button.

This has to be on every machine that you run your app on.

It has an app id, certificates, and the device id. Ok, that's interesting. What kind of certificate did he say - development or distribution?

Because of the multiple apps, a single device can have multiple provisioning profiles.

So, this one's apparently for development. So, in other words, creating the certificate is step one, the provisioning is step 2 - even for just the development, forget about uploading to the app store for now. This is just to get it onto your machine.

So, you have to go to provisioning, development tab, then click add profile. This is the id of the provisioning profile on your device.

So, it will show you the certificate you just created, the app id (where did that come from - probably one of the previous steps I skipped over) and the devices (also added previously, no doubt). You select the relevant choices, and click submit to generate the provision profile.

You need to download and install it to get the app running on your device.

All team members are allowed to do this.

Once you've downloaded it, drag it to Organizer / devices, or drag it to xcode.

Ok, that's fine. However, that doesn't cover distribution.

My question is, why this grid:

Certificates: Developer Distribution
Provisioning Developer Distribution
Distribution Distribute Application

Ok, well, under the third entry, here are the steps:


Obtaining your iOS Distribution Certificate
Create and download your iOS Distribution Provisioning Profile for App Store Distribution
Creating and Downloading a Distribution Provisioning Profile for Ad Hoc Distribution
Building your Application with Xcode for Distribution
Verifying a Successful Distribution Build
Updating your Application


Ok, so I guess a distribution certificate is different than the developer id certificate.

The same way the provisioning profile is different for developer and distribution.

So, let's redo these steps and see if it makes any difference.

First, let's create the CSR:

Generating a Certificate Signing Request

To request an iOS Distribution Certificate, you first need to generate a Certificate Signing Request (CSR) utilizing the Keychain Access application in Mac OS X Leopard. The creation of a CSR will prompt Keychain Access to simultaneously generate your public and private key pair establishing your iOS Distribution identity. Your private key is stored in the login Keychain by default and can be viewed in the Keychain Access application under the ‘Keys’ category. To generate a CSR:

In your Applications folder, open the Utilities folder and launch Keychain Access.
In the Preferences menu, set Online Certificate Status Protocol (OSCP) and Certificate Revocation List (CRL) to “Off”.
OCSP Preferences
Choose Keychain Access -> Certificate Assistant -> Request a Certificate from a Certificate Authority. Note: If you have a private key highlighted in the Keychain during this process, the resulting Certificate Request will not be accepted by the Provisioning Portal. Confirm that you are selecting “Request a Certificate From a Certificate Authority...” and not selecting “Request a Certificate From a Certificate Authority with …”

Request a Certificate

In the User Email Address field, enter your email address. Please ensure that the email address entered matches the information that was submitted when you registered as an iOS Developer.
In the Common Name field enter your Company/Organization/Department name. Please ensure that the name entered matches the information that was submitted when you registered as an iOS Developer.
No CA Email Address is required.
Select the ‘Saved to Disk’ radio button and if present, select ‘Let me specify key pair information’ and click ‘Continue’.
Save Certificate to Disk
If ‘Let me specify key pair’ was selected, specify a file name and click ‘Save’. In the following screen select ‘2048 bits’ for the Key Size and ‘RSA’ for the Algorithm. Click ‘Continue’.
Key Pair Information
The Certificate Assistant will create a CSR file on your desktop.



This whole process is designed to create the CSR, then download it and add it to the keychain. This will be a distribution certificate.


Ok, well, I tried deleting the build directory and recreating, but that didn't work. I still get the invalid binary.

Well, I could try deleting the certificates, as suggested here:

Your not using the correct certificates when building your app. Delete your certificates in Provisioning Portal and create new ones and update them in Xcode. Goto Organizer / Provisioning Profiles / Refresh and allow Xcode to fetch the latest ones.

Again, clean all of your targets. You can even go to /Users/%USERNAME%/Library/Developer/Xcode/DerivedData and delete all of the directories in there (mainly your app in question).

Then Under TARGET in Xcode, goto Build Settings and choose the NEW certificates.

Also, use Archive *(under Product in Xcode 4.0+)* in Xcode. When the build is complete, Organizer should open to your Application Archives, if not, go there and click on Archives. Select your app and choose Validate..., once your app is validated, click Submit....

Another thing to note is that Entitlements.plist is no longer needed, so that is not an issue for you.



Well, I tried replacing the distribution certificate/profile. That didn't work. I also tried zipping up the .app file and submitting it through the application loader, but that didn't work. So, I guess I'm just going to create a new project from scratch, and create a new app. That ought to do the trick.

What's the matter with Apple? Why don't they at least tell you why the app won't submit? Harummph.

Monday, October 10, 2011

XCode 4 Submitting an app to the app store notes

Ok, we've been through the painful process of submitting an app to the app store before. The time has come to do it - again. The last time was actually more difficult, because we were submitting the up under a client's name. This time, it's under our own company's name, so hopefully, that will at least slightly simplify the process.

Ok, so what I id last time was log in to the developer portal with my company email address, which brings us here:

https://developer.apple.com/devcenter/ios/index.action

Then, go to the iOS provisioning portal.

Welcome to the iOS Provisioning Portal

The iOS Provisioning Portal is designed to take you through the necessary steps to test your applications on iOS devices and prepare them for distribution.


If I look under the "Provisioning" link, I have a Provisioning profile called JlptVocabQuizProfile.

Ok, looking at my previous post on getting my app on to the ipod, I see I created an app id called this:

JlptVocabQuiz

Ok, let's just walk through the process. That usually seems to do the trick.

https://developer.apple.com/ios/manage/distribution/index.action


Ok, I think I can start here:

Building your Application with Xcode for Distribution

Launch Xcode and open your project.
- done

If you have not already done so, drag the Distribution Provisioning Profile downloaded from the Provisioning Portal onto the Xcode or iTunes icon in the dock (or, drag into ‘~/Library/MobileDevice/Provisioning Profiles’ directory.)


Ok, I'm having trouble following the next entry about duplicating the release. So, let's try this:


http://developer.apple.com/library/mac/#documentation/ToolsLanguages/Conceptual/Xcode4UserGuide/DistApps/DistApps.htm

Distributing Applications

Before submitting an application to the App Store or share it with others, you create an application archive.

You can then share your application archive with application testers and other application developers or distribute it to users.

To have the application considered for inclusion on the App store, you submit the archive to iTunes Connect.

To ensure your application archive passes essential iTunes Connect validation tests, you can have Xcode validate it before you submit it.

// let's do that.



Archive Your Application

Before archiving your application, ensure that the binary is self contained.

That is, if it relies on static libraries, ensure that those libraries are part of the application binary by

setting the Skip Install build setting to Yes in the Release build configuration of the target that builds and archives the application.

// That last sentence is a mouthful.



Ok, if I click the highest level node of the project, then click on the target in the main window, and then click on build settings in the tab bar across the top, I think I can see what they're getting at.

Ah, SO to the rescue:

http://stackoverflow.com/questions/5280914/archive-does-not-appear-in-xcode4-organizer


For the "Release" configuration do the following:

Set "Skip Install" to YES on your project

Set "Skip Install" to NO on your application target

Set "Skip Install" to YES on all static library targets that are included

Choose Product -> Archive from the menu.

This worked for me, and is based on advice from the Apple Dev Forums.





Ok, after clearing up a problem with the prefix header - highlight the project, target, build settings, and find "Prefix Header", and give the release version the right name, it created the archive. Yay!

But when I try to validate it, it says "no suitable application records were found", and "Please make sure you have set up a a record for this application on iTunes connect.

So, I've skipped some type of step here.

Ok, from this url:


http://developer.apple.com/library/ios/#documentation/Xcode/Conceptual/ios_development_workflow/145-Distributing_Applications/distributing_applications.html

Ensure that an application record for the application exists in iTunes Connect. The information in this record must match the application you want to validate or submit for publication.

These are the main matching criteria:

Application Name

Bundle ID

Version


I think I just need to get back to the iOS developer portal and walk through the whole thing, start to finish.

Well, how about this:

https://developer.apple.com/appstore/

Ok, this looks like a pretty good link:

https://developer.apple.com/appstore/resources/submission/

"Prepare App for Submission".

Ok. Now we're talking.

Get started with iTunes Connect

iTunes Connect is a suite of web-based tools that allows you to submit and manage your applications for distribution on the App Store.

// Everything's named after iTunes. Annoying.

In iTunes Connect you will be able to check the

status of your contracts,

set up your tax and banking information,

obtain sales and finance reports,

request promotional codes,

manage

users,

applications,

metadata, and your

In-App Purchase catalog.


Ok. wonderful.

Set up user accounts

When creating and editing an iTunes Connect user account,

you are able to define user roles and notifications.

Defining notifications determine the type of emails your users will receive regarding your iTunes Connect account.

There are five distinct user roles Admin, Technical, Finance, Sales and Legal. Log in to iTunes Connect


Next:


Gather information and assets for your application

In addition to your binary, you will need to submit or assign several attributes for your app, including:

Application Name
Application Description
Primary and Secondary Category
Subcategories
Copyright
App Rating
Keywords
SKU Number
Application URL
Screen shots
Support URL
Support Email Address
End User License Agreement
Pricing, Available Date, Territories
Large App Icon 512 x 512


App Name: JLPT Vocabulary Quiz App

Application Description:

I'll copy it from the Android

Primary and Secondary Category: Education, then?

Subcategory: I dont think it applies

Copyright

2011 KanjiSoft Systems

App Rating

all

Keywords

JLPT, Vocabulary, Quiz, Practice, Game, Frequency of Use

SKU Number

I think that's a fill in

Application URL

Errr.. ?

Screen shots

I can make those

Support URL

www.kanjisoft.com

Support Email Address

support@kanjisoft.com

End User License Agreement

skip

Pricing, Available Date, Territories
$3.99

Large App Icon 512 x 512

I can make one

Ok, here we go. A 12-year old kid made a video on youtube - say you have to make a distribution provisioning profile.

http://www.youtube.com/watch?v=OtlH4wGbWp0&feature=related

So, go here on the ios developer's portal: (iOS Provisioning Portal, provisioning, distribution)

http://developer.apple.com/ios/manage/provisioningprofiles/viewDistributionProfiles.action


And sure enough, I don't have any of those profiles.

Ok, I'm going to create one. click new, then give it a name:

JLPTVocabQuizDistributionProfile

And select the App Id JLPTVocabQuiz, so that's already been created, when I created the provisioning profile.

Ok, great. It's pending.

JLPTVocabQuizDistributionProfi... 98X643NLB6.* Pending

Ok, now let's download it to a folder on the desktop, JLPTUpload.

Ok, also download the provisioning certificate, called developer_identity.cer

Ok, let's go back to this:

http://developer.apple.com/ios/manage/distribution/index.action

As I said here:

http://gettingintomobile.blogspot.com/2011/06/bataan-death-march-resumed-submitting.html


Ok, so that's the one with this Bataan death march sequence:

Obtaining your iOS Distribution Certificate
Create and download your iOS Distribution Provisioning Profile for App Store Distribution
Creating and Downloading a Distribution Provisioning Profile for Ad Hoc Distribution
Building your Application with Xcode for Distribution
Verifying a Successful Distribution Build
Updating your Application



In the Preferences menu, set Online Certificate Status Protocol (OSCP) and Certificate Revocation List (CRL) to “Off”.

// done

Choose Keychain Access -> Certificate Assistant -> Request a Certificate from a Certificate Authority.


Submitting a Certificate Signing Request for Approval
After creating a CSR, log in to the iOS Provisioning Portal and navigate to

Certificates’ -> ‘Distribution’ and click the ‘Add Certificate’ button.

Click the Upload file button, select your CSR and click ‘Submit’. If the Key Size was not set to 2048 bits during the CSR creation process, the Portal will reject the CSR.
Approve your iOS Distribution Certificate.


Ok, I think this is basically what I just did.

I guess the name of the .cer file is "distributionidentity.cer". I I had to click the wwdr link download whatever.




Downloading and Installing iOS Distribution Certificates
In the ‘Certificates’-->’Distribution’ section of the Portal,

Control-Click the WWDR Intermediate Certificate link and select “Saved Linked File to Downloads” to initiate download of the certificate.

// did this

After downloading, double-click the certificate to launch Keychain Access and install.


In the same area of the Provisioning Portal, click on the name of the iOS Distribution Certificate to download.
On your local machine, double-click the downloaded .cer file to launch Keychain Access and install your certificate.



Ok, it looks like I have one certified private key, somehow through this whole process.

I have several other public and private keys, but I have one good solid, certified private key under my company name.

Next.



Saving your Private Key and Transferring to Other Systems

It is critical that you save your private key somewhere safe in the event that you need to build your application on multiple Macs or decide to reinstall your system OS.

Without your private key, you cannot sign binaries in Xcode and there you will be unable to upload your application to the App Store or install your application on any Apple device.

When a CSR is generated, the Keychain Access application creates a private key on your login keychain.

This private key is tied to your user account and cannot be reproduced if lost due to an OS reinstall.

If you plan to do development and testing on multiple systems, you will need to import your private key onto all of the systems you’ll be doing work on.





To export your private key and certificate for safe-keeping, open up the Keychain Access Application and select the “Keys” category.

Highlight the private key associated with your iOS Distribution Certificate and select “Export Items” from the ‘File’ menu. Save your
key in the Personal Information Exchange (.p12) file format.

You will be prompted to create a password which will be used when you attempt to import this key on another computer.
You can now transfer this .p12 file between systems. Double-click on the .p12 to install on a system. You will be prompted for the password you first entered above.



Ok, next is:

Create and download your iOS Distribution Provisioning Profile for App Store Distribution


To successfully build your application with Xcode for distribution via the App Store, you first need to create and download an

App Store Distribution Provisioning Profile.


These are different than the Development Provisioning Profiles that were used earlier in that Apple will only accept applications if they are built with an App Store Distribution Provisioning Profile.


Team Agents should navigate to the Provisioning section of the Provisioning Portal and select the Distribution tab.
Select the App Store radio button.
Enter the name for your Distribution Provisioning Profile.
Confirm your iOS Distribution Certificate has been created and is displayed.
Select your wild-card App ID to build all of your applications with your single Distribution Provisioning Profile.
Click ‘Submit’.
Click on the name of the Distribution Provisioning Profile to download the .mobileprovision file.
Drag the .mobileprovision onto the Xcode or iTunes icon in the dock to install.


Ok, that's done.

It was called: JLPTVocabQuizDistributionProfile.mobileprovision

Ok, this next step:

Creating and Downloading a Distribution Provisioning Profile for Ad Hoc Distribution


Ok, I don't need this. I'm not doing ad-hoc.

Next:

Building your Application with Xcode for Distribution

1. Launch Xcode and open your project.
2. If you have not already done so, drag the Distribution Provisioning Profile downloaded from the Provisioning Portal onto the Xcode or iTunes icon in the dock (or, drag into ‘~/Library/MobileDevice/Provisioning Profiles’ directory.)

3. Open the Xcode project and Duplicate the “Release” configuration in the Configurations pane of the project's Info panel. Rename this new configuration “Distribution”.


Here we go again. With Xcode 4, there is no "Duplicate" button.

Is this where I just build the archive?

======= update ================

I missed this earlier - go to iTunesConnect portal, and select "manage your apps"

Then select "Add an app"

My app name is:

JLPT Vocabulary Quiz

Bundle Id is selectable, this one is JLPT Vocab quiz (why is there not an "all apps" selection)

My bundle id suffix is com.kanjisoft.jlptvocabquiz - and I changed it in the info.plist, too.

For sku, i put SKU001

Ok, the path is laid out here:

iTunesConnect_DeveloperGuide.pdf

What it comes down to is, log into itunes connect, add your app, and then drill down a couple of times beneath your icon display (to get to "version details) until you come to a button on the upper right that says "Ready to upload".

That will put you into a status of "waiting for upload".

That still doesn't cover submitting the binary. I'm looking for the apple docs on that, but having a tough time.

This url:

http://stackoverflow.com/questions/5412004/how-do-i-submit-an-app-through-xcode-4s-organizer-im-getting-an-error

talks about issues with submitting an app in xcode4.

Ok, let's try this one again:


http://developer.apple.com/library/mac/#documentation/ToolsLanguages/Conceptual/Xcode4UserGuide/DistApps/DistApps.html

Distributing Applications

Before submitting an application to the App Store or share it with others, you create an application archive.

You can then share your application archive with application testers and other application developers or distribute it to users.

To have the application considered for inclusion on the App store, you submit the archive to iTunes Connect.

To ensure your application archive passes essential iTunes Connect validation tests, you can have Xcode validate it before you submit it.


Ok, so it's clear that it's the application archive, e.g. zip file, that is submitted.


Archive Your Application

Before archiving your application, ensure that the binary is self contained.

That is, if it relies on static libraries, ensure that those libraries are part of the application binary by setting the Skip Install build setting to Yes in the Release build configuration of the target that builds and archives the application.



How do I know it it relies on static libraries? I have a flurry library, which is .a.

It is a static library. I found a good link here:

http://stackoverflow.com/questions/5295890/iphone-ad-hoc-build-using-xcode-4

I'm just going to set the skip-install to "yes" for now, on the release, build settings.

But I'll get back to that later. For now, continue with the apple doc.


Archive your product for submission to iTunes Connect or for sharing with others.

Schemes
// What is a scheme in this case? It looks like, run on device, simulator for iPad, iPod, etc.

have an Archive action

with settings you use to customize the archive that Xcode creates when you choose Product > Archive.


To archive your application . . .
From the Scheme pop-up menu, choose a scheme.
From the same menu, choose Edit Active Scheme to display the scheme dialog.
In the left column, select Archive.
Choose a build configuration. // should I have a new one for this?

Specify a name for the archive and click OK.

Choose Product > Archive.

O.k. Trying the Product > archive.

It built the archive, and I tried to upload it to the appstore from organizer.

But I got the error the bundle id didn't match. I had guessed at jlptvocablquiz, but rats, it was JLPTQuizApp. So, I'm changing it in plist.

But when I ran the product, archive, I this nasty-gram:

Application failed codesign verification. The signature was invalid, or it was not signed with an Apple submission certificate. (-19011)


After much panicky googling, I tried the experiment of changing the code-sign to the deployment certificate / profile instead of the development certificate on the release for build settings. That got rid of the error.

But the next problem was that, even though the build succeeded, it wasn't appearing in organizer.

So, I'm trying this, from SO:

This is based on another answer from a similar question which can be found at Archive does not appear in xcode4 organizer

I take no credit for this answer, but this together with the suggestions from Ron fixed the problem for me.

For the "Release" configuration do the following:

Set "Skip Install" to YES on your project

Set "Skip Install" to NO on your application target

Set "Skip Install" to YES on all static library targets that are included

Choose Product -> Archive from the menu.


Well, ok, I did the first two, but I don't see any static target.

I'll try to submit without it, but I don't know if it will work. Pfft.

Next it gives me a problem with UIBackroundModes being null. Well, I'm not doing anything like playing audio, so, I don't need that variable, so I'm just deleting it.

Try again.

Ok, well, it's taking a lot longer to upload, at least.

Maybe too long...how big is it, anyway?

At last - "submission succeeded". Hooray!

Saturday, October 8, 2011

Adding flurry Analytics to your iOS app

Adding flurry analytics to your app is really simple - I just did in - and really did take five (or maybe 10) minutes, just like they promised.

All you have to do is:

1) Find the flurry site:

http://www.flurry.com/product/analytics/index.html

2) Click on sign up and fill out the simple form.

3) Validate in your email.

4) You'll be shown your app name (which you provided) and a long key once you validate successfully.

5) At this point, you're just following instructions from online or the pdf.

6) Use finder to copy the downloaded "FlurryAnalytics" folder into your project.

7) Click file, add files, and select the folder to add it to the project.

8) Go to your app delegate and add this to the header:

#import "FlurryAnalytics.h"

9) Add this to your "applicationDidFinishLaunchingWitgOptions" method in your app delegate:


[FlurryAnalytics startSession:@"ZMRKQ29J8683RVL5Y3VQ"];

That's it!

Thursday, October 6, 2011

Adding links to a textview

Ok, so, we're getting close to the release. I added the quiz numbers by level yesterday. Now, things I still need to do:

1) Add links to my web set, JLPT web set
3) Submit the app
4) Disable the next question button when question is asked
5) Get rid of the back button on NextLevel
6) Splash

I'm having trouble motivating myself. It's not like what I'm doing right now is hard. I've always enjoyed programming. It just seems tough to get going. Once I get into it, I should start enjoying it more. The key is to enjoy the learning process.

Btw, I've come across Stanfords iTunes course in iOS programming. It's really great - gives you a great background. I wish I had found it sooner.

Ok, so, how to add a link to the display? Learning. Calm. Fun.

Well, SO has some insight:



textView.text = @"Some text with link in it : http://http://stackoverflow.com";
textView.dataDetectorTypes = UIDataDetectorTypeLink;


So, this looks pretty simple. Let's try it.

Well, he wants UITextview, I assume because of the name. Why would that be?

Well, this looks pretty good:

Detecting phone numbers and links in an iPhone application
Q: How do I detect phone numbers and links in an iPhone application?

A: How do I detect phone numbers and links in an iPhone application?

// http://developer.apple.com/library/ios/#qa/qa1495/_index.htmlAs of iPhone OS 3.0, text views and web views support the detection of phone numbers, email addresses, and HTTP links. iPhone OS 3.0 scans for these types of data, highlights them when found, and renders them into clickable URLs as shown in Figure 1. You can detect phone numbers and links programmatically or by using Interface Builder 3.1.2 or later.
Figure 1 Detecting phone numbers, email addresses, and HTTP links in a text view



This looks pretty good:

Using Interface Builder

Follow these steps to detect phone numbers and links using Interface Builder 3.1.2 or later:

Open the Nib file containing your web view or text view in Interface Builder.

Select your view, then choose Tools > Attributes Inspector. The Attributes Inspector contains the Detect Phone Numbers and Detect Links options, which respectively turn on phone numbers detection and links (email and URLs) detection.

Check one of these options or both of them according to your needs as shown in Figure 2.



Well, what happens when I do that is the keyboard pops up instead of take me to a hyperlink. I think I need to mark it as non-editable.

Ok, good. That works. What have we learned? To create a link in the IB, use UITextView, and mark it is non-editable.

Ok, well, I really just need to acknowledgement url. I can leave it that for now.

Wednesday, October 5, 2011

Xcode and the broken pipe

Ok, today there are some items which we're going to tackle which are relatively minor. First, I want to take the JLPT level out of the starting title - it might be confusing to the users. Secondly, I would like to display a simple welcoming dialog, just saying thanks and you can change the level, maybe a couple of other things. Third, I'm getting wraps in the middle of a word on the english definitions. Fourth I'm getting a funny message when I run it on the iPod, I want to get rid of that.

Starting with that last, here is the message:

Error starting executable 'JlptQuizApp'

putpkt: write failed: Broken pipe

I can get rid if it in the simulator by resetting it, but why should I have to do that? Why does it keep coming back? Also, I'm getting on on the iPod.

Here's how one posting from SO that says how to fix it:

That's essentially an internal error in Xcode's ability to talk to your phone. It doesn't mean you've done anything wrong, it's a bug in the development system. It'll go away after quitting and restarting Xcode, and possibly after restarting your Mac. I haven't seen the error in a while-- it's possible that other voodoo like removing the app from the phone may be necessary.


Ok, I've restart my iPhone, and restarted XCode. Let's try it.

Good - it seems to have gotten rid of it. Let's hop it stays that way.

Ok, let's get rid of the title on the start page.

Well, we'll just change that one to show the level:

// build the title
NSMutableString *temp1 = [[NSMutableString alloc] initWithString:@"Level "];
NSInteger jlptLevel = [Utils getJlptLevel];
NSString *temp2 = [NSString stringWithFormat:@"%d", jlptLevel];
[temp1 appendString:temp2];
self.title = temp1;


I'm getting kind of concerned because I'm getting a blank screen sometimes, which means it's not initializing the questions, I guess.

How do I get it to repeat that?

I wonder if I delete the app if it clears the settings. Let's try that.

Right. When I first start the app, if I go directly to settings, I get an error that the end quiz number is zero if I try to save the settings, even if it's 10. This is because it's checking it against the wrong value,

[appState.questionDict count]


which hasn't even been initialized yet.

Instead, it should be getting it directly from the database. Fortunately, I've already set up a method for this:

-(NSInteger) getLastWordNum:(NSInteger) level;


Uhf. I'm getting into the twisted validation logic again. Wait, I can just use the current setting selected from the widget which only shows valid values for the jlpt level. What's that logic for getting the value?

Ok, this is better:



else if (endNumInt > [Utils getNumberOfWordsForLevel:self.jlptLevel] ) {



Ok, that seems to be working ok. That's it for now.

Tuesday, October 4, 2011

Resetting the numbers at the end of the quiz!

Ok, just a quick-hitter. I'm going to change the logic to handle coming to the end of the list of vocabulary words. Let's take a look at the Android app. Weird - can't find it. I know it's there.

Ah, here it is - in NextLevelActivity:

// check for last quiz number
boolean wasResetToBeginning = appState
.incrementQuizStartAndFinish(this);


if (wasResetToBeginning) {
text = "Congratulations - you have completed the quiz! Resetting to start";
}


Ok, this code obviously goes in the iPhone version of the NextLevel - NextLevelViewController.

Here's what we have now:

// increment the quiz range by the difference between start and end
[Utils incrementQuizRange];


Let's go back and look at the Android app procedure:


public boolean incrementQuizStartAndFinish(Context context) {

boolean wasResetToBeginning = false;
int numQuestionsInQuiz = quizEndNum - quizStartNum + 1;
quizStartNum = quizEndNum + 1;
quizEndNum = quizStartNum + numQuestionsInQuiz - 1;

int lastNum = Utils.getLastWordNumber(context, this.jlptLevel);

if (quizStartNum > lastNum) {
this.quizStartNum = 1;
this.quizEndNum = numQuestionsInQuiz - 1;
wasResetToBeginning = true;
}

InitUtils.saveState(context);
return wasResetToBeginning;
}


Yes, I like that code. Good old logic.

Ok, let's see what we currently have for the iPhone:

+ (void) incrementQuizRange {

NSInteger startNum = [Utils getStartNum];
NSInteger endNum = [Utils getEndNum];
NSInteger diff = endNum - startNum;

startNum = endNum + 1;
endNum = endNum + diff + 1;

[Utils setStartNum: startNum];
[Utils setEndNum: endNum];

}



So, the first thing I need to do is get a method comparable to this:


int lastNum = Utils.getLastWordNumber(context, this.jlptLevel);


That method looks like this:

static int getLastWordNumber(Context context, int inputLevel) {

DataBaseHelper dbHelper;
dbHelper = DataBaseHelper.createDB(context);

int lastNum = dbHelper.getLevelCount(inputLevel);

dbHelper.close();

return lastNum;
}


Here's getLevelCount:

public int getLevelCount(int level) {

ArrayList rows = null;

rows = selectByLevel(level);

return rows.size();


}

Ok, so let's add this to the Utils:

+ (NSInteger *) getLastWordNum:(NSInteger) jlptLevel;

Ok, this is a little different because I have a common_select method that's sticking all the answers into AppState, which I don't want. I maybe can add a boolean saying whether or not do do that, but the Android implementation is cleaner because it just returns an array.

Ok, I just need to add this method:

- (void) common_select: (NSString *) querySQL statement: (sqlite3_stmt *) statement cntr_question: (int) cntr_question {

[self common_select: querySQL statement: statement cntr_question: cntr_question add_to_app_state: YES];

}


Ok, here's the method. I'm not happy about the way it worked out. I swear to the ends of the earth I'll refactor this to return an NSArray. It will be beautiful.



So, I don't have to change the current calling code. I had a little trouble remembering that the method call needs to be defined after the called method. Also, these should all be static methods. I'll refactor that later.

Ok, here's how the method is called:


MyDatabaseReader *myDatabaseReader = [[MyDatabaseReader alloc] init];

NSInteger jlptLevel = [Utils getJlptLevel];

[myDatabaseReader selectByLevel:jlptLevel];


Hmph - all I really needed to do was return an NSArray.

Ok, here's the code to get the count. It's really the same as select all by level without the row count returned, and the app state update set to no.

-(NSInteger) getLastWordNum:(NSInteger) level
{
const char *dbpath = [databasePath UTF8String];

sqlite3_stmt *statement;

if (sqlite3_open(dbpath, &jlptDB) == SQLITE_OK)
{
int cntr_question = 0;

NSLog(@"getLastWordNum, open ok");


NSString *querySQL = [NSString stringWithFormat: @"SELECT kanji, hiragana, english FROM v_all_words_by_level_freq_sequence WHERE level = %d; order by level, number", level];

int rowCount = [self common_select: querySQL statement: statement cntr_question: cntr_question add_to_app_state: NO];

sqlite3_close(jlptDB);
return rowCount;
}
else {
NSLog(@"select all, open failed");
return 0;
}
}


Ok, let's keep moving. I've got the Utils method set up. Ok, all I have to do now is update the utils to indicate if it hit the last method.

Ok, this is the Utils method now:

+ (BOOL) incrementQuizRange {

BOOL wasResetToBeginning = NO;

NSInteger startNum = [Utils getStartNum];
NSInteger endNum = [Utils getEndNum];
NSInteger diff = endNum - startNum;

int jlptLevel = [Utils getJlptLevel];

MyDatabaseReader *myDatabaseReader = [[MyDatabaseReader alloc] init];

int lastNum = [myDatabaseReader getLastWordNum:jlptLevel];
//int lastNum = Utils.getLastWordNumber(context, this.jlptLevel

[myDatabaseReader release];


startNum = endNum + 1;
endNum = endNum + diff + 1;

if (startNum > lastNum) {
startNum = 1;
endNum = diff - 1;
wasResetToBeginning = YES;
}



[Utils setStartNum: startNum];
[Utils setEndNum: endNum];

return wasResetToBeginning;
}


No problems there.

Ok, now, at last, back to the NextLevelViewController.

Set up a connection to the message on the nib as a UILabel/IBOutlet.

And try this code out:

// increment the quiz range by the difference between start and end
BOOL wasResetToBeginning = [Utils incrementQuizRange];

if (wasResetToBeginning) {
self.congratMessage.text = @"You have completed all the words - restarting!";
}
else {
self.congratMessage.text = @"You have advanced to the next level!";
}


Oops, got this message:

setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key congratMessage.'


Ah, spelling:

// increment the quiz range by the difference between start and end
BOOL wasResetToBeginning = [Utils incrementQuizRange];

if (wasResetToBeginning) {
self.congratMessage.text = @"You have completed all the words - restarting!";
}
else {
self.congratMessage.text = @"You have advanced to the next level!";
}



And the synthesize - and change the nib.

Ok, now that's working. Now, let's get near the end.

Ah, as I suspected. A crash.

Ok, as it turns out, I must not have carried the math over correctly.

This works, but needs more testing:


+ (BOOL) incrementQuizRange {

BOOL wasResetToBeginning = NO;

MyDatabaseReader *myDatabaseReader = [[MyDatabaseReader alloc] init];

int jlptLevel = [Utils getJlptLevel];

int lastNum = [myDatabaseReader getLastWordNum:jlptLevel];
//int lastNum = Utils.getLastWordNumber(context, this.jlptLevel

[myDatabaseReader release];


NSInteger startNum = [Utils getStartNum];
NSInteger endNum = [Utils getEndNum];
int numQuestionsInQuiz = endNum - startNum + 1;


startNum = endNum + 1;
endNum = startNum + numQuestionsInQuiz - 1;

if (startNum > lastNum) {
startNum = 1;
endNum = numQuestionsInQuiz;
wasResetToBeginning = YES;
}


[Utils setStartNum: startNum];
[Utils setEndNum: endNum];

return wasResetToBeginning;
}

Adding a link to a UITextView

Ok, so, we're getting close to the release. I added the quiz numbers by level yesterday. Now, things I still need to do:

1) Add links to my web set, JLPT web set
3) Submit the app
4) Disable the next question button when question is asked
5) Get rid of the back button on NextLevel
6) Splash

I'm having trouble motivating myself. It's not like what I'm doing right now is hard. I've always enjoyed programming. It just seems tough to get going. Once I get into it, I should start enjoying it more. The key is to enjoy the learning process.

Btw, I've come across Stanfords iTunes course in iOS programming. It's really great - gives you a great background. I wish I had found it sooner.

Ok, so, how to add a link to the display? Learning. Calm. Fun.

Well, SO has some insight:



textView.text = @"Some text with link in it : http://http://stackoverflow.com";
textView.dataDetectorTypes = UIDataDetectorTypeLink;


So, this looks pretty simple. Let's try it.

Well, he wants UITextview, I assume because of the name. Why would that be?

Well, this looks pretty good:

Detecting phone numbers and links in an iPhone application
Q: How do I detect phone numbers and links in an iPhone application?

A: How do I detect phone numbers and links in an iPhone application?

// http://developer.apple.com/library/ios/#qa/qa1495/_index.htmlAs of iPhone OS 3.0, text views and web views support the detection of phone numbers, email addresses, and HTTP links. iPhone OS 3.0 scans for these types of data, highlights them when found, and renders them into clickable URLs as shown in Figure 1. You can detect phone numbers and links programmatically or by using Interface Builder 3.1.2 or later.
Figure 1 Detecting phone numbers, email addresses, and HTTP links in a text view



This looks pretty good:

Using Interface Builder

Follow these steps to detect phone numbers and links using Interface Builder 3.1.2 or later:

Open the Nib file containing your web view or text view in Interface Builder.

Select your view, then choose Tools > Attributes Inspector. The Attributes Inspector contains the Detect Phone Numbers and Detect Links options, which respectively turn on phone numbers detection and links (email and URLs) detection.

Check one of these options or both of them according to your needs as shown in Figure 2.



Well, what happens when I do that is the keyboard pops up instead of take me to a hyperlink. I think I need to mark it as non-editable.

Ok, good. That works. What have we learned? To create a link in the IB, use UITextView, and mark it is non-editable.

Ok, well, I really just need to acknowledgement url. I can leave it that for now.