Monday, August 26, 2013

A Weird Reason Why iOS App is Rejected

I made a new iPhone app and submitted to App Store. However, it is rejected because of 'Invalid Binary'. And I received an email telling me this:


iPhone 5 Optimization Requirement - Your binary is not optimized for iPhone 5. New iPhone apps and app updates submitted targeting iOS 6 and above must support the 4-inch display on iPhone 5 and must include a launch image with the -568h size modifier immediately following the <basename> portion of the launch image's filename. Launch images must be PNG files and located at the top-level of your bundle, or provided within each .lproj folder if you localize your launch images. Learn more about iPhone 5 support and app launch images by reviewing the iOS Human Interface Guidelines and iOS App Programming Guide.

I have submitted several apps and all support iPhone 5. I know we should add Default.png, Default@2x.png, Default-568h@2x.png to an app. I noticed this sentence "Launch images must be PNG files and located at the top-level of your bundle, or provided within each .lproj folder if you localize your launch images." The key is EACH .lproj folder.

I localized launch images for English, Simplified Chinese, Traditional Chinese. However, I used appirater which contains many localizations. My app was rejected because there are no launch images in these .lproj folders. So we can solve this problem by removing unnecessary localizations from appirater. (Another solution is localizing launch images for these localizations. But I don't want to duplicate too many images.)

Steps:
1. Open your project with Xcode.
2. Delete AppiraterLocalizable.strings, choose 'Remove Reference'.
3. Add files to 'Your Project'...
4. Choose appirater/en.lproj/AppiraterLocalizable.strings (Do not choose appirater/en.lproj folder)
5. Repeat Step 4 for other languages you need.
6. Clean your project and rebuild.

Wednesday, July 24, 2013

Build SQLCipher on Mac OS X

SQLCipher is encrypted SQLite. The following are the steps to build it on Mac OS X.

1. Donwload and build OpenSSL code.
$ curl -o openssl-1.0.0e.tar.gz http://www.openssl.org/source/openssl-1.0.0e.tar.gz
$ tar xzf openssl-1.0.0e.tar.gz
$ cd openssl-1.0.0e
$ ./Configure darwin64-x86_64-cc
$ make
// 'make install' if you want to install it
Now we get libcrypto.a in the same folder.

2. Download and build SQLCipher code.
In another directory,
git clone https://github.com/sqlcipher/sqlcipher.git
$ cd sqlcipher
Change '/path/to/libcrypto.a' in the following command to your path
$ ./configure --enable-tempstore=yes CFLAGS="-DSQLITE_HAS_CODEC" LDFLAGS="/path/to/libcrypto.a"
$ make
Now we get sqlcipher executable with static linking. However it does not work well for me.
I have to build sqlcipher again with dynamic linking.
$ make clean
./configure --enable-tempstore=yes CFLAGS="-DSQLITE_HAS_CODEC" LDFLAGS="-lcrypto"
$ make
We get sqlcipher executable again. This one works for me.

3. Try sqlcipher
Go to parent folder
$ cd ..
Create a db without encryption.
$ sqlcipher/sqlcipher hello
sqlite> create table test(id integer);
sqlite> .q
$ hexdump -C hello | head -15
00000000  53 51 4c 69 74 65 20 66  6f 72 6d 61 74 20 33 00  |SQLite format 3.|
00000010  04 00 01 01 00 40 20 20  00 00 00 01 00 00 00 02  |.....@  ........|
00000020  00 00 00 00 00 00 00 00  00 00 00 01 00 00 00 04  |................|
00000030  00 00 00 00 00 00 00 00  00 00 00 01 00 00 00 00  |................|
00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 01  |................|
00000060  00 2d e2 29 0d 00 00 00  01 03 cd 00 03 cd 00 00  |.-.)............|
00000070  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000003c0  00 00 00 00 00 00 00 00  00 00 00 00 00 31 01 06  |.............1..|
000003d0  17 15 15 01 47 74 61 62  6c 65 74 65 73 74 74 65  |....Gtabletestte|
000003e0  73 74 02 43 52 45 41 54  45 20 54 41 42 4c 45 20  |st.CREATE TABLE |
000003f0  74 65 73 74 28 69 64 20  69 6e 74 65 67 65 72 29  |test(id integer)|
00000400  0d 00 00 00 00 04 00 00  00 00 00 00 00 00 00 00  |................|
00000410  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

create a db with encryption.
$ sqlcipher/sqlcipher hello2

sqlite> PRAGMA key='mypass';
sqlite> PRAGMA cipher = 'aes-256-cfb';
sqlite> create table test(id integer);
sqlite> .q

Now the db is encrypted.
$ hexdump -C hello2 | head -15
00000000  1a b9 66 10 2e 09 ec f6  8a de 82 bc 59 1f 02 c1  |..f.........Y...|
00000010  5f 75 8b 94 f6 ec e9 d6  05 ad 20 57 32 10 42 a6  |_u........ W2.B.|
00000020  8e fe 23 ab cf 55 e5 dd  9d ba 99 f6 a9 16 fa f6  |..#..U..........|
00000030  61 3f 9a 80 68 15 fb 87  e8 ee 34 61 1a 30 ed cc  |a?..h.....4a.0..|
00000040  9e 5f a2 d9 19 8b 52 17  e7 6f 10 80 5b 67 88 b7  |._....R..o..[g..|
00000050  ce 13 2a 9b dc c2 28 b6  85 a3 22 81 45 7e 50 cd  |..*...(...".E~P.|
00000060  54 6a 56 2d af 69 7e 8d  4c 57 0d d2 e7 09 76 22  |TjV-.i~.LW....v"|
00000070  f2 41 79 cc 70 f7 3c d6  03 ac 0c 55 b9 58 fe ca  |.Ay.p.<....U.X..|
00000080  92 e6 50 de 26 26 0f 89  80 97 b1 12 4f ea 46 4d  |..P.&&......O.FM|
00000090  c8 c9 bc 7a 72 4f 16 3c  a4 c8 48 76 5b de f3 67  |...zrO.<..Hv[..g|
000000a0  c6 f9 28 d5 be 66 c3 17  19 e7 0e c6 8c a1 98 77  |..(..f.........w|
000000b0  c8 fe 8f bc 06 ca 73 25  f1 55 45 01 ed 6d 01 41  |......s%.UE..m.A|
000000c0  90 79 28 cb 4d b5 f2 98  0b 55 26 b8 5c 5f 5b 91  |.y(.M....U&.\_[.|
000000d0  15 bd 5a c2 4e 68 2b 47  79 62 0f 8a f5 bf 5d f6  |..Z.Nh+Gyb....].|
000000e0  c3 8c 9d c4 3c 3d 74 5e  1f 60 8c 33 b3 1f d2 e4  |....<=t^.`.3....|

Note: I found there are some problems in changing default codec. I failed to read the encrypted db. Do not run this line:
PRAGMA cipher = 'aes-256-cfb';


Monday, November 05, 2012

Do Not Use CoffeeScript

CoffeeScript is not a framework. It is used to generate readable, clean, tight and safe Javascript code.

Pros:
1. Python style whitespacing
2. Ruby styled lightweight syntax

Javascript:
    var validate = function(form) {
      var required_field_names = ['name','email'];
      return false;
    }
Coffee:
    validate = (form) ->
      required_field_names = ['name','email']
      return false


CoffeeScript returns the result of the last expression in a function
    validate = (form) ->
      required_field_names = ['name','email']
      false

Though there are many reasons to use CoffeeScript.The biggest reason I decided not to use CoffeeScript is debugging. When you debug your web app, you see javascript. What you see is not what you wrote.



Friday, July 20, 2012

Can't dismiss keyboard on iPad (Solution inside)

Sometimes, you see the following code of dismissing keyboard not working on iPad but working on iPhone:

[_textField resignFirstResponder];

That's because a modal view in UIModalPresentationFormSheet mode on iPad ignores it by default.


To let it work, we need to add this method into your view controller:

- (BOOL)disablesAutomaticKeyboardDismissal {
    return NO;
}

If your view controller is in a navigation controller, add this method into your customised navigation controller.

- (BOOL)disablesAutomaticKeyboardDismissal {
    return [self.topViewController disablesAutomaticKeyboardDismissal];
}

Can't change UITableView background colour on iPad (Solution Inside)

If your app is an universal app and you changed the background colour of an UITableView, you will see it works on iPhone but does not work on iPad.

The reason is we can't modify the background colour of the backgroundView of the tableview on iPad. (I think it is a bug. But Apple does not fix it for years.)

Solution: create a new backgroundView.

_tableView.backgroundView = nil;
_tableView.backgroundView = [[[UIView alloc] init] autorelease];
_tableView.backgroundColor = [UIColor clearColor];

Thursday, July 12, 2012

Validate Mac App Store Receipt 2012

As many Mac developer did to validate Mac app store receipt, I use roddi's code (https://github.com/roddi/ValidateStoreReceipt Thanks!). But recently I found it does not work! The code fails to validate the sample receipt.

Because I just restored my machine from TimeMachine. I thought maybe there was something wrong in my dev environment. I spent a lot of hours to make sure my development provisioning and certificates are correct. But it still fails to validate the sample receipt.

I read Apple's doc carefully (https://developer.apple.com/library/mac/#releasenotes/General/ValidateAppStoreReceipt/_index.html), and find the solution.

roddi's code is still working. You need not change it. (Just need to get the latest version)

Follow these steps (internet required):

1. Log out from Mac App Store app.

2. Remove USE_SAMPLE_RECEIPT flag from your project settings -> Preprocessor Macros.

3. Compile your project

4. Find this app in Finder

5. Double click it in Finder to run. Do not run it in Xcode.

6. The OS will ask you to log in with your Apple ID. Do not log in with your real iTunes account. You need to log in with the test account. Find it or create it in the iTunesconnect website.

7. The OS will say something like "Your app is broken. Download it in App Store". Ignore this message. If you "Show Package Contents" of this app in Finder, you will see there is a file _MASReceipt/receipt. The OS installed a development receipt. We will not need the old sample receipt any more. That's why we remove USE_SAMPLE_RECEIPT debugging flag.

Done. You can debug your app now.