CMTimeGetSeconds crash app - objective-c

I use AVPlayer to play video link and add observer to update progress bar. But with the video which have time more than 10s, my app get crash. Below is my code
Play video button press:
#IBAction func playVideoButtonPressed(sender: AnyObject) {
if let contentUrl = curVideoModel?.video_Content_Url{
player = AVPlayer(URL: NSURL(string: contentUrl))
player?.actionAtItemEnd = AVPlayerActionAtItemEnd.None
playerLayer = AVPlayerLayer(player: player)
playerLayer!.frame = CGRectMake(0, 0, self.imvVideoThumbnail.frame.size.width, self.imvVideoThumbnail.frame.size.height)
playerLayer!.backgroundColor = UIColor.blackColor().CGColor
playerLayer!.videoGravity = AVLayerVideoGravityResizeAspectFill
self.playvideoHolderView.layer.addSublayer(playerLayer)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "itemDidFinishPlaying:", name: AVPlayerItemDidPlayToEndTimeNotification, object: player?.currentItem)
player?.addPeriodicTimeObserverForInterval(CMTimeMakeWithSeconds(1.0/60.0, Int32(NSEC_PER_SEC)), queue: nil, usingBlock: { (time) -> Void in
self.updateProgressbar()
})
player!.play()
}
}
The function call back when video finish play:
NSNotificationCenter.defaultCenter().removeObserver(self, name: AVPlayerItemDidPlayToEndTimeNotification, object: player?.currentItem)
self.progressBar.progress = 0
if notification.name == AVPlayerItemDidPlayToEndTimeNotification{
if player != nil{
player?.pause()
}
if playerLayer != nil{
playerLayer!.removeFromSuperlayer()
}
}
And the function to update progressbar:
var duration: Double
var time: Double
if player != nil{
duration = CMTimeGetSeconds(player!.currentItem.duration)
time = CMTimeGetSeconds(player!.currentTime())
} else{
duration = 0
time = 0
}
self.lblTotalTime.text = "\(Int(duration))"
self.lblCurrentTime.text = "\(Int(time))"
self.progressBar.progress = Float(time / duration)
P/S: It crash on real device(iPod touch iOS 8.3). But play OK on simulator
EDIT: Maybe the comment of Peter is right. I check: if player!.currentItem.duration.value > 0 before get: duration = CMTimeGetSeconds(player!.currentItem.duration) .Then the crash is fixed**

Related

Speaker isn't an available input when airpods are connected

I am trying to override me application's audio to speaker when the user hits a button but speaker doesn't show as an available audioutput when airpods are connected. What can i do to make the sound go to speaker? This code works when any other bluetooth device is connected.
po AVAudioSession.sharedInstance().availableInputs
AVAudioSessionPortDescription: 0x283dc3410, type = MicrophoneBuiltIn; name = iPhone Microphone; UID = Built-In Microphone; selectedDataSource = (null)>
VAudioSessionPortDescription: 0x283dc32b0, type = BluetoothHFP; name = Lindsey’s AirPods; UID = 94:B0:1F:C3:FF:6B-tsco; selectedDataSource = (null)>
var err: Error? = nil
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(AVAudioSession.Category.playAndRecord, mode: .voiceChat, options: [.allowBluetooth, .allowBluetoothA2DP, .mixWithOthers])
} catch {
NSLog("Unable to change audio category because : \(String(describing: err?.localizedDescription))")
err = nil
}
do {
try AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSession.PortOverride.speaker)
} catch let error as NSError {
print("audioSession error turning on speaker: \(error.localizedDescription)")
}

Google Drive API IOS Permissions of GTLRDriveService

I am playing around with the google drive API and trying to build a simple app that uploads a picture to my google drive. The app is supposed to upload a picture once the user is signed in, however it gives an error of
"2017-09-14 00:55:20.342237-0400 driveTest[6705:1647551] An error
occurred: Error Domain=com.google.GTLRErrorObjectDomain Code=403
"Insufficient Permission"
UserInfo={GTLRStructuredError=GTLRErrorObject 0x1c4251d30:
{message:"Insufficient Permission" errors:[1] code:403},
NSLocalizedDescription=Insufficient Permission}"
I have tried to pass it the service which is of type GTLRDriveService to the initSetup() function of the userSetUp class, but to no avail. Could someone please point me to the right track as to why my permissions are not working even though I have logged on correctly, and the part where I am passing in the GTLRDriveService is in the code that runs after a sucessful login.
I instantiate a userSetUp object and I
let setUpUser = userSetUp()
setUpUser.initSetup(service)
I have userSetUp written in objective c as such and it is bridged correctly as I am able to instantiate it in my viewcontroller file which is written in swift.
UserSetUp:::::::
#import "userSetUp.h"
#import <GoogleSignIn/GoogleSignIn.h>
#import GoogleAPIClientForREST;
#implementation userSetUp
- (void) initSetup:(GTLRDriveService *) driveService {
printf("heloooooaiosuoiadoidauoalo");
//GTLRDriveService *driveService = [GTLRDriveService new];
//NSData *fileData = [[NSFileManager defaultManager] contentsAtPath:#"files/apple.jpg"];
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"apple" ofType:#"jpg"];
NSData *fileData = [NSData dataWithContentsOfFile:filePath];
GTLRDrive_File *metadata = [GTLRDrive_File object];
metadata.name = #"apple.jpg";
//metadata.mimeType = #"application/vnd.google-apps.document";
GTLRUploadParameters *uploadParameters = [GTLRUploadParameters uploadParametersWithData:fileData
MIMEType:#"image/jpeg"];
uploadParameters.shouldUploadWithSingleRequest = TRUE;
GTLRDriveQuery_FilesCreate *query = [GTLRDriveQuery_FilesCreate queryWithObject:metadata
uploadParameters:uploadParameters];
query.fields = #"id";
[driveService executeQuery:query completionHandler:^(GTLRServiceTicket *ticket,
GTLRDrive_File *file,
NSError *error) {
if (error == nil) {
//NSLog(#"File ID %#", file.identifier);
printf("it worked");
} else {
NSLog(#"An error occurred: %#", error);
}
}];
printf("upload complete!");
}
#end
And Viewcontroller. swift
import GoogleAPIClientForREST
import GoogleSignIn
import UIKit
class ViewController: UIViewController, GIDSignInDelegate, GIDSignInUIDelegate {
// If modifying these scopes, delete your previously saved credentials by
// resetting the iOS simulator or uninstall the app.
private let scopes = [kGTLRAuthScopeDriveReadonly]
let service = GTLRDriveService()
let signInButton = GIDSignInButton()
let output = UITextView()
override func viewDidLoad() {
super.viewDidLoad()
// Configure Google Sign-in.
GIDSignIn.sharedInstance().delegate = self
GIDSignIn.sharedInstance().uiDelegate = self
GIDSignIn.sharedInstance().scopes = scopes
GIDSignIn.sharedInstance().signInSilently()
signInButton.frame = CGRect(x: view.frame.width/2 - signInButton.frame.width , y: view.frame.height/2, width: signInButton.frame.width, height: signInButton.frame.height)
// Add the sign-in button.
view.addSubview(signInButton)
// Add a UITextView to display output.
output.frame = view.bounds
output.isEditable = false
output.contentInset = UIEdgeInsets(top: 20, left: 0, bottom: 20, right: 0)
output.autoresizingMask = [.flexibleHeight, .flexibleWidth]
output.isHidden = true
view.addSubview(output);
//let itsASetup()
}
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!,
withError error: Error!) {
if let error = error {
showAlert(title: "Authentication Error", message: error.localizedDescription)
self.service.authorizer = nil
} else {
self.signInButton.isHidden = true
self.output.isHidden = false
self.service.authorizer = user.authentication.fetcherAuthorizer()
listFiles()
}
}
// List up to 10 files in Drive
func listFiles() {
let query = GTLRDriveQuery_FilesList.query()
query.pageSize = 10
service.executeQuery(query,
delegate: self,
didFinish: #selector(displayResultWithTicket(ticket:finishedWithObject:error:))
)
}
// Process the response and display output
#objc func displayResultWithTicket(ticket: GTLRServiceTicket,
finishedWithObject result : GTLRDrive_FileList,
error : NSError?) {
if let error = error {
showAlert(title: "Error", message: error.localizedDescription)
return
}
var text = "";
if let files = result.files, !files.isEmpty {
text += "Files:\n"
for file in files {
text += "\(file.name!) (\(file.identifier!))\n"
}
} else {
text += "No files found."
}
output.text = text
let setUpUser = userSetUp()
setUpUser.initSetup(service)
}
// Helper for showing an alert
func showAlert(title : String, message: String) {
let alert = UIAlertController(
title: title,
message: message,
preferredStyle: UIAlertControllerStyle.alert
)
let ok = UIAlertAction(
title: "OK",
style: UIAlertActionStyle.default,
handler: nil
)
alert.addAction(ok)
present(alert, animated: true, completion: nil)
}
}
try to Change your scope like:
class ViewController: UIViewController, GIDSignInDelegate, GIDSignInUIDelegate
{
// If modifying these scopes, delete your previously saved credentials by
private let scopes = ["https://www.googleapis.com/auth/drive"]
...
}

how to send the locations to the server continuously for 5 hours when the application is in background mode ?

I am making an app in which I have to send the locations to the server on an Api call . It can work in the background also . I have implemented the code for this , but when I came back from background . My current work is not resuming . It is going just to the dashboard page , not backing to the page which is in the background .
Here is my code in the appdelegate
func applicationWillEnterForeground(_ application: UIApplication) {
if !isComingFromTrip {
locationManager.stopUpdatingLocation()
}
locationStarted = false
}
func applicationDidEnterBackground(_ application: UIApplication) {
if isComingFromTrip {
if UIApplication.shared.applicationState == .background {
print("start backgroun tracking from appdelegate")
locationManager.startUpdatingLocation()
}
//change locationManager status after time
self.runBackgroundTask(20)
} else {
isComingFromTrip = false
}
FIRMessaging.messaging().disconnect()
print("Disconnected from FCM.")
}
func runBackgroundTask(_ time: Int) -> Void {
if UIApplication.shared.applicationState == .background {
//create UIBackgroundTaskIdentifier and create tackground task, which starts after time
backgroundUpdateTask = app.beginBackgroundTask(expirationHandler: {() -> Void in
self.app.endBackgroundTask(self.backgroundUpdateTask)
self.backgroundUpdateTask = UIBackgroundTaskInvalid
})
DispatchQueue.global(qos: .default).async(execute: {() -> Void in
var t = Timer.scheduledTimer(timeInterval: TimeInterval(time), target: self, selector: #selector(self.startTrackingBg), userInfo: nil, repeats: false)
RunLoop.current.add(t, forMode: RunLoopMode.defaultRunLoopMode)
RunLoop.current.run()
})
}
}
func startTrackingBg() {
//write background time remaining
print(String(format: "backgroundTimeRemaining: %.0f", UIApplication.shared.backgroundTimeRemaining))
//set default time
var time: Int = 60
//if locationManager is ON
if locationStarted == true {
//stop update location
locationManager.stopUpdatingLocation()
locationStarted = false
}
else {
//start updating location
locationManager.startUpdatingLocation()
locationStarted = true
//Time how long the application will update your location
time = 5
}
self.runBackgroundTask(time)
}
func StartupdateLocation() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.requestAlwaysAuthorization()
if #available(iOS 9.0, *) {
locationManager.allowsBackgroundLocationUpdates = true
} else {
// Fallback on earlier versions
}
locationManager.pausesLocationUpdatesAutomatically = false
locationManager.startUpdatingLocation()
}
I have enabled the background mode also Target->Capabilities-> Location
and also added key for this .

How to add snooze effect once a notification is delivered in ios 10

I am implementing UserNotification in my app. When the notification gets fired it shows two action, in one i want to add snooze effect, it must snooze after 5 mins again. How to handle it ? thanks for all ! help if any one do have idea
Well to snooze notification you can create another notification with same details of current notification and increase the fire date by 5 mins.
Here is the code I used :
func snoozeScheduledNotification(notification:UILocalNotification) -> Void {
// Snooze for 10 mins
let localNotification = UILocalNotification()
localNotification.fireDate = notification.fireDate?.addingTimeInterval(60*10)
localNotification.repeatInterval = NSCalendar.Unit(rawValue: 0) // 0 = No Repeat
localNotification.alertBody = notification.alertBody
localNotification.soundName = notification.soundName
localNotification.userInfo = notification.userInfo
localNotification.category = notification.category
UIApplication.shared.scheduleLocalNotification(localNotification)
}
Hope it helps you.
The shortest and simplest code I found about it
For Swift 3/4
extension UNNotification {
func snoozeNotification(for hours: Int, minutes: Int, seconds: Int) {
let content = UNMutableNotificationContent()
content.title = "Another Alert"
content.body = "Your message"
content.sound = .default()
let identifier = self.request.identifier
guard let oldTrigger = self.request.trigger as? UNCalendarNotificationTrigger else {
debugPrint("Cannot reschedule notification without calendar trigger.")
return
}
var components = oldTrigger.dateComponents
components.hour = (components.hour ?? 0) + hours
components.minute = (components.minute ?? 0) + minutes
components.second = (components.second ?? 0) + seconds
let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: false)
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
debugPrint("Rescheduling failed", error.localizedDescription)
} else {
debugPrint("rescheduled success")
}
}
}
}
You just need to call it this way :
response.notification.snoozeNotification(for: 0, minutes: 0, seconds: 30)
Credit goes to Simon Ljungberg : https://gist.github.com/simme/96264d5ceee394083d18e2c64f42a3a9
For iOS10, use this code.
Use this code in AppDelegate.swift file.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
let center = UNUserNotificationCenter.current()
let category = UNNotificationCategory(identifier: "identifier", actions: [], intentIdentifiers: [])
center.setNotificationCategories([category])
center.requestAuthorization(options: [.badge, .alert , .sound]) { (greanted, error) in
print(error)
}
return true
}
You can put this code in any view controller.
let content = UNMutableNotificationContent.init()
content.title = "Notification Title"
content.subtitle = "Notification Sub-Title"
content.body = "Notification Body"
content.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
let request = UNNotificationRequest(identifier: "identifier", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
UNUserNotificationCenter.current().delegate = self
if (error != nil){
//handle here
}
}
You can handle notification using following method:
extension UIViewController: UNUserNotificationCenterDelegate {
public func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Swift.Void) {
completionHandler( [.alert, .badge, .sound])
}
public func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Swift.Void) {
print("Tapped in notification")
}
}
You can use this Blog as reference and Example.

Switch from Built-in Speaker to Bluetooth Speaker

I just found a code that switches the speaker between the built-in and the connected bluetooth speaker:
changeResult = [[AVAudioSession sharedInstance] overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker
error:&audioError];
But this is in Obj-C, is there anyone here who is kind enough to convert this to Swift?
Thanks!
UPDATE: Here's the code I am using to record a video. Here's the scenario. I am playing a music while the bluetooth speaker is connected. So the sound output is in the external speaker. When I click the record button, the sound output transfers to the built-in speaker. And that is my problem. The output should always be in the external speaker.
#IBAction func record_video(sender: AnyObject) {
var initialOutputURL = NSURL(fileURLWithPath: "")
do
{
initialOutputURL = try NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true).URLByAppendingPathComponent("output").URLByAppendingPathExtension("mov")
}catch
{
print(error)
}
if !isRecording
{
isRecording = true
if let outputs = captureSession.outputs as? [AVCaptureOutput] {
for output in outputs {
captureSession.removeOutput(output)
}
}
do
{
try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord, withOptions: [AVAudioSessionCategoryOptions.MixWithOthers, AVAudioSessionCategoryOptions.AllowBluetooth])
//print(audioSession.setOutputDataSource)
}
catch
{
print("Can't Set Audio Session Category: \(error)")
}
do
{
try audioSession.setMode(AVAudioSessionModeVideoRecording)
}
catch
{
print("Can't Set Audio Session Mode: \(error)")
}
// Start Session
do
{
try audioSession.setActive(true)
}
catch
{
print("Can't Start Audio Session: \(error)")
}
UIView.animateWithDuration(0.5, delay: 0.0, options: [.Repeat, .Autoreverse, .AllowUserInteraction], animations: { () -> Void in
self.record.transform = CGAffineTransformMakeScale(0.75, 0.75)
}, completion: nil)
let audioInputDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeAudio)
AVCaptureDevice.devicesWithMediaType(AVMediaTypeAudio)
do
{
if let availableInputs = audioSession.availableInputs {
for input in availableInputs {
if input.portType == AVAudioSessionPortBuiltInMic ||
input.portType == AVAudioSessionPortHeadsetMic {
inputs.append(input)
try audioSession.setPreferredInput(input)
}
}
}
let audioInput = try AVCaptureDeviceInput(device: audioInputDevice)
// Add Audio Input
if captureSession.canAddInput(audioInput)
{
captureSession.addInput(audioInput)
}
else
{
NSLog("Can't Add Audio Input")
}
/**
let videoInput: AVCaptureDeviceInput
do
{
videoInput = try AVCaptureDeviceInput(device: captureDevice)
// Add Video Input
if captureSession.canAddInput(videoInput)
{
captureSession.addInput(videoInput)
}
else
{
NSLog("ERROR: Can't add video input")
}
}
catch let error
{
NSLog("ERROR: Getting input device: \(error)")
}
*/
try AVAudioSession.sharedInstance().overrideOutputAudioPort(.Speaker)
videoFileOutput = AVCaptureMovieFileOutput()
captureSession.addOutput(videoFileOutput)
captureSession.sessionPreset = AVCaptureSessionPresetHigh
captureSession.automaticallyConfiguresApplicationAudioSession = false
videoFileOutput?.startRecordingToOutputFileURL(initialOutputURL, recordingDelegate: self)
}
catch let error
{
NSLog("Error Getting Input Device: \(error)")
}
}
else
{
isRecording = false
UIView.animateWithDuration(0.5, delay: 0, options: [], animations: { () -> Void in
self.record.transform = CGAffineTransformMakeScale(1.0, 1.0)
}, completion: nil)
record.layer.removeAllAnimations()
videoFileOutput?.stopRecording()
}
}