Sunday, September 7, 2014

How to add a child pane (page) in iOS setting app

The following steps can be used to to add a child pane (page) in iOS settings app for your application:
1. open settings.bundle, select the item after which you want to add the new item to open the child pane.
2. select "Editor->Add Item" menu to add a new dictionary item
3. expand the new dictionary item, change the type item to "child pane"
4. change the title item to the text you want to display
5. add a new filename item, set it to the plist file name, for example "mychildsettings"
6. open the folder of settings.bundle in Finder, then show the content of the settings bundle
7. copy setting.plist and renamed the copied file to mychildsettings.plist
8. open mychildsettings.plist in xcode and update its content to the settings you want to show in the child pane

When using NSUserDefault interface to get and set the item value defined in child pane page, there is no difference as the item defined in main settings plist file. Just need to specify the key to get and set the item value. Similarly, using registerDefault method also has no difference on the items.

The only small difference is if you need to read the default value defined in the child plist file for a item, you will need to specify the plist file name as below

NSDictionary *settingsInfo = [NSDictionary dictionaryWithContentsOfFile:[
                                                                             [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"] stringByAppendingPathComponent:@"mychildsettings.plist"]];

NSLog(@"%@", settingsInfo);

Note the key in NSUserDefault is case sensitive.

Monday, September 1, 2014

Understanding Entity Framework Code First

The feature of Code First for Entity Framework will allow developer to write code to manage data in database without first creating the table in the database. Although it is helpful in some case, it may also cause confusion if you are not sure what actually happened behind the door.

After the Entity Framework is added into the project, and the models are defined. You will need to create a dbContext class to define the dataset to expose the model as below
       [Table("Books")] // Table name
        public class Book
        {
            [Key] // Primary key
            public int BookID { get; set; }
            public string BookName { get; set; }
            public string ISBN { get; set; }
            public float price { get; set; }
        }

  public class BookDbContext :DbContext
    {
        public DbSet<Book> Books { get; set; }
        public DbSet<Review> Reviews { get; set; }
    }.

That is all required for Entity Framework to automatically create the database table with the proper schema. Note you do not need to define the connection string for the database, Visual Studio will automatically select the database based on the following rules:
  • If a local SQL Express instance is available (installed by default with Visual Studio 2010) then Code First has created the database on that instance
  • If SQL Express isn’t available then Code First will try and use LocalDb (installed by default with Visual Studio 2012)
  • The database is named after the fully qualified name of the derived context, in our case that is WebApplication1.BookDbContext

To verify this, run   command in Package Manager Console
Enable-Migrations
add-migration init
update-database

And then connecting to SQLExpress or localDB's database WebApplication1.BookDbContext, you will see the table scheme is automatically created.

At this moment, you can also update configuration seed method to create the initial data, once the code is added, just run "update-database" again will populate the record in the table.

At anytime, if you change the model class public property (scheme), you can run the below command again to update the database. Note seed method is run with the newschema update, so if you do not want to use the seed method to add the initial record again, then you will need commenting out the code in seeding method
add-migration myadditionchange
update-database


In certain cases, the database may already exist, and you only need to use code first to create the model table, if so, you will need to add a connectionstring in web.config under configuration section
<connectionStrings>
    <add name="BookContext" connectionString="Data Source=JOHN-PC\SQLEXPRESS;Initial Catalog=WebApplication1.BookDbContext;Integrated Security=True"
      providerName="System.Data.SqlClient" />
  </connectionStrings>

The name is used by contextdb's base class constructor to find the right connection string to use.
public BlogDbContext() : base("name=BookContext") 
        { 
        } 

Note if you get anything wrong during migration, you can always use 
enable-migrations -force
to reset the migration.
 
 

Sunday, August 17, 2014

ios block, crash and retain cycle

Block is just like other objective c object, it can be retained if a variable strong refer to it.

When block is used as method argument, the block will retain or keep a strong reference to all the object variables used within the block. Usually, after the block is executed, the block will be released and then the block will also release all the variables it refers to within the block implementation.

This may cause retain cycle, if any variable referred by the block also retain the block itself. As quite ofter, the block is used as a completion handler and saved as a property for later use. The result is, the main (self) object keeps a reference to the block as a property, and the block keeps a reference to the main (or self) object automatically when the main or self object, or any of its property or instance member is used within the block. So even if all external references have been set to nil for the main object, the object by itself can keep alive as long as the property that refers to block is not set to nil.

One of the solution is using __block declaration. But it may cause other troubles. As when __block is used, block will not automatically retain the object to keep it alive, so if the object is no longer be referred in any other place, it will be release and as a result, when block is executed, the object may become nil. If the application does not handle the case, it may cause crash at runtime. Similarly, create a weak reference variable just before creating the block and then use the weak variable within the block works in the same way.

Note, when __block is not used, it only prevents the object itself to be changed within block, but data of the object, such as its properties or instance member can still be change as usual. 

Install server certificate to iOS real device using web server

Usually it requires to send an email or create mobile provision file to put a server certificate file (.cer) file into ios device for trusting https connection. But if you have a web server available, it is easier to install the server certificate from the web server.

To do so, just rename the .cer file to p12 file or convert the .cer file to .der file, and put it it a web server folder. Then access the file from web url from the device mobile safari, it will automatically prompt you to install the server certificate into device profile

The same way can also be used to install server cert to ios simulator. As with XCode 6, for ios 7/7.1 simulator, it is no longer supported to just drag and drop the certificate to simulator screen. The drag and drop still works on ios 8 simulator.

Thursday, August 14, 2014

Behavior of ios cache on NSURLConnection and UIWebView

iOS provides NSUrlCache for application to manage cache behavior, but this class is more for native connection with NSUrlConnection and NSURLSession, if the application involves UIWebView request, then using NSURLCache may not get expected behavior. As the cache behavior in UIWebView does not go through the NSUrlCache API.

To verify this, a simple testing app is created to send both xmlhttprequest in UIWebView and native NSURLConnection request, it also subclass the default NSUrlCache to get more log and control for the testing. The web server will return a response with Cache-control header of "max-age=1000", so as to be sure the cache is valid.

1. NSURLConnection request:
When sending native NSURLConnection request, response is cached for the first request, in the following requests, [NSURLCache cachedResponseForRequest] is always called to return the cached response, and the request does not go to server as expected.

If [NSURLCache RevmoeAllCachedRespones] is called, the cache will be cleared, and the next request will go to server to get response

If [NSURLCache StoreCacheResponse] is disabled to never store response, then each request will go to server, as no cache is available on client side.

2. WebView xmlhttprequest
When sending xmlhttprequest from uiWebView, response is cached for the first request, however, for the following request, the cached response will be loaded automatically without invoking [NSUrlCacah cachedResponseForRequest] method, basically, UIWebView handles the cache by itself without NSUrlCache.

If  [NSURLCache RevmoeAllCachedRespones] is called, when sending the xmlhttprequest, it can still get the cached response from somewhere without going to server.

If [NSURLCache StoreCacheResponse] is disabled to never store response, it does not affect the xmlhttpRequest for uiWebView, it can still get cached response with going to server

3. NSUrlProtocol
This behavior also affects NSUrlProtocol if it is used to cached request from UIWebView, if a xmlhttpRequest from uiWebView is already cached, then the uiWebView will directly load the cached response without invoking the canInitWithRequest method, so the NSUrlProtcol does not even get a chance to handle the request.

In addition, for requests sent from uiWebView xmlhttprequest, even if willCacheResponse method to not allow NSUrlProtocol's NSUrlConnection to store any cache, the next request can still get cached response directly without canInitWithRequest be called. Also, calling [NSURLCache RevmoeAllCachedRespones] has no effect on the cached data by xmlhttprequest, it can still load cached response successfully.

Based on the above testing, the NSURLCache API only works as expected for native NSUrlConnection function. It does not work for xmlhttpRequest sent from UIWebView.



Monday, August 11, 2014

Fix build link error when change target from ios 6.0 to ios 7.0/7.1

After converting an ios project, an link error happens related to c++ std library as shown below:

"std::basic_filebuf<char, std::char_traits<char> >::is_open() const"
"std::basic_stringbuf<char, std::char_traits<char>, std::allocator<char> >::str() const"
"std::basic_ios<char, std::char_traits<char> >::widen(char) const"
"std::istream& std::istream::_M_extract<double>(double&)"

In order to fix the issue, open ios project library setting and add "libstdc++.6.0.9.dylib" to link Binary With Libraries section.

Sunday, July 27, 2014

Understanding IBOutlet and IBAction in InterfaceBuider

In XCode interface builder, when adding a UI element in storyboard, it only creates an UI element, for example UITableView or UILabel, or UIViewController, and it does not give the element reference to be accessed by external module.

However, each of these class can define property, method and IBOutlet & IBAction for external access. Actually, the IBOutlet is just a regular property, the only extra thing IBOutlet does is telling Interface Builder that this property is available to associate with other instance within the current uiViewController scope at design time, so developer can make the association easily in interface builder between IBOutlet and other elements.

So IBOutlet is only meaningful within InterfaceBuilder's UIViewController context so as to allow design time association. Similarly, the Referencing outlet shows the current UI element instance is referenced by all other instances' outlet property.

Note, you can add any object into interface builder, and they will be allocated at runtime by interface builder, and any IBOutlet can associate with those arbitrary object, even if they are nothing to do with UIView.