Category Archives: Swift

NSURLSession/NSURLConnection HTTP load failed when trying to stream an Office 365 Video on iOS 9

Yesterday, I was playing with Office 365 Video and I wanted to be able to play/stream videos from Office 365 into an iOS app.

The first step to be able to read a video was to retrieve the streaming URL of that video in Azure Media Services (which is used behind Office 365 to offer streaming capabilities).

It’s pretty simple using the Office 365 REST API :

https://tenant.sharepoint.com/portals/hub/_api/VideoService/Channels(guid'01234567-abcd-cded-1234-1234567890ab')/Videos(guid'01234567-abcd-cded-1234-1234567890ab')/GetPlaybackUrl(0)

When you execute that request, you retrieve the URL to play/stream the video from Azure Media Services in HLS format (HTTP Live Streaming). This URL should look something like :

https://cvprdb302v.cloudvideo.azure.net/api/ManifestProxy?playbackUrl=https://cdn-cvprdb302m01.streaming.mediaservices.windows.net/.../Manifest(format=m3u8-aapl)&token=...

To play/stream the video into an iOS app, you can use the built-in video player (AVPlayer) which is compatible with HLS format. To do that, you just have to write few lines of code :

let URL = NSURL(string: "...")  // Put here the URL retrieved from GetPlaybackUrl
let playerViewController = AVPlayerViewController()
playerViewController.player = AVPlayer(URL: URL!)
presentViewController(playerViewController, animated: true, completion: nil)
playerViewController.player?.play()

If you run this code on iOS 8, there’s no problem and everything should work fine. But if you run the same code on iOS 9, it should throw an exception like that :

NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)

You have to wonder why the same code don’t work on the last version of iOS. The answer is quite simple : App Transport Security (ATS).

In iOS 9, there are new security mechanisms that have been implemented to ensure that, when an app uses network communications, the traffic is encrypted to ensure confidentiality.

But if you look the URL of the video, it uses HTTPS so the traffic is encrypted. Then why an exception was thrown ? It seems that the ciphers used to encrypt the traffic are considered as unsafe by iOS 9.

What to do to be able to play/stream the video in that case ? It’s possible to configure how ATS works by modifying the info.plist file of your app.

There are many ways to do that and the most easiest is to add the following key/values :

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSAllowsArbitraryLoads</key>
  <true/>
</dict>

For in-house apps or development purposes, it’s very useful but not secured because ATS is completely disabled. If you read Apple’s documentation, it also could be a reason to reject your app if you want to publish it on the AppStore.

If you want to manage more precisely which URLs can be used without problems with ATS, you have to add exception domains in your info.plist as you can see above :

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSExceptionDomains</key>
  <dict>
    <key>mediaservices.windows.net</key>
    <dict>
      <key>NSIncludesSubdomains</key>
      <true/>
      <key>NSExceptionRequiresForwardSecrecy</key>
      <false/>
    </dict>
    <key>cloudvideo.azure.net</key>
    <dict>
      <key>NSIncludesSubdomains</key>
      <true/>
      <key>NSExceptionRequiresForwardSecrecy</key>
      <false/>
 </dict>
</dict>

With these settings, we configure ATS to allow more ciphers to encrypt HTTPS traffic, for two domains (and all sub-domains) used by Azure Media Services to stream videos.

If you prefer to use the editor available in Xcode to edit your info.plist, rather than manually editing the XML file, you just have to add key/values like :

Office365 Video - App Transport Security

Now if we execute our app, we are able to play/stream the video without any exceptions. 😉

Advertisements

Developing a FileSystemWatcher for OS X by using FSEvents with Swift 2.0

When Swift was announced at WWDC 2014, I was very disappointed to not be able to use some of powerful C APIs such as FSEvents, to write applications in pure Swift language.

A lot of C APIs needs a C function pointer to pass a callback function when you use the API. From Swift 1.0 to Swift 1.2, it was not possible to use C function pointer in pure Swift language.

It was possible to use FSEvents by wrapping it in an Objective-C class, and by calling this class in Swift by using the bridging features offered by the language, but it was not what I attempted to accomplish.

But things change and at WWDC 2015, Apple announced the possibility to use C function pointers with Swift 2.0.

Today I’m very pleased to give you a sample code which is a good starting point to create your own FileSystemWatcher using the FSEvents API written with Swift 2.0.

import Foundation

public class FileSystemWatcher {

    // MARK: - Initialization / Deinitialization
    
    public init(pathsToWatch: [String], sinceWhen: FSEventStreamEventId) {
        self.lastEventId = sinceWhen
        self.pathsToWatch = pathsToWatch
    }

    convenience public init(pathsToWatch: [String]) {
        self.init(pathsToWatch: pathsToWatch, sinceWhen: FSEventStreamEventId(kFSEventStreamEventIdSinceNow))
    }
    
    deinit {
        stop()
    }

    // MARK: - Private Properties
    
    private let eventCallback: FSEventStreamCallback = { (stream: ConstFSEventStreamRef, contextInfo: UnsafeMutablePointer<Void>, numEvents: Int, eventPaths: UnsafeMutablePointer<Void>, eventFlags: UnsafePointer<FSEventStreamEventFlags>, eventIds: UnsafePointer<FSEventStreamEventId>) in
        print("***** FSEventCallback Fired *****", appendNewline: true)
        
        let fileSystemWatcher: FileSystemWatcher = unsafeBitCast(contextInfo, FileSystemWatcher.self)
        let paths = unsafeBitCast(eventPaths, NSArray.self) as! [String]
        
        for index in 0..<numEvents {
            fileSystemWatcher.processEvent(eventIds[index], eventPath: paths[index], eventFlags: eventFlags[index])
        }
        
        fileSystemWatcher.lastEventId = eventIds[numEvents - 1]
    }
    private let pathsToWatch: [String]
    private var started = false
    private var streamRef: FSEventStreamRef!
    
    // MARK: - Private Methods
    
    private func processEvent(eventId: FSEventStreamEventId, eventPath: String, eventFlags: FSEventStreamEventFlags) {
        print("\t\(eventId) - \(eventFlags) - \(eventPath)", appendNewline: true)
    }
    
    // MARK: - Pubic Properties
    
    public private(set) var lastEventId: FSEventStreamEventId
    
    // MARK: - Pubic Methods
    
    public func start() {
        guard started == false else { return }
        
        var context = FSEventStreamContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
        context.info = UnsafeMutablePointer<Void>(unsafeAddressOf(self))
        let flags = UInt32(kFSEventStreamCreateFlagUseCFTypes | kFSEventStreamCreateFlagFileEvents)
        streamRef = FSEventStreamCreate(kCFAllocatorDefault, eventCallback, &context, pathsToWatch, lastEventId, 0, flags)
        
        FSEventStreamScheduleWithRunLoop(streamRef, CFRunLoopGetMain(), kCFRunLoopDefaultMode)
        FSEventStreamStart(streamRef)
        
        started = true
    }
    
    public func stop() {
        guard started == true else { return }

        FSEventStreamStop(streamRef)
        FSEventStreamInvalidate(streamRef)
        FSEventStreamRelease(streamRef)
        streamRef = nil

        started = false
    }
    
}

I hope this will help you in your future applications and feel free to share this article if you found it helpful 😉

Developing Yammer apps for iOS/OS X with ADAL, REST API and Swift 2.0

You’re interested to develop applications on top of Yammer (Microsoft’s Enterprise Social Network) but you’re currently working on iOS/OS X devices ? No problem 😉

Yammer offer to developers a REST API to allow them build applications on top of the product. Microsoft has also started to move the authentication mechanism to Office 365, to let people to use the same credentials as for other services in their company (Outlook, SharePoint, Skype for Business…).

In this article, we’ll discover what are the main steps to create our first application which will use the Yammer REST API and Office 365 authentication, in conjunction with Swift 2.0, the new programming language of Apple platforms, updated during the WWDC 2015.

Declare your application in Azure Active Directory

The first step to accomplish to be able to develop an application which will call the Yammer REST API with the Office 365 authentication (based on Azure AD), is to declare your application in Azure.

To do this, you need to be a global administrator of your tenant, and you can achieve this by using the Azure Management Portal : https://manage.windowsazure.com

On the Azure Management Portal, go to Active Directory section, select the appropriate directory, select applications and create a new native application.

After you fill the requested information (a name and a redirect URL), you have to declare for each service (SharePoint, Exchange, Active Directory…) what permissions are needed by your application as you can see below.

Few weeks ago, Microsoft has released a new permission which allows you to use the Yammer REST API (it’s a preview for now). So just add it to your application (as you can see below) and save your modifications.

Office 365 Yammer - Azure AD Permissions

Authenticate with ADAL

The second step to develop your application is to acquire an authentication token from Azure AD and to pass it to the Yammer REST API when you want to retrieve data.

The simplest way to acquire an authentication token from Azure AD is to use the Azure Active Directory Library (aka. ADAL) which can be downloaded for free on GitHub : https://github.com/AzureAD/azure-activedirectory-library-for-objc

After downloading the library and integrating it in your application (cf. documentation on GitHub on how to achieve this goal), you can acquire a token with just few lines of code.

var authError: ADAuthenticationError?
var authContext = ADAuthenticationContext(authority: "https://login.windows.net/common", error: &authError)
var bearerToken: String!

authContext.acquireTokenWithResource("https://www.yammer.com/", clientId: "...", redirectUri: "...", completionBlock: { (result: ADAuthenticationResult!) in
    guard (authError == nil) && (result.accessToken != nil) else {
        // Authentication Error
        return
    }

    bearerToken = result.accessToken
}

The Swift code above is pretty simple to understand :

  • We declare an authentication context
  • We call the acquireTokenWithResource method with required parameters (clientId and redirectUri are the same you filled in step #1)
  • If the token was successfully acquired, we store it in a variable to use it later

Use the Yammer REST API

Now that we have a valid authentication token, we are now able to use the Yammer REST API. If you need some information about all the capabilities available in that API, you can refer to the official documentation : https://developer.yammer.com/v1.0/docs/userscurrentjson

In Swift, we are able to use all the frameworks (Foundation, AppKit, UIKit…) available on iOS/OS X and which allow us to perform advanced operations in a simple manner.

To perform HTTP communications, we can use the NSURLSession class. With just few lines of code, we are able to send a request to a server (synchronously or asynchronously) and to retrieve the response.

let URLSession = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let request = NSMutableURLRequest(URL: NSURL(string: "https://www.yammer.com/api/v1/users/current")!)
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue(bearerToken, forHTTPHeaderField: "Auhtorization")
let task = URLSession.dataTaskWithRequest(request) { (data: NSData?, response: NSURLResponse?, error: NSError?) in
    guard (data != nil) else {
        // No data returned
        return
    }
    processResult(data)
}
task?.resume()

The most important part in the code above, to authenticate the request, concerns the addition of an Authorization header to the request. The header is a concatenation of the token retrieved in step 2 with the Bearer keyword such as “Authorization: Bearer our_authentication_token“.

If the request is successfully executed (it returns data), then we call the processResult method by passing the retrieved data as parameter.

Deserialize JSON response and catch errors

The last step to perform in our application is to parse the retrieved data. Because we have passed an Accept header to the previous request to indicate that we want to retrieve the data as JSON, we just have to deserialize and to use them.

Once again thanks to Foundation which expose a class (NSJSONSerialization) to perform this operation.

If you used Swift 1.x during the last year, focus your attention on the code below. A brand new way to deserialize and catch potential errors has been introduced with Swift 2.0 (do … catch).

func processResult(data: NSData?) {    
    do {
        let JSONObject = try NSJSONSerialization.JSONObjectWithData(data!, options: [])
        print(JSONObject, appendNewline: true)
    } catch let error as NSError {
        print(error, appendNewline: true)
    }
}

In the code above, we just print the deserialize object so it should display something like that :

{
    "activated_at" = "2015/01/01 01:01:00 +0000";
    admin = true;
    "birth_date" = "";
    "can_broadcast" = true;
    "can_browse_external_networks" = 1;
    "can_create_new_network" = 1;
    contact = { ... };
    department = Direction;
    email = "...";
    expertise = "Office 365 REST API, SharePoint, iOS, Objective-C, Swift";
    "external_urls" = ();
    "first_name" = "Stéphane";
    "follow_general_messages" = 0;
    "full_name" = "Stéphane Cordonnier";
    guid = "<null>";
    id = 123456789;
    interests = "<null>";
    "job_title" = "Technology Addict";
    "kids_names" = "<null>";
    "last_name" = Cordonnier;
    location = Paris;
    ...
}

If you want to use and parse the deserialized object, it’s a key/value dictionary so it’s pretty simple to use it.