Friday, 17 June 2016

XCode UI Automation Testing Developement

Introduction


There is a problem that we meet everyday in our development process. An application has a scenario to test; In first build, this scenario already tested with Pass status. Later, in some newer builds, some functions are added or edited. These functions somehow related to the scenario we tested in the first build. So we need to re-test this scenario again to make sure our application runs smoothly. And the problem is we need to test the same scenario from build to build. It takes a lot of time to re-test a scenario, especially, it is tested manually.



Today I’ll introduce you a new technique to help us reduce a lot of time for re-testing, which is integrated in XCode: Automation UI Testing.




What is XCode UI Testing?


I’m sure that all you us already know about the concept of UI Testing. Before WWDC 2015, Xcode already has a tool included in Instruments. Instruments is a set of tool for developer to play with their apps. That tool is called: Automation.


Automation in Instruments

But the script in Automation is written based on Javascript syntax and not really familiar to mobile developer, especially iOS developer. Besides, Automation cannot help us with debugging, detail error report and cannot put all test cases in 1 file.

Automation code is written in Javascript



But XCode UI testing can solve all the problems above. Introduced in WWDC 2015, Xcode UI Testing is a brand new tool which is integrated in Xcode 7. We can take a look at a small part in Xcode UI Testing file. We can see it is more clearly, more beautiful, and more friendly to iOS developer.


Automation code is written in Swift


Core technologies include XCTest framework and Accessibility. 


  • XCTest provides the framework for UI testing capabilities, integrated with Xcode. Creating and using UI testing expands upon what you know about using XCTest and creating unit tests. You create a UI test target, and you create UI test classes and UI test methods as a part of your project. You use XCTest assertions to validate that expected outcomes are true. Test is fully compatible with both Objective-C and Swift. 
  • Accessibility is the core technology that allows disabled users the same rich experience for iOS and OS X that other users receive. It includes a rich set of semantic data about the UI that users can use can use to guide them through using your app. Accessibility is integrated with both UIKit and AppKit and has APIs that allow you to fine-tune behaviors and what is exposed for external use. UI testing uses that data to perform its functions.

There also be UI Recording. This is a really great feature of XCode UI Testing which help developer write automation test code without putting too much effort. Because the code which is  generated by the recording is in swift/objectiveC, the logic of UI element is unified to the development. This would make our test cases built quickly and exactly.



APIs



There are 3 APs:

  1. XCUIApplication: is a proxy for app being tested. It is independent from the application because of running in a separate process.
  2. XCUIElement:
    • XCUIElement is a proxy for UI elements.
    • Elements can be different types: buttons, cells, windows, etc.
    • Elements has its own identifiers - strings generated by the accessibility system: accessibility identifiers, labels, titles etc.
  3. XCUIElementQuery: XCUIElementQuery provides access to a specific element. The result of running query is a set of current UI elements visible for the accessibility system.

With these 3 APIs, coding ui automation test can emulate almost (with exceptions) end-user interaction.



XCTestAssertions


To evaluate the result, we al so have Assertions listed by category. XCTestAssertions are categorized into 5 groups. Whenever we put assertions in our automation test code, we can evaluate this is correctly functioned or not. For example, I delete an element, I want to determine whether the element is existed or not, I can put XCTestAssertion to compare value as well as existence of the element. Assertion will fail if the value is not equal and we will get the fail report and we can know exactly where the error is. How convenient it is!






Conclusion


XCode UI Testing opens to iOS and OSX developer as well as tester a new way to do automation test for iOS and OSX, easier, faster and familiar. But since it’s still a new tool for us, it still has some limitations:


  • Require iOS 9+ to run tests. Not only SDK, but latest iOS must be installed on testing device/simulator.
  • Require device to be configured for development and connected to trusted host running Xcode.
  • Require access to accessibility features set from privacy settings.
  • Running on Mac OS only: if your QA-team have no Macs (like our do) that may be a problem.
  • You could test only your main app: extensions neither watch app testing is not supported.
  • Can't find any documentation for the moment.
  • Can't find way to test anything outside the app. Can't switch to safari, make URL request and then go back again.
  • Some other limitations may happen when running UI test
  • Limitation on events, we have tap(), doubleTap() but no longPress(). Instead of longPress(), we have to use an alternative way such as pressForDuration().
  • Elements must be recognised by Accessibility. For some complex application, this can be more complicated.
  • This also required iOS 9 and above to run test. So Testers also need a MAC to run all the test.
  • Moreover, lack of documentation is another challenge that we have to deal with.


Prospects


But over all of these challenges, we have a fabulous tool to work with. 


  • If possible, we can save much time on testing. 
  • In the future, Instrument Automation will be removed from Instruments and Apple recommends are moving to Xcode UI Testing for all the UI Testing.
  • 1 more important things is: this is a tool from Apple, so the update to newer version of iOS with new UI control will also be supported at the same time of updating. With third party, we have to wait some time for it to update after the iOS updating.


So. It’s worth for trying right now!

Saturday, 21 May 2016

Secure Database in Android with Sqlcipher

"How to secure the user data?" is the common question for developer, especially when our application work with user's sensitive data: Address, phone number, email, password,...

There are many useful tips for a security app in Android can be found here
https://developer.android.com/training/articles/security-tips.html
These are the technics to avoid DIRECT ATTACK (The hacker user the security holes of the app itself)

But what about INDIRECT ATTACK: The virus does not go after your application directly. Instead it goes after to Android OS. The it can copy and exploit the data of other applications.

In Android, and also other mobile platforms, OS provide an OS security level for each app that called Sandbox (Internal storage only)
It guarantees an app cannot access to the data of others. So the data in internal storage of Android application(/data/data/...) is safe with with INDIRECT ATTACK.
It's true with default user privilege. But if the phone is rooted, user can access anything in it, the Sandbox barrier is not available in this case.

An indirect attacker + root user (rooted phone) can view all data in phone.

So how to protect the data even though the attacker can get it? The answer is encryption. Encryption add an layer of protection. The virus/attacker would see the garbled data.
There two kind of stored data in Android: normal file and database file.

  • Normal file: It's easy to apply encryption. Developer just chooses an suitable encryption that provide by platform API then encrypt/decrypt easily.
    This is the good tutorial about it
    http://www.developer.com/ws/android/encrypting-with-android-cryptography-api.html
  • Database file: We cannot just encrypt/decrypt the database file as above because there are many operations on database during application life cycle. It's will be a performance disaster.
Today, I will introduce a way - a library - to do encryption on Android database file. May be you here it before: Sqlcipher.
SQLCipher is an open source library that provides transparent, secure 256-bit AES encryption of SQLite database files.
SQLCipher has been adopted as a secure database solution by many commercial and open source products, making it one of the most popular encrypted database platforms for Mobile, Embedded, and Desktop applications
https://www.zetetic.net/sqlcipher/about/ 


SQLCipher provide transparent, secure 256-bit AES encryption of SQLite database files.

An very interesting feature of it is transparent. It means, with it, developer don't need to change the original database API to make it work. In Android, the source code with Database, Cursor, DatabaseHelper is still valid.

I do not waste your time anymore. Now, I'll introduce how to integrate SQLCipher to an Android project with Android Studio.
The tutorial for Eclipse can be found here https://www.zetetic.net/sqlcipher/sqlcipher-for-android/

1. Firstly, this is the location of the library https://bintray.com/developernotes/maven/net.zetetic%3Aandroid-database-sqlcipher/view. Choose a version, the select your build configuration. I choose Gradle for this example

2. Create an simple example to apply the library.I use the Login project template of Android.

3. After create project successfully, add the following line (from step 1) to gradle file of module app
compile 'net.zetetic:android-database-sqlcipher:3.4.0'
 4. In this sample we need save/retrieve account information to database to register and login. So we need a model class. It's called Account.
public class Account {
    public long mID;    public String mName;    public String mPassword;    public double mTime;
    public Account() {
    }

    public Account(long id, String name, String password, float time) {
        mID = id;        mName = name;        mPassword = password;        mTime = time;    }
}
5. As usual, we will create a database helper class to work with sqlite in Android
mport android.content.Context;import android.util.Log;
import net.sqlcipher.database.SQLiteDatabase;import net.sqlcipher.database.SQLiteOpenHelper;
/** * Created by sumpham on 5/19/16. */public class CipherDatabaseHelper extends SQLiteOpenHelper {

    public static final String TABLE_TARGET = "table_result";    public static final String COLUMN_ID = "_id";    public static final String COLUMN_NAME = "col_name";    public static final String COLUMN_PASSWORD = "col_password";    public static final String COLUMN_TIME = "col_time";    private static final String ACCOUNT_CREATE = "create table "            + TABLE_TARGET + "(" + COLUMN_ID            + " integer primary key autoincrement, "            + COLUMN_NAME + " text, "            + COLUMN_PASSWORD + " text, "            + COLUMN_TIME + " real);";
    private static final String DATABASE_NAME = "accounts.db";
    private static final int DATABASE_VERSION = 1;
    @Override    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        sqLiteDatabase.execSQL(ACCOUNT_CREATE);    }

    @Override    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
        Log.v(CipherDatabaseHelper.class.getName(),                "Upgrading database from version " + i + " to "                        + i1 + ", which will destroy all old data");        sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + TABLE_TARGET);        onCreate(sqLiteDatabase);    }

    public CipherDatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);    }
}
Note that: we use "net.sqlcipher" instead  of "android.database.sqlite" of Android. The APIs are same between two package.

6. To easily work with database, I create a wrapper class to manipulate with Account table.
import android.content.ContentValues;import android.content.Context;import android.support.annotation.NonNull;
import net.sqlcipher.Cursor;import net.sqlcipher.SQLException;import net.sqlcipher.database.SQLiteDatabase;
import java.util.ArrayList;import java.util.List;
/** * Created by sumpham on 5/19/16. */public class AccountTable {
    private SQLiteDatabase mDatabase;    private CipherDatabaseHelper mDbHelper;
    /**     * Array of all columns in the table     */    private String[] mAllColumns = {
            CipherDatabaseHelper.COLUMN_ID,            CipherDatabaseHelper.COLUMN_NAME,            CipherDatabaseHelper.COLUMN_PASSWORD,            CipherDatabaseHelper.COLUMN_TIME    };
    public AccountTable(Context context) {
        mDbHelper = new CipherDatabaseHelper(context);    }

    /**     * Open the SQLite database     */    public void open() throws SQLException {
        mDatabase = mDbHelper.getWritableDatabase("my_secure_key");    }

    /**     * Close the SQLite database     */    public void close() {
        mDbHelper.close();    }

    public Account addAccount(@NonNull final Account result) {
        ContentValues values = new ContentValues();        values.put(CipherDatabaseHelper.COLUMN_NAME, result.mName);        values.put(CipherDatabaseHelper.COLUMN_PASSWORD, result.mPassword);        values.put(CipherDatabaseHelper.COLUMN_TIME, result.mTime);
        final long insertId = mDatabase.insert(CipherDatabaseHelper.TABLE_TARGET, null, values);
        Cursor cursor = mDatabase.query(CipherDatabaseHelper.TABLE_TARGET,                mAllColumns, CipherDatabaseHelper.COLUMN_ID + " = "                        + insertId, null, null, null, null);
        cursor.moveToFirst();
        Account resultInserted = cursorToAccount(cursor);        cursor.close();
        return resultInserted;    }

    public Account getAccountByName(String username) {
        Account rv = null;        List<Account> accounts = new ArrayList<>();        Cursor cursor = mDatabase.query(CipherDatabaseHelper.TABLE_TARGET, mAllColumns, "col_name = ?",                new String[]{username}, null, null, null, null);        if(null != cursor && cursor.getCount() > 0) {
            cursor.moveToFirst();           rv = cursorToAccount(cursor);            cursor.close();        }

        return rv;    }

    /**     * Take the cursor from a database query and create fill the result structure     *     * @param cursor Database cursor from sqlite query     * @return new result structure     */    private Account cursorToAccount(@NonNull final Cursor cursor) {
        Account result = new Account();        result.mID = cursor.getLong(0);        result.mName = cursor.getString(1);        result.mPassword = cursor.getString(2);        result.mTime = cursor.getFloat(3);        return result;    }


}
As named, The function "getAccountByName" to get and account from database by username and "addAccount" to add an account to database. And again, we use "net.sqlcipher" package.
We need password to open database: getWritableDatabase


7.   We finished with prepare helper classes for the demo. Let apply it to application work flow.

  • Go to LoginActivity.java, add following line to declare variable for AccountTable

AccountTable accountsTb; 

  •  Add a function to initialize sqlcipher database
    Please note that the call SQLiteDatabase.loadLibs(..) must be called before other actions to database.
private void initCipherDatabase() {
    SQLiteDatabase.loadLibs(this);    accountsTb = new AccountTable(this);    accountsTb.open();}

  •  Put this function to the end of onCreate function.
  • Go to UserLoginTask implementation to apply our database code. This class after done
public class UserLoginTask extends AsyncTask<Void, Void, Boolean> {

    private final String mEmail;    private final String mPassword;
    UserLoginTask(String email, String password) {
        mEmail = email;        mPassword = password;    }

    @Override    protected Boolean doInBackground(Void... params) {
        // TODO: attempt authentication against a network service.
        try {
            // Simulate network access.            Thread.sleep(2000);        } catch (InterruptedException e) {
            return false;        }

        if(null != accountsTb) {
            Account account = accountsTb.getAccountByName(this.mEmail);            if (null != account) {
                return account.mPassword == this.mPassword;            } else {
                Account newAccount = new Account();                newAccount.mPassword = mPassword;                newAccount.mName = mEmail;                return accountsTb.addAccount(newAccount) != null;            }
        } else {
            return false;        }

    }

    @Override    protected void onPostExecute(final Boolean success) {
        mAuthTask = null;        showProgress(false);
        if (success) {
            Toast.makeText(LoginActivity.this, "Login/register success", Toast.LENGTH_SHORT).show();        } else {
            mPasswordView.setError(getString(R.string.error_incorrect_password));            mPasswordView.requestFocus();        }
    }

    @Override    protected void onCancelled() {
        mAuthTask = null;        showProgress(false);    }
}
The heart of this code is
if(null != accountsTb) {
    Account account = accountsTb.getAccountByName(this.mEmail);    if (null != account) {
        return account.mPassword.contentEquals(this.mPassword);
    } else {
        Account newAccount = new Account();        newAccount.mPassword = mPassword;        newAccount.mName = mEmail;        return accountsTb.addAccount(newAccount) != null;    }
} else {
    return false;}
We just search the account in database with current username. If it's exist, compare the saved password with current. Otherwise, add a new account to database.
  • Build and run code to see how it work.

8. To view the encrypted database by SQLCipher, you can download the SQLCipher version of SQLiteBrowser here http://sqlitebrowser.org/. You need the database password to open

Complete sample source here Source code

Conclusion

It's very easy to apply SQLCipher to your project. But if you're working in a project that the performance is the most important, you should be careful. Because the encryption will take time to do.
Here is the test result to consider
https://www.zetetic.net/blog/2011/5/7/sqlcipher-performance-and-sqlcipherspeed.html

This library is also available for iOS platform

Friday, 6 May 2016

Swift Tips You May Not Know But Should

Improve the Readability of Constants
A neat way to utilize structs in Swift is to make a file for all the constants in our application. This is useful because Swift allows us to nest structures like so:
import Foundation

struct Constants {

    struct FoursquareApi {
        static let BaseUrl = "https://api.foursquare.com/v2/"
    }
    
    struct TwitterApi {
        static let BaseUrl = "https://api.twitter.com/1.1/"
    }

    struct Configuration {
        static let UseWorkaround = true
    }
    
}
This nesting gives us a namespace for our constants. For example, we could useConstants.FoursquareApi.BaseUrl to access Foursquare's BaseUrl constant. This makes things more readable and provides a layer of encapsulation around related constants.
Avoid NSObject and @objc to Improve Performance
Swift allows us to extend classes from NSObject to get Objective-C runtime features for an object. It also allows us to annotate Swift methods with @objc to allow the methods to be used by the Objective-C runtime.
Supporting the Objective-C runtime means that method calls are going to be using dynamic dispatch instead of static or vtable dispatch. This end result is that methods that support the Objective-C runtime have a four times performance penalty when called. In practical usage, the performance hit may be negligible but the cool thing is that armed with this knowledge, we now know that method execution in Swift is four times faster than Objective-C.
Use Method Swizzling in Swift
Method swizzling is a technique that substitutes one method implementation for another. If you're not familiar with swizzling, check out this blog post. Swift optimizes code to call direct memory addresses instead of looking up the method location at runtime as in Objective-C. So by default swizzling doesn’t work in Swift classes unless we:
  • Disable this optimization with the dynamic keyword. This is the preferred choice, and the choice that makes the most sense if the codebase is entirely in Swift.
  • Extend NSObject. Never do this only for method swizzling (use dynamicinstead). It’s useful to know that method swizzling will work in already existing classes that have NSObject as their base class, but we're better off selectively choosing methods with dynamic .
  • Use the @objc annotation on the method being swizzled. This is appropriate if the method we would like to swizzle also needs to be exposed to Objective-C code at the same time.
Update: As requested, we've added an example of swizzling purely in Swift. It still requires the Objective-C runtime, but our class doesn’t inherit from NSObject and our methods are not marked as @objc. Click here to see it in action in a playground.
import UIKit
​
class AwesomeClass {
    dynamic func originalFunction() -> String {
        return "originalFunction"
    }
    
    dynamic func swizzledFunction() -> String {
        return "swizzledFunction"
    }
}
​
let awesomeObject = AwesomeClass()
​
print(awesomeObject.originalFunction()) // prints: "originalFunction"
​
let aClass = AwesomeClass.self
let originalMethod = class_getInstanceMethod(aClass, "originalFunction")
let swizzledMethod = class_getInstanceMethod(aClass, "swizzledFunction")
method_exchangeImplementations(originalMethod, swizzledMethod)
​
print(awesomeObject.originalFunction())  // prints: "swizzledFunction"
Reference: http://savvyapps.com/blog/swift-tips-for-developers

Wednesday, 27 April 2016

Xamarin evolve 2016 key notes

Xamarin Evolve is the largest cross-platform mobile event in the world, where over 1,500 developers, industry leaders, and Xamarin experts converge to advance the state of the art, discuss mobile strategy, and define the future of apps.
Hyatt RegencyOrlando, FL, April 24–28, 2016
Xamarin includes many awesome features in next release. These are some quick notes:
1. Xamarin introduces the iOS simulator for visual studio developer on Windows OS. From now, Visual studio's fan can develop full mobile platforms: Android, iOS, Windows phone on Windows machine without a Mac to simulate an iOS device.
2. Some important update for Xamarin forms:
  • Xamarin form design preview: help developers view the UI forms design side by side with code. Hopefully, there will be drag and drop feature here in the future :).
  • Xamarin workbook: No need to create new project to experience new code of Xamarin, Workbook will help you all: Write code on the left and view the result immediately on the right side. Similar with playground of iOS but more powerful: It supports write and view UI code.
3. Xamarin UI test recording: Support record UI actions and translate to code then replay to test. This will help save the effort to write the UI automation testing code. This feature is also introduced for OSX, iOS developer by Apple in latest WWDC.

4. Full integrate with Microsoft Azure service: Data entity mapping, offline mode...

5. Support Source control, Test cloud, CI with Visual studio team service
  • Test cloud live: Remote debug with many devices on Microsoft test cloud. This feature is great but I wonder about the responding time and what happen if the connection is not stable.
  • Hockey app: Allow the distributed tester can download and test the application, the owner can views and analysis the test result. This is the same with Apple Testflight.
...
Now, we can use these features in the Alpha channel of Xamarin Studio
Xamarin -> Check for update -> Update channel -> Alpha
I'm trying with it but Xamarin studio often freeze (on Mac). Hope it'll be fixed soon.

Watch live here: https://evolve.xamarin.com/
Summary here:  https://blog.xamarin.com/


Monday, 28 March 2016

Facebook Open Graph Stories - iOS

How It Works

People use stories to share the things they're doing, the people they're doing them with and the places where they happen. Let people share stories about your app on Facebook through a structured, strongly typed API.
To publish Open Graph stories with the Share dialog, you do not need to implement Facebook Login or ask for additional permissions. For more information, see Share Dialog.
If you create a custom sharing UI to publishing Open Graph stories, you need to implement Facebook Loginand request the publish_actions permission from people using your app. This also means you need to submit your app for review, see Login Review. Both of these steps help make sure people using your app are aware of the action they are taking by sharing and helps provide a high-quality experience.
If you create custom Open Graph actions and objects for your app, you should also submit your app for review to ensure your new objects, actions and capabilities associated with actions comply with Facebook policies. See App Review.
Open Graph stories have four basic elements:
  • Actor - The person who posts the story
  • App - Every story includes attribution to the app that created it
  • Action - Activity the actor performs
  • Object - The thing the actor interacts with

Privacy

When you configure an action you should determine the default privacy level. People using the app can choose to override that default privacy level with any privacy level that's more restricted, but not more open.

Review Process

To ensure that all Open Graph stories shared on Facebook are high quality, all apps that want to share stories must go through a review process. Reviews are generally done within three business days.