I'm using CallKit to connect Twilio RTC and I'd like to know more about CallKit.
There is a timer in the lock screen of the CallKit and I am trying to start the timer after I receive response from the server. Right now the timer would just start counting right after the "connecting" phase, in which my response haven't even started yet.
It seems like starting after calling [action fulfill], but how should I implement with the http call? or Twilio function?
If I'm calling from the call history of the native call app made by Apple, where should I store the mapping information for UUID to the user-id I'd like to call?
I know that the callee can save these information in CXUpdate and show without problem, but how about the caller? I can't tell from the examples of saving call information in CXHandle.
Thanks ahead for reading through :)
Intiate call timer after call connecting state to connected
var callTimer: Timer?
var timeDuration: TimeInterval = 0.0
//MARK:- CallTimer
func initiateCallTimer() {
if callTimer == nil {
self.callTimer = Timer.scheduledTimer(timeInterval: kRefreshTimeInterval, target: self, selector: #selector(CallViewController.refreshCallTimer(_:)), userInfo: nil, repeats: true)
}
}
func callTimerInvalidate() {
if callTimer != nil {
callTimer?.invalidate()
callTimer = nil
}
}
#objc func refreshCallTimer(_ sender: Timer) {
timeDuration += kRefreshTimeInterval
print(timeDuration)
self.lblConnectiong.text = string(withTimeDuration: timeDuration)
}
func string(withTimeDuration timeDuration: TimeInterval) -> String? {
let minutes = Int(timeDuration / 60)
let seconds = Int(timeDuration) % 60
let timeStr = String(format: "%ld:%02ld", minutes, seconds)
return timeStr
}
and invalidate after rejected or end
self.callTimerInvalidate()
for this call from recent call list
In your app's Info.plist, you must have INStartAudioCallIntent and/or INStartVideoCallIntent in the NSUserActivityTypes key, and your app delegate must implement the -application:continueUserActivity:restorationHandler: method to handle the start call intent.
Related
So I am creating an app that has countdown timer. When the app quits I am using observers to know if the app is in background. If it is, I invalidate the timer and store the quit time in userDefaults. Then when the app comes back to foreground, I create a new timer and calculate the time that the app has been in background and subtract it from the total duration in order to get the elapsed time. When app goes to the background, I am storing the time in UserDefaults:
#objc func applicationDidEnterBackground() {
let defaults = UserDefaults.standard
let quitTime = Date()
defaults.set(quitTime, forKey: "quitTimeKey") //Storing the time of quit in UserDefaults
timer?.invalidate()
}
Then I create a new instance of timer when app enters foreground:
#objc func appEntersForeground() {
calculateTimeLeft()
if (timer == nil)
{
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(handleCountdown), userInfo: nil, repeats: true)
}
}
Then I check the elapsed time:
func checkElapsedTime() -> Double {
let currentTime = Date()
let appQuitTime = UserDefaults.standard.object(forKey: "quitTimeKey") as? Date ?? Date.distantFuture
let elapsedTime = currentTime.timeIntervalSince(appQuitTime)
return elapsedTime
}
I am also printing the time difference:
let timeDifference = checkElapsedTime()
print("timeDifference = \(timeDifference)")
Question: However, here is an issue. When I am using the app and I slide the notification center down and back up for not even a second, I get a timeDifference reading of few thousand seconds.
What could be the reason here? Is this iOS 12 bug? This only happens when I pull the notification center down when I am in the app.
Alright so I got it working. Basically when you are sliding down the notification center, you are calling applicationWillResignActive. So instead of calling applicationDidEnterBackground, I used applicationWillResignActive for the notification and it started working all fine!
Something really odd is happening with my code.
I made a rather simple Timer function that is triggered by button.
The button calls a first method, then the method use another function to do the counting, then it triggers something when the time's up.
And everything works fine.
here's the code.
// This is the declaration of the launch button.
#IBAction func playLater(_ sender: Any) {
if isTimerRunning == false {
runTimer()
}
}
var seconds = 10
var timer = Timer()
var isTimerRunning = false
var resumeTapped = false
//the timer function that sets up the duration and launches the counting.
func runTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(self.updateTimer)), userInfo: nil, repeats: true)
isTimerRunning = true
}
//this part is making sure that you go from 10 to 0 and when it's at 0, something happens. In this very case, it plays a song.
#objc func updateTimer() {
if seconds < 1 {
timer.invalidate()
isTimerRunning = false
playNow((Any).self)
} else {
seconds -= 1
timerLabel.text = timeString(time: TimeInterval(seconds))
timerLabel.text = String(seconds)
}
}
The thing is that I also want to be able to triger that same function from the Apple Watch.
I made a WCSession that is working well, the messages are passing, it's ok.
So I'm using this code to launch the same function when the user pushes a button on the apple Watch. That part of the code is on the same iOS swift file.
#available(iOS 9.0, *)
func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
// do something
isTimerRunning = false
playLater(Any.self)
}
As you can see, I'm not even trying to add some code, I just call the same function used with the iOS button.
But this time, it's not working. The first part of the method is responding, I saw that runTimer() is working, but it's not going to updateTimer().
Maybe I'm missing something here, but, what is the difference ? Why if it comes from the push of a button it's working, and if it's called directly from "the inside" nothing happens ?
If you have any educated guesses, or even, clues, I'd be grateful.
Thanks !
I have an application that tends to stay awake in the background due to it playing music; however, on a rare occasion if the music stops between songs for longer than expected(can be relatively short amount of time) due to connectivity issues or the user mutes the music, the application will become suspended. I am aware of UIApplicationExitOnSuspend but unlike the description of it, this actually exits on entering background. I have done a fair amount of research, I am aware there are no system notifications for entering suspending state.
Is there anyway to identify on leaving suspended state that the app was suspended? Alternatively is there anyways to do something similar to UIApplicationExitOnSuspend except only when the application actually is suspended not just when it goes into the background?
You could try using the backgroundtimeremaining property on UIApplication, poll it with a timer on some interval, and should that value get close enough to zero - set a flag, or even a value in userDefaults, which you could then check and unset after coming back to the foreground?
The documentation reads:
This property contains the amount of time the app has to run in the background before it may be forcibly killed by the system. While the app is running in the foreground, the value in this property remains suitably large. If the app starts one or more long-running tasks using the beginBackgroundTask(expirationHandler:) method and then transitions to the background, the value of this property is adjusted to reflect the amount of time the app has left to run.
I ended up creating a small class to identify when the app has gone into a suspended state:
class SuspendedMonitor {
static let shared = SuspendedMonitor()
var delegate: SuspendedMonitorDelegate?
private let Interval = 10.0
private let MaxDelta = 2.0
private var _timestamp: Double
private var _timer: Timer?
private init() {
_timestamp = timeInMs()
}
public func start() {
_timestamp = timeInMs()
NotificationCenter.default.addObserver(
self,
selector: #selector(enterForeground),
name: NSNotification.Name.UIApplicationWillEnterForeground,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(enterBackground),
name: NSNotification.Name.UIApplicationDidEnterBackground,
object: nil
)
}
#objc func enterForeground() {
checkSync()
_timer?.invalidate()
}
#objc func enterBackground() {
_timestamp = timeInMs()
setupTimer()
}
#objc func incrementTime() {
_timestamp = _timestamp + (Interval * 1000)
}
private func setupTimer() {
_timer = Timer.scheduledTimer(
timeInterval: Interval,
target: self,
selector: #selector(incrementTime),
userInfo: nil,
repeats: true
)
}
private func checkSync() {
if timeInMs() - _timestamp > ((Interval + MaxDelta) * 1000) { appSuspended() }
}
private func appSuspended() {
delegate?.appDidSuspend(self)
}
}
protocol SuspendedMonitorDelegate: class {
func appDidSuspend(_ monitor: SuspendedMonitor)
}
I have a sliderValueChange function which updates a UILabel's text. I want for it to have a time limit until it clears the label's text, but I also want this "timed clear" action to be cancelled & restarted or delayed whenever the UISlider is moved within the time limit before the "timed clear" action takes place.
So far this is what I have:
let task = DispatchWorkItem {
consoleLabel.text = ""
}
func volumeSliderValueChange(sender: UISlider) {
task.cancel()
let senderValue = String(format: "%.2f", sender.value)
consoleLabel.text = "Volume: \(senderValue)"
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3, execute: task)
}
Obviously, this approach does not work, since cancel() apparently cannot be reversed.. (or at least I don't know how). I also don't know how to start a new task at the end of this function which will be cancelled if the function is recalled..
Am I going about this the wrong way? Is there something I am overlooking to make this work?
Use a timer:
weak var clearTimer: Timer?
And:
override func viewDidLoad() {
super.viewDidLoad()
startClearTimer()
}
func startClearTimer() {
clearTimer = Timer.scheduledTimer(
timeInterval: 3.0,
target: self,
selector: #selector(clearLabel(_:)),
userInfo: nil,
repeats: false)
}
func clearLabel(_ timer: Timer) {
label.text = ""
}
func volumeSliderValueChange(sender: UISlider) {
clearTimer?.invalidate() //Kill the timer
//do whatever you need to do with the slider value
startClearTimer() //Start a new timer
}
The problem is that you are cancelling the wrong thing. You don't want to cancel the task; you want to cancel the countdown which you got going when you said asyncAfter.
So use a DispatchTimer or an NSTimer (now called a Timer in Swift). Those are counters-down that can be cancelled. And then you can start counting again.
I am new to swift programming and I don't know how to call a method at regular interval of time. I have a demo app for service call but i don't know how can i call it at regular interval of time.
You can create an object of NSTimer() and call a function on definite time interval like this:
var updateTimer = NSTimer.scheduledTimerWithTimeInterval(15.0, target: self, selector: "callFunction", userInfo: nil, repeats: true)
this will call callFunction() every 15 sec.
func callFunction(){
print("function called")
}
Here is a simple example with start and stop functions:
private let kTimeoutInSeconds:NSTimeInterval = 60
private var timer: NSTimer?
func startFetching() {
self.timer = NSTimer.scheduledTimerWithTimeInterval(kTimeoutInSeconds,
target:self,
selector:Selector("fetch"),
userInfo:nil,
repeats:true)
}
func stopFetching() {
self.timer!.invalidate()
}
func fetch() {
println("Fetch called!")
}
If you get an unrecognized selector exception, make sure your class inherits from NSObject or else the timer's selector won't find the function!
Timer variant with a block (iOS 10, Swift 4)
let timer = Timer.scheduledTimer(withTimeInterval: 5, repeats: true) { (timer) in
print("I am called every 5 seconds")
}
Do not forget call invalidate method
timer.invalidate()
GCD approach (will tend to drift a bit late over time)
func repeatMeWithGCD() {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 5) {
print("I am called every 5 seconds")
self.repeatMeWithGCD()//recursive call
}
}
Do not forget to create a return condition to prevent stackoverflow error