Friday, March 22, 2019

Understand Android TrivialDrive_v2 sample project

0. TrivialDriveActivity extends BaseGamePlayActivity
This class is the root activity of the app, and it only sets the layout resource id.

1. BaseGamePlayActivity extends FragmentActivity implements BillingProvider
Main class implements the application's logic. 

The main members include 
MainViewController mViewController
BillingManager mBillingManager

The methods in the BillingProvider interface is implemented through the MainViewController's method with the same name. BillingProvider interface is used by application to communicate with billing library  

public interface BillingProvider
    BillingManager getBillingManager(); 
    boolean isPremiumPurchased(); 
    boolean isGoldMonthlySubscribed(); 
    boolean isTankFull(); 
    boolean isGoldYearlySubscribed(); 


When user clicks purchase button, it shows the UI AcquireFragment (derived from DialogFragment) implemented in sku (stock keeping unit) skulist folder, and then allow user to purchase the product.

When user clicks drive button, it will reduce the count for the tank, and show it on UI.

showRefreshUI and updateUI methods are used to update activity image based on current purchase state. It retrieves the purchase state from MainViewController and then reflects the state in UI elements.

1. 1 Public class MainViewController 
The controller class between UI Activity (BaseGamePlayActivity mActivity) and BillingLibrary (UpdateListener mUpdateListener), its data members hold the state of the purchase. MainViewController mainly handles UI related logic.

It has date member of
private final UpdateListener mUpdateListener;  
private BaseGamePlayActivity mActivity;
mUpdateListener is implemented as a inner class, its main function is passing the purchasing information to mActivity.
The data member int mTank defined in MainViewController is used to track how much gas the car has, when user clicks drive button, it will reduce by 1. User can click purchase button to purchase more gas.

The data member of mGoldMonthly and mGoldYearly keep the purchase status of subscription.

1.1.1 private class UpdateListener implements BillingUpdateListener
UpdateListener implements BillingUpdateListener interface, which is defined in BillingManager class. It allows mainViewController to get purchase notification from BillingManager, such as client setup finished, purchase state updated, etc

public interface BillingUpdatesListener {     
void onBillingClientSetupFinished();     
void onConsumeFinished(String token, @BillingResponse int result);     
void onPurchasesUpdated(List<Purchase> purchases);
}

1.2 public class BillingManager implements PurchaseUpdateListener
BillingManager mainly handled purchase logic by calling Android Bill APIs

It has the data member of
    private BillingClient mBillingClient;
    private final Activity mActivity;  //from constructor parameter
    BillingUpdatesListener mBillingUpdatesListener;  //from constructor parameter

BillingManager implements Android PurchaseUpdateListener interface, which is the main interface for application to handle just happened inapp purchase result through its single method of
onPurchasesUpdated(int responseCode, List<Purchase> purchases)
This method is called for both purchases initiated by your app and the one initiated by play store, such as subscription auto renew.
onPurchaseUpdated is also called when app starts and calls the queryPurchases method to get the local cached purchased items, so onPurchaseUpdated method can initialize the application state based on the purchase information.

In BillingManager constructor, it first creates BillingClient, and set the listener of PurchaseUpdateListener to itself.

BillingManager has a data member of mBillingUpdateListener, which is implemented by MainViewController, this data member is used by BillingManager to notify MainViewController of purchase activity.

When app starts, billingManager calls BillingClient.startConnection method with a listener to tell BillingUpdateListener.onBillingClientSetupFinished that the connection to billing service is ready. Then it calls BillingManager.queryPurchases(), which calls BillingClient.queryPurchases to get PurchaseResult info. If the device has purchased any product, then BillingManager calls handlePurchase to process the result, and also call MainViewController's onPurchasesUpdated method to update the UI.

BillingManager's querySkuDetailsAsync method uses BillingClient.querySkuDetailsAsync method to get details of each SKU item, and sends back the result to AcquireFragment.

BillingManager has a data member of mPurchases, which contains a list of purchased items

2. UI items for purchasing 

2.1 AcquireFragment
When user clicks purchase button, the app shows AcquireFragment,  in its onCreateView method, it calls handleManagerAndUIReady method, which calls querySkuDetails() method.
AcquireFragment's addSkuRows method calls BillingManager's querySkuDetailsAsync method to get each SKU's detailed information from play store, and return it as an SkuDetails list. The list is then used as adapter for AcquireFragment to provide the recycleView's data.

AcquireFragment has a data member of BillingManager to get purchase information from play store.

When user clicks the buy button from the inapp purchase item list, the corresponding uiFactoryDelegate's onButtonClicked method is called to start the purchase process. It does so by calling BillingManager's initiatePurchaseFlow method, which calls BillingClient's launchBillingFlow method to start the actual purchase.

When purchase finished, billingManager's onPurchasesUpdated method adds the purchased item into mPurchases list, and then calls .onPurchasesUpdated method to notify mainViewController to update the UI based on the new purchase state.

2.2 SkusAdapter
SkusAdapter used by AcquireFragment to manage RowViewHolder list item.

2.3 UIDelegatesFactory
UIDelegateFactory holds a dictionary, the key is string of each SKU IDs defined in Google Play Console's inapp products page, the value is the an delegate class which handles how to bind view for each type of purchase item. Those SKU IDS are items that can be purchased by user.

2.4 RowViewHolder
RowViewHolder holds a reference to each purchase view item's elements, so it can easy access the view item. When user clicks buy button to purchase an item, it receives the button clicks event and calls into UIManager to handle it.

2.5 UIManager
UIManager gets the SkuRowData item based on view position, and then it calls UIDelegateFactory's to invoke the view item's onButtonClicked method.

3. Application handles inapp purchase state 
Usually, when app starts it calls queryPurchase to get the already purchased items, and then applies the result to onPurchaseUpdated method to update the app state based on purchase result.

When a new purchase happens by calling BillingClient.launchBillingFlow, the purchase result will be reported by onPurchaseUpdate listener set to BillingClient instance. The app state will be updated based on new purchase result.

As a result, when app is just installed on a device, after it is starts, it should call queryPurchase first to try to get the cached purchase result. If current purchase is available, then app should give user permission and continue.

The local purchase history is stored in Play Store app, not in the application itself's data storage, so uninstall the application will not remove the purchase history from the device.

Regarding the issue of user cancelling subscription, the application does not need to check or calculate when auto renew subscription is cancelled by user and expired. Instead application only needs to check the returned purchase history from BillingManger.queryPurchases method, if a purchase item is returned from the method it indicates a valid subscription still exists, and user should get the required permission. If no purchase item is returned, then the subscription is expired, and the app should not give user the required permission.

The application should only handle three purchase states:
1. Not subscribed. Disable permission to protected resource, and also prompt to subscribe.
2. Subscribed with auto renew of true. Allow user to access the protected resource.
3. Subscribed with auto renew of false. Current subscription is still valid, but user cancelled the subscription or credit card information is expired. Allow user to access the protected resource, and also prompt user to restore the subscription and check the credit card information.

Monday, January 28, 2019

How to make ios UITableViewCell automatically resize based on content in label text

Usually the rows in UITableViewCell have fixed height based on cell prototype constraint design, however, sometimes, certain cell may include a large blob of text, such as term and conditions, privacy policy, etc, so the cell height needs to be automatically resized to fit the content.

The below code is required to automatically resize a default UITableViewCell object's height:

\\override two tableview callback methods
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return UITableViewAutomaticDimension
    }
    
    func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
        return UITableViewAutomaticDimension
    }

\\when creating the cell with long text content, set numberOfLine property to 0 
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  
     var cell : UITableViewCell? = nil
     if (indexPath.row==1){
        
                cell = UITableViewCell()
                cell?.textLabel?.text = "Terms and Condition\r\n" +
                   "This is a long text string.\r\n" +
                   "This is a long text string.\r\n" +
                   "This is a long test string"
                cell?.textLabel?.numberOfLines = 0;
            }
     }
     return cell!

    }


Wednesday, January 23, 2019

javascript "if" condition check on undefined variable or property

In Javascript, when using if statement to check a variable's or a property's value is true or false, different result may happen depending whether the variable is declared, assigned or not.

If a javascript variable is not declared, then using if check on the variable will causes unreferenced exception
try
{
    if (someVariable){
         console.log("not get here")
    }
    else{
         console.log("not get here")
    }
}
catch(err){
     console.log("throw reference error: " +err);
}

if the variable is declared, but not defined, then the if check will return false

var someVariable1;
if (someVariable1) {
    console.log("if condition false")
}
else
{
  console.log("Get it here")

}

So for variable check, it is better to check typeof variable to undefined
try
{
    if (typeof someVariable5 == 'undefined'){
         console.log("get here")
    }
    else{
         console.log("not get here")
    }
}
catch(err){
     console.log("not get here: " +err);
}

var someVariable6;
if (typeof someVariable6 == 'undefined') {
    console.log("get here")
}
else
{
  console.log("Not get here")

}



For property, if the base object is undeclared or undefined, then exception will happen

try
{
    if (someVariable2.someProperty){
         console.log("not get here")
    }
    else{
         console.log("not get here")
    }
}
catch(err){
     console.log("throw reference error " + err);
}

var someVariable3;
try{
if (someVariable3.someProperty) {
    console.log("not get here")
}
else
{
  console.log("not get here")
}
}catch (err){
     console.log("throw type error " + err);
}


if the base variable has a value, then if check works

var someVariable4 = {};
if (someVariable4.someProperty) {
    console.log("if condition false")
}
else
{
  console.log("get here")
} 


For property check, first need to check the base variable is not undefined, which means it has a value assigned, and then check the property directly using if condition



try
{
    if (typeof someVariable7 != 'undefined' && (someVariable7.someProperty)){
         console.log("not get here")
    }
    else{
         console.log("get here")
    }
}
catch(err){
     console.log("not throw exception " + err);
}

var someVariable8;
try{
if (typeof someVariable8 != 'undefined' && (someVariable8.someProperty)) {
    console.log("not get here")
}
else
{
  console.log("get here")
}
}catch (err){
     console.log("not throw exception " + err);
}

var someVariable9 = {};
if (typeof someVariable9 != 'undefined'  && (someVariable9.someProperty)) {
    console.log("not get here")
}
else
{
  console.log("get here, property is undefined")

}

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