Tuesday, April 23, 2019

Equatable and Comparable protocol in Swift

In Swift, sort, sorted have two parameter types, one does not have parameter, another one takes a closure.
mutating func sort()
mutating func sort(by areInIncreasingOrder: (Element, Element) throws -> Boolrethrows

Similar for contains method, one takes an element parameter, and another one take a closure.

func contains(_ element: Element) -> Bool
func contains(where predicate: (Element) throws -> Bool) rethrows -> Bool

Which one to use depends on whether the element type conforms to Equatable (for contains method) or Comparable (for sort method) protocol.

When calling sort or contains method with the closure parameter, there is no limitation on element type, so any element can be used for those methods.

However, when calling contains method with an element object parameter, the element type must conforms to Equatable protocol. Equatable protocol defines a == method, so == can be used in swift code to compare whether two elements are equal or not. When contains(element: Element) is called, it internally uses Equatable protocol's == method to compare whether the input parameter is contained in the collection.

Similarly, when calling sort method with empty parameter, the element type must conforms to Comparable protocol. Comparable protocol inherits from Equatable protocol, which defines some additional method of >  <  >=  <=, so those operators can be used to compare two element objects' value in swift code, Sort method internally uses those method to sort the elements in a collection.

In a word, if an element types conforms to Equatable (or Comparable) protocol, then the simple version of sort (or contains) method can be used, otherwise, the one with the closure parameter must be used. 

Tuesday, April 9, 2019

Debug ASP.Net project deployed to MS Azure from Visual Studio

The below steps can be used to debug asp.net core project deployed to MS Azure app service from Visual Studio 2017.
1. From Visual Studio 2017, publish the asp.net core app to Azure, setting the Configuration to "Debug"
2. In MS Azure portal, app service configuration, select Settings->Configuration, and set Remote Debugging to On, and select Remote Visual Studio version to 2017
3. From Visual Studio, select menu of View->Cloud Explorer, find the MS Azure App service item which has the project deployed to, and then right click it, and select "Attach Debugger"



In addition, for debug purpose, you may want to run asp.net application deployed on Azure in development mode, instead of production mode, so that the error page can contain more information about the error.

To do so, in Azure portal, app service's Settings/Configuration section, add a new setting as below:
ASPNETCORE_ENVIRONMENT     Development

Monday, April 1, 2019

Asp.net core project to migrate database scheme in a separate library project

For large asp.net core web project, the data access logic is usually put into a separate project, however, the database connection string is still configured in main project's appsettings.json file, so some extra settings is required when the data access project uses the connection string from main asp.net core project.
The data access library already added the below dependency
microsoft.EntityFrameworkCore
microsoft.EntityFrameworkCore.Design
Microsoft.EntityFrameworkCore.Sqlserver

1. update appsettings.json to include connection string
{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
   "TravelInsuranceDb":  "Data Source=(localdb)\\MSSQLLocalDB; Initial Catalog=TravelInsurance;Integrated Security=True;"
  }
}


2. in main project's startup.cs, update configureServices method to AddDbContextPool, so that when needed, the custom DBContext object can be created by asp.net core to be used by application
    public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContextPool<DataAccessDbContext>(options =>
            {
                options.UseSqlServer(Configuration.GetConnectionString("TravelInsuranceDb"));
            });
    

3. in data access library, create a DBContext sub class with a DBSet property
   public class DataAccessDbContext : DbContext
    {
        public DataAccessDbContext(DbContextOptions<DataAccessDbContext> options) : base(options)
        {
        }

        public DbSet<User> Users{get; set;}
    }
}
 public class User
    {
        [Key]
        public String Email { get; set; }  //this is the unique key for each user
    }

4. using dotnet migrations command line tool to create migration script. Whenever the entity structure is updated in the project, a new migration should be added. The -s parameter tells data access project where to find the connection string to connect to the database. After running the below command from data access library folder, the data access project should create the migration cs file for how to do the migration on database
dotnet ef migrations add initialcreate -s ..\travelinsurance\travelinsurance.csproj

5. using dotnet ef database command to update database, and verify the database is created
dotnet ef database update -s ..\travelinsurance\travelinsurance.csproj

6. Assume user entity needs a new int field "Point", first update the User class as below 
    public class User
    {
        [Key]
        public String Email { get; set; }  //this is the unique key for each user
        public int Point { get; set; }
    }
Then run the below command to add a new migration step script in the data access project.
dotnet ef migrations add initialcreate -s ..\travelinsurance\travelinsurance.csproj

7. run the database update command again to update the sql server, and verify the new column is added in the table.
dotnet ef database update -s ..\travelinsurance\travelinsurance.csproj


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