Thursday, October 22, 2015

iOS 9 Issues

This is just a list for keeping track of iOS 9 related issues I've run into.


1) If you get a list of font families, some of those families do not contain any actual fonts - only seems to affect newer devices.  (reported to apple - marked as dupe)

2) Weird issues with UIView hit test not propagating.   Oddball things where it works everywhere but on an iPhone 6s.  Generally, once recompiled, it works fine.  Actually, I've found that recompiling or changing the hit test code does not work.  In fact, any old style touchesBegan type tap handling does not work on the iPhone 6s.

3) CSS Sibling selectors do not work in certain conditions, for example:

input:checked + label ~ div {
    z-index: 1;
}


changing to:

input:checked + label + div {
    z-index: 1;
}


does work, but doesn't work in iOS 8.   Adding both rules seems to do the trick.

4) Using willRotateToInterfaceOrientation to force a UISplitViewController to refresh the layout no longer works.  Should use  setPreferredDisplayMode instead, as in:

    [sv setPreferredDisplayMode:(hideMaster)?UISplitViewControllerDisplayModePrimaryHidden : UISplitViewControllerDisplayModeAllVisible];

for example.

5) External http calls not working (after iOS 9 recompile).  Need to add

NSAppTransportSecurity
   NSAllowsArbitraryLoads  : YES

to info plist.




Tuesday, October 6, 2015

How to use the UIFocusGuide for navigation

I really didn't quite understand how UIFocusGuides were supposed to work until after several hours of banging my head against the wall.  I think I got it now....

The focus guide, like the layout guide it extends, can be thought of as an invisible view - in this case, an invisible focusable view.

A UIFocusGuide functions as a bridge between different areas of the screen since focus navigation only works up/down or left/right.

Consider a screen that has two objects A and B.  

     AAAA

               BBBB

With the default focus behaviors, there is no way to transition from A to B because B is not directly to the right or bottom.

Thus, to make this work, you'd need a focus guide in between, like


     AAAA

FFFFFFFFFFFFFFFFFFFFF

               BBBB


and we would set the focus guide's preferredFocusedView = B to start.  

So, given this example layout, if view A has the focus, and the user navigates down, it will find the focus guide (F), and the focus guide will then redirect the focus to the guide's  preferredFocusedView, in this case B.

Of course, if B has the focus, you'd want to update the focus guide so its preferredFocusedView is now A, so that when the user navigates up, it goes from B to A.

Okay, now that you get the concept, what's the code look like?  Something like this (Warning: I have not compiled/run this segment):


UIFocusGuide *F;

- (void)viewDidLoad
{
   [super viewDidLoad];
   
   F = [UIFocusGuide new];
   [self.view addLayoutGuide:F];

   [F.topAnchor constraintEqualToAnchor:A.bottomAnchor].active = YES;
   [F.heightAnchor constraintEqualToConstant:1].active = YES;
   [F.leftAnchor constraintEqualToAnchor:self.view.leftAnchor].active = YES;
   [F.rightAnchor constraintEqualToAnchor:self.view.rightAnchor].active = YES;

   [F setPreferredFocusedView:B];
}
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
{

   if (context.nextFocusedView == A){
       [F setPreferredFocusedView:B];
   } else if (context.nextFocusedView == B){
       [F setPreferredFocusedView:A];
   }
}


Apple has a simple example in the UIKitCatalog demo that demonstrates the concept in swift.  The layout in that example case is more like:

AAAAA    BBBBB

CCCCC    FFFFF

and it shows the navigation between B and C via F.

Monday, August 31, 2015

Upload a file to Google Drive via PHP using REST

Disclaimer: I am (at best) a novice PHP coder.  There may well be better ways to accomplish this task, but this is what worked for me.

First off, Google does provide a nice-looking PHP library for working with Drive, but I was reluctant to introduce more libraries into our already complex and fragile server environment.

Using REST, Google's  documentation explains that there are three ways to upload files to Drive - using a media upload, a multipart upload, and a resumable upload. 

For our task, we were working with small spreadsheets so the resumable upload was overkill.

We really wanted to use multipart but gave up after many hours of trying but getting only 400 - Bad Request errors.  We tried using the exact type message that Google detailed in its documentation, but it still didn't work.   (Had I been a better PHP programmer, perhaps I could have figured out what was wrong).

Anyways, we moved on and used a combination of a media POST, to upload the file contents followed by a PATCH, to update the title.  Here's the code:


POSTing the file to upload.  (Assumes you have an authorization token).  The result of the file_get_contents call will be a JSON structure that represents a file resource.


$authstr = 'Bearer '.$token;

$fileToUpload = "datafiles/".$_FILES['uploadedFile']['name'].".csv";

$url = 'https://www.googleapis.com/upload/drive/v2/files?uploadType=media&convert=true'; 

$options = array(
        'http' => array(
            'header'  => "Content-type: application/vnd.ms-excel\r\nAuthorization: ".$authstr."\r\n",
            'method'  => 'POST',
            'content' => file_get_contents($fileToUpload),
        ),
 );
$context  = stream_context_create($options);
$result = file_get_contents($url, false, $context);

$json = json_decode($result);


PATCHing the file to change the title.  We use a different URL here and identify the file using the File Id returned from the media upload POST.  We tell it that we're passing a JSON structure and send along the attributes to change.


$url = 'https://www.googleapis.com/drive/v2/files/'.$json->id;
 

$options = array(
        'http' => array(
            'header'  => "Content-type: application/json\r\nAuthorization: ".$authstr."\r\n",
            'method'  => 'PATCH',
            'content' => '{"title" : "My New Filename"}',
        ),
 );

$context  = stream_context_create($options);

$result = file_get_contents($url, false, $context);
$json = json_decode($result);



Have a better solution?  I'd be interested to hear about it....




Thursday, May 28, 2015

Backing up Cordova databases, local storage, and CDVLocalStorage on iOS

On iOS, Cordova has a complex class, CDVLocalStorage which manages backup behavior via the BackupWebStorage flag.   It was a little hard to understand the gist of what was going on, so I'm describing the general process of how it works here.... Of course, please note that this information has a limited shelf life due to new releases of Cordova and iOS.

Another note is that if you're building with Xcode, the  BackupWebStorage flag is in the config.xml in the Staging area.

Anyways, there are three available values, none, local, and cloud.

When set to none, Cordova does very little and defers to the default iOS behavior which is to store local storage and WebSQL databases in Library/Caches.  This is an area which will not be backed up using iCloud. 

local has the most complex activity.  When set to local, Cordova adds a listener so that when the user exits the app, the databases and local storage will be copied to the Documents/backups directory.  This allows a user plugging their device into their computer and running iTunes backup to save those databases/local storage.  However, to prevent the iCloud backup from copying these files, the Documents/backups directory is marked with the "do not backup" flag.

When BackupWebStorage is set to cloud, the following is set:

[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"WebKitStoreWebDataForBackup"];

This flag causes iOS to move the databases/local storage files from Library/Caches to Library/WebKit.  Once there, the databases/local storage will be picked up by the iCloud backup.


CDVLocalStorage also creates various Cloud/NoCloud directories but it appears that none of these are ever used.