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

Sunday, July 7, 2013

How to get apple developer provision profile id

When each time the provision profile is updated in apple developer portal website, downloading the profile will get a new id. If the id does not match, the build will fail because of the signing error saying "the provision profile XXXXXX cannot be found".
However, it seems xcode organizer does not show the profile id for available profiles for identitying the signing error. Neither the Apple developer portal will show this profile id when downloading the profile.
Fortunately, the provision profile id is shown on the iphone configuration tool. So it can be used to find out which profile has which id required by signing.

xcode build settings for debug symbol

Generate debug symbol 
The debug symbol for xcode project is controlled by project settings of "Apple LLVD compiler 4.2"/"generate debug symbol". The recommendation is setting it to YES for both debug and release build. So that it can be used for crash report symbolization.
For application, this will generate a separate dsym file in build output folder. For static library, this will only increase the size of the generated a file

Deployment postprocessing
This is a generic control to decide whether other deployment operation should be performed, particular, Strip Linked Product is executed or not. 
For static library, the recommended setting is NO for both release and debug build, to not remove symbols.
For application build, the recommended setting is Yes for release build and No for debug build. So that release build can have a smaller size.

Strip Debug Symbols During copy
This flag decides when copying the dependent library for building the project, should the library's symbol be removed. It does not affect the build output binary. 
The recommended setting is set to NO for both release and debug build, as the build output needs to include the dependent libraries' symbol to debug or symbolize the library code. 
(It seems for ios project, this setting does not have any effect.)

Strip linked product
This flag will reduce the size of the executable by removing the symbol from it. But it will cause the crash dump not have any symbol, and it will need a separate symbol file to symbolize the crash report.
The flag will not change sym file size.
For library, the recommended setting is NO, so it includes the debug symbol in the .a file
For application, if the app size matters, set it to YES. otherwise, set it to NO.