Can't cast video via google cast correctly in ios app - ios

I'm working on a project with custom video player, based on AVPlayer. Trying to integrate google cast. I've made integration based on google tuts: https://codelabs.developers.google.com/codelabs/cast-videos-ios/
But with conversion to swift. Everything seems to work fine, when cast, if video player opens, and there is connected device (or if I connect from panel), I form meta info for file, and it's passed to google cast - everything works fine.
But, i have strange behavior:
1) Start casting, open video, then another video, then third video.
2) Stop casting
3) Go to another video, enable casting, but it doesn't start this video. It start casting the first video I opened earlier....
I tried to find any method that clears cache or queue, but there is no.. Please, help
class VideoVC: UIViewController, UIGestureRecognizerDelegate, GCKSessionManagerListener {
var filmTitle: String!
var toPass: String!
var film: MovieDetails!
var filmDetails: Movie!
var sessionManager: GCKSessionManager?
var castSession: GCKCastSession?
var castMediaController: GCKUIMediaController?
var checkPlayed = 0
override func viewDidLoad() {
super.viewDidLoad()
sessionManager = GCKCastContext.sharedInstance().sessionManager
sessionManager?.add(self)
castMediaController = GCKUIMediaController()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let videoURL = toPass {
if let video = URL(string: videoURL) {
player = AVPlayer(url: video)
player.allowsExternalPlayback = true
player.usesExternalPlaybackWhileExternalScreenIsActive = true
playerController.player = player
self.addChildViewController(playerController)
self.view.addSubview(playerController.view)
playerController.view.frame = self.view.frame
self.view.sendSubview(toBack: playerController.view)
}
}
if isCastEnabled() {
playSelectedItemRemotely()
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
player.replaceCurrentItem(with: nil)
}
func buildMediaInformation() -> GCKMediaInformation {
let metaData = GCKMediaMetadata(metadataType: GCKMediaMetadataType(rawValue: 1)!)
metaData.setString(filmTitle, forKey: kGCKMetadataKeyTitle)
if let imageUrl = URL(string: filmDetails.poster_cast!) {
let image = GCKImage(url: imageUrl, width: 340, height: 450)
metaData.addImage(image)
}
if let episode = film.serial_episode, let season = film.serial_season, season != "", episode != "", let title = film.title, title != "" {
let subtitle = "\(title) \(episode) серия \(season) сезон"
metaData.setString(subtitle, forKey: kGCKMetadataKeySubtitle)
}
let duration = Double(film.duration!)
let mediaInfo = GCKMediaInformation(contentID: toPass!,
streamType: GCKMediaStreamType.buffered,
contentType: film.contentType!,
metadata: metaData as GCKMediaMetadata,
streamDuration: duration,
mediaTracks: nil,
textTrackStyle: nil,
customData: nil)
print("toPass: \(toPass!)")
print("duration: \(duration)")
return mediaInfo
}
func playSelectedItemRemotely() {
let castSession = GCKCastContext.sharedInstance().sessionManager.currentCastSession
if (castSession != nil) {
castSession?.remoteMediaClient?.loadMedia(self.buildMediaInformation(), autoplay: true)
self.dismiss(animated: true, completion: nil)
}
else {
print("no castSession!")
}
}
func sessionManager(_ sessionManager: GCKSessionManager, didStart session: GCKSession) {
playSelectedItemRemotely()
}
func sessionManager(_ sessionManager: GCKSessionManager, didResumeSession session: GCKSession) {
}
func sessionManager(_ sessionManager: GCKSessionManager, didEnd session: GCKSession, withError error: Error?) {
let castSession = GCKCastContext.sharedInstance().sessionManager.currentCastSession
castSession?.endAndStopCasting(true)
}
func sessionManager(_ sessionManager: GCKSessionManager, didFailToStart session: GCKSession, withError error: Error) {
Utils.showOverAnyVC("Ошибка подключения", message: "Попробуйте еще раз!")
}
func isCastEnabled() -> Bool {
switch GCKCastContext.sharedInstance().castState {
case GCKCastState.connected:
print("cast connected")
return true
case GCKCastState.connecting:
print("cast connecting")
return true
case GCKCastState.notConnected:
print("cast notConnected")
return false
case GCKCastState.noDevicesAvailable:
print("cast noDevicesAvailable")
return false
}
}}
and my appdelegate:
class AppDelegate: UIResponder, UIApplicationDelegate, GCKLoggerDelegate, UNUserNotificationCenterDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let options = GCKCastOptions(receiverApplicationID: "F443E49F")
GCKCastContext.setSharedInstanceWith(options)
GCKLogger.sharedInstance().delegate = self
let appStoryboard = UIStoryboard(name: "NewMain", bundle: nil)
let navigationController = appStoryboard.instantiateViewController(withIdentifier: "MainNavigation")
let castContainerVC: GCKUICastContainerViewController = GCKCastContext.sharedInstance().createCastContainerController(for: navigationController)
castContainerVC.miniMediaControlsItemEnabled = true
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = castContainerVC
self.window?.makeKeyAndVisible()
GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true
return true
}
func logMessage(_ message: String, fromFunction function: String) {
print("message: \(function)")
}}

On possible solution could be due to:
sessionManager?.add(self)
You add the delegate but at no point do you clear it. As a result, the VideoVC is never destroyed due to the retained reference from the session manager. When you reopen the VideoVC the session manager is still also accessing the delegate from the first time you loaded it.
Because of this, when the following is called:
func sessionManager(_ sessionManager: GCKSessionManager, didStart session: GCKSession) {
This is being called in your first instance of VideoVC which now has the wrong file information.
You can monitor this by putting a print(self) into the above method and look at the memory pointer value. Check that it also matches the same memory pointer value that is called in viewDidLoad
Update
To better manage the delegate change the following method: viewDidDisappear()
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
player.replaceCurrentItem(with: nil)
//this stops the session manager sending callbacks to your VideoVC
sessionManager.remove(self)
}

Related

How to turn off Background Music using a Toggle Switch in Xcode using Swift?

I am playing background music in my app from the App delegate once the app launches. Now, In my 2nd V.C. I have set up a toggle switch to turn om/off the background music. But, whenever I am running the follwing code, my app is crashing giving me this error:-
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
Could anyone please let me know how can I implement the following task in my V.C. Would appreciate your help! Thanks:)
**App Delegate**
let vc = SecondViewController()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
vc.playMusic()
return true
}
**Second View Controller**
import UIKit
import AVFoundation
class SecondViewController: UIViewController {
#IBOutlet weak var musicToggleSwitch: UISwitch!
var music: AVAudioPlayer!
let vc_1 = ViewController()
override func viewDidLoad() {
super.viewDidLoad()
self.musicToggleSwitch.setOn(UserDefaults.standard.bool(forKey: "musicToggleState"), animated: true)
}
#IBAction func musicToggleSwitch(_ sender: UISwitch) {
if (musicToggleSwitch.isOn == true) {
if (music.isPlaying == false) {
music.play()
}
}
else {
if (music.isPlaying == true) {
music.stop()
}
}
UserDefaults.standard.set(sender.isOn, forKey: "musicToggleState")
}
func playMusic() {
if let musicURL = Bundle.main.url(forResource: "Music", withExtension: "mp3") {
if let audioPlayer = try? AVAudioPlayer(contentsOf: musicURL) {
music = audioPlayer
music.numberOfLoops = -1
music.play()
}
}
}
}
This should solve your issues.
If it still doesn't play, add some breakpoints in the creation block of the music variable to see what goes wrong.
class SecondViewController: UIViewController {
#IBOutlet weak var musicToggleSwitch: UISwitch!
var music: AVAudioPlayer? = {
guard let musicURL = Bundle.main.url(forResource: "Music", withExtension: "mp3") else {
return nil
}
let audioPlayer = try? AVAudioPlayer(contentsOf: musicURL)
audioPlayer?.numberOfLoops = -1
return audioPlayer
}()
override func viewDidLoad() {
super.viewDidLoad()
self.musicToggleSwitch.setOn(UserDefaults.standard.bool(forKey: "musicToggleState"), animated: true)
}
#IBAction func musicToggleSwitch(_ sender: UISwitch) {
guard let music = music, sender.isOn != music.isPlaying else {
return
}
if sender.isOn {
music.play()
} else {
music.stop()
}
UserDefaults.standard.set(sender.isOn, forKey: "musicToggleState")
}
func playMusic() {
music?.play()
}
}
The problem is this line:
let vc = SecondViewController()
That is not the same SecondViewController that you see in your interface. So playMusic is called on the wrong view controller, and on the view controller you can see music remains nil and you crash.

Google ChromeCast iOS (Swift 3) can't play video

I need help! Once i connected the device to chromecast, it crashed right when it's about to load. I have no clue why it cause that. I followed GoogleCast for Docs and few examples and seems that it's missing something. Could you guys help me?
This my code
This is on my OnCreate
override func viewDidLoad() {
sessionManager = GCKCastContext.sharedInstance().sessionManager
sessionManager?.add(self)
castMediaController = GCKUIMediaController()
if isCastEnabled() {
playSelectedItemRemotely()
}
}
//////////////////////////////////////////////////////
// //
// Start du Google ChromeCast //
// //
//////////////////////////////////////////////////////
private func buildMediaInformation() -> GCKMediaInformation {
let metadata = GCKMediaMetadata(metadataType: GCKMediaMetadataType.generic)
metadata.setString("Title", forKey: kGCKMetadataKeyTitle)
metadata.setString("Studio", forKey: kGCKMetadataKeyStudio)
let mediaInfo = GCKMediaInformation(contentID: "streamURL",
streamType: GCKMediaStreamType.none,
contentType: "video/m3u",
metadata: metadata,
streamDuration: 60,
mediaTracks: nil,
textTrackStyle: nil,
customData: nil)
print(mediaInfo.contentID)
return mediaInfo
}
func isCastEnabled() -> Bool {
switch GCKCastContext.sharedInstance().castState {
case GCKCastState.connected:
print("cast connected")
return true
case GCKCastState.connecting:
print("cast connecting")
return true
case GCKCastState.notConnected:
print("cast notConnected")
return false
case GCKCastState.noDevicesAvailable:
print("cast noDevicesAvailable")
return false
}
}
func playSelectedItemRemotely() {
let castSession = GCKCastContext.sharedInstance().sessionManager.currentCastSession
if (castSession != nil) {
castSession?.remoteMediaClient?.loadMedia(self.buildMediaInformation(), autoplay: true)
self.dismiss(animated: true, completion: nil)
}
else {
print("no castSession!")
}
}
func sessionManager(_ sessionManager: GCKSessionManager, didStart session: GCKSession) {
playSelectedItemRemotely()
}
func sessionManager(_ sessionManager: GCKSessionManager, didResumeSession session: GCKSession) {
}
func sessionManager(_ sessionManager: GCKSessionManager, didEnd session: GCKSession, withError error: Error?) {
let castSession = GCKCastContext.sharedInstance().sessionManager.currentCastSession
castSession?.endAndStopCasting(true)
}
func sessionManager(_ sessionManager: GCKSessionManager, didFailToStart session: GCKSession, withError error: Error) {
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// playerView.player?.replaceCurrentItem(with: nil)
//
// //this stops the session manager sending callbacks to your VideoVC
// sessionManager?.remove(self)
}
And this is on my AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let castAppID = "myKey"
let options = GCKCastOptions.init(receiverApplicationID: castAppID)
GCKCastContext.setSharedInstanceWith(options)
GCKLogger.sharedInstance().delegate = self
I got it. My issue was in the Google Cast Console i had a remote receiver. Need to have a custom style receiver or media. So make sure you have that right!! Or just use a kGCKMediaDefaultReceiverApplicationID on your AppID to get the default one.

warning: attempt to present whose view is not in the window hierarchy

I have a UITabView in UIViewController, all tab items are linked to other UIViewControllers. I have written a swift code of downloading a file through internet. when I select second tabItem, this code runs well, it downloads and previews the downloaded file, Then when I click on first tabItem and then again click on second tabItem; file downloads well but it doesn't show any preview instead xCode gives me a warning message:
What I want is download file and preview file both should work when I again click on the second tabItem. whatever the code is.
warning: attempt to present QLPreviewController on KPIViewController whose view is not in the window hierarchy
I have found many solutions on the internet but it didn't work
first solution says to use
let viewer = UIDocumentInteractionController(URL: NSURL(fileURLWithPath: path))
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(viewer, animated: true, completion: nil)
but this function
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(viewer, animated: true, completion: nil)
do not accept
UIDocumentInteractionController
second solution says to override the existing presentViewController function to
override func presentViewController(viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)?) {
let APP_DELEGATE = UIApplication.sharedApplication().delegate
var presentedModalVC:UIViewController = (APP_DELEGATE!.window?!.rootViewController?.presentedViewController)!
if presentedModalVC == true {
while((presentedModalVC.presentedViewController) != nil){
presentedModalVC = presentedModalVC.presentedViewController!
}
presentedModalVC.presentViewController(viewControllerToPresent, animated: flag, completion: nil)
}
else{
APP_DELEGATE?.window!!.rootViewController?.presentViewController(viewControllerToPresent, animated: flag, completion: nil)
}
}
I tried this but it also needs a UIViewController in its parameters where I have UIDocumentInteractionController
I know these function cannot accept UIDocumentInteractionController type viewController.
here is my whole swift code:
// KPIViewController.swift
// download
//
// Created by me on 15/03/2016.
// Copyright © 2016 me. All rights reserved.
//
import UIKit
class KPIViewController: UIViewController,UITabBarDelegate, NSURLSessionDownloadDelegate, UIDocumentInteractionControllerDelegate{
#IBOutlet weak var tabBar1: UITabBar!
#IBOutlet weak var login_Item: UITabBarItem!
#IBOutlet weak var QAreport_Item: UITabBarItem!
#IBOutlet weak var KpiWebView: UIWebView!
#IBOutlet weak var progressView: UIProgressView!
var downloadTask: NSURLSessionDownloadTask!
var backgroundSession: NSURLSession!
var downloadReport:Bool!
var AuditCodeOfDashboardCell:String?
var AuditCodeForPDF:String?
let isDirectory: ObjCBool = false
override func viewDidLoad() {
super.viewDidLoad()
self.progressView.hidden = true
downloadReport = false
// Do any additional setup after loading the view.
self.tabBar1.delegate = self
}
override func viewDidAppear(animated: Bool) {
self.progressView.hidden = true
downloadReport = false
let backgroundSessionConfiguration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier("backgroundSession")
backgroundSession = NSURLSession(configuration: backgroundSessionConfiguration, delegate: self, delegateQueue: NSOperationQueue.mainQueue())
progressView.setProgress(0.0, animated: false)
var requestURL = NSURL!()
var request = NSURLRequest!()
// loading data from web
if AuditCodeOfDashboardCell != nil{
print(self.AuditCodeOfDashboardCell)
requestURL = NSURL(string:“my URL string&\(AuditCodeOfDashboardCell)”)
request = NSURLRequest(URL: requestURL!)
AuditCodeForPDF = AuditCodeOfDashboardCell
AuditCodeOfDashboardCell = nil
}else{
requestURL = NSURL(string:“my URL string”)
request = NSURLRequest(URL: requestURL!)
}
KpiWebView.loadRequest(request)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem) {
print("selected tabItem: \(item.tag)")
switch (item.tag) {
case 1:
let loginVC = self.storyboard!.instantiateViewControllerWithIdentifier("loginViewController") as! LoginView
presentViewController(loginVC, animated: true, completion: nil)
break
case 2:
if AuditCodeForPDF != nil{
downloadReport = true
let url = NSURL(string: “my URL string&\(AuditCodeForPDF)”)!
urlToDownload = url
}
// if let resultController = storyboard!.instantiateViewControllerWithIdentifier(“2”) as? QAReportViewController {
// presentViewController(resultController, animated: true, completion: nil)
// }
break
default:
break
}
if downloadReport == true{
let url = NSURL(string: “my URL string&\(AuditCodeForPDF)”)!
downloadTask = backgroundSession.downloadTaskWithURL(url)
self.progressView.hidden = false
downloadTask.resume()
downloadReport = false
}
}
// - - Handling download file- - - - - - - - -
func URLSession(session: NSURLSession,
downloadTask: NSURLSessionDownloadTask,
didFinishDownloadingToURL location: NSURL){
let path = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
let documentDirectoryPath:String = path.first!
let fileManager = NSFileManager()
var destinationURLForFile = NSURL(fileURLWithPath: documentDirectoryPath.stringByAppendingString("/Report.pdf"))
if fileManager.fileExistsAtPath(destinationURLForFile.path!){
// showFileWithPath(destinationURLForFile.path!)
do{
try fileManager.removeItemAtPath(destinationURLForFile.path!)
destinationURLForFile = NSURL(fileURLWithPath: documentDirectoryPath.stringByAppendingString("/Report.pdf"))
}catch{
print(error)
}
}
do {
try fileManager.moveItemAtURL(location, toURL: destinationURLForFile)
// show file
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.showFileWithPath(destinationURLForFile.path!)
})
}catch{
print("An error occurred while moving file to destination url")
}
}
func showFileWithPath(path: String){
let isFileFound:Bool? = NSFileManager.defaultManager().fileExistsAtPath(path)
if isFileFound == true{
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let viewer = UIDocumentInteractionController(URL: NSURL(fileURLWithPath: path))
viewer.delegate = self
viewer.presentPreviewAnimated(true)
})
}
}
func URLSession(session: NSURLSession,
downloadTask: NSURLSessionDownloadTask,
didWriteData bytesWritten: Int64,
totalBytesWritten: Int64,
totalBytesExpectedToWrite: Int64){
progressView.setProgress(Float(totalBytesWritten)/Float(totalBytesExpectedToWrite), animated: true)
}
func documentInteractionControllerViewControllerForPreview(controller: UIDocumentInteractionController) -> UIViewController{
return self
}
func documentInteractionControllerDidEndPreview(controller: UIDocumentInteractionController) {
print("document preview ends")
}
}
I cannot find any proper solution that solve my problem. I am new with swift
please anyone on help me. Thanks in advance
UIDocumentInteractionController is not kind of UIViewController. So you cannot present an UIDocumentInteractionController with presentViewController: method.
Checkout https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIDocumentInteractionController_class/
You can presenting a document preview or options menus with UIDocumentInteractionController.

swift prepareforsegue with mmdrawercontroller

I have searched every where to find a solution to this error i am getting, i have also tried to use, nsuserdefaults, struct and global var to pass on my vars to other viewcontrollers. I am use mmdrawer and i have set a navigationcontrol on my first viewcontroller that who i named userOview and identifier membersArea. Whenever i try to use prepareforsegue i am getting the following error
Could not cast value of type 'xxxxxxxx.DjInformation' (0x115da8) to 'UINavigationController' (0x3ad405e0).
My appdelegate looks like this
import UIKit
import CoreData
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var centerContainer: MMDrawerController?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
let isLoggedIn:Int = prefs.integerForKey("ISLOGGEDIN") as Int
if (isLoggedIn == 1){
var rootViewController = self.window!.rootViewController
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var centerViewController = mainStoryboard.instantiateViewControllerWithIdentifier("memberArea") as! userOverview
var leftViewController = mainStoryboard.instantiateViewControllerWithIdentifier("LeftSideViewController") as! LeftSideViewController
var rightViewController = mainStoryboard.instantiateViewControllerWithIdentifier("RightSideViewController")as! RightSideViewController
var leftSideNav = UINavigationController(rootViewController: leftViewController)
var centerNav = UINavigationController(rootViewController: centerViewController)
var rightNav = UINavigationController(rootViewController: rightViewController)
centerContainer = MMDrawerController(centerViewController: centerNav, leftDrawerViewController: leftSideNav,rightDrawerViewController:rightNav)
centerContainer!.openDrawerGestureModeMask = MMOpenDrawerGestureMode.PanningCenterView;
centerContainer!.closeDrawerGestureModeMask = MMCloseDrawerGestureMode.PanningCenterView;
window!.rootViewController = centerContainer
window!.makeKeyAndVisible()
UIApplication.sharedApplication().setStatusBarHidden(true, withAnimation: UIStatusBarAnimation.None)
}
return true
}
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application terminates.
self.saveContext()
}
// MARK: - Core Data stack
lazy var applicationDocumentsDirectory: NSURL = {
// The directory the application uses to store the Core Data store file. This code uses a directory named "bfd.Be_Fit_Donate" in the application's documents Application Support directory.
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
return urls[urls.count-1] as! NSURL
}()
lazy var managedObjectModel: NSManagedObjectModel = {
// The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
let modelURL = NSBundle.mainBundle().URLForResource("Be_Fit_Donate", withExtension: "momd")!
return NSManagedObjectModel(contentsOfURL: modelURL)!
}()
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = {
// The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
// Create the coordinator and store
var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("Be_Fit_Donate.sqlite")
var error: NSError? = nil
var failureReason = "There was an error creating or loading the application's saved data."
if coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil, error: &error) == nil {
coordinator = nil
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
dict[NSLocalizedFailureReasonErrorKey] = failureReason
dict[NSUnderlyingErrorKey] = error
error = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog("Unresolved error \(error), \(error!.userInfo)")
abort()
}
return coordinator
}()
lazy var managedObjectContext: NSManagedObjectContext? = {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
let coordinator = self.persistentStoreCoordinator
if coordinator == nil {
return nil
}
var managedObjectContext = NSManagedObjectContext()
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()
// MARK: - Core Data Saving support
func saveContext () {
if let moc = self.managedObjectContext {
var error: NSError? = nil
if moc.hasChanges && !moc.save(&error) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog("Unresolved error \(error), \(error!.userInfo)")
abort()
}
}
}
}
and the view controller i am trying to send the prepareforsegue from is as follows
import UIKit
import AVFoundation
import AVKit
import Foundation
import Social
public var AudioPlayer = AVPlayer()
public var SelectedSongNumber = Int()
public var TrackName = String()
public var TrackImage = String()
public var TrackDJ = String()
class MusicListTableViewController: UITableViewController, AVAudioPlayerDelegate{
#IBOutlet weak var playerView: UIView!
#IBOutlet weak var songName: UILabel!
#IBOutlet weak var playButton: UIButton!
#IBOutlet weak var trackDjName: UILabel!
#IBOutlet weak var imageArtwork: UIImageView!
var trackName = [String]()
var artistLabel = [String]()
var trackUrl = [String]()
var artWork = [String]()
var tags = [String]()
var artistId = [String]()
var djInfo = ""
#IBAction func facebookButton(sender: UIButton) {
if SLComposeViewController.isAvailableForServiceType(SLServiceTypeFacebook){
var facebookSheet:SLComposeViewController = SLComposeViewController(forServiceType: SLServiceTypeFacebook)
facebookSheet.setInitialText("Share on Facebook")
self.presentViewController(facebookSheet, animated: true, completion: nil)
} else {
var alert = UIAlertController(title: "Accounts", message: "Please login to a Facebook account to share.", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
#IBAction func tritterButton(sender: UIButton) {
if SLComposeViewController.isAvailableForServiceType(SLServiceTypeTwitter){
var twitterSheet:SLComposeViewController = SLComposeViewController(forServiceType: SLServiceTypeTwitter)
twitterSheet.setInitialText("Share on Twitter")
self.presentViewController(twitterSheet, animated: true, completion: nil)
} else {
var alert = UIAlertController(title: "Accounts", message: "Please login to a Twitter account to share.", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
#IBAction func myPlayList(sender: UIButton) {
var centerViewController = self.storyboard?.instantiateViewControllerWithIdentifier("myMusicList") as! myMusicList
var centerNavController = UINavigationController(rootViewController: centerViewController)
var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.centerContainer!.centerViewController = centerNavController
appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Right, animated: true, completion: nil)
}
#IBAction func favoriteButton(sender: UIButton) {
var favSong = trackName[SelectedSongNumber]
var alertView:UIAlertView = UIAlertView()
alertView.title = "Nummer Toegevoegd"
alertView.message = "Het nummer \(favSong) is nu toegevoegd aan uw favorieten muziek lijst"
alertView.delegate = self
alertView.addButtonWithTitle("OK")
alertView.show()
}
#IBAction func djInformation(sender: UIButton) {
var centerViewController = self.storyboard?.instantiateViewControllerWithIdentifier("djInformation") as! DjInformation
var centerNavController = UINavigationController(rootViewController: centerViewController)
var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.centerContainer!.centerViewController = centerNavController
appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Right, animated: true, completion: nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var artist_label = artistLabel[SelectedSongNumber]
if (segue.identifier == "djInformation" ){
var detailVC = segue.destinationViewController as! UINavigationController
let targetController = detailVC.topViewController as! DjInformation
targetController.djInfo = "hello"
}
}
func getMusicListJSON(){
let urlString = "http://xxxxxxxxxx"
let urlEncodedString = urlString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
let url = NSURL( string: urlEncodedString!)
var task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, innerError) in
let json = JSON(data: data)
let musicArray = json.arrayValue
//NSLog("\(json)")
dispatch_async(dispatch_get_main_queue(), {
for musiclist in musicArray
{
let track_name = musiclist["track_name"].stringValue
let artist = musiclist["artist"].stringValue
let track_url = musiclist["track_url"].stringValue
let art_work = musiclist["artwork"].stringValue
let track_tags = musiclist["tags"].stringValue
let artist_id = musiclist["artist_id"].stringValue
//println( "track_name: \(track_name) artist: \(artist) track_url: \(track_url) artwork: \(art_work) track_tags: \(track_tags) artist_id: \(artist_id)" )
self.trackName.append(track_name)
self.artistLabel.append(artist)
self.trackUrl.append(track_url)
self.artWork.append(art_work)
self.tags.append(track_tags)
self.artistId.append(artist_id)
}
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
return
})
})
}
task.resume()
}
override func viewDidLoad() {
super.viewDidLoad()
getMusicListJSON()
playButton.addTarget(self, action: "playButtonTapped:", forControlEvents: .TouchUpInside)
playerView.frame = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height * 0.7)
playButton.hidden = true
var error: NSError?
var success = AVAudioSession.sharedInstance().setCategory(
AVAudioSessionCategoryPlayAndRecord,withOptions: .DefaultToSpeaker, error: &error)
if !success{
NSLog("Failed to set audio session category, Error: \(error)")
}
}
func playButtonTapped(sender: AnyObject){
// set play image to pause wehen video is paused and also back
if AudioPlayer.rate == 0
{
AudioPlayer.play()
playButton.setImage(UIImage(named:"pause"), forState: UIControlState.Normal)
} else {
AudioPlayer.pause()
playButton.setImage(UIImage(named: "play"), forState: UIControlState.Normal)
}
}
#IBAction func slideOutMenu(sender: AnyObject) {
var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Left, animated: true, completion: nil)
}
#IBAction func musicMenu(sender: AnyObject) {
var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Right, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBarHidden = true
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return trackName.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("playerCell", forIndexPath: indexPath) as! playerTableViewCell
// Configure the cell...
cell.artistName.text = trackName[indexPath.row]
cell.trackName.text = artistLabel[indexPath.row]
return cell
}
// set the song to play according to the table row selected. Also set the name and artist.
func playSong() {
var playnumber = trackUrl[SelectedSongNumber]
var TrackName = trackName[SelectedSongNumber]
var TrackImage = artWork[SelectedSongNumber]
var TrackDJ = artistLabel[SelectedSongNumber]
var DjId = artistId[SelectedSongNumber]
AudioPlayer = AVPlayer(URL: NSURL(string: playnumber))
AudioPlayer.play()
songName.text = TrackName
trackDjName.text = TrackDJ
load_artwork(TrackImage)
playButton.hidden = false
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
SelectedSongNumber = indexPath.row
playSong()
}
// set the song artwork to display in the player for the current playing song.
func load_artwork(urlString: String){
var imgURL: NSURL = NSURL(string: urlString)!
let request: NSURLRequest = NSURLRequest(URL: imgURL)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: {(response: NSURLResponse!, data: NSData!,error: NSError!) -> Void in
if error == nil {
self.imageArtwork.image = UIImage(data: data)
}
})
}
}
These are the two codes that i think should be the cause of the problem. But so far i can't find any way to pas any variables and i will be needing that to complete my app. What am i doing wrong here?
I can really use some help here to get this to work. Maybe my navigation controller is correctly setup. As the first viewcontroller should be embedded in it. But i am not sure i have done that properly.
Thanks for the help.
I have found the solution which was very simple if you know what you should do. When you are using mmdrawer and would like to send over data you could just create a prepareforsegue, but you also need to create a segue with overrides the mmdrawer. I find that you use you use the following just like you would do with prepareforsgue
#IBAction func djInformation(sender: UIButton) {
var artist_label = artistLabel[SelectedSongNumber]
var centerViewController = self.storyboard?.instantiateViewControllerWithIdentifier("djInformation") as! DjInformation
centerViewController.djINfo = "\(artist_label)"
var centerNavController = UINavigationController(rootViewController: centerViewController)
var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.centerContainer!.centerViewController = centerNavController
appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Right, animated: true, completion: nil)
}
so basically what you do in prepareforsegue you can also do in button that is initiating the mmdrawercontroller.
Hope this makes sense and helps some one else. I am in no way a pro and i am not sure if this is proper or more something like a hack. but it fast and easy.

Tutorial for Google autocomplete places api for swift

I would like to have an autocomplete textfield that autocompletes locations for me like the one for android:
https://developers.google.com/places/training/autocomplete-android
Does anyone know where I can find a tutorial for this or an example?
Thanks!
Steps :
Add the Alamofire CocoaPods in your swift project.
Find your Google place API key on Google APIs Console.
Add following code
ViewController.swift
import UIKit
class ViewController: UIViewController {
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let gpaViewController = GooglePlacesAutocomplete(
apiKey: "YOUR GOOGLE PLACE API KEY",
placeType: .Address
)
gpaViewController.placeDelegate = self
presentViewController(gpaViewController, animated: true, completion: nil)
}
}
extension ViewController: GooglePlacesAutocompleteDelegate {
func placeSelected(place: Place) {
println(place.description)
}
func placeViewClosed() {
dismissViewControllerAnimated(true, completion: nil)
}
}
GooglePlacesAutocomplete.swift
import UIKit
import Alamofire
enum PlaceType: Printable {
case All
case Geocode
case Address
case Establishment
case Regions
case Cities
var description : String {
switch self {
case .All: return ""
case .Geocode: return "geocode"
case .Address: return "address"
case .Establishment: return "establishment"
case .Regions: return "regions"
case .Cities: return "cities"
}
}
}
struct Place {
let id: String
let description: String
}
protocol GooglePlacesAutocompleteDelegate {
func placeSelected(place: Place)
func placeViewClosed()
}
// MARK: - GooglePlacesAutocomplete
class GooglePlacesAutocomplete: UINavigationController {
var gpaViewController: GooglePlacesAutocompleteContainer?
var placeDelegate: GooglePlacesAutocompleteDelegate? {
get { return gpaViewController?.delegate }
set { gpaViewController?.delegate = newValue }
}
convenience init(apiKey: String, placeType: PlaceType = .All) {
let gpaViewController = GooglePlacesAutocompleteContainer(
apiKey: apiKey,
placeType: placeType
)
self.init(rootViewController: gpaViewController)
self.gpaViewController = gpaViewController
let closeButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Stop, target: self, action: "close")
gpaViewController.navigationItem.leftBarButtonItem = closeButton
gpaViewController.navigationItem.title = "Enter Address"
}
func close() {
placeDelegate?.placeViewClosed()
}
}
// MARK: - GooglePlaceSearchDisplayController
class GooglePlaceSearchDisplayController: UISearchDisplayController {
override func setActive(visible: Bool, animated: Bool) {
if active == visible { return }
searchContentsController.navigationController?.navigationBarHidden = true
super.setActive(visible, animated: animated)
searchContentsController.navigationController?.navigationBarHidden = false
if visible {
searchBar.becomeFirstResponder()
} else {
searchBar.resignFirstResponder()
}
}
}
// MARK: - GooglePlacesAutocompleteContainer
class GooglePlacesAutocompleteContainer: UIViewController {
var delegate: GooglePlacesAutocompleteDelegate?
var apiKey: String?
var places = [Place]()
var placeType: PlaceType = .All
convenience init(apiKey: String, placeType: PlaceType = .All) {
self.init(nibName: "GooglePlacesAutocomplete", bundle: nil)
self.apiKey = apiKey
self.placeType = placeType
}
override func viewDidLoad() {
super.viewDidLoad()
let tv: UITableView? = searchDisplayController?.searchResultsTableView
tv?.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
}
// MARK: - GooglePlacesAutocompleteContainer (UITableViewDataSource / UITableViewDelegate)
extension GooglePlacesAutocompleteContainer: UITableViewDataSource, UITableViewDelegate {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return places.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.searchDisplayController?.searchResultsTableView?.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
// Get the corresponding candy from our candies array
let place = self.places[indexPath.row]
// Configure the cell
cell.textLabel.text = place.description
cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
delegate?.placeSelected(self.places[indexPath.row])
}
}
// MARK: - GooglePlacesAutocompleteContainer (UISearchDisplayDelegate)
extension GooglePlacesAutocompleteContainer: UISearchDisplayDelegate {
func searchDisplayController(controller: UISearchDisplayController, shouldReloadTableForSearchString searchString: String!) -> Bool {
getPlaces(searchString)
return false
}
private func getPlaces(searchString: String) {
Alamofire.request(.GET,
"https://maps.googleapis.com/maps/api/place/autocomplete/json",
parameters: [
"input": searchString,
"type": "(\(placeType.description))",
"key": apiKey ?? ""
]).responseJSON { request, response, json, error in
if let response = json as? NSDictionary {
if let predictions = response["predictions"] as? Array<AnyObject> {
self.places = predictions.map { (prediction: AnyObject) -> Place in
return Place(
id: prediction["id"] as String,
description: prediction["description"] as String
)
}
}
}
self.searchDisplayController?.searchResultsTableView?.reloadData()
}
}
}
GooglePlacesAutocomplete.xib
Hope this will help others.
Here's full updated code for Google Autocomplete place API.
Xcode 10.0 & Swift 4.2
Follow this link as to Get Google API KEY.
After Getting the API KEY
Install Cocoa Pods:
source 'https://github.com/CocoaPods/Specs.git'
target 'YOUR_APPLICATION_TARGET_NAME_HERE' do
pod 'GooglePlaces'
pod 'GooglePlacePicker'
pod 'GoogleMaps'
end
Appdelegate File:
import UIKit
import GooglePlaces
let GOOGLE_API_KEY = "AIzaSyCuZkL7bh_hIDggnJob-b0cDueWlvRgpck"
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
GMSPlacesClient.provideAPIKey(GOOGLE_API_KEY)
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
ViewController File:
import UIKit
import GooglePlaces
class ViewController: UIViewController ,CLLocationManagerDelegate{
var placesClient: GMSPlacesClient!
// Add a pair of UILabels in Interface Builder, and connect the outlets to these variables.
#IBOutlet var nameLabel: UILabel!
#IBOutlet var addressLabel: UILabel!
let locationManager = CLLocationManager()
var resultsViewController: GMSAutocompleteResultsViewController?
var searchController: UISearchController?
var resultView: UITextView?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
locationManager.delegate = self
if CLLocationManager.authorizationStatus() == .notDetermined
{
locationManager.requestAlwaysAuthorization()
}
placesClient = GMSPlacesClient.shared()
// self.addToNavbar()
// self.addToSubview()
self.addToPopover()
}
func addToNavbar(){
resultsViewController = GMSAutocompleteResultsViewController()
resultsViewController?.delegate = self
searchController = UISearchController(searchResultsController: resultsViewController)
searchController?.searchResultsUpdater = resultsViewController
// Put the search bar in the navigation bar.
searchController?.searchBar.sizeToFit()
navigationItem.titleView = searchController?.searchBar
// When UISearchController presents the results view, present it in
// this view controller, not one further up the chain.
definesPresentationContext = true
// Prevent the navigation bar from being hidden when searching.
searchController?.hidesNavigationBarDuringPresentation = false
}
func addToSubview(){
resultsViewController = GMSAutocompleteResultsViewController()
resultsViewController?.delegate = self
searchController = UISearchController(searchResultsController: resultsViewController)
searchController?.searchResultsUpdater = resultsViewController
let subView = UIView(frame: CGRect(x: 0, y: 65.0, width: 350.0, height: 45.0))
subView.addSubview((searchController?.searchBar)!)
view.addSubview(subView)
searchController?.searchBar.sizeToFit()
searchController?.hidesNavigationBarDuringPresentation = false
// When UISearchController presents the results view, present it in
// this view controller, not one further up the chain.
definesPresentationContext = true
}
func addToPopover(){
resultsViewController = GMSAutocompleteResultsViewController()
resultsViewController?.delegate = self
searchController = UISearchController(searchResultsController: resultsViewController)
searchController?.searchResultsUpdater = resultsViewController
// Add the search bar to the right of the nav bar,
// use a popover to display the results.
// Set an explicit size as we don't want to use the entire nav bar.
searchController?.searchBar.frame = (CGRect(x: 0, y: 0, width: 250.0, height: 44.0))
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: (searchController?.searchBar)!)
// When UISearchController presents the results view, present it in
// this view controller, not one further up the chain.
definesPresentationContext = true
// Keep the navigation bar visible.
searchController?.hidesNavigationBarDuringPresentation = false
searchController?.modalPresentationStyle = .popover
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus)
{
print(status)
}
// Add a UIButton in Interface Builder, and connect the action to this function.
#IBAction func getCurrentPlace(_ sender: UIButton) {
placesClient.currentPlace(callback: { (placeLikelihoodList, error) -> Void in
if let error = error {
print("Pick Place error: \(error.localizedDescription)")
return
}
self.nameLabel.text = "No current place"
self.addressLabel.text = ""
if let placeLikelihoodList = placeLikelihoodList {
print("placeLikelihoodList -- \(placeLikelihoodList)")
let place = placeLikelihoodList.likelihoods.first?.place
if let place = place {
self.nameLabel.text = place.name
self.addressLabel.text = place.formattedAddress?.components(separatedBy: ", ")
.joined(separator: "\n")
print(place.name)
print(place.coordinate)
print(place.placeID)
print(place.phoneNumber)
print(place.formattedAddress ?? "")
}
}
})
}
}
//MARK: Extentions
// Handle the user's selection.
extension ViewController: GMSAutocompleteResultsViewControllerDelegate {
func resultsController(_ resultsController: GMSAutocompleteResultsViewController,
didAutocompleteWith place: GMSPlace) {
searchController?.isActive = false
// Do something with the selected place.
print("Place name: \(place.name)")
print("Place address: \(String(describing: place.formattedAddress))")
print("Place attributions: \(place.attributions)")
}
func resultsController(_ resultsController: GMSAutocompleteResultsViewController,
didFailAutocompleteWithError error: Error){
// TODO: handle the error.
print("Error: ", error.localizedDescription)
}
// Turn the network activity indicator on and off again.
func didRequestAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
func didUpdateAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
}
Lightweight Solution!
Instead of using Google framework and Third party library to make simple requests I created a simple library where you can Make a number of Google api requests like Google Autocomplete, Google ReverseGeo , Place Information and Path api for getting path between two location.
To use the library all you have to do is
step-1 Import GoogleApiHelper into your project.
step-2 Initialise GoogleApiHelper
GoogleApi.shared.initialiseWithKey("API_KEY")
step-3 Call the methods
var input = GInput()
input.keyword = "San francisco"
GoogleApi.shared.callApi(input: input) { (response) in
if let results = response.data as? [GApiResponse.Autocomplete], response.isValidFor(.autocomplete) {
//Enjoy the Autocomplete Api
} else { print(response.error ?? "ERROR") }
}
You can find the library here
Using Alamofire get the autocomplete Google places result from data, you can show it in table view cell
plist configuration
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Code
import UIKit
import Alamofire
class GooglePlacesViewController: UIViewController,UISearchBarDelegate,UITableViewDataSource,UITableViewDelegate {
#IBOutlet weak var srchLocation: UISearchBar!
#IBOutlet weak var tblLoction: UITableView!
var arrPlaces = NSMutableArray(capacity: 100)
let operationQueue = OperationQueue()
let currentLat = 51.5033640
let currentLong = -0.1276250
var LocationDataDelegate : LocationData! = nil
var tblLocation : UITableView!
var lblNodata = UILabel()
override func viewDidLoad()
{
super.viewDidLoad()
lblNodata.frame = CGRect(x: 0, y: 80, width:
self.view.frame.size.width, height: self.view.frame.size.height-60)
lblNodata.text = "Please enter text to get your location"
self.view.addSubview(lblNodata)
srchLocation.placeholder = "Ente your location details"
lblNodata.textAlignment = .center
srchLocation.delegate = self
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
self.beginSearching(searchText: searchText)
}
func beginSearching(searchText:String) {
if searchText.characters.count == 0 {
self.arrPlaces.removeAllObjects()
tblLoction.isHidden = true
lblNodata.isHidden = false
return
}
operationQueue.addOperation { () -> Void in
self.forwardGeoCoding(searchText: searchText)
}
}
//MARK: - Search place from Google -
func forwardGeoCoding(searchText:String) {
googlePlacesResult(input: searchText) { (result) -> Void in
let searchResult:NSDictionary = ["keyword":searchText,"results":result]
if result.count > 0
{
let features = searchResult.value(forKey: "results") as! NSArray
self.arrPlaces = NSMutableArray(capacity: 100)
print(features.count)
for jk in 0...features.count-1
{
let dict = features.object(at: jk) as! NSDictionary
self.arrPlaces.add(dict)
}
DispatchQueue.main.async(execute: {
if self.arrPlaces.count != 0
{
self.tblLoction.isHidden = false
self.lblNodata.isHidden = true
self.tblLoction.reloadData()
}
else
{
self.tblLoction.isHidden = true
self.lblNodata.isHidden = false
self.tblLoction.reloadData()
}
});
}
}
}
//MARK: - Google place API request -
func googlePlacesResult(input: String, completion: #escaping (_ result: NSArray) -> Void) {
let searchWordProtection = input.replacingOccurrences(of: " ", with: ""); if searchWordProtection.characters.count != 0 {
let urlString = NSString(format: "https://maps.googleapis.com/maps/api/place/autocomplete/json?input=%#&types=establishment|geocode&location=%#,%#&radius=500&language=en&key= your key",input,"\(currentLocationLatitude)","\(currentLocationLongtitude)")
print(urlString)
let url = NSURL(string: urlString.addingPercentEscapes(using: String.Encoding.utf8.rawValue)!)
print(url!)
let defaultConfigObject = URLSessionConfiguration.default
let delegateFreeSession = URLSession(configuration: defaultConfigObject, delegate: nil, delegateQueue: OperationQueue.main)
let request = NSURLRequest(url: url! as URL)
let task = delegateFreeSession.dataTask(with: request as URLRequest, completionHandler:
{
(data, response, error) -> Void in
if let data = data
{
do {
let jSONresult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as! [String:AnyObject]
let results:NSArray = jSONresult["predictions"] as! NSArray
let status = jSONresult["status"] as! String
if status == "NOT_FOUND" || status == "REQUEST_DENIED"
{
let userInfo:NSDictionary = ["error": jSONresult["status"]!]
let newError = NSError(domain: "API Error", code: 666, userInfo: userInfo as [NSObject : AnyObject])
let arr:NSArray = [newError]
completion(arr)
return
}
else
{
completion(results)
}
}
catch
{
print("json error: \(error)")
}
}
else if let error = error
{
print(error)
}
})
task.resume()
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrPlaces.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let tblCell = tableView.dequeueReusableCell(withIdentifier: "locationCell")
let dict = arrPlaces.object(at: indexPath.row) as! NSDictionary
tblCell?.textLabel?.text = dict.value(forKey: "description") as? String
tblCell?.textLabel?.numberOfLines = 0
tblCell?.textLabel?.sizeToFit()
return tblCell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
if LocationDataDelegate != nil
{
let dict = arrPlaces.object(at: indexPath.row) as! NSDictionary
print(dict.value(forKey: "terms") as! NSArray)
let ArrSelected = dict.value(forKey: "terms") as! NSArray
LocationDataDelegate.didSelectLocationData(LocationData: ArrSelected)
}
self.dismiss(animated: true, completion: nil)
}
}
class AddNewAddressVC: UIViewController,UITextFieldDelegate{
func autocompleteClicked() {
let autocompleteController = GMSAutocompleteViewController()
autocompleteController.delegate = self
// Specify the place data types to return.
let fields: GMSPlaceField = GMSPlaceField(rawValue: UInt(GMSPlaceField.name.rawValue) |
UInt(GMSPlaceField.placeID.rawValue))!
autocompleteController.placeFields = fields
// Specify a filter.
let filter = GMSAutocompleteFilter()
filter.type = .address
autocompleteController.autocompleteFilter = filter
// Display the autocomplete view controller.
present(autocompleteController, animated: true, completion: nil)
}
#IBAction func action_selectGooglePlaces(_ sender: UIButton) {
autocompleteClicked()
}
}
extension AddNewAddressVC: GMSAutocompleteViewControllerDelegate {
// Handle the user's selection.
func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
print("Place name: \(place.name)")
print("Place ID: \(place.placeID)")
print("Place attributions: \(place.attributions)")
dismiss(animated: true, completion: nil)
}
func viewController(_ viewController: GMSAutocompleteViewController, didFailAutocompleteWithError error: Error) {
// TODO: handle the error.
print("Error: ", error.localizedDescription)
}
// User canceled the operation.
func wasCancelled(_ viewController: GMSAutocompleteViewController) {
dismiss(animated: true, completion: nil)
}
// Turn the network activity indicator on and off again.
func didRequestAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
func didUpdateAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
}

Resources