Wednesday, July 30, 2014

[Vietnamese] Hướng dẫn làm Push Notification

Chúng ta biết Push notification là một chức năng Apple hổ trợ nhà phát triển, để đưa ra những thông báo từ nhà phát triển, hay những cập nhật thay đổi,  tới thiết bị cài đặt sản phẩm. Nó giúp cho người dùng có thể có sự quan tâm nhất định đến sản phẩm, nhiều nhà phát triển coi đó là một công cụ để tiếp cận người dùng.

Ở bài viết này tôi sẽ hướng dẫn các bạn một quy trình để tạo một cái Push Notification.


I.   TẠO CERTIFICATE REQUEST

Mở KeyChain( Vào Application/Utilities)
ñ Trên Menu: KeyChain Access → Certificate Assistant → Request a Certificate from a Certificate Authority

ñ Nhập User Email Address, Common Name (bỏ qua CA Email Address), chọn Save to Disk.

ñ Nhập tên lưu file .cer. Ví dụ ở đây là: testpushnotification.certSigningRequest. Ở đây lưu ra Desktop


II.         TẠO MỘT APP ID

Mỗi ứng dụng Iphone sử dụng APNs phải có một ID duy nhất. Ở đây bạn sẽ học cách thế nào để tạo một App ID cho Push Notification
1. Đăng nhập iPhone Developer Program tại: http://developer.apple.com/iphone/. Click vào iPhone Developer Program Portal bên góc phải màn hình (Hình 4).

Hình 4. Hướng dẫn vào iPhone Developer Program Portal
2. Bạn sẽ vào thấy phần Program Portal (Hình 5).

Hình 5. Màn hình iPhone Developer Program Portal
3. Click vào App IDs  bên trái màn hình sau đó chọn New App ID(Hình 6).

Hình 6. Màn hình App ID

4. Nhập Description bạn muốn, ở đây tôi nhập "TestPushNotification"  và chọnGenerate New cho Bundle Seed ID. Ở Bundle Identifier, nhập com.ait.testlovecinema. Click Submit (Hình 7).

Hình 7. Creating a new App ID
5.     Bạn sẽ thấy App ID mà bạn đã tạo (Hình 8)

Hình 8. Tạo App ID

1.  CẤU HÌNH MỘT APP ID CHO PUSH NOTIFICATIONS

Khi một App ID được tạo, bạn cần cấu hình nó cho push notifications.
1. Để cấu hình một App ID cho push notification, bạn cần click button Configure ở bên phải màn hình App ID.  (Hình 9).

Hình 9. Cấu hình một App ID cho push notification service
Check vào Enable cho Apple Push Notification và sau đó click Configure button ở bên phải dòng Development Push SSL Certificate.
2. Bạn sẽ thấy màn hình Apple Push Notification service SSL Certificate Assistant. Click Continue (Hình 10).

Hình 10. Màn hình Apple Push Notification service SSL Certificate Assistant
3. Click button  Choose File button để tìm tới file Certificate Request bạn đã lưu ở trước (ở đây là file testpushnotification.certSigningRequest). Click Generate (Hình 11).

Hình 11. Taọ SSL certificate
4. SSL Certificate sẽ được tạo ngay. Click Continue (Hình 12).

Hình 12. APNs SSL certificate đã đươc tạo
5. Click button Download Now để Download SSL Certificate. Click Done (Hình 13).

Hình 13. Download Certificate đã được tạo
6. SSL Certificate mà bạn download có tên là aps.developer.identity.cer. Double-click để cài đặt nó vào ứng dụng Keychain Access(Hình 14). SSL certificate sẽ được sử dụng trong ứng dụng của bạn bởi vậy nó có thể liên hệ với APNs để gửi push notifications tới ứng dụng của bạn.

Hình 14. Cài đặt Certificate đã được tạo vào ứng dụng Keychain Access
Mở rộng ra bạn sẽ thấy private key và certificate xuất hiện trong keychain. Click chuột phải Certificate chon Export, và lưu với tên bạn chọn, ở đây tôi đặt tên là cert.p12. rồi sau đó chọn phần private key rồi click chuột phải chọn Export, và lưu với tên bạn chọn, ở đây tôi đặt tên là key.p12. → Chú ý đây là bước quan trọng, vì dề bị nhầm lẫn, nhiều người sẽ chỉ tạo một lần file p.12;


2.   TẠO PROVISIONING PROFILE

Bước tiếp theo là tạo Provisioning Profile, để bạn cở thể cài đặt ứng dụng lên thiết bị thật.
1. Quay lại iPhone Development Program Portal, click vào Provisioning phía bên tái màn hình, rồi sau đó chọn New Profile (Hình 15).

Hình 15. Chọn Provisioning.
            2. Đánh testPushNotificationProfile vào phần Profile name. ChọnTestPushNotification (đã tạo ở trên) tại phần App ID. Cuối cùng, chọn select all các thiết bị tmà bạn muốn cung cấp(bạn có thể đăng ký các thiết bị này ở iPhone Developer Program Portal thông qua Devices). Click Submit (Hình 16).


Figure 16. Tạo Provisioning profile

3.    


Sau khi tạo bạn sẽ thấy nó ở trạng thái pending..., bạn có thể reset lại sẽ thấy được button Download.(Hình 17).

                                    Figure 17. Pending the approval of the provisioning profile
4.     Download về bạn sẽ có file testPushNotificationProfile.mobileprovision.

3.  Tạo file PEM
Bật Terminal (Application/Utilites). Chọn đến Desktop

$cd /Users/ait/Desktop

Chuyển file .cer thành .pem. Ở đây ta chuyển file aps_developer_identity.cer mà đã down về thành file testPushNotificatioin.pem

$ openssl x509 -in aps_developer_identity.cer -inform der -out testPushNotification.pem

Chuyển file certificate key .p12 (cert.p12 đã tạo sẳn ở keychain) thành file .pem (ở đây tôi đặt tên là cert.pem). Rồi nhập Password, mà lúc bạn đã tạo file cert.p12 (Chú ý nên để là một password dể nhớ, ở đây tôi chọn là 12345).

$ openssl pkcs12 -clcerts -nokeys -out cert.pem -in cert.p12
Enter Import Password:
MAC verified OK

Chuyển đổi private key (bạn đã tạo ở keychain là key.p12) thành file .pem. Rồi nhập password vào và bạn tạo một passphrase mới. Bạn cần nhập passphrase mơi sẽ được sử dụng mà hoá(encrypt) file .PEM . Bạn nên chọn một đoạn mã an toàn. Chú ý: Nếu bạn không nhập passphrase cho PEM, openssl sẽ không đưa ra môt thông báo lỗi, file .PEM đã tạo sẽ không có khóa riêng phần trong nó.

$ openssl pkcs12 -nocerts -out key.pem -in key.p12
Enter Import Password:
MAC verified OK
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:

File cert.pem và file key.pem sẽ được sử dụng để script php của bạn giao tiếp với APNS.
Nếu bạn muốn bỏ passphrase của private key trong key.pem thì làm như sau:

$ openssl rsa -in key.pem -out key.unencrypted.pem
Enter pass phrase for key.pem:
writing RSA key

 Kết hợp certificate và key (bước này là bước quan trọng để tạo file .pem)

$ cat cert.pem key.unencrypted.pem >ck.pem

Bạn có thể kiếm tra kết nối với server test của Apple : gateway.sandbox.push.apple.com 2195
$ telnet gateway.sandbox.push.apple.com 2195
Trying 17.172.238.209...
Connected to gateway.sandbox.push-apple.com.akadns.net.
Escape character is '^]'.
Connection closed by foreign host.

Ví dụ tạo file Php :
<?php

/**
 * job actions.
 *
 * @package    pushNotification
 * @subpackage job
 * @author     HungNX
 * @version    1.0
 */

                        /*
                         * Khoi tao apns de gui notification len AppStore
                         * */
                        //Begin
                        $payload['aps']['alert']['action-loc-key']=(string)"View";
                        $payload['aps']['alert']['body'] ='Test Push Notification';
                        $payload['aps']['badge']=(int)1;
                        $payload['aps']['sound']='default';
                        $payload['poke_id'] = '1';
                        $payload = json_encode($payload);
                         
                        $apnsHost = 'gateway.sandbox.push.apple.com';
                        $apnsPort = 2195;
                        $apnsCert = 'ck.pem';
                         
                        $streamContext = stream_context_create();
                        stream_context_set_option($streamContext, 'ssl', 'local_cert', $apnsCert);
                         
                        $apns = stream_socket_client('ssl://gateway.sandbox.push.apple.com:2195', $error, $errorString, 2, STREAM_CLIENT_CONNECT, $streamContext);
                        //End
                       
                        // send a message
                        $tokenId = '72006935 e94ef3a3 46034f81 50f3da6d d1dc9344 98903717 28f959ff d29ba70c';
                        $apnsMessage = chr(0) . chr(0) . chr(32) . pack('H*', str_replace(' ', '', $tokenId)) . chr(0) . chr(strlen($payload)) . $payload;
                        if($apns)
                                    echo 'Connected...';
                        fwrite($apns, $apnsMessage);          
                        //close connection
                        socket_close($apns);
                        fclose($apns);
                        exit();
                       
Chạy thử :
$ php actions.class.php
Connected...
Warning: socket_close(): supplied resource is not a valid Socket resource in /Users/ait/Desktop/actions.class.php on line 41



4.  Đăng ký Push Notification cho App
Hiện thức trong file Appdelegate.m
Trong hàm : - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
/*************** [[UIApplication sharedApplication] registerForRemoteNotificationTypes:
     (UIRemoteNotificationTypeAlert |
      UIRemoteNotificationTypeBadge |
       UIRemoteNotificationTypeSound)];

**********/

/****************Push Notification **********/
//send token device to sver
- (void)application:(UIApplication *)app
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
   
    NSString *returnedStr = [NSString stringWithFormat:@"%@",deviceToken];
    NSString *str2=[returnedStr substringWithRange:NSMakeRange(1, [returnedStr length]-2)]; //"<"抜き
   
    NSURL * urlaaa = [NSURL URLWithString:[NSString stringWithFormat:@"http://....?device_id=%@&os=ios", str2]];
   
    NSMutableURLRequest * request = [[NSMutableURLRequest alloc] initWithURL:urlaaa];
    NSURLResponse *response = nil;
    NSError *error = nil;
NSData *result = [NSURLConnection sendSynchronousRequest:requestreturningResponse:&response error:&error];
    [request release];   
    NSString *resultString = [[NSString allocinitWithData:resultencoding:NSUTF8StringEncoding];
}

- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err {
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
   
}
/**********************************************/

Tóm tắt:


Thursday, July 24, 2014

Benefits to using `instancetype`

Yes, there are benefits to using `instancetype` in all cases where it applies. I'll explain in more detail, but let me start with this bold statement: Use `instancetype` whenever it's appropriate, which is whenever a class returns an instance of that same class.

In fact, here's what Apple now says on the subject:

> In your code, replace occurrences of `id` as a return value with `instancetype` where appropriate. This is typically the case for `init` methods and class factory methods. Even though the compiler automatically converts methods that begin with “alloc,” “init,” or “new” and have a return type of `id` to return `instancetype`, it doesn’t convert other methods. **Objective-C convention is to write `instancetype` explicitly for all methods.**

* Emphasis mine. Source: [Adopting Modern Objective-C](https://developer.apple.com/library/ios/releasenotes/ObjectiveC/ModernizationObjC/AdoptingModernObjective-C/AdoptingModernObjective-C.html#//apple_ref/doc/uid/TP40014150-CH1-SW11)

With that out of the way, let's move on and explain why it's a good idea.

First, some definitions:

     @interface Foo:NSObject
     - (id)initWithBar:(NSInteger)bar; // initializer
     + (id)fooWithBar:(NSInteger)bar;  // class factory
     @end

For a class factory, you should **always** use `instancetype`. The compiler does not automatically convert `id` to `instancetype`. That `id` is a generic object. But if you make it an `instancetype` the compiler knows what type of object the method returns.

This is **not** an academic problem. For instance, `[[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData]` will generate an error on Mac OS X (**only**) **Multiple methods named 'writeData:' found with mismatched result, parameter type or attributes**. The reason is that both NSFileHandle and NSURLHandle provide a `writeData:`. Since `[NSFileHandle fileHandleWithStandardOutput]` returns an `id`, the compiler is not certain what class `writeData:` is being called on.

You need to work around this, using either:

    [(NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData];

or:

    NSFileHandle *fileHandle = [NSFileHandle fileHandleWithStandardOutput];
    [fileHandle writeData:formattedData];

Of course, the better solution is to declare `fileHandleWithStandardOutput` as returning an `instancetype`. Then the cast or assignment isn't necessary.

(Note that on iOS, this example won't produce an error as only `NSFileHandle` provides a `writeData:` there. Other examples exist, such as `length`, which returns a `CGFloat` from `UILayoutSupport` but a `NSUInteger` from `NSString`.)

For initializers, it's more complicated. When you type this:

    - (id)initWithBar:(NSInteger)bar

…the compiler will pretend you typed this instead:

    - (instancetype)initWithBar:(NSInteger)bar

This was necessary for ARC. This is described in Clang Language Extensions [Related result types][1]. This is why people will tell you it isn't necessary to use `instancetype`, though I contend you should. The rest of this answer deals with this.

There's three advantages:

1. **Explicit.** Your code is doing what it says, rather than something else.
2. **Pattern.** You're building good habits for times it does matter, which do exist.
3. **Consistency.** You've established some consistency to your code, which makes it more readable.

## Explicit ##

It's true that there's no *technical* benefit to returning `instancetype` from an `init`. But this is because the compiler automatically converts the `id` to `instancetype`. You are relying on this quirk; while you're writing that the `init` returns an `id`, the compiler is interpreting it as if it returns an `instancetype`.

These are *equivalent* to the compiler:

    - (id)initWithBar:(NSInteger)bar;
    - (instancetype)initWithBar:(NSInteger)bar;

These are not equivalent to your eyes. At best, you will learn to ignore the difference and skim over it. **This is not something you should learn to ignore.**

## Pattern ##

While there's no difference with `init` and other methods, there **is** a difference as soon as you define a class factory.

These two are not equivalent:

    + (id)fooWithBar:(NSInteger)bar;
    + (instancetype)fooWithBar:(NSInteger)bar;

You want the second form. If you are used to typing `instancetype` as the return type of a constructor, you'll get it right every time.

## Consistency ##

Finally, imagine if you put it all together: you want an `init` function and also a class factory.

If you use `id` for `init`, you end up with code like this:

    - (id)initWithBar:(NSInteger)bar;
    + (instancetype)fooWithBar:(NSInteger)bar;

But if you use `instancetype`, you get this:

    - (instancetype)initWithBar:(NSInteger)bar;
    + (instancetype)fooWithBar:(NSInteger)bar;

It's more consistent and more readable. They return the same thing, and now that's obvious.

## Conclusion ##

Unless you're intentionally writing code for old compilers, you should use `instancetype` when appropriate.

You should hesitate before writing a message that returns `id`. Ask yourself: Is this returning an instance of this class? If so, it's an `instancetype`.

There are certainly cases where you need to return `id`, but you'll probably use `instancetype` much more frequently.


(http://stackoverflow.com/)

Sunday, July 13, 2014

Automatic convert API's Json to Objective C model file.


Resource download

How to detect screen lock/unlock events on the iPhone !

To solve it i use "Darwin Notifications". You can detect the event when device lock/unlock by "com.apple.springboard.lockcomplete"

Let's follow this code:

   

//call back

static void displayStatusChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)

{

    // the "com.apple.springboard.lockcomplete" notification will always come after the "com.apple.springboard.lockstate" notification

  

    NSString *lockState = (__bridge NSString*)name;

    NSLog(@"Darwin notification NAME = %@",name);

  

    if([lockState isEqualToString:@"com.apple.springboard.lockcomplete"])

    {

        NSLog(@"DEVICE LOCKED");

    }

    else

    {

        NSLog(@"LOCK STATUS CHANGED");

    }

}





-(void)registerforDeviceLockNotification

{

    //Screen lock notifications

    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center

                                    NULL, // observer

                                    displayStatusChanged, // callback

                                    CFSTR("com.apple.springboard.lockcomplete"), // event name

                                    NULL, // object

                                    CFNotificationSuspensionBehaviorDeliverImmediately);

  

    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center

                                    NULL, // observer

                                    displayStatusChanged, // callback

                                    CFSTR("com.apple.springboard.lockstate"), // event name

                                    NULL, // object

                                    CFNotificationSuspensionBehaviorDeliverImmediately);



}