Controlling Screen Orientation of iOS Apps

By default an iOS app will support all possible orientations (landscape, portrait left, portrait right, etc.). When a user rotates the device the app will automatically rotate to fill the screen. In many cases this may not be a desirable behavior. For example, your app may have been designed for portrait mode only. In more complex cases you may have to support different orientations for different screens of the app. For example, a video player must work in either portrait or landscape mode where as the rest of the app only supports portrait.

The process to control how the app behaves when the screen is rotated can be a bit confusing. This article will shed some light on the issue.

Continue reading

Working with NEHotspotHelper

I had to evaluate NEHotspotHelper for a project I am working on. The documentation from Apple is sordid to say the least. Here are a few bits and pieces of information that will help you understand the API better.

Apply for the Network Extension Entitlement

You may already know this but to use the NEHotspotHelper API you will need to apply for the Network Extension Entitlement from Apple. It may take several days for Apple to approve or reject your application.

Modify your Provisioning Profiles

Once you are granted the entitlement, go to iOS developer console and add the entitlement to your developer and deployment provisioning profiles. Many on the Internet will tell you to create a new set of provisioning profiles. That is not necessary. You can add entitlements to existing profiles. Download these modified profiles and re-install them on your development machine.

Add Background Mode

In our case we wanted to use the Wi-Fi Hotspot Authentication API of NEHotspotHelper. This requires the app to run in the background mode. Edit your Info.plist file and add the network-authentication background mode.

Use the Wi-Fi Hotspot Authentication API

The purpose of the authentication API is to implement custom application level logic to authenticate the connection to a WIFI hotspot. This authentication is a two step process:

  1. First connect to the hotspot providing the hotspot’s password.
  2. Then perform custom authentication. For example, this can involve communicating with a server and passing additional credentials.

This process uses a callback based approach. The first thing you do is register a closure or lambda that is invoked at various times with a command object.

let options: [String: NSObject] = [kNEHotspotHelperOptionDisplayName : "Join this WIFI" as NSObject]
let queue: DispatchQueue = DispatchQueue(label: "com.mobiarch", attributes: DispatchQueue.Attributes.concurrent)

NSLog("Started wifi scanning.")

NEHotspotHelper.register(options: options, queue: queue) { (cmd: NEHotspotHelperCommand) in
  NSLog("Received command: \(cmd.commandType.rawValue)")
}

There are three command types that you need to be aware of to perform custom authentication.

  • NEHotspotHelperCommandType.filterScanList – iOS will call your closure with this command type when the user opens the WIFI Settings page. You can obtain a list of all available hotspots from the command. Your code needs to create a filtered list of hotspots that you will like to authenticate with. You also need to set the WIFI password for these hotspots at this point. Once the closure returns, iOS will show the display name below the filtered hotspots. The display name is set using the kNEHotspotHelperOptionDisplayName option as shown in the code above. Note: Filtering does not really remove other hotspots from the list in the WIFI settings page. It is simply meant to identify the networks that your app deems appropriate for authentication.
  • NEHotspotHelperCommandType.evaluate – If the user taps on one of your filtered hotspots and makes a connection the closure is called again with an evaluate command. You can perform custom business logic to evaluate the connected hotspot. If it is appropriate, your code should set a high confidence level for the network.
  • NEHotspotHelperCommandType.authenticate – After the evaluation phase the closure is called again with an authenticate command. Your code can now perform custom authentication logic and respond with a success or failure status. If successful the connection is finally established.

Remember, your callback closure will only be called if the user opens the WIFI Settings page. Also, the user must manually initiate a connection to a hotspot. Your code can not automatically connect.

A sample code for the lambda can look like this.

{ (cmd: NEHotspotHelperCommand) in
    NSLog("Received command: \(cmd.commandType.rawValue)")
    
    if cmd.commandType == NEHotspotHelperCommandType.filterScanList {
        //Get all available hotspots
        var list: [NEHotspotNetwork] = cmd.networkList! 
        //Figure out the hotspot you wish to connect to
        let desiredNetwork : NEHotspotNetwork? = getBestScanResult(list) 

        if let network = desiredNetwork {
            network.setPassword("password") //Set the WIFI password

            let response = cmd.createResponse(NEHotspotHelperResult.success)

            response.setNetworkList([network])
            response.deliver() //Respond back with the filtered list
        }
    } else if cmd.commandType == NEHotspotHelperCommandType.evaluate {
        if let network = cmd.network {
            //Set high confidence for the network
            network.setConfidence(NEHotspotHelperConfidence.high) 

            let response = cmd.createResponse(NEHotspotHelperResult.success)
            response.setNetwork(network)
            response.deliver() //Respond back
        }
    } else if cmd.commandType == NEHotspotHelperCommandType.authenticate {
        //Perform custom authentication and respond back with success
        // if all is OK
        let response = cmd.createResponse(NEHotspotHelperResult.success)
        response.deliver() //Respond back
    }
}

For additional details about the different command types, see the official document.

Facebook Login Using Native iOS API

Introduction

If you want to add Facebook login support to your app, you have two main choices:

  • Use the native account management support built into iOS.
  • Use the API available from Facebook.

In this article I will explain how to use the first option. It has a few advantages:

  • User has to login to Facebook only once from the iOS Settings app.
  • There is no third party library to download and add to your app.

Register Your App

Go to http://facebook.com/developer and register your iOS app. You will need to supply the bundle ID of your app. You can get that from Xcode by selecting your target.

image

After you register the app, you will receive an application ID which is a large integer number.

image

We will need to use this app ID later.

Create the Facebook Account

Launch the iOS Settings app and click Facebook.

images

Log in to Facebook from there to add the account to your device.

Login from Your App

In your view controller’s header file, add two properties.

@import Accounts;

@interface ViewController : UIViewController

@property (nonatomic, strong) ACAccountStore *accountStore;
@property (nonatomic, strong) ACAccount *facebookAccount;

@end

In the view controller class, add this method.

-(void) facebookLogin
{
    NSLog(@"Trying to access FB account.");

    self.accountStore = [[ACAccountStore alloc]init];
    ACAccountType *FBaccountType= [self.accountStore 
        accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];

    NSString *appID = @"1540000000000031"; //The app ID issued by Facebook
    NSDictionary *dictFB = [NSDictionary dictionaryWithObjectsAndKeys:
                            appID, ACFacebookAppIdKey,
                            @[@"email"], ACFacebookPermissionsKey,
                            nil];

    [self.accountStore requestAccessToAccountsWithType:FBaccountType options:dictFB completion:
     ^(BOOL granted, NSError *e) {
         if (granted)
         {
             NSArray *accounts = [self.accountStore accountsWithAccountType:FBaccountType];
             //it will always be the last object with single sign on
             self.facebookAccount = [accounts lastObject];


             ACAccountCredential *facebookCredential = [self.facebookAccount credential];
             NSString *accessToken = [facebookCredential oauthToken];
             NSLog(@"Successfully logged in. Access Token: %@", accessToken);
        } else {
             //Failed
             NSLog(@"error getting permission: %@",e);
         }
     }];
}

Note that the login happens asynchronously. You must save the ACAccountStore object as a property. Otherwise it will go out of scope and get destroyed.

Finally, call the method from viewDidLoad.

- (void)viewDidLoad {
    [super viewDidLoad];
    //Login to Facebook
    [self facebookLogin];
}

External Parameter Name in Swift

I was never a big fan of external parameter names in Objective-C. It leads to too much typing. Example of external parameter names in action shown in boldface below:

[[[UIAlertView alloc] initWithTitle:@"A Message"
  message: @"Hello World"
  delegate:nil
  cancelButtonTitle:@"OK"
  otherButtonTitles:nil] show];

Supporters claim that this leads to better readability. But, I disagree. I have never felt the need for this when coding in C or Java.

Needless to say that I was sad to see external parameter names repeated in Swift. To make things worse, the rules have become inconsistent. I will try to summarize the rules in this article.

Continue reading