Tuesday, October 29, 2013

Compare files with xcode utility on mac

Xcode includes an utility called fileMerge.app to compare files on mac.
To use the utility, open Finder and then open the content of XCode.app package, goto /Applications/Xcode.app/Contents/Applications folder, you should see the fileMerge.app.

Since it is not convenient to always do so to use this utility, you can add a shortcut to this app in your application folder. To do so, right click FileMerge.app and select "Make Alias" menu item, it will create an alias item for the app, then copy the alias item to your application folder, and rename it to FileMerge.app for future use.

The utility can also be opened from xcode by following steps

Start xcode, click menu open developer tool, and select the following menu items:
1. start xcode
2. select menu item from "xcode->Open developer tools->File Merge"



Sunday, October 27, 2013

Choose networking API for ios application implementation

There are three networking API types available to implement the network communication function for ios apps.

Foundation API: This is the objective C API, it provides a set of high level NS classes to handle the network connection. This should be your first choice as it is the easiest way to send and receive the data between server and request.  The related document is "URL Loading System Programming Guide"

Core Foundation API: This is a set a C APIs from core foundation framework. These APIs include similar functions as Foundation classes, but provide more control over the network behavior, such as overriding the proxy settings. It should be used if the application cannot use objective C, or the application needs to override the proxy settings on device. The related document is "CFNetwork Programming Guide"

Socket API: This is lower level network C APIs, it should only be used if the application implements its own network protocol other than Http(s) or Ftp. It is discouraged to be used in ios app because it does not activate the cellular radio. The related document is "Networking Programming Topics"

Note for network security testing, the behavior is different when testing on ios simulator or real device. If a certificate is installed on ios 7 simuator system by drag and drop a cert on simulator, the cert is available when querying the application keychain. However, on real device, the certificate installed by configuration profile is not visible when application querying its keychain certificate.

Thursday, October 17, 2013

Understanding Apple developer portal: AppID, certificate, devices and provision profile

Certificate - to identify who creates the app:
When you build the ios app, you need to sign it with your private key, similar as signing https data. To generate a certificate, you will need first create a certificate request, and submit it to Apple portal. Apply will sign the public key with Apple certificate, so the public key becomes a certificate. What shows on the certificate tab on apple portal web site is the certificate signed by apple developer root certificate. The private key should always kept on your mac book.
In a word, the certificate originated from your developer mac book and it is the public key signed by Apple.

What the certificate tab in apple account shows is the certificate (public key part) of your certificate. If you open the keychain utility in your mac, you should see the certificate with the private key for the signing certificate. Xcode will use the private key to sign the application. And the devices that contains the certificate (public key) can verify the integrity of the application
  
In addition to developoer signing certificate, there is another push certificate for sending push notificate from your local server to apple APNS service. This push certificate is used to identify your server to connect to apple site. Similar to signing certificate, you create a Certificate Signing Request from your local server and upload it to Apple Development portal, after Apple sign it, you can download it and use it to initialize the APNS connection from your server application to apple APNS service. Note push certificate is associated with appid, so on apple developer portal site, you need first select an App ID to enable and create the push certificate. You can create two push certificate for each appid, development and distribution push certificates, if you app is signed with developer signing certificate, then using development push certificate If you app is signed with distribution signing certificate, then using distribution push certificate. Note after the push certificate is generated, it can be used on any box, (not just the box generated the signing request), as it only needs to identify which apple dev account generates it, instead of which server sends the request.

Device - to indicate who can run the app
You can collect the device ids from xcode's organizer, and then add it into the device tab in apple portal site. All devices added will have the permission to run the application created by you.

AppID - to identity an application in app store
AppID contains two parts: seeding id and bundle id
Seeding id is used to share keychain information between multiple apps created by you
Bundle id is unique for each of your app, it is also used by app push service and iCloud storage. Wildcard * can be used in the bundle it to match multiple apps. Although wildcard cannot be used for push service.
These information is used by Apple to identity your application and assign the service to it. 

Provision Profile
Profile is the piece that joins certificate, appid, devices (for developer profile) settings, you need those settings to create a profile. 

There are two kinds of profiles:
Development profiles: These are tied to specific devices, so the app can only run on those devices.
Distribution profiles: These are used to sign your app before you submit it to Apple for approval. They contain no device-specific information, but you can’t use them to install apps on any device yourself, because Apple still has to sign the app after the approval process. 
There is a special distribution profile called ad hoc provision profile, it can be created similar as apple store distribution profile, but used for internal tester. To run the ad hoc build on a test device, the application must be built with ad hoc provision file, and the device is included in ad hoc device list. The device also needs to have the ad hoc provision file installed in order to run the application with ad hoc build. 

After downloading the profile from apple developer portal to your mac book, double click it and you should see it installed on your xcode's organizer/Library section.
Note you also need to install the provision file on the device in order to run the app on device. If you deploy the app from Xcode to device, it will automatically deploy provision file also. Otherwise, just drag and drop the provision file from xcode organizer\Library\Provisioning profile to connected device's Provision Profiles section.

Friday, September 20, 2013

Steps to setup Apache reverse proxy on Window 7

1. Download Apache web server for Windows binary and install it.
2. In httpd.conf, update the listening port number
Listen 7000
3. uncomment the following lines to load the required modules
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule ssl_module modules/mod_ssl.so
4. enable virtual host by uncomment the following line
Include conf/extra/httpd-vhosts.conf
5. Edit : conf\httpd-vhosts.conf, for example, the following settings can be used to redirect request
from
torn004613401a.dhcp.tor2.sap.corp:7000/proxy/entity/odata
to
torn00461340a.dhcp.tor2.sap.corp/entity/odata, 


NameVirtualHost *:7000
<VirtualHost *:7000>
    ServerName torn00461340a.dhcp.tor2.sap.corp
    ServerAlias dymmyhost
    ErrorLog "logs/dummy-host.dhcp.tor2.sap.corp-error.log"
    CustomLog "logs/dummy-host.dhcp.tor2.sap.corp-access.log" common
    ProxyPass /proxy/ http://torn00461340a.dhcp.tor2.sap.corp/
    ProxyPassReverse /proxy/ http://torn00461340a.dhcp.tor2.sap.corp/

</VirtualHost>

Note extra steps are required to handle cookie path and domain and https connection.

Wednesday, September 4, 2013

Steps to create a CRUD oData service with ASP.Net Web API

Quite a lot of ASP.Net Web API shows how to create a read-only oData service, which is not very useful in real project, the following steps can be used to create a full CRUD webapi odata service that can run on iis. The project is inspired by pluralsight's ASP.Net oData training project as well as other online help information.

Tools:
VS 2012 on Windows 7

1. Create a new MVC 4 project
2. Add Entity Framework and import Northwind database, use Windows integrated mode for database connection
3. Add WebApi oData package using NeGet, the ApiController does not work with circular json serialize, so it has to use EntitySetController to create controller class.
4. update WebApiConfig.cs as follows
        public static void Register(HttpConfiguration config)
        {
            config.Routes.MapODataRoute("Northwind", "odata", GetImplicitEDM());

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
           ....
        }

        private static IEdmModel GetImplicitEDM()
        {
            ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
            var customers = builder.EntitySet<Customer>("Customers");
            builder.EntitySet<Order>("Orders");
            builder.EntitySet<Shipper>("Shippers");
            return builder.GetEdmModel();
        }
    }

5. create a new CustomersController class, the name needs matching the EntitySet name.
public class CustomersController : EntitySetController<Customer, string>
{
   NORTHWNDEntities _context = new NORTHWNDEntities();

        [Queryable]
        public override IQueryable<Customer> Get()
        {
            return _context.Customers;
        }

        protected override Customer GetEntityByKey(string key)
        {
            return _context.Customers.FirstOrDefault(c => c.CustomerID == key);
        }

        [Queryable]
        public IQueryable<Order> GetOrdersFromCustomer([FromODataUri] string key)
        {
            return _context.Orders.Where(o => o.CustomerID == key);
        }

        protected override string GetKey(Customer entity)
        {
            return entity.CustomerID;
        }

        //sample request form fiddler
        //header
        //Content-Type: application/json
        //payload
        //{"CustomerID":"ABCDE","CompanyName":"ABCDE","ContactName":"ABCDE","ContactTitle":"ABCDE"}
        protected override Customer CreateEntity(Customer entity)
        {
            _context.Customers.Add(entity);
            _context.SaveChanges();
            return entity;
        }

        //sample request from fiddler, only specified fields from client will be set to the new objects, all other
        //fields will be set to null
        //url
        // put for http://localhost:58317/odata/Customers('ABCDE')
        //header
        //Content-Type: application/json
        //payload, customerid field will be ignored
        //{"CustomerID":"AAAA","CompanyName":"AAAA"}
        protected override Customer UpdateEntity(string key, Customer update)
        {
            if (!_context.Customers.Any(c => c.CustomerID == key))
            {
                throw new HttpResponseException(
                    Request.CreateODataErrorResponse(
                      HttpStatusCode.NotFound,
                      new ODataError
                      {
                         ErrorCode = "EntityNotFound",
                         Message = "Customer key " + key + " not found"
                      }));
            }

            update.CustomerID = key;
            _context.Customers.Attach(update);
            _context.Entry(update).State = System.Data.EntityState.Modified;
            _context.SaveChanges();
            return update;
        }

        //Similar to post but keeping the existing fields in object if client side does not specify them
        protected override Customer PatchEntity(string key, Delta<Customer> patch)
        {
         
            var customer = _context.Customers.FirstOrDefault(c => c.CustomerID == key);

            if (customer == null)
            {
                throw new HttpResponseException(
                    Request.CreateODataErrorResponse(
                      HttpStatusCode.NotFound,
                      new ODataError
                      {
                          ErrorCode = "EntityNotFound",
                          Message = "Customer key " + key + " not found"
                      }));
            }

            patch.Patch(customer);
            _context.SaveChanges();
            return customer;
        }

        //sample request from fiddler, only specified fields from client will be set to the new objects, all other
        //fields will be set to null
        //url
        //delete for http://localhost:58317/odata/Customers('ABCDE')
        //header
        //Content-Type: application/json
        public override void Delete([FromODataUri] string key)
        {

            var customer = _context.Customers.FirstOrDefault(c => c.CustomerID == key);

            if (customer == null)
            {
                throw new HttpResponseException(
                    Request.CreateODataErrorResponse(
                      HttpStatusCode.NotFound,
                      new ODataError
                      {
                          ErrorCode = "EntityNotFound",
                          Message = "Customer key " + key + " not found"
                      }));
            }
            _context.Customers.Remove(customer);
            _context.SaveChanges();
        }


        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            _context.Dispose();
        }
}

6. Start the app on IIS express, it should handle the oData request. But it will fail when running on IIS, continue the following steps to run it on IIS
7.Restart VS 2012 in admin mode, and change the project setting to create IIS virtual directory and use IIS for testing
8. Edit C:\Windows\System32\inetsrv\config\applicationHost.config for the following part
<add name="ASP.NET v4.0" autoStart="true" managedRuntimeVersion="v4.0" managedPipelineMode="Integrated">
<processModel identityType="ApplicationPoolIdentity" loadUserProfile="true" setProfileEnvironment="true" /></add>

(refer to http://blogs.msdn.com/b/sqlexpress/archive/2011/12/09/using-localdb-with-full-iis-part-1-user-profile.aspx and http://blogs.msdn.com/b/sqlexpress/archive/2011/12/09/using-localdb-with-full-iis-part-2-instance-ownership.aspx for details if you still get database connection error).

Tuesday, September 3, 2013

Some note about Linq, WCF, EntityFramework, WebApi, oData

The key to understand the syntax of linq, is it is built based on IEnumerable, so everything is based on a single element from the IEnumerable collection, if you see a variable declaration coming out of nowhere, then you can think of it as a instance of a IEnumerable instance. This is particular useful to understand Lambda syntax

WCF provides an easy way to implemenat oData service, but it is used mainly for .net client. If you create a oData service to support ios, Android devices, then it is better to create the oData service with WebApi, developer has better control over web API then WCF's customization.

EntityFramework helps to expose the backend data as objects, but when using WebApi, it is not very stable to serialize the dependent objects included in the foreign key field, the easy solution is just returning the foreign key field as a simple field data instead of returning it as a child  object. As for mobile client, it is not proper to return all objects included in object mapping, if client needs dependent objects, it is better to let client send another separate request.

When using EntitySetController to build oData endpoint, be sure the controller name matches the entity name, otherwise the resource cannot be found:

  private static IEdmModel GetImplicitEDM()
        {
            ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
            builder.EntitySet<Customer>("Customers");
            builder.EntitySet<Order>("Orders");
            builder.EntitySet<Order_Detail>("OrderDetails");
            return builder.GetEdmModel();
        }

public class CustomersController : EntitySetController<Customer, string>
   

Fiddler: Set username and password for basic authentication

1. open fiddler, click Tools->TextWizard... menu item,
2. select checkbox of "To Base64"
3. input your user name and password in top textbox such as
myUsername:myPassword
4. Go back to fiddler composer screen and add a header of below, the last past is the output of
Authorization: Basic bXlVc2VybmFtZTpteVBhc3N3b3Jk