Monday, December 17, 2018

Avoid repeated prompt for username and password to allow keychain access on mac

Quite often Mac asks user to give permission for certain operations so the application is can access keychain items. Sometimes even if user inputs the right user name and password, user is still not able to finish the operation. The following steps can be used to avoid this

For client certificate keychain item:
1. Identify which keychain item is used by the operation,
2. open keychain app and select and right click the item, select Get Info menu item
3. open trust section, change "Use System Default" to "Always Trust"
4. save the change



For password keychain item
1. Identify which keychain item is used by the operation,
2. open keychain app and select and right click the item, select Access Control tab
3. select "allow all applications to access the item"
4. save the change



Tuesday, December 11, 2018

iOS viewcontroller completion block and retain cycle

It is well known that when referring self or instance variable inside a block, it is better to use
a __weak reference variable to avoid memory leak due to retain cycle. But this may not be necessary in all the cases, and sometimes it is better to use self instead a weak self reference.

First let see a case how retain cycle happens.
@interface RetainCycle : NSObject{
    void (^testBlock)(void);
}

@property (strong) NSString* mystr;
-(void)setup;

@end

@implementation RetainCycle
-(void)dealloc{
    NSLog(@"RetainCycle instance: %@ deallocated", self);
}

-(id)init {
    NSLog(@"Init called %@", self);
    return self;
}

-(void)setup{
    testBlock= ^{
        self.mystr = @"hi";
        NSLog(@"Integer is: %@", self.mystr);
    };
}
@end

For the above RetainCycle interface, if an app calls the below method
-(void) test {
    RetainCycle* rc = [[RetainCycle alloc] init];
    NSLog(@"RetainCycle obj: %@ created", rc);
    [rc setup];
}
then after the method returns, the RetainCycle instance will not be released. The reason is self is retained within the testBlock due to reference to self.mystr, and testBlock is retained as a property of RetainCycle.

The means retain cycle only happens, if the block is keeping retained by self or its property.

If we change the above code to the below line, then the retain cycle related memory leak will not happen

    __weak void (^testBlock)(void);



Now, we can look another popular case, where retain cycle does not happen, that is the completion block used by view controller.

@interface TestViewController : UIViewController


@end

@implementation TestViewController

-(void)dealloc{
    NSLog(@"*******TestViewController instance: %@ deallocated", self);
}


- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
}

- (IBAction)onDismiss:(id)sender {
    [self.presentingViewController dismissViewControllerAnimated:YES completion:^{
        NSLog(@"Test View controller dismissed %@", self);
    }];
}
@end

In this case, after creating and showing the viewcontroller, user can click the dismiss button to dismiss the view controller. Notice the dismissViewController method's completion block also refers self for NSLog output, but it would not cause memory leak due to retain cycle.

The reason is although the block retains the self for writing NSLog data. But self (viewController) only temporarily retain the block. After the viewController is dismissed, the completion block returned by view controller will be released, and so block will deallocate itself and all objects retained by itself.  So there is no need to change self to a weak reference in the completion block used by view controller. This is also indicated by the xcode debugger, which will call _Block_release after the view controller is dismissed

Actually in most case, we may want to use strong self for view controller completion block, as the operation will need the view controller alive to finish, such as read the text input from the view controller's text field.




Sunday, November 25, 2018

UnsafePointer and UnsafeMutablePointer

When calling objective c function from swift code, the objective c type needs mapping to swift type.

For primary type used as value type parameter, like int, NSString*, they can be mapped to primary type of swift, as Int32, String.

For NSObject inherited interface types, as they are always used as reference type (pointer), so they can also mapped to the corresponding swift types, or keep Objective C original type. For example, NSArray will map to [Any], NSDictionary will map to [HashTable: Any]. But NSMutableDictionary and NSMutableArray will keep the same type name in swift code.

For C struct when used as value type parameter, the same type name will be in swift code.

The thing gets complex when pointer are used for primary type and c struct.

When pointer is used, if the pointer is defined with const, which means the object pointed by the pointer cannot be changed, then the parameter type will mapped to UnsafePointer<Type>; otherwise, it will be mapped to UnsafeMutablePointer<Type>. The major difference between UnsafePointer and UnsafeMutablePointer is UnsafePointer.pointee is read-only, while UnsafeMutablePointer.pointee is writable.

Objective C

-(int) sump:(int*)vp1 add:(const int*)vp2;
maps to

func sump(_ vp1: UnsafeMutablePointer<Int32>!, add vp2:UnsafePointer<Int32>!) -> Int32


If an objective c function only takes a pointer parameter, and on swift side an swift object is passed to the function, then you can use helper method of withUnsafePointer or withUnsafeMutablePointer to simplify the logic. As shown below, the input parameter b is converted to UnsafePointer bb in the function block.


var  b : Int32 = 10
let result :Int32 = withUnsafePointer(to: b) { (bb: UnsafePointer<Int32>) -> Int32 in
      var t : Int32 = bb.pointee
      let mt : UnsafeMutablePointer<Int32>? = UnsafeMutablePointer<Int32>(&t)
  
      let m : Int32 = o.sump(mt, add:bb)
      return m
}

after the code block returns, b is still 10.


The similar function can also be called using withUnsafeMutablePointer, which will allow to update b's value if it is updated inside the objective c function as shown below

var  b : Int32 = 10
let result2 = withUnsafeMutablePointer(to: &b) { (bb: UnsafeMutablePointer<Int32>) -> Int32 in
          
    let mt : UnsafePointer<Int32>? = UnsafePointer<Int32>(bb)
  
    let m : Int32 = o.sump(bb, add:mt)
    return m
}

if sump function updates first parameter's value, then after the above code block returns, the variable b's value will be changed to the updated value.


Saturday, November 24, 2018

Manually create swift bridging header file in xcode project

In order to call objectiveC library from swift project, swift project needs a bridging header file to import the ObjectiveC header file, so swift code can get the functions signature defined in objectiveC library.

If swift project is already created when importing ObjectiveC, then you need manually create the bridging header file with the below steps:

1. in swift project, clicking add new file, and add a objective C coca file, for example, adding a default ViewController class
2. Xcode will show a dialog to ask you whether to create a bridging header file
3. Click Create bridging Header button to create the bridging header file
4. Once the bridge header file is created, in the swift project, delete the objective C .h and .m file by moving them to trash
5. In swift project settings general tabs' "linked Framework and Libraries" section, add the Objective C library project
6 update swift project Bridging Header file, to include the objectiveC header file. For example, if the ObjectiveC library is named as "ObjcLibrary", then add the below line

#import <ObjcLibrary/ObjcLibrary.h>


After importing the c .h file into the swift project, quite often you want to check the generated swift function signature based on c .h file. You can do so with the below steps
1. open the c version .h file
2. open the Assistant Editor window, and  then open the same c version .h file in it
3. click "Related items" button, and then select "Generated Interfaces", which will allow you to choose swift version for the generated swift .h file
Now, you can compare the c .h file and the generated swift interface side by side






Sunday, November 18, 2018

Handle ios inAppPurchase auto-renewing subscription without server side receipt validation

Instead of asking user to purchase your app before downloading it, inAppPurchase provides more options to allow user to purchase the app. In order to leverage ios inAppPurchase function, app developers need to integrate ios StoreKit into the application.

Auto renew subscription is a typical case for this purpose if you want to charge users periodically for using your app.

Two modules to be implemented in order to support auto renew inAppPurchase function
1. Get product information
Based on product id defined in app store connection, application can query the product information using SKProductsRequest object. In the didReceiveResponse delegate method, the product information (SKProduct) is available in the returned SKProductsResponse.products. User needs this information for making a purchase.

2. Purchase the subscription
2.1 In order to purchase a subscription, application first creates a SKPayment object based on SKProduct object returned in step 1, then call SKPaymentQueen.default.add(SKPayment:) method to start the purchase

2.2 StoreKit processes the purchase request submitted in SKPaymentQueue, and then inform the application about the purchase status and result in SKPaymentQueue's updatedTransactions method. To do that, application must add a queue observer (implementation of of SKPaymentTransactionObserver protocol) to get payment queue events.
SKPaymentQueue.default().add(self)

When purchased state is reported by updatedTransactions method, it indicates the purchase is finished, and the app should give user the permission to access the resource, and then call finishTransaction to remove the transaction from paymentQueue. Note finishTransaction should also be called even if the transaction failed.

func paymentQueue(_ queue: SKPaymentQueue,

                    updatedTransactions transactions: [SKPaymentTransaction]){
   switch transaction.transactionState {
      case .purchased:
         queue.finishTransaction(transaction)
         //give user permission
     case .restored:
          queue.finishTransaction(transaction)
         //restore the transaction and give user permission
     case .failed:
        queue.finishTransaction(transaction)
        //inform user the purchase failure
      }
    }
}

For auto-renewing subscription, the transaction date and related information can be stored in keychain, so if user deletes the app from device and then installed it again, the purchase record will still be there, so user does not need to restore the purchase record before using the app.

If user purchased the auto renew subscription and then switch to a different phone, then before let user to purchase the subscription, you should allow user to restore the subscription by calling the below method.
   SKPaymentQueue.default().restoreCompletedTransactions()
When the subscription gets restored on the new device, the application paymentQueue:updatedTransaction method will received the stored event. The original transaction is available in the transaction.origin property. The actual transaction date should come from transaction.origin.transactionDate property.

Note when the subscription is auto renewed, the application's paymentQueue:updatedTransaction  queue will be called after the AppStore automatically renew the subscription without user interaction. In this case, the transaction parameter contains the information for the current renew's information The transaction.origin contains the original initial transaction info. So transaction.transactionDate should be used to update the subscription date.

For simple application, it can just compare the current date with the transaction date extended with the subscription period, if the subscription is still within that period, then allow users to use the app. Otherwise, ask user to purchase or restore the subscription.

Choose inapppurchase product type:
1. users can purchase consumable products many times, but can only buy non-consumable project only once. If users try to buy non-consumable project again after previous purchase, iOS SDK will show an message box to user to prevent the purchase.
2. Although user can buy Non-Renewing Subscription more than once, but when user buy it second time, ios SDK will show a dialog saying "You've already purchased this subscription, tap buy to renew or extend it." This may cause confusion to user.
3. Auto-renewable subscription does not have a way for user to easily unsubscribe the product, this may cause concerns to users for using it. Although it is easy for application to handle renew as it automatically renewed by app store, and the application only needs to handle the renew transaction result.

Choose between free trial period and limited function before user buys 
Before users purchase your inapp purchase item, you may want to let users first experience your application. You can do this by either providing a free trial period, or you can limit only partial functions are available for users before they purchase the product.
Comparing with free trial period, it is relative easier to provide limited function for users, for example, you can only allow user to watch a single chapter of a book, or view few minutes of a movie. As with free trial period approach, you have to implement brand new logic in your application to manage the beginning and end of free trial period, and also properly handle the case when user delete and reinstall the app on the device. In addition, some inapppurchase product types may not support free trial period by App Store.

Prompt user for purchase result
When user subscribes an item, after the purchase is finished, iOS SDK will show a message box to user to report the success result. So application does not need to show a message box to report the result to user. However, if user restores an subscription to a device, after the restoring is finished, iOS SDK does not show anything to user, so application should show a message box to tell user the restoring has succeeded.

Saturday, November 17, 2018

Handle ios NSURLSession authentication request

Some notes about ios NSURLSession authentication

1. Session and task delegate
Although NSURLSessionTaskDelegate can be used to handle task specific event, there is no api to allow setting a task delegate for each individual data task when it is created.
Both NSURLSessionDelegate and NSURLSessionTaskDelegate is set when calling NSURLSession
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(id<NSURLSessionDelegate>)delegate
method. That means the NSURLSessionDelegate and NSURLSessionTaskDelegate must be implemented in the same object if you need to handle both them in your app.

Note, both NSURLSessionDelegate and NSURLSessionTaskDelegate defines the didReceiveChallenge method for application to handle the authentication challenge.

For NSURLAuthenticationMethodServerTrust and NSURLAuthenticationMethodClientCertificate authentication methods, if didReceiveChallenge is implemented in NSURLSessionDelegate, then the session delegate will be called. Otherwise, the didReceiveChallenge method implemented in NSURLSessionTaskDelegate will be called.

For NSURLAuthenticationMethodDefault and NSURLAuthenticationMethodHTTPBasic authentication method, they will only be called on NSURLSessionTaskDelegate's didReceivedChallenge method, and if not implemented there, the request will fail and will not invoke the NSURLSessionDelegate's implementation.


2. Handle NSURLAuthenticationMethodServerTrust authentication
For NSURLAuthenticationMethodServerTrust, if you just want the same bebaviour of how Mobile Safari handles the server trust authentication, then it is better to just let ios default handler to handle it by calling the completionHandler with below parameter

completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil)
You only need to implement your customization logic if you want to trust all server cert or implement server certificate pin function.

3. Timeout for user interaction during authentication handling
When handling authentication challenge, you may want to show some UI to allow user interaction, such as trusting self sign server cert for server trust, or picking a client certificate for client certification challenge. 

However, this does not work for NSURLAuthenticationMethodServerTrust and NSURLAuthenticationMethodClientCertificate authentication methods. As for most server, if the authentication challenge's completion block is not called in few seconds, the server will abort the TCP connection, and then the client will get the same challenge again (the previousFailureCount is still 0 for the new challenge). At this moment, even if the app handles the second challenge properly, the original datatask request cannot finish, and the datatask's didCompleteWithError method is never called. So in order to handle the case, if the application gets the second pending request before the first one's completion block is called, the app should cancel the current data request, so the datatask's didCompleteWithError will be called to finish the data request.

As a result, in order to properly handle NSURLAuthenticationMethodServerTrust and NSURLAuthenticationMethodClientCertificate challenge, the application should avoid any user interaction, so that the app can finish the authentication and call the completion block in 1 or 2 seconds.

Note this behavior is different from NSURLAuthenticationMethodHTTPBasic (or NSURLAuthenticationMethodDefault) authentication methods. As for NSURLAuthenticationMethodHTTPBasic challenge, usually user needs to input the username and password, the test shows if the completion block is not called, the server will just wait without automatically aborting the connection, until it is called later.

This issue for not calling the completion block for NSURLAuthenticationMethodServerTrust and NSURLAuthenticationMethodClientCertificate challenge is even more serious for WKWebView's didReceiveAuthenticationChallenge method
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;
If the completionHandler is not called in few seconds, then the app will just crash.

By the way, client certificate mutual authentication for wkwebview is supported in iOS 12.

Thursday, November 8, 2018

Understand cordova channel event handler

Javascript supports common event handling with addEventListener and dispatchEvent methods. The event is defined on a particular object, such as document or window object. This limits the case when different objects want to have a shared event subscription manager.

For this purpose, cordova implemented a channel module to manage the event. Different modules can create their own named events (channels), but those events are handled by a shared cordova channel object.

Modules need to first call the below method to access the shared cordava channel object
  var channel = require('cordova/channel'),

With the channel object, it can call create or createStick method to create a named channel (event).
   var namedChannel = channel.create('myChannel');

The create/createStick method are the only method exposed by channel module, which can be used to create named channel instance, this is similar to the Factory pattern. As all named channel lives in the same shared cordova channel object, be sure the name is unique when creating the named channel.

Although named channel all lives in the shared channel object, they are not visible to external caller, so the module which creates the named channel instance should keep a variable reference to it, usually the channels are saved in a channels dictionary with channel name as key, and channel object as value. The owner of the named channel object then exposes APIs to allow other modules to call the instance method on the namedChannel object to subscribe, unsubscribe, or fire the event on the named channel.

For the document object, cordova.js already created few cordova common events, like onDeviceReady, etc, so other modules can use them to get cordova notification events. However, in this case, caller can directly call the javascript addEventListener method to subscribe a event without going through the cordova channel APIs. The reason is Cordova.js already override the document.addEventListener method, so it checks if the matched event name is created by cordova channel API, then it will be handled by cordova channel module, otherwise, it will be handled by default javascript addEventListener method.