Errors while implementing Chromecast in swift 3 - ios

I seem to have a problem implementing ChromeCast features in a project of mine.
I have been trying to implement the GCKDeviceScannerListener Singleton Class on a UIViewController, however its delegate methods are not getting called.
The deviceDidComeOnline method of GCKDeviceScannerListener never gets called.
Instead I have a bunch of error displayed by the chromeCast logger as followed:
+[NSMutableDictionary(GCKAdditions) gck_loadFromCacheWithName:] - Device cache file file:///Users/martin/Library/Developer/CoreSimulator/Devices/318D2E15-C4B0-47D2-97AF-CD560A6063AE/data/Containers/Data/Application/C117BB98-88DA-4586-B119-0683DAD82FEB/Library/Caches/gck_nearby_devices.plist doesn't exist.
+[NSMutableDictionary(GCKAdditions) gck_loadFromCacheWithName:] - Device cache file file:///Users/martin/Library/Developer/CoreSimulator/Devices/318D2E15-C4B0-47D2-97AF-CD560A6063AE/data/Containers/Data/Application/C117BB98-88DA-4586-B119-0683DAD82FEB/Library/Caches/gck_network_cache.plist doesn't exist.
scanning started
+[NSMutableDictionary(GCKAdditions) gck_deleteCacheWithName:] - Device cache file file:///Users/martin/Library/Developer/CoreSimulator/Devices/318D2E15-C4B0-47D2-97AF-CD560A6063AE/data/Containers/Data/Application/C117BB98-88DA-4586-B119-0683DAD82FEB/Library/Caches/gck_device_cache.plist doesn't exist.
+[NSMutableDictionary(GCKAdditions) gck_deleteCacheWithName:] - Device cache file file:///Users/martin/Library/Developer/CoreSimulator/Devices/318D2E15-C4B0-47D2-97AF-CD560A6063AE/data/Containers/Data/Application/C117BB98-88DA-4586-B119-0683DAD82FEB/Library/Caches/gck_device_cache_v1.plist doesn't exist.
+[NSMutableDictionary(GCKAdditions) gck_deleteCacheWithName:] - Device cache file file:///Users/martin/Library/Developer/CoreSimulator/Devices/318D2E15-C4B0-47D2-97AF-CD560A6063AE/data/Containers/Data/Application/C117BB98-88DA-4586-B119-0683DAD82FEB/Library/Caches/gck_device_cache_v2.plist doesn't exist.
+[NSMutableDictionary(GCKAdditions) gck_loadFromCacheWithName:] - Device cache file
file:///Users/martin/Library/Developer/CoreSimulator/Devices/318D2E15-C4B0-47D2-97AF-CD560A6063AE/data/Containers/Data/Application/C117BB98-88DA-4586-B119-0683DAD82FEB/Library/Caches/gck_device_cache_v3.plist doesn't exist
I can't seem to figure out why I have these errors. But it seems that the deviceScanner never even finds my receiver device.
My viewController code is:
class ChromeCastViewController: UIViewController, GCKDeviceScannerListener, GCKDeviceManagerDelegate, GCKMediaControlChannelDelegate{
fileprivate let kCancelTitle = "Cancel"
fileprivate let kDisconnectTitle:String! = "Disconnect"
// Publicly available receiver to demonstrate sending messages - replace this with your
// own custom app ID.
fileprivate let kReceiverAppID = "XXXXXXXXX"
fileprivate lazy var btnImage:UIImage = {
return UIImage(named: "icon-cast-identified.png")!
}()
fileprivate lazy var btnImageselected:UIImage = {
return UIImage(named: "icon-cast-connected.png")!
}()
fileprivate var deviceScanner:GCKDeviceScanner?
fileprivate var deviceManager:GCKDeviceManager?
fileprivate var mediaInformation:GCKMediaInformation?
fileprivate var selectedDevice:GCKDevice?
#IBOutlet weak var googleCastButton: UIBarButtonItem!
#IBOutlet weak var backButton: UIBarButtonItem!
#IBAction func backAction(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
self.startScan()
}
func startScan() {
let filterCriteria = GCKFilterCriteria(forAvailableApplicationWithID: kReceiverAppID)
self.deviceScanner = GCKDeviceScanner(filterCriteria: filterCriteria)
if let deviceScanner = self.deviceScanner {
deviceScanner.add(self)
deviceScanner.startScan()
print("scanning started")
deviceScanner.passiveScan = true
}
}
// MARK: GCKDeviceScannerListener
func deviceDidComeOnline(_ device: GCKDevice) {
print("deviceDidComeOnline")
print("Device found: \(device.friendlyName)");
self.updateButtonStates()
}
func deviceDidGoOffline(_ device: GCKDevice) {
print("deviceDidGoOffline()")
print("Device went away: \(device.friendlyName)");
self.updateButtonStates()
}
func deviceDidChange(_ device: GCKDevice) {
print("deviceDidChange()");
}
func updateButtonStates() {
print("updateButton")
if (deviceScanner!.devices.count > 0) {
// Show the Cast button.
navigationItem.rightBarButtonItems = [googleCastButton!]
if (deviceManager != nil && deviceManager?.connectionState == GCKConnectionState.connected) {
// Show the Cast button in the enabled state.
googleCastButton!.tintColor = UIColor.blue
} else {
// Show the Cast button in the disabled state.
googleCastButton!.tintColor = UIColor.gray
}
} else{
// Don't show Cast button.
navigationItem.rightBarButtonItems = []
}
}
}
Thank you in advance for any help or tips you can give me.
Best regards
UPDATE:
I have modified my code base to follow google v3 guidelines.
I now instantiate a GCKCastContext in AppDelegate in order to use google widgets.
But It seems that GCKCastContext or functionalities associated with the singleton are never called after initialising it. I have tried to add the GCKDiscoveryManagerListener to my AppDelegate to see if was detecting my ChromeCast device.
The code for my AppDelegate is :
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GCKLoggerDelegate,GCKDiscoveryManagerListener {
var window: UIWindow?
fileprivate let kReceiverAppID = "XXXXXXXXXX"
var discoveryManager: GCKDiscoveryManager
override init(){
let options:GCKCastOptions = GCKCastOptions(receiverApplicationID: kReceiverAppID)
GCKCastContext.setSharedInstanceWith(options)
self.discoveryManager = GCKCastContext.sharedInstance().discoveryManager
super.init()
self.discoveryManager.add(self)
self.discoveryManager.passiveScan = true
self.discoveryManager.startDiscovery()
GCKLogger.sharedInstance().delegate = self
}
.....
func log(fromFunction function: UnsafePointer<Int8>, message: String) {
let functionName = String(cString: function)
print(functionName + " - " + message);
}
func didUpdateDeviceList() {
print("didUpdateDeviceList with \(discoveryManager.deviceCount) devices")
(0..<discoveryManager.deviceCount).forEach { index in
print(index, discoveryManager.device(at: index))
}
}
func didStartDiscoveryForDeviceCategory(deviceCategory: String) {
print("GCKDiscoveryManagerListener: \(deviceCategory)")
print("FOUND: \(self.discoveryManager.hasDiscoveredDevices)")
}
func willUpdateDeviceList(){
print("will update device was called")
}
}
The functions didUpdateDeviceList, didStartDiscoveryForDeviceCategory
, willUpdateDeviceList are never called, meaning no Chrome device is ever found making the widgets unusable.
Thank again for any help

Related

How can I create an Instance of NSManagedObject in a NotesApp without a Button - Apple's NoteApp Style?

I started learning programming and I decided to try out my first Note Taking App.
My Goal is to create an App similar to the iPhone's NoteApp. Therefore, I wanted the note's title be set when the User writes in the TextView as the first line. Therefore, I created a NoteViewController, which contains a TextView and a NoteIndexViewController, which is a TableViewController, both embedded in a NavigationController.
I'm also using Core Data to store the data.
The problem is that I don't know how I can commit those changes to the DataBase without using a button. I know how to create an instance of the NSManagedObject - in NoteIndexViewController to create new notes in the TableView using a Button:
#IBAction func addNotePressed(_ sender: UIBarButtonItem) {
let newNoteIndex = NoteIndex(context: self.context)
newNoteIndex.name = "Temporal Name"
notesArray.append(newNoteIndex)
saveNoteIndex()
performSegue(withIdentifier: K.segueToNote, sender: self)
}
But I'm completely lost if I want to commit the changes without a "Save Button" to create the instance and also committing changes. This is the code I got so far. Notice that I did not set any Note() object.
class NoteViewController: UIViewController {
var noteArray = [Note]()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var selectedNote: NoteIndex? {
didSet {
loadData()
}
}
var firstLine: String?
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
loadData()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
if !textView.text.isEmpty {
if let newLine = textView.text.firstIndex(of: "\n") {
let firstLetter = textView.text.startIndex
let lineBrake = textView.text.index(before: newLine)
let lettersTillPosition = textView.text.distance(from: firstLetter, to: lineBrake)
firstLine = (textView.text as NSString).substring(to: lettersTillPosition)
} else {
if textView.text.count >= 30{
firstLine = (textView.text as NSString).substring(to: 30)
} else {
firstLine = (textView.text as NSString).substring(to: textView.text.count)
}
}
selectedNote!.name = firstLine
saveCurrentNote()
}
}
//MARK: - Data Manipulation Methods
func saveCurrentNote() {
do {
try context.save()
} catch {
print("Error saving cateogry \(error)")
}
}
func loadData(with request: NSFetchRequest<Note> = Note.fetchRequest()) {
// goToIndex is the relationship between the IndexNote entity and Note. And when Back button is pressed the code tend also to break in this part.
request.predicate = NSPredicate(format: "goToIndex.name MATCHES %#", selectedNote!.name!)
do {
noteArray = try context.fetch(request)
} catch {
print("This is a load error: \(error)")
}
}
}
extension NoteViewController: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
saveCurrentNote()
}
}
Here is a possible solution for your question. You can use Notification Center to monitor if the user is interrupted and if so you can do a quick save.
Place these in the scene delegate
func sceneWillResignActive(_ scene: UIScene) {
let notificationName = NSNotification.Name(ReuseIdentifier.pause)
NotificationCenter.default.post(name: notificationName , object: nil)
}
func sceneDidDisconnect(_ scene: UIScene) {
let notificationName = NSNotification.Name(ReuseIdentifier.quit)
NotificationCenter.default.post(name: notificationName, object: nil)
}
Place something like this where the user data is being saved.
/// Monitors application state for major changes.
/// - Pause Observer: Adds observer that notifies application if application is no longer active (enters foreground).
/// - Quit Observer: Adds observer that notifies application if terminated.
private func checkForPauseOrQuit(){
NotificationCenter.default.addObserver(self,
selector: #selector(autoSave),
name: NSNotification.Name(ReuseIdentifier.pause),
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(autoSave),
name: NSNotification.Name(ReuseIdentifier.quit),
object: nil)
}
And then for your selector method you create your NSManagedObject and capture whatever values the user may have started typing.
On startup you do the reverse, and make sure to erase the values. This should function only as a temporary holding container not your main entity. Check out my note application for reference:
https://github.com/victis23/AwesomeNote/tree/WorkingBranch/AwesomeNote

Embed Unity inside iOS in own ViewController

Using Unity 2019.3.0f3 and its Unity as a library feature I'm trying to embed a Unity project inside my iOS application.
Unity officially only supports full screen rendering. Nevertheless I'm looking for a way around that restriction.
In previous versions of Unity i successfully used swift-unity to do the integration. Within this approach it is easy to just get the View where Unity is rendering to (using UnityGetGLView()). I had no problems regarding stability or resources.
Using the new library approach, every time I try to access the UnityView, unity forces it's complete Window as keyWindow.
I tried accessing the UnityView in my own ViewController using
if let unityView = UnityFramework.getInstance()?.appController()?.rootViewController.view {
// insert subview at index 0 ensures unity view is behind current UI view
view?.insertSubview(unityView, at: 0)
}
But that immediately activates the complete unity-window and hides my parenting UITabBarController.
Trying to make the UnityFramework.getInstance()?.appController()?.rootViewController a child of my UITabBarController failed with the same result.
Furthermore it is not possible to add a child ViewController. Only adding subviews seems possible.
Does anybody know where that window-behaviour is located or how i can access the UnityView (or the RootViewController) and use it freely?
I found a solution to the problem based on this approach from the unity forum. Using this approach I'm able to use the UnityViewController as a child in my own TabBarController.
The approach is working for Unity 2019.3.0f3, but I'm not sure if it will work in future versions. It feels like Unity tries to actively prevent such use. Then again I found hints in comments in the library-code that would suggest that a modified ViewController-Hierarchy was at least contemplated e.g. in UnityAppController+ViewHandling.h. But the instructions are unclear and methods with the hinted names don't exist.
Solution
1. Create UnityEmbeddedSwift.swift
The official example App provided by Unity is a real mess. I ended up using the UnityEmbeddedSwift.swift from the linked forum post with additions for pausing. This class encapsulates all Unity-related functionality in one clean class.
//
// UnityEmbeddedSwift.swift
// Native
//
// Created by NSWell on 2019/12/19.
// Copyright © 2019 WEACW. All rights reserved.
//
//
// Created by Simon Tysland on 19/08/2019.
// Copyright © 2019 Simon Tysland. All rights reserved.
//
import Foundation
import UnityFramework
class UnityEmbeddedSwift: UIResponder, UIApplicationDelegate, UnityFrameworkListener {
private struct UnityMessage {
let objectName : String?
let methodName : String?
let messageBody : String?
}
private static var instance : UnityEmbeddedSwift!
private var ufw : UnityFramework!
private static var hostMainWindow : UIWindow! // Window to return to when exiting Unity window
private static var launchOpts : [UIApplication.LaunchOptionsKey: Any]?
private static var cachedMessages = [UnityMessage]()
// MARK: - Static functions (that can be called from other scripts)
static func getUnityRootViewController() -> UIViewController! {
return instance.ufw.appController()?.rootViewController
}
static func getUnityView() -> UIView! {
return instance.ufw.appController()?.rootViewController?.view
}
static func setHostMainWindow(_ hostMainWindow : UIWindow?) {
UnityEmbeddedSwift.hostMainWindow = hostMainWindow
let value = UIInterfaceOrientation.landscapeLeft.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
}
static func setLaunchinOptions(_ launchingOptions : [UIApplication.LaunchOptionsKey: Any]?) {
UnityEmbeddedSwift.launchOpts = launchingOptions
}
static func showUnity() {
if(UnityEmbeddedSwift.instance == nil || UnityEmbeddedSwift.instance.unityIsInitialized() == false) {
UnityEmbeddedSwift().initUnityWindow()
}
else {
UnityEmbeddedSwift.instance.showUnityWindow()
}
}
static func hideUnity() {
UnityEmbeddedSwift.instance?.hideUnityWindow()
}
static func pauseUnity() {
UnityEmbeddedSwift.instance?.pauseUnityWindow()
}
static func unpauseUnity() {
UnityEmbeddedSwift.instance?.unpauseUnityWindow()
}
static func unloadUnity() {
UnityEmbeddedSwift.instance?.unloadUnityWindow()
}
static func sendUnityMessage(_ objectName : String, methodName : String, message : String) {
let msg : UnityMessage = UnityMessage(objectName: objectName, methodName: methodName, messageBody: message)
// Send the message right away if Unity is initialized, else cache it
if(UnityEmbeddedSwift.instance != nil && UnityEmbeddedSwift.instance.unityIsInitialized()) {
UnityEmbeddedSwift.instance.ufw.sendMessageToGO(withName: msg.objectName, functionName: msg.methodName, message: msg.messageBody)
}
else {
UnityEmbeddedSwift.cachedMessages.append(msg)
}
}
// MARK - Callback from UnityFrameworkListener
func unityDidUnload(_ notification: Notification!) {
ufw.unregisterFrameworkListener(self)
ufw = nil
UnityEmbeddedSwift.hostMainWindow?.makeKeyAndVisible()
}
// MARK: - Private functions (called within the class)
private func unityIsInitialized() -> Bool {
return ufw != nil && (ufw.appController() != nil)
}
private func initUnityWindow() {
if unityIsInitialized() {
showUnityWindow()
return
}
ufw = UnityFrameworkLoad()!
ufw.setDataBundleId("com.unity3d.framework")
ufw.register(self)
// NSClassFromString("FrameworkLibAPI")?.registerAPIforNativeCalls(self)
ufw.runEmbedded(withArgc: CommandLine.argc, argv: CommandLine.unsafeArgv, appLaunchOpts: UnityEmbeddedSwift.launchOpts)
sendUnityMessageToGameObject()
UnityEmbeddedSwift.instance = self
}
private func showUnityWindow() {
if unityIsInitialized() {
ufw.showUnityWindow()
sendUnityMessageToGameObject()
}
}
private func hideUnityWindow() {
if(UnityEmbeddedSwift.hostMainWindow == nil) {
print("WARNING: hostMainWindow is nil! Cannot switch from Unity window to previous window")
}
else {
UnityEmbeddedSwift.hostMainWindow?.makeKeyAndVisible()
}
}
private func pauseUnityWindow() {
ufw.pause(true)
}
private func unpauseUnityWindow() {
ufw.pause(false)
}
private func unloadUnityWindow() {
if unityIsInitialized() {
UnityEmbeddedSwift.cachedMessages.removeAll()
ufw.unloadApplication()
}
}
private func sendUnityMessageToGameObject() {
if (UnityEmbeddedSwift.cachedMessages.count >= 0 && unityIsInitialized())
{
for msg in UnityEmbeddedSwift.cachedMessages {
ufw.sendMessageToGO(withName: msg.objectName, functionName: msg.methodName, message: msg.messageBody)
}
UnityEmbeddedSwift.cachedMessages.removeAll()
}
}
private func UnityFrameworkLoad() -> UnityFramework? {
let bundlePath: String = Bundle.main.bundlePath + "/Frameworks/UnityFramework.framework"
let bundle = Bundle(path: bundlePath )
if bundle?.isLoaded == false {
bundle?.load()
}
let ufw = bundle?.principalClass?.getInstance()
if ufw?.appController() == nil {
// unity is not initialized
// ufw?.executeHeader = &mh_execute_header
let machineHeader = UnsafeMutablePointer<MachHeader>.allocate(capacity: 1)
machineHeader.pointee = _mh_execute_header
ufw!.setExecuteHeader(machineHeader)
}
return ufw
}
}
2. Modify AppDelegate.swift
Sets window and launch options needed by UnityEmbeddedSwift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
UnityEmbeddedSwift.setHostMainWindow(window)
UnityEmbeddedSwift.setLaunchinOptions(launchOptions)
return true
}
3. Create RootTabBarController.swift
This class sets up the hierarchy.
It is important to use the UnityRootViewController right after calling UnityEmbeddedSwift.showUnity().
The Tab-Switching is not nice, but if it is missing Unity will pause (or freeze?) during loading. The timing seems to depend on the Unity-Projects loading time. It can be faster for small projects and needs more time for larger projects.
import UIKit
class RootTabBarController: UITabBarController, UITabBarControllerDelegate {
var unityNC: UINavigationController?
var nativeNC: UINavigationController?
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
// start unity and immediatly set as rootViewController
// this loophole makes it possible to run unity in the same window
UnityEmbeddedSwift.showUnity()
let unityViewController = UnityEmbeddedSwift.getUnityRootViewController()!
unityViewController.navigationItem.title = "Unity"
unityNC = UINavigationController.init(rootViewController: unityViewController)
unityNC?.tabBarItem.title = "Unity"
let nativeViewController = UIViewController.init()
nativeViewController.view.backgroundColor = UIColor.darkGray
nativeViewController.navigationItem.title = "Native"
nativeNC = UINavigationController.init(rootViewController: nativeViewController)
nativeNC?.tabBarItem.title = "Native"
viewControllers = [unityNC!, nativeNC!]
// select other tab and reselect first tab to unfreeze unity-loading
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2, execute: {
self.selectedIndex = 1
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01, execute: {
self.selectedIndex = 0
})
})
}
// MARK: - UITabBarControllerDelegate
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
// pause unity if unity-tab is not selected
if viewController != unityNC {
UnityEmbeddedSwift.pauseUnity()
} else {
UnityEmbeddedSwift.unpauseUnity()
}
}
}
4. Modify Main.storyboard
Modify the storyboard to start with the RootTabBarController.
For anyone who is still interested in preventing the freezing, I am building on top of aalmigthy's answer:
You do not need to add a TabBar controller and switch between the tabs. All you need to do is:
Add the Unity view as a subview
Send the subview to back
Here's the modified ViewController class (no need for a tab bar):
import UIKit
class HybridViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
UnityEmbeddedSwift.showUnity()
let uView = UnityEmbeddedSwift.getUnityView()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
self.view.addSubview(uView!)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
self.view.sendSubviewToBack(uView!)
})
})
}
}

On Tap of UILabel (gesture recogniser) finding nil in tableview prototype cell for one cell and its working fine for another two cells

I am trying to implement UITapGestureRecognizer, Idea is that on tap of label I will get a pop up displaying the number to make call and it should come up with pop up alert saying call or cancel!!
Code I've written worked for me in 3 to 4 places but I am stuck at one point
In this screen I have a tableview with prototype cells grouped type here, please check this Image:
Link For Image
Third Cell
Now If I am Tapping 065668982 I have canOpenURL: failed for URL: "telprompt://065668982" - error: "This app is not allowed to query for scheme telprompt" which actually works on iPhone not on simulator and it pulls to call which is working fine.
Second Cell
If I am Tapping 065454858 I have canOpenURL: failed for URL: "telprompt://065668982" - error: "This app is not allowed to query for scheme telprompt" which actually works on iPhone not on simulator and it pulls to call which is working fine.
first Cell
But for first one it never works and end up with fatal error: unexpectedly found nil while unwrapping an Optional value
NOTE : I am Getting phone Number from an API and append the data in view controller to UITableViewCell.
I Hope I make sense, Thanks in advance for any help also if I am not clear please comment below
Here is my code:
import UIKit
import Foundation
class XyzTableViewCell: UITableViewCell
{
#IBOutlet weak var phoneNumber: UILabel!
var touchContact : String = ""
var myCell: MyCellData! {
didSet {
self.updateUI()
}
}
func updateUI()
{
touchContact = vicarCell.phone_no
//Tap Gesture
tapGestureAddonView()
}
//MARK:- Tap to Call and Open Email
func tapGestureAddonView(){
let contacttap = UITapGestureRecognizer(target: self, action:("contactTapped"))
contacttap.numberOfTapsRequired = 1
phoneNumber!.userInteractionEnabled = true
phoneNumber!.addGestureRecognizer(contacttap)
}
func contactTapped() {
// do something cool here
print("contactTapped")
print(touchContact)
dispatch_async(dispatch_get_main_queue())
{
if UIApplication.sharedApplication().canOpenURL(NSURL(string: "telprompt://\(self.touchContact)")!){
UIApplication.sharedApplication().openURL(NSURL(string: "telprompt://\(self.touchContact)")!)
}
else{
//showAlert("Info",message: "Your device could not called" ,owner: self)
}
}
}
The issues: 1) you should add gesture only once 2) you should check NSURL for nil. Let me assume that you use storyboard and improve your code a bit
class XyzTableViewCell: UITableViewCell
{
#IBOutlet weak var phoneNumber: UILabel!
var touchContact : String = ""
var myCell: MyCellData! {didSet {self.updateUI()}}
func updateUI() {
touchContact = myCell.phone_no // did you mean myCell instead vicarCell?
}
override func awakeFromNib() {
super.awakeFromNib()
tapGestureAddonView()
}
func tapGestureAddonView(){
let contacttap = UITapGestureRecognizer(target: self, action: #selector(contactTapped))
contacttap.numberOfTapsRequired = 1
phoneNumber!.userInteractionEnabled = true
phoneNumber!.addGestureRecognizer(contacttap)
}
func contactTapped() {
print("contactTapped")
print(touchContact)
if touchContact.isEmpty {return}
guard let url = NSURL(string: "telprompt://\(touchContact)") else {
print("url string invalid")
return
}
dispatch_async(dispatch_get_main_queue())
{
if UIApplication.sharedApplication().canOpenURL(url){
UIApplication.sharedApplication().openURL(url)
} else{
//showAlert("Info",message: "Your device could not called" ,owner: self)
}
}
}
}

Chromecast listener is not working when i create seperate class from Controller

Created class:
class ChromeCast:NSObject {
//MARK:Chromecast Vars
var applicationMetadata: GCKApplicationMetadata?
var selectedDevice: GCKDevice?
var deviceManager: GCKDeviceManager?
var mediaInformation: GCKMediaInformation?
var mediaControlChannel: GCKMediaControlChannel?
var deviceScanner: GCKDeviceScanner
lazy var kReceiverAppID:String = {
// You can add your own app id here that you get by registering with the
// Google Cast SDK Developer Console https://cast.google.com/publish
return kGCKMediaDefaultReceiverApplicationID
}()
override init() {
let filterCriteria = GCKFilterCriteria(forAvailableApplicationWithID:
kGCKMediaDefaultReceiverApplicationID)
deviceScanner = GCKDeviceScanner(filterCriteria:filterCriteria)
}
func startChromeCastScanning() {
// Initialize device scanner
deviceScanner.addListener(self)
deviceScanner.startScan()
deviceScanner.passiveScan = false
Log.Info("Start Scanning")
}
}
extension ChromeCast: GCKDeviceScannerListener {
func deviceDidComeOnline(device: GCKDevice!) {
Log.Info("Device found: \(device.friendlyName)")
}
func deviceDidGoOffline(device: GCKDevice!) {
Log.Info("Device went away: \(device.friendlyName)")
}
}
and calling it from my view controller as:
someFunctionInViewController() {
let chrome = ChromeCast()
chrome.startChromeCastScanning()
}
Delegate is not getting called back . when device discovered.
But same works when i add all properties and function in view controller it self.
But i want to seperate it from controller. what is the issue?
I Figured out the problem
instead of local i declare global property for Chromecast in my controller
var chrome : ChromeCast?
and then from my function i called it as
func someFunction() {
chrome = ChromeCast()
chrome?.startChromeCastScanning()
}

How do I get music to persist as I navigate from view to view?

So, I have mastered the art of playing sounds/music using one view in iOS, but I am now trying to make a more robust app for an musical artist. Thus far, it involves segues from a "menu" ViewController to his Bio, Show Times, and "Listening Room" etc. Thus far, I created a an "AudioManager" class:
import UIKit
import AVFoundation
import MediaPlayer
class AudioManager: NSObject {
let defaltSong = ["Hell&BackCaf/01 Black Sheep", "caf"]
weak var delegate : PlayerDelegate?
var musicPlayer1 = AVAudioPlayer()
var trackNumber = 0
var musicAudioPath = NSBundle.mainBundle().pathForResource("Hell&BackCaf/01 Black Sheep", ofType: "caf")
var musicAudioPathURL = NSURL()
var error:NSError? = nil
var songList = [["Hell&BackCaf/01 Black Sheep", "caf"], ["Hell&BackCaf/02 Hell & Back", "caf"], ["Hell&BackCaf/03 Save Me", "caf"], ["Hell&BackCaf/04 Broken feat. Hillary Dodson", "caf"], ["Hell&BackCaf/05 Do Or Die", "caf"], ["Hell&BackCaf/06 Divided", "caf"]]
func ButtonPlay(song: NSString, type: NSString) {
error = nil
musicAudioPath = NSBundle.mainBundle().pathForResource(song as String, ofType: type as String)
musicAudioPathURL = NSURL(fileURLWithPath: self.musicAudioPath!)!
musicPlayer1 = AVAudioPlayer(contentsOfURL: musicAudioPathURL, error: &error)
musicPlayer1.prepareToPlay()
musicPlayer1.play()
}
func loadFirstSong() {
error = nil
musicAudioPath = NSBundle.mainBundle().pathForResource("Hell&BackCaf/01 Black Sheep", ofType: "caf")
musicAudioPathURL = NSURL(fileURLWithPath: self.musicAudioPath!)!
musicPlayer1 = AVAudioPlayer(contentsOfURL: musicAudioPathURL, error: &error)
if error == nil {
musicPlayer1.prepareToPlay()
} else {
println(error)
}
}
func advanceTrack(){
if trackNumber < songList.count - 1 {
self.trackNumber++
ButtonPlay(songList[trackNumber][0], type: songList[trackNumber][1])
} else {
trackNumber = 0
ButtonPlay(songList[trackNumber][0], type: songList[trackNumber][1])
}
}
func previousTrack(){
if trackNumber > 0 {
trackNumber--
ButtonPlay(songList[trackNumber][0], type: songList[trackNumber][1])
} else {
trackNumber = songList.count - 1
ButtonPlay(songList[trackNumber][0], type: songList[trackNumber][1])
}
}
func audioPlayerDidFinishPlaying(AVAudioPlayer!, successfully: Bool) {
self.delegate?.soundFinished(self)
println("song over")
}
}
I then used it in my MusicRoomViewController:
import UIKit
import AVFoundation
class MusicRoomViewController: UIViewController, AVAudioPlayerDelegate {
let audioManager = AudioManager()
#IBAction func pressedBackButton(sender: UIButton) {
audioManager.previousTrack()
}
#IBAction func pressedPlayButton(sender: UIButton) {
audioManager.musicPlayer1.play()
}
#IBAction func pressedForwardButton(sender: UIButton) {
audioManager.advanceTrack()
}
override func viewDidLoad() {
super.viewDidLoad()
audioManager.loadFirstSong()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func viewWillDisappear(animated: Bool) {
if self.isMovingFromParentViewController()
{
audioManager.musicPlayer1.play()
}
}
}
But now when I navigate to any other 'page' of the app, the music stops. After researching, I know that I am going about this all wrong, but I can't seem to figure out what I need to do to keep the audio playing until the app is closed or the user presses stop (which does not exist yet, I know). Any thoughts??
You're going to have to make your AudioManager class a singleton. What is happening, is the AudioManager instance you're creating is being deallocated once you navigate away from the view you created it in. A singleton object is created once it is first accessed, and will persist in memory until the application's lifecycle ends or it is explicitly deallocated. For more information on Swift singletons, check out this page. It's a useful pattern to learn.
Make the following modifications to AudioManager (taken from the above website):
private let _AudioManagerSharedInstance = AudioManager()
class AudioManager {
static let sharedInstance = AudioManager()
}
Access AudioManager by using AudioManager.sharedInstance, for example you can call AudioManager.sharedInstance.previousTrack().
You could create a singleton, or instantiate the AudioManager in your AppDelegate class and refer to that like so:
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.audioManger...

Resources