How to programmatically discover the iOS devices list and Connect for mirroring using airplay?

Tom Sawyer picture Tom Sawyer · Dec 6, 2015 · Viewed 7.9k times · Source

I want to create an application, where user didn't know about the discovery of nearby devices (like iphone, ipad and appletv, etc)and select the only appleTv, if available in nearby then connect atomically and start mirror. For this I read the AirPlay :

Using AirPlay, users redirect audio and video from iTunes or an iOS-based device to either an Apple TV (and from there to a home theater system) or to an AirPlay-enabled sound system. AirPlay can stream media coming live from the Internet, media already stored in iTunes, or media stored on an iOS-based device. AirPlay can stream Internet-based media when it is playing in iOS apps, in the Safari browser on iOS-based devices, or in iTunes on any platform

but i have no need the airplay button, i want to programmatically discover device and connect only with appleTv , after that start mirroring.

I read many blogs and googled a lot, but didn't find anything useful. http://spin.atomicobject.com/2012/04/23/ios-mirroring-and-programmatic-airplay-selection/ gives some explanation, but it's appear.

Please give me the suggesstion, How to achieve this kind of stuff in a great manner.

Answer

Mark Bourke picture Mark Bourke · Jul 4, 2016

Sorry this is superdy duperdy late and you've probably already implemented something else but after DAYS of research and trial and error I have finally got something to work.

A lot of developers have really advanced UI in their applications, and use lots of custom popovers and controllers that provide a very unique look to their application and then have to use the stock AirPlay controller UI which takes the user completely out of the apps experience and can be a complete eyesore.

Others want better UX; ie. Instead of having to separate menus for people to stream their current media to (One for AirPlay and one for everything else), they want to combine the two so the user has a really great experience.

Unfortunately, using the public API's available today, there is really nothing you can do. Spotify tries to make it work in their application, and while better than most approaches, still leaves a lot to be desired. enter image description here enter image description here

However, if for some reason, you do not want to release your app on the app store, you have a good code obfuscator that can hide your function calls from apple (highly unlikely) or you want to distribute your app on another store (Cydia etc.) there is an option for you: Private API's.

Lots of people shy away when they hear those two words but once you get used to the format, it's really the same as normal Cocoa programming minus the Xcode code completion.

Stop Blabbering and just GIMME DE CODEZ

I am not going to introduce you to interacting with Private API's; you can read other articles that will delve much deeper than I would ever be willing to. Without any further ado, this is what I came up with.

Step 1: Read the reverse engineered header files that other much smarter people have posted on github (https://github.com/nst/iOS-Runtime-Headers/blob/master/Frameworks/). It's really worth reading all of the header files because some of the stuff you can do is really interesting.

Step 2: Get down to business. Now, I will be doing this in Swift but I will provide Objective-C links below. I would recommend Objective-C doing this in because most of these classes are written in Objective-C and lots of the methods starting with an "_" swift mistakes for iVars and it just gets really messy. But if you must, heres how you would.

So instead of the conventional way of [class performSelector:methodName] for methods and [instance valueForKey:_iVar] for accessing instance variables it is way easier to use protocols in Swift; mostly because perform selector SUCKS in Swift.

So we are going to basically create a local representation of the classes we will later need to use.

@objc protocol MPAVRoutingControllerProtocol {
    optional func fetchAvailableRoutesWithCompletionHandler(completion: (routes: [MPAVRouteProtocol]) -> Void)
    optional func pickRoute(route: MPAVRouteProtocol) -> Bool
    optional func setDelegate(delegate: NSObject)
}

@objc protocol MPAVRouteProtocol {
    optional func routeName() -> String
    optional func routeUID() -> String
    optional func isPicked() -> Bool
    optional func wirelessDisplayRoute() -> MPAVRouteProtocol
}

@objc protocol MPAudioDeviceControllerProtocol {
    optional func setRouteDiscoveryEnabled(enabled: Bool)
    optional func routeDescriptionAtIndex(index: Int) -> [String: AnyObject]
}

extension NSObject : MPAVRoutingControllerProtocol, MPAVRouteProtocol, MPAudioDeviceControllerProtocol {

}

Now this is really where the magic happens. Somewhere in your view controller, we create the classes using NSClassFromString()

let MPAudioDeviceControllerClass: NSObject.Type =  NSClassFromString("MPAudioDeviceController") as! NSObject.Type
let MPAVRoutingControllerClass: NSObject.Type = NSClassFromString("MPAVRoutingController") as! NSObject.Type

We then create objects from these classes and interact with them by calling on our protocol functions.

let routingController = MPAVRoutingControllerClass.init() as MPAVRoutingControllerProtocol
let audioDeviceController = MPAudioDeviceControllerClass.init() as MPAudioDeviceControllerProtocol

Now that we have fully initialized objects we can do the actual cool stuff. So first thing we need to do is start the daemon for searching for the airplay devices.

audioDeviceController.setRouteDiscoveryEnabled!(true)

This step is optional but recommended. Instead of updating your UI for changes that may or may not be there, we can assign ourselves the delegate of MPAVRoutingController and get a delegate call when devices have become active.

routingController.setDelegate!(self)

We then implement the function.

func routingControllerAvailableRoutesDidChange(controller: MPAVRoutingControllerProtocol) {
}

Then inside this function we can fill up our datasource for our UI.

routingController.fetchAvailableRoutesWithCompletionHandler! { (routes) in

}

So starting off, this method we are calling off the routingController is going to return us an array of MPAVRoute objects that we can extract information about. All the information you should need is in the MPAVRouteProtcol but should you need more you could call audioDeviceController.routeDescriptionAtIndex!(an Index). This will return detailed information such as what type the airplay device is, the mac address, whether it supports video and lots more.

Then when you need to select a route, you would call routingController.pickRoute!(selectedRoute). You don't need to worry about passwords, iOS will take care of that automatically for you (Errors, caching alert controllers etc.).