How to set UIActivityIndicator async - ios

I have a really common problem trying to create spinning activity indicator during an user authentication task with Firebase
I tried to use CGD dispatch_async but that doesn't seem to handle my issue.
Here is my code
#IBAction func SignMeIn(sender: AnyObject) {
ActivityIndicator.hidden = false
ActivityIndicator.startAnimating()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { () -> Void in
NSLog("Before login func")
self.logIn()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
NSLog("After login func")
self.ActivityIndicator.stopAnimating()
self.ActivityIndicator.hidden = true
})
});
}
func logIn(){
myRootRef.authUser(TXT_User.text, password: TXT_Password.text,
withCompletionBlock: { error, authData in
if error != nil {
NSLog(error.debugDescription)
} else {
NSLog("Connected !")
}
})
}
The thing is I surely do something wrong since in debug mode appears in this order :
"Before login func"
"After login func"
"Connected !"
Whereas I should have
"Before login func"
"Connected !"
"After login func"
What am I doing wrong please ?
Thank you very much for your help :) !

Your problem is that you have 2 async tasks
1. Login completion block
2. Activity indicator stop
If you want to stop activity indicator after the login process you should move the code in the completion block like this
#IBAction func SignMeIn(sender: AnyObject) {
ActivityIndicator.hidden = false
ActivityIndicator.startAnimating()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { () -> Void in
NSLog("Before login func")
self.logIn()
});
}
func logIn(){
myRootRef.authUser(TXT_User.text, password: TXT_Password.text,
withCompletionBlock: { error, authData in
if error != nil {
NSLog(error.debugDescription)
} else {
NSLog("Connected !")
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
NSLog("After login func")
self.ActivityIndicator.stopAnimating()
self.ActivityIndicator.hidden = true
})
})
}

The problem is that execution returns from logIn immediately, and not when the completion block is called.
To do what you need, you have to make the call to the main queue within the logIn completion handler - like this
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { () -> Void in
NSLog("Before login func")
self.logIn()
}
func logIn(){
myRootRef.authUser(TXT_User.text, password: TXT_Password.text,
withCompletionBlock: { error, authData in
// success or fail, you need to stop the Activity Indicator
dispatch_async(dispatch_get_main_queue(), { () -> Void in
NSLog("After login func")
self.ActivityIndicator.stopAnimating()
self.ActivityIndicator.hidden = true
})
});
if error != nil {
NSLog(error.debugDescription)
} else {
NSLog("Connected !")
}
})
}

Related

Swift Local Notifications Checker Main Thread Only

I have a method with a completion that returns a bool. But I cant figure out how to get around this error.
Error
UISwitch.isOn must be used from main thread only
Button Action
#IBAction func notificationSwitch(_ sender: Any) {
LocalNotification().checkEnabled(completion: { (success) -> Void in
// When download completes,control flow goes here.
if success == false{
print("Cant Turn On")
self.notificationToggle.isOn = false
} else {
print("Can Turn On")
if self.notificationToggle.isOn == true {
self.notificationToggle.isOn = false
} else {
self.notificationToggle.isOn = true
}
}
})
}
also already tried wrapping the LocalNotifications().... in DispatchQueue.main.async but still get the same error
You're almost there. It is not the checkEnabled that needs to be wrapped in the call to get onto the main thread, but the stuff "inside" it:
LocalNotification().checkEnabled(completion: { (success) -> Void in
DispatchQueue.main.async {
if success == false {

Can't prevent user from cancelling Touch ID

When the Touch ID alert is displayed, there is also a "Cancel" button. I would prefer to NOT allow the user to cancel because they are prohibited from continuing any further. 1. Is there a way to remove the "Cancel" button. 2. If the "Cancel" button is required, how can I force the user to re-authenticate with a fingerprint? If authenticate() is called a second time, the Touch ID API just lets them in. There is no alternative passcode and I'd hate to have to code up yet another view controller for it.
func authenticate() {
let myContext:LAContext = LAContext()
let authError:NSErrorPointer = nil
if (myContext.canEvaluatePolicy(.DeviceOwnerAuthenticationWithBiometrics, error: authError)) {
myContext.evaluatePolicy(.DeviceOwnerAuthenticationWithBiometrics, localizedReason: "Press fingerprint", reply: { (success:Bool, error:NSError?) -> Void in
if success == true {
log.debug("SUCCESSFUL AUTHENTICATION")
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.performSegueWithIdentifier("showUI", sender: self)
})
}
else {
log.debug("FAILED AUTHENTICATION")
self.authenticate()
}
})
}
}
You need to dispatch your failure call to self.authenticate on the main queue;
func authenticate() {
let myContext:LAContext = LAContext()
let authError:NSErrorPointer = nil
if (myContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: authError)) {
myContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Press fingerprint", reply: { (success:Bool, error:NSError?) -> Void in
if success {
log.debug("SUCCESSFUL AUTHENTICATION")
DispatchQueue.main.async {
self.performSegueWithIdentifier("showUI", sender: self)
}
}
else {
log.debug("FAILED AUTHENTICATION")
DispatchQueue.main.async {
self.authenticate()
}
}
})
}
}

Get notification from nested async call with dispatch_group_async

So I have situation when I've want do some async stuff, post status on Facebook and get notified when it's done posting. I was hoping that dispatch_group_async will do the job but now I hit the wall.
Here is how at this moment logic look like.
Func
func saveAndPost() {
//1. save
dispatch_group_async(group, queue) { () -> Void in
print("saving to DB")
//some func
print("saved to DB")
}
//2. post
dispatch_group_async(group, queue, { () -> Void in
print("publishing on FB")
//some func
//async request to ACAccount {
print("access")
//async performRequestWithHandler{
print("posted")
//anwser from request
}
}
}
print("published on FB")
})
dispatch_group_notify(group, queue, { () -> Void in
print("done")
dispatch_async(dispatch_get_main_queue(), { () -> Void in
//go to next view
})
})
}
Output
saving to DB
saved to DB
publishing on FB
published on FB
done
post
posted
My goal is to get notified when whole posting process is done. Maybe this is not possible with dispatch_group_async and I have to use KVO or even PromiseKit?
SOLUTION
David got me thinking and dispatch_group_enter & 'dispatch_group_leave' right what I needed. So at this moment my logic look like this.
func saveAndPost() {
//1. save
dispatch_group_async(group, queue) { () -> Void in
print("saving to DB")
//some func
print("saved to DB")
}
//2. post
dispatch_group_enter(group)
print("publishing on FB")
//some func
//async request to ACAccount {
print("access")
//async performRequestWithHandler{
print("posted")
//anwser from request
dispatch_group_leave(group)
}
}
}
print("published on FB")
dispatch_group_notify(group, queue, { () -> Void in
print("done")
dispatch_async(dispatch_get_main_queue(), { () -> Void in
//go to next view
})
})
}
As you can see part 2. is now modified.
My preferred method to tackle this when dealing with async calls:
var asyncGroup = dispatch_group_create()
dispatch_group_enter(asyncGroup)
asyncCall1.start() {
//end of callback
dispatch_group_leave(asyncGroup)
}
dispatch_group_enter(asyncGroup)
asyncCall2.start() {
asyncCall3.start() {
asyncCall4.start() {
dispatch_group_leave(asyncGroup)
}
}
}
dispatch_group_notify(asyncGroup, dispatch_get_main_queue(), {
println("done")
})
asyncCall1.start() { ... } and asyncCall2.start() { ... } are just sorta psuedocode to show you how it works.
Every dispatch_group_enter call needs to be balanced by a dispatch_group_leave call or you'll never get into the dispatch_group_notify block.

How to use callbacks to make Asynchronous calls in Swift so that main UI does not Hang?

I am trying to make Asynchronous call (to Parse) and during the call I DON'T want my Main UI to get hung.
Below is the function I am trying to call my ViewController. Below that function is the code I am using from my ViewController.
In the line sleep(4) line the main UI of the ViewController LoginVC does get stuck. Am I not using callbacks for the Asynchronous call correctly?
class Utils {
func logIntoWebService(uname: String, pwd: String, completion: ((result:Bool?) -> Void)!) {
PFUser.logInWithUsernameInBackground(uname, password:pwd) {
(user, error) -> Void in
if error == nil {
if user != nil {
// Yes, User Exists
//UI IS STUCK DURING THIS SLEEP(10)
sleep(10)
completion(result: true)
} else {
completion(result: false)
}
} else {
completion(result: false)
}
}
}
}
I am calling the above function from my ViewController. Below is the call from my ViewController
class LoginVC: UIViewController {
var mUtils: Utils = Utils()
mUtils.loggedInStatus({ (result) -> Void in
println("Response from logInToParse = " + String(stringInterpolationSegment: result))
})
}
You are getting confuse between background threads and completion handler.
logInWithUsernameInBackground is a async function that runs in the background however all the code that runs in the completion handler runs back in the main thread:
completion: ((result:Bool?) -> Void)!) {//The code here runs in the main thread}
So basically from the time that your application start communicate with Parse.com, until the result was back, that code was running asynchronous in a background thread, when it finish and your application received the response it runs in the completion block back in the main thread.
Now lets say you really want to run some code in the background in the completion block, than you would use something like:
let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
println("Now this code will run in the background thread")
})
All the new quality of service classes are:
QOS_CLASS_USER_INTERACTIVE
QOS_CLASS_USER_INITIATED
QOS_CLASS_UTILITY
QOS_CLASS_BACKGROUND
Or in your case:
PFUser.logInWithUsernameInBackground(uname, password:pwd) {
(user, error) -> Void in
if error == nil {
if user != nil {
// Yes, User Exists
let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
sleep(10)
})
completion(result: true)
} else {
completion(result: false)
}
} else {
completion(result: false)
}
}
For more information see Apple documentation

how to share Image on Facebook using Swift in IOS

I want to share an image on Facebook using swift language. I am able to share image using Objective C.
I tried using
1) How to Share image + text with facebook in swift iOS?
but not working, then I tried using other options but not able to share image using swift language. then I tried this
2) https://github.com/rajeshsegu/facebook-ios-swift/tree/master/FBApp
I copied Facebook.swift and write another function for share image my code for Facebook.swift
import Foundation
import Social
let FB = Facebook();
class Facebook {
var fbSession:FBSession?
init(){
self.fbSession = FBSession.activeSession();
}
func hasActiveSession() -> Bool{
let fbsessionState = FBSession.activeSession().state;
if ( fbsessionState == FBSessionState.Open
|| fbsessionState == FBSessionState.OpenTokenExtended ){
self.fbSession = FBSession.activeSession();
return true;
}
return false;
}
func login(callback: () -> Void){
let permission = ["publish_actions","email","user_location","user_birthday","user_hometown","user_photos","user_about_me"];
let activeSession = FBSession.activeSession();
let fbsessionState = activeSession.state;
var showLoginUI = true;
if(fbsessionState == FBSessionState.CreatedTokenLoaded){
showLoginUI = false;
}
if(fbsessionState != FBSessionState.Open
&& fbsessionState != FBSessionState.OpenTokenExtended){
FBSession.openActiveSessionWithPublishPermissions(permission, defaultAudience: FBSessionDefaultAudience.Friends, allowLoginUI: showLoginUI, completionHandler: { (session:FBSession!, state:FBSessionState, error:NSError!) -> Void in
if(error != nil){
println("Session Error: \(error)");
}
self.fbSession = session;
// println("Session : \(self.fbSession?.permissions)");
callback();
})
// FBSession.openActiveSessionWithReadPermissions(
// permission,
// allowLoginUI: showLoginUI,
// completionHandler: { (session:FBSession!, state:FBSessionState, error:NSError!) in
//
// if(error != nil){
// println("Session Error: \(error)");
// }
// self.fbSession = session;
// println("Session : \(self.fbSession?.permissions)");
// callback();
//
// }
// );
return;
}
callback();
}
func logout(){
self.fbSession?.closeAndClearTokenInformation();
self.fbSession?.close();
}
func getInfo(){
FBRequest.requestForMe()?.startWithCompletionHandler({(connection:FBRequestConnection!, result:AnyObject!, error:NSError!) in
if(error != nil){
println("Error Getting ME: \(error)");
}
println("\(result)");
var dictData:NSDictionary!=result as? NSDictionary
});
}
func handleDidBecomeActive(){
FBAppCall.handleDidBecomeActive();
}
func shareImage (imageName:UIImageView){
let fbsessionState = FBSession.activeSession().state;
if(fbsessionState == FBSessionState.Open)
{
//var arr : NSArray=NSArray(array: ["publish_actions"])
self.fbSession?.requestNewPublishPermissions(["publish_actions"], defaultAudience:FBSessionDefaultAudience.Friends, completionHandler: { (session:FBSession!, error:NSError!) -> Void in
if(error == nil){
var requestConneciton:FBRequestConnection=FBRequestConnection()
requestConneciton.errorBehavior=FBRequestConnectionErrorBehavior.None
requestConneciton.addRequest(FBRequest(forUploadPhoto:imageName.image)) { (connection:FBRequestConnection!, result:AnyObject!, error:NSError!) -> Void in
println("\(error)");
println("\(result)");
//[self showAlert:#"Photo Post" result:result error:error];
}
requestConneciton.start()
}
else if(error.fberrorCategory == FBErrorCategory.UserCancelled){
var alt:UIAlertView!=UIAlertView(title:"Permission denied", message:"Unable to get permission to post", delegate:nil, cancelButtonTitle:"Ok")
alt.show()
}
})
}
}
func showAlert(msg:NSString!,result:AnyObject,error:NSError!) {
var alertTitle:NSString!
var alertMsg:NSString!;
if (error == nil) {
if((error.fberrorUserMessage != nil && FBSession.activeSession().isOpen) ) {
alertTitle = "";
}
else{
// Otherwise, use a general "connection problem" message.
alertMsg = "Operation failed due to a connection problem, retry later.";
}
}
else {
//var dictResult:NSDictonary = result as NSDictionary
alertMsg="Successfully posted "
var alertObj:UIAlertView!=UIAlertView(title:"Demo App", message:alertMsg, delegate:nil, cancelButtonTitle:"Ok");
alertObj.show();
}
}
func performPublishAction(action:() -> Void){
var arrP:NSArray!=NSArray(array: ["publish_actions"]);
fbSession?.requestNewPublishPermissions(arrP, defaultAudience:FBSessionDefaultAudience.Friends, completionHandler: { (session:FBSession!, error:NSError!) -> Void in
if(error == nil){
action()
}
else if(error.fberrorCategory == FBErrorCategory.UserCancelled){
var alt:UIAlertView!=UIAlertView(title:"Permission denied", message:"Unable to get permission to post", delegate:nil, cancelButtonTitle:"Ok")
alt.show()
}
})
}
}
and In ViewController.swift
import UIKit
class ViewController: UIViewController,FBLoginViewDelegate {
#IBOutlet var imageObj:UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func btnFBLoginClick(sender: UIButton) {
FB.login(self.handleLogin);
}
func handleLogin(){
println("SUCCESS");
FB.getInfo();
}
#IBAction func btnShareclick(sender: UIButton) {
FB.shareImage(imageObj)
}
}
Login button click working perfect and it can fetch all data of login user, but when i share the image using FB.shareImae(imageObj) its give me a permission error, I am working on this point from last 2 days now I am stuck. if i write same code in Objective C its working fine.
eror :
permissions:(
"public_profile",
email,
"user_friends"
)>, com.facebook.sdk:ParsedJSONResponseKey={
body = {
error = {
code = 200;
message = "(#200) Permissions error";
type = OAuthException;
};
};
code = 403;
}}
Can any one help me? to find out this problem...
I don't want to use SLComposeViewController, I want to use Facebook framework.
Thank you in advance!
This is a code i created in an old project. Give it a try ;)
#IBAction func btn_Share(sender: AnyObject) {
let facebookPost = SLComposeViewController(forServiceType: SLServiceTypeFacebook)
facebookPost.completionHandler = {
result in
switch result {
case SLComposeViewControllerResult.Cancelled:
//Code to deal with it being cancelled
break
case SLComposeViewControllerResult.Done:
//Code here to deal with it being completed
break
}
}
facebookPost.setInitialText("Test Facebook") //The default text in the tweet
facebookPost.addImage(masked_image) //Add an image
facebookPost.addURL(NSURL(string: "http://facebook.com")) //A url which takes you into safari if tapped on
self.presentViewController(facebookPost, animated: false, completion: {
//Optional completion statement
})
}`
I solved my problem (I put my comment here cause I don't have enough place to explain). So I was trying to share a link on Facebook.
First I was wrong with the params, I was writing
var params=["http://www.google.com":"link"]
and the correct way is
var params=["link":"http://www.google.com"]
(Because of the way it's written in Obj-C on dev.facebook.com I got confused).
Secondly, if I follow the logic of the original github project, I call my function this way:
FB.performPublishAction(self.shareLinkOnFB)
where performPublishAction (in the Facebook class) ask for the new publish permissions:
func performPublishAction(action:() -> Void){
fbSession?.requestNewPublishPermissions(fbSettings.publishPermissions, defaultAudience: fbSettings.publishAudience, completionHandler: {(session:FBSession!, error:NSError!)->Void in
if error==nil {
println("perform publish action / no error")
action()
}else{
println("perform publish action / error: \(error.localizedDescription)")
println(error)
}
})
and shareLinkOnFb function calls FB.requestToShareLink():
func shareLinkOnFB(){
FB.requestToShareLink()
}
func requestToShareLink(){
var params=["link":"\(self.link)", "message":"\(self.message)"]
FBRequestConnection.startWithGraphPath("/me/feed", parameters:params, HTTPMethod: "POST", completionHandler: { (connection:FBRequestConnection!, result:AnyObject!, error:NSError!) in
if error != nil {
println("Request to share link / An error occurred: \(error.localizedDescription)")
println(error)
//handle error
}else{
println("link successfully posted: \(result)")
}
})
}
I'm not sure you have the same problem but I hope it can help you.
check this guy's answer https://stackoverflow.com/a/58481784/12250012
also now facebook is not allowing programatically.
You have to request in your facebook app the "publish_actions" permissions.
I have had the same issue. I have solve it by writing such code. If you have an active Facebook session this code is perfectly work.
func performPublishAction(callBack:() -> Void){
self.fbSession?.requestNewPublishPermissions(["publish_actions"], defaultAudience: FBSessionDefaultAudience.Friends, completionHandler: {(session:FBSession!, error:NSError!)->Void in
if error == nil {
callBack()
}else{
println(error)
}
})
}
func shareImage (imageName:UIImage){
var requestConneciton:FBRequestConnection=FBRequestConnection()
requestConneciton.errorBehavior=FBRequestConnectionErrorBehavior.None
requestConneciton.addRequest(FBRequest(forUploadPhoto:imageName)) { (connection:FBRequestConnection!, result:AnyObject!, error:NSError!) -> Void in
println("\(error)");
println("\(result)");
}
requestConneciton.start()
}
use of these methods will be as:
facebookHelper.performPublishAction { () -> Void in
facebookHelper.shareImage(self.itemImage)
//NkCommonClass.displayToast("Posted successfully on your wall.")
}

Resources