I'm trying to use CTCallCenter in Swift, however it always displays error.
I suppose it may cause in how to use closure but actually I don't familiar about it.
Does anybody have idea to resolve this issue?
Here is my code
import CoreTelephony
class ViewController: UIViewController{
var callCenter:CTCallCenter = CTCallCenter()
override func viewDidLoad() {
callCenter.callEventHandler(call:CTCall) -> Void in{
//will get CTcall status here
}
}
}
There are three errors.
1, Braced block of statements is an unused closure
2, Expected expression
3, Consecutive statements on a line must be separated by ";".
I tried to change as it indicated but any ways are not correct.
Thanks in Advance!
I got this working using the following code:
import CoreTelephony
class SomeClass: UIViewController {
private var callCenter = CTCallCenter()
override func viewDidLoad() {
super.viewDidLoad()
callCenter.callEventHandler = { (call:CTCall!) in
switch call.callState {
case CTCallStateConnected:
println("CTCallStateConnected")
self.callConnected()
case CTCallStateDisconnected:
println("CTCallStateDisconnected")
self.callDisconnected()
default:
//Not concerned with CTCallStateDialing or CTCallStateIncoming
break
}
}
}
func callConnected(){
// Do something when call connects
}
func callDisconnected() {
// Do something when call disconnects
}
}
Hope it helps.
From the Apple documentation :
Responding to Cellular Call Events
Dispatched when a call changes state.
Declaration:
var callEventHandler: ((CTCall!) -> Void)!
Discussion:
This property’s block object is dispatched on the default priority global dispatch queue when a call changes state. To handle such call events, define a handler block in your application and assign it to this property. You must implement the handler block to support being invoked from any context.
If your application is active when a call event takes place, the system dispatches the event to your handler immediately. However, call events can also take place while your application is suspended. While it is suspended, your application does not receive call events. When your application resumes the active state, it receives a single call event for each call that changed state—no matter how many state changes the call experienced while your application was suspended. The single call event sent to your handler, upon your application returning to the active state, describes the call’s state at that time.
For example, suppose your application changes from the active to the suspended state while a call is in the connected state. Suppose also that while your application is suspended, the call disconnects. When your application returns to the active state, you get a cellular call event indicating that the call is disconnected.
Here is a more complex example. Suppose your application changes from the active to the suspended state after the user has initiated a call but before it connects (that is, your application suspends while the call is in the dialing state). Suppose further that, while your application is suspended, the call changes first to the connected state and then to the disconnected state. When your application returns to the active state, you get a single cellular call event indicating that the call is disconnected.
May be now you can understand how to declare that.
Related
I'm using SingleLiveEvent to communicate my ViewModel and my Activity. Something like that (pseudocode):
class MyActivity: BaseActivity{
fun onCreate(){
//Init viewmodel and so on
viewModel.commands.observe(this, { command ->
logger.debug("Command received","------>>>>>>>"+command.javaClass.simpleName)
processCommand(command)
})
}
}
And my ViewModel is something like:
class MyViewModel(application: Application) : BaseAndroidViewModel(application) {
val command: SingleLiveEvent<CustomCommands> = SingleLiveEvent()
init{
loadOneThing()
command.postValue(CustomCommands.MessageCommand("one thing loaded"))
loadAnotherThing()
command.postValue(CustomCommands.MessageCommand("another thing loaded"))
}
}
The problem that I'm having, is that the Activity is receiving only the last command, and that is per design. SingleLiveEvent is a Child class from LiveData, and the documentation says the following for the method postValue:
* If you called this method multiple times before a main thread executed a posted task, only
* the last value would be dispatched.
Interestingly, if I set a breakpoint on the line that posts the commands, the emulator/device/main thread has time enough to process the first command, and the second command is sent too. But when executing the app without breakpoints, if the tasks that the viewmodel does between commands are done very fast (no rest requests or things like that, but some calculations), the main thread does not have time enough to finish the first command, and the second command is ignored.
But I really need the View to receive all events/commands that the ViewModel sends.
I suppose the SingleLiveEvent is not the right tool for that use case, nor is LiveData, because of the problem of already consumed events being resent when the device is rotated and so on.
Somebody knows a better approach to do this?
Thanks in advance!
I have faced same problem today. I'm also using SingleLiveEvent for commands/event. I have solved this problem using
commands.value = event instead of commands.postValue(event). Then I wonder why it behaving like that. I found this article. In the article,
But for postValue, the value will be updated twice and the number of times the observers will receive the notification depends on the execution of the main thread. For example, if the postValue is called 4 times before the execution of the main thread, then the observer will receive the notification only once and that too with the latest updated data because the notification to be sent is scheduled to be executed on the main thread. So, if you are calling the postValue method a number of times before the execution of the main thread, then the value that is passed lastly i.e. the latest value will be dispatched to the main thread and rest of the values will be discarded.
I hope it help someone that faced same problem.
have you tried using EventObserver?
/**
* Used as a wrapper for data that is exposed via a LiveData that represents an event.
*/
open class Event<out T>(private val content: T) {
#Suppress("MemberVisibilityCanBePrivate")
var hasBeenHandled = false
private set // Allow external read but not write
/**
* Returns the content and prevents its use again.
*/
fun getContentIfNotHandled(): T? {
return if (hasBeenHandled) {
null
} else {
hasBeenHandled = true
content
}
}
/**
* Returns the content, even if it's already been handled.
*/
fun peekContent(): T = content
}
/**
* An [Observer] for [Event]s, simplifying the pattern of checking if the [Event]'s content has
* already been handled.
*
* [onEventUnhandledContent] is *only* called if the [Event]'s contents has not been handled.
*/
class EventObserver<T>(private val onEventUnhandledContent: (T) -> Unit) : Observer<Event<T>> {
override fun onChanged(event: Event<T>?) {
event?.getContentIfNotHandled()?.let {
onEventUnhandledContent(it)
}
}
}
Use it with live data
val someEvent: MutableLiveData<Event<Unit>>= MutableLiveData()
when you need to some event
fun someEventOccured(){
someEvent.value = Event(Unit)
}
Fragment file, observe the Event
viewModel.someEvent.observe(this, EventObserver {
//somecode
})
I'm implementing Personal VPN by PacketTunnel. Through startVPNTunnel method of NETunnelProviderManager, i checked whether VPN Connection is run well.
However, I have a problem. I added the exit code of vpn connection in applicationWillTerminate to disable vpn connection when app is terminated like following code below. But it doesn't work.
If test this code, loadAllFromPreferences is run but callback function of loadAllFromPreferences isn't called. This code runs well anywhere except applicationWillTerminate. Why it doesn't work? Is there any way to disable vpn when app is terminated?
func applicationWillTerminate(_ application: UIApplication) {
NETunnelProviderManager.loadAllFromPreferences { (managers, error) in
var manager:NETunnelProviderManager?=nil;
for m in managers! {
if m != nil {
if m.localizedDescription == "profile" {
manager = m;
break
}
}
}
manager?.connection.stopVPNTunnel()
}
}
As per Apple's documentation of applicationWillTerminate(application:):
Your implementation of this method has approximately five seconds to perform any tasks and return. If the method does not return before time expires, the system may kill the process altogether.
If applicationWillTerminate is called but the closure is not make sure that it does not take more than "approximately" (sic) 5 seconds.
I have not used the libraries you are using but from a general point of view there are probably better strategies than iterating over all available managers. Like storing a reference/identifier to a given manager when starting a connection and using that reference/identifier to terminate it.
Why dart calls my function "aFunction" after Step2? If I execute this code this text below in console:
Step2
Step1
My code:
void main()
{
...
stream.listen(aFunction);
print("Step2");
...
}
void aFunction()
{
print("Step1");
}
Thanks for help.
One of the few promises that a Dart Stream makes is that it generates no events in response to a listen call.
The events may come at a later time, but the code calling 'listen' is allowed to continue, and complete, before the first event is fired.
We originally allowed streams to fire immediately on a listen, but when we tried to program with that, it was completely impossible to control in practice.
The same is true for listening on a future, for example with 'then'. The callback will never come immediately.
Events should generally act as if they were fired by the top-level event loop, so the event handler doesn't have to worry if other code is running - other code that might not be reentrant.
That is not always the case in practice. One event handler may trigger other events through a synchronous stream controller, effectively turning one event into anoter. That requires the event handler to know what it is doing. Synchronous controllers are intended for internal use inside, e.g., a stream transformer, and using a synchronous stream controller isn't recommended in general.
So, no, you can't have the listen call immediately trigger the callback.
You can listen to a stream synchronously if you created a StreamController with the sync option enabled. Here is an example to get what you describe:
var controller = new StreamController<String>(sync: true);
var stream = controller.stream.asBroadcastStream();
stream.listen((text) => print(text));
controller.add("Step1");
print("Step2");
For example:
- (void)someFunc {
[self someFunc1];
[self someFunc2];
[self someFunc3];
}
I call someFunc. As I understand if I interrupt the application then the application doesn't guarantee that all the inner code in someFunc will be performed.
I must call someFunc1, someFunc2 and someFunc3 only once.
The problems I don't know how to solve:
someFunc1, someFunc2 and someFunc3 should be called atomically.
storing info for next launch. For example if we successfully have performed someFunc1 only then at next launch the application should call someFunc2 and someFunc3 only.
I know about method applicationWillTerminate:, but I don't know how to solve the current issue with it.
EDITED
Multitasking is not a solution because Even if the device is running iOS 4 or later, the device may not support multitasking., so it doesn't solve the general problem and makes the final solution more difficult only.
EDITED
For those who spam with off topic answers: read the title first - Save state when user exits an application. Where have you seen here putting the application into background?
This does't make sense. If these functions are running on the main thread, there is no way that the application can terminate normally while your functions are running. This is because the events sent like applicationWillTerminate: are sent on the same thread.
If your function is running on a different thread to the main thread, you will need to save some state information after each function completes, but you still have a race condition.
It might be better to check your application's state before running each function. For example, if you have a three step login/registration process with a server, you should query the server to see if the stage has been completed already before running it.
It's difficult to be more specific without knowing what you are doing in these functions.
You should use background tasks !
Take a look at the documentation here :
Executing a Finite-Length Task in the Background
Put the call of someFunc in the middle of the background task.
If your app goes to the background state, you'll have extra time to finish the execution of the method.
Make your functions to return bool, and when you call them, store the bool value to nsdefaults.
When the app restarts,check the bools from sndefaults, and if they are NO, run the functions and update them.
Nobody wants to help. So my temporary solution:
to save a last state I use a writing to a file because it enables to set its operation as atomic/nonatomic
I have replaced this code with something like this:
typedef enum {
state1,
state2,
state3
} MyState;
#property (assign) MyState state;
-(void)someFunc {
switch (state) {
case state1:
{
[self someFunc1];
state = state2;
[self someFunc];
break;
}
case state2:
{
[self someFunc2];
state = state3;
[self someFunc];
break;
}
default:
break;
}
}
I have a CTCallCenter instance created (alloc/init) after launch. The event handler is never called (neither an NSLog nor a breakpoint fire). If I ask it later about current calls it returns nil. If I call [[[CTCallCenter alloc] init] currentCalls] at the same time then it does return the calls.
As an example while a call is active (I brought the app back to the foreground and a timer later fired) here is a single NSLog showing the long lived CTCallCenter instance, what it returns for currentCalls and what a new transient instance returns:
CTCallCenter(0x1e5639c0): {
server_connection: 0x1e5640e0 currentCalls: {(
)} callEventHandler: 0xa0c90
}
(null)
{(
CTCall (0x1e5a0fb0) {
callState: [CTCallStateIncoming]
Call ID: [1EB2A082-4A12-48C2-A76C-2244F8F402EE]
}
)}
It is apparent there is a handler registered, and that the long lived instance thinks there are no calls. I'm happy to always use a transient instance to get the call list, but I really need the event handler to fire.
I have found the answer. The [[CTCallCenter alloc] init] must be run in the main queue. I was running it in a custom serial dispatch queue. Presumably the CTCallCenter code is doing something with the current queue or perhaps requiring a run loop.
If you do the alloc/init outside of the main queue then it will be correct if immediately called for currentCalls but that data never gets updated nor does the event handler fire. There is no mention of this in the documentation, nor are errors returned or exceptions raised.