getAllVoiceShortcuts function returns twice - ios

I'm fetching voiceShortcurts related with my app. Below function goes into completion block twice. It returns true at first, then false which true is the right one. Why It goes into completion block twice?
public static func updateVoiceShortcuts(completion: #escaping ((_ haveShortcutAlready: Bool) -> Void)) {
INVoiceShortcutCenter.shared.getAllVoiceShortcuts { (voiceShortcutsFromCenter, error) in
if let voiceShortcutsFromCenter = voiceShortcutsFromCenter {
self.voiceShortcuts = voiceShortcutsFromCenter
completion(true)
} else {
if let error = error as NSError? {
print(error)
}
completion(false)
}
}
}

Related

Firestore iOS - Why does my array get printed out multiple times?

I'm successfully able to display a users friends array in a table view, but when I print out the array it get's printed out 3 times instead of 1 time, how do I fix this?
Friend System model:
var removeFriendListener: ListenerRegistration!
func addFriendObserver(_ update: #escaping () -> Void) {
removeFriendListener = CURRENT_USER_FRIENDS_REF.addSnapshotListener{ snapshot, error in
self.friendList.removeAll()
guard error == nil else {
#if DEBUG
print("Error retreiving collection")
#endif
return
}
for document in snapshot!.documents {
let id = document.documentID
self.getUser(id, completion: { (user) in
self.friendList.append(user)
update()
})
}
if snapshot!.documents.count == 0 {
update()
}
}
}
func removeFriendObserver() {
removeFriendListener.remove()
}
Friend System View Controller:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
FriendSystem.system.addFriendObserver { () in
DispatchQueue.main.async {
self.tableView.reloadData()
}
print(FriendSystem.system.friendList)
}
}
Array printed out
[App.User]
[App.User, App.User]
[App.User, App.User, App.User]
you should not call your update() here:-
for document in snapshot!.documents {
let id = document.documentID
self.getUser(id, completion: { (user) in
self.friendList.append(user)
update()
})
}
instead you should call it after the for loop gets completed.

Correct memory management in closures

I have method with closure.
It works like this
func ping() -> pingProtocol {
let factory = pingFactory()
let ping = factory.getPing() // returns pingProtocol conforming instance, for example "PingService"
return ping
}
func performPing(success: #escaping () -> (), fail: #escaping () -> ()) {
ping().status { (ready, response) in
if ready {
success()
} else {
fail()
}
}
}
Realisation of ping is abstraction over Alamofire.
class PingService {
func status(completion: #escaping (Bool, ServiceResponse) -> Void) {
doSimpleRequest(endPoint: "/api/get_state/", param: [:], completion: completion)
}
func doSimpleRequest(endPoint: String, param: [String: Any], completion: #escaping (Bool, ServiceResponse) -> Void) {
APIManager.shared.postRequest(mode: APIMode.Service, endPoint: endPoint, parameters: param) { [weak self] (response, error) in
guard let strongSelf = self else {
return
}
guard let response = response as? [String: Any] else { return }
let serviceResponse = ServiceResponse(dict: response)
if error != nil {
completion(false, serviceResponse)
return
}
completion(true, serviceResponse)
}
}
}
The problem is that with weak self guard in self is always triggers. If I use unowned, I get crash. If I dont use weak or unowned it works fine.
The ping method itself lives in viewmodel of controller, and controller stays on screen when completion block is needed to be executed, so the problem is in deallocation of ping instance, not in viewmodel thats holds it.
If I use capture list I dont get desired behavior, but If I do I suspect that I get memory leak.
What is best practice here ?

How to change protocol to typealias or closure

In my code I do not want to use protocol I want use closures but I couldn't get it done because I am new on Swift.
Here is the example of class
class SplashPresenterImp: SplashPresenter, OnFinishedListener {
private var interactor: SplashInteractor
private var splashNetworkProtocol: SplashNetworkProtocol
init() {
interactor = SplashNetworking()
}
func startDownloadConfigs(splashNetworkProtocol: SplashNetworkProtocol){
if interactor != nil {
interactor.loadConfigs(listener: self)
self.splashNetworkProtocol = splashNetworkProtocol
}
}
func startDownloadDictionary(splashNetworkProtocol: SplashNetworkProtocol) {
if interactor != nil {
interactor.loadDictionary(listener: self)
self.splashNetworkProtocol = splashNetworkProtocol
}
}
func onFinishedGetDictionary(dictionary: Dictionary) {
//save dictionary
if splashNetworkProtocol != nil {
splashNetworkProtocol.onSuccess()
}
}
func onFinishedGetConfigs(config: Config) {
//save configs
if splashNetworkProtocol != nil {
splashNetworkProtocol.onSuccess()
}
}
func onFinishedWithError(error: AMError) {
if splashNetworkProtocol != nil {
splashNetworkProtocol.onError(error: error)()
}
}
}
Here is the protocol
protocol SplashNetworkProtocol: class {
func onSuccess()
func onError(error: AMError)
}
What I want to have on my viewcontroller to have closure when downloadConfig is complete to start downloadDictionary.
I know how it handle on Java here is the code
mPresenter.startDownloadConfigs(new SplashNetworkProtocol() {
#Override
public void onSuccess() {
downloadDictionary();
}
#Override
public void onError(final AMError error) {
}
});
I want to have same result in swift. Is anyone can give me advice how to do this?
More clearly I want get rid of SplashNetworkProtocol and use only closure.
swift result should be this
mPresenter.startDownloadConfigs(onSuccess: {} onError{}
Should be as simple as:
func startDownloadDictionary(onSuccess: () -> Void, onError: () -> Void)
But even better to use a single closure that handles both success and error. For instance, with an error as an optional parameter:
func startDownloadDictionary(onCompletion: (Error?) -> Void)
A full example:
func someOtherFunc() {
startDownloadDictionary(onCompletion: {(error) -> Void in
if let error = error {
print(error.localizedDescription)
}
//no error
})
}
func startDownloadDictionary(onCompletion: (Error?) -> Void)
{
//dostuff
var error: Error?
// if error happens, create it
onCompletion(error)
}
If you need help with Swift closure syntax, this is a good resource:
http://fuckingswiftblocksyntax.com/

Execute completion handler when condition is met

I have a function which relies on the completionHandler of another function. This completionHandler should be called when the delegate method completedLogin is called. Below is a snippet of my code:
class loginClass : LoginScreenDelegate {
var loginCompleted : Bool = false
func performLogin(completionHandler: (() -> Void)) {
...
let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
while self.loginCompleted != true {
// Do nothing
}
completionHandler()
})
}
func didLogin(sender: LogInScreen, success: Bool) {
// Do nothing
}
func completedLogin(sender: LogInScreen) {
self.loginCompleted = true
}
}
However, using a while loop inside a background thread seems like a very resource intensive way. I have tried using NSTimer() but the problem is is that it executes another function so i cannot use my callback function anymore. Is there a better / resource friendly way to keep checking this?
You have to add a completion handler to the function which needs to be completed before the other like this:
func completedLogin(sender: LogInScreen, completionHandler :(evaluatedSuccessfully: Bool) -> ()){
self.loginCompleted = true
completionHandler(evaluatedSuccessfully: true)
}
And then you can just call this function in any other function like this:
completedLogin(sender: <your sender>){ success in
If success{
//do something
}
else{
//report an error
}
}
class loginClass : LoginScreenDelegate {
var loginCompleted : Bool = false
var completionHandler: (() -> Void)!
func performLogin(completionHandler: (() -> Void)) {
...
self.completionHandler = completionHandler
}
func didLogin(sender: LogInScreen, success: Bool) {
// Do nothing
}
func completedLogin(sender: LogInScreen) {
self.loginCompleted = true
self.completionHandler()
}
}

Swift: Return boolean in GCD Completion Block

I have a function written in Swift. I want the completion block to return a boolean. How can I go about doing this? I am using Grand Central Dispatch.
func myFunc() -> Bool
{
var success:Bool = false
// code here
dispatch_async(dispatch_get_main_queue(), {
return success
)}
)}
}
thanks!
Standard why of dealing with this async nature is not to return value, but pass in completion handler:
func myFunc(completion:(success: Bool) -> ()) {
var success:Bool = false
// code here
dispatch_async(dispatch_get_main_queue()) {
completion(success: success)
}
}
Then work with it:
myFunc({ (success) in
// ...
})
More swifty way (Swift 5):
func myFunc(completion: #escaping (Bool) -> Void) {
var success = false
// code...
completion(success)
}
}
Usage:
myFunc { success in
if success {
// code ...
}
}

Resources