How to login to Facebook on SwiftUI? - ios

There are not many resources explaining Facebook Login with SwiftUI. I'm not sure whether my code requires a ViewController or not because Facebook's LoginManager.login() contains a ViewController parameter - however this doesn't really translate to SwiftUI.
Regardless, I am trying to login the user to Facebook when they click on the Button below:
LoginView.swift
import Foundation
import SwiftUI
import FBSDKLoginKit
struct LoginView: View {
#EnvironmentObject var auth: UserAuth
var body: some View {
ZStack {
VStack {
Image("launcher_logo").resizable()
.scaledToFit()
.frame(height: 100)
.padding(.top, 100)
Spacer()
Button(action: {
FBLogin()
}) {
Text("Continue with Facebook")
}.foregroundColor(Color.black)
when the Button is clicked, it initialises FBLogin below - which fires login() in its init():
Model:
class FBLogin: LoginManager {
let loginButton = FBLoginButton()
let token = AccessToken.current
let permissions = ["user_birthday", "user_gender", "public_profile"]
override init(){
super.init()
logIn(permissions: permissions, from: nil)
print("fb init()")
}
override func logIn(permissions: [String], from fromViewController: UIViewController?, handler: LoginManagerLoginResultBlock? = nil) {
// TODO
}
}
However I'm not sure what to do from there. At the moment, only fb init() prints but I want to execute the login and listen to the login result.
Any idea?

Just create LoginManager instance inside ObservableObject and then customise login completion. You can easily use inside SwiftUI View. No need UIViewControllerRepresentable. Here is my sample.
struct ContentView: View {
#ObservedObject var fbmanager = UserLoginManager()
var body: some View {
Button(action: {
self.fbmanager.facebookLogin()
}) {
Text("Continue with Facebook")
}
}
}
class UserLoginManager: ObservableObject {
let loginManager = LoginManager()
func facebookLogin() {
loginManager.logIn(permissions: [.publicProfile, .email], viewController: nil) { loginResult in
switch loginResult {
case .failed(let error):
print(error)
case .cancelled:
print("User cancelled login.")
case .success(let grantedPermissions, let declinedPermissions, let accessToken):
print("Logged in! \(grantedPermissions) \(declinedPermissions) \(accessToken)")
GraphRequest(graphPath: "me", parameters: ["fields": "id, name, first_name"]).start(completionHandler: { (connection, result, error) -> Void in
if (error == nil){
let fbDetails = result as! NSDictionary
print(fbDetails)
}
})
}
}
}
}

Have a look on a callback wrapper for Combine to use Facebook Login and Graph API Request with SwiftUI: https://github.com/axmav/combine-extensions
LoginManager()
.logIn(permissions: [.publicProfile, .email])
.flatMap { loginResult -> AnyPublisher<Account?, Error> in
switch loginResult {
case .failed(let error):
return Fail(outputType: Account?.self, failure: error).eraseToAnyPublisher()
case .cancelled:
return Just(Account?.none)
.setFailureType(to: Error.self)
.eraseToAnyPublisher()
case .success(let grantedPermissions, let declinedPermissions, let accessToken):
return GraphRequest(graphPath: "me", parameters: ["fields": "id, name, first_name"])
.start(type: Account.self)
}
}

Related

How do I implement facebook login?

I'm trying to implement facebook login on IOS using the new Facebook SDK. I'm getting stuck because I'm using my own custom facebook login button, where the SDK requires you to use its own login button. Can someone please help? I need to implement the part where someone click on the custom login button and authenticates using facebook after granting permission. Thanks!
I looked at the Documentation and it only provides support for Objective C and not swift
Here's what I had before the SDK update that no longer works
let loginManager = LoginManager()
loginManager.logIn(permissions: [ ".publicProfile, .email" ], viewController: self) { loginResult in
switch loginResult {
case .failed:
self.showMessage("Error logging you in.", "Could not log you in. Please try again.", .error)
case .cancelled:
self.showMessage("User canceled login.", "User cancelled login. Please try again.", .error)
case .success:
self.setFBUserData()
}
}
You can do this tricks. Initially create a facebookLoginButton but this will be hidden.
let facebookLoginButton = FBLoginButton(frame: .zero, permissions: [.publicProfile, .email])
facebookLoginButton.delegate = self
facebookLoginButton.isHidden = true
and in your custom button action you just needed to process the action
#IBAction func registerWithFacebook(_ sender: UIButton) {
facebookLoginButton.sendActions(for: .touchUpInside)
}
Hope you can understand.
About this trick you can check more: https://medium.com/#emmavagradyan/handle-facebook-login-with-custom-button-in-ios-3f1c99faeb55
Configuration about FacebookLogin: https://itnext.io/swift-facebook-ios-login-sdk-dada412d8341
For the Custom Design I did this way. First create a customView For the Custom Button and add Tap Gesture to that View.
create outlet of your view
#IBOutlet weak var fbLoginContainerView: UIView!
then add tap gesture to that view
let fbContainerTapGesture = UITapGestureRecognizer(target: self, action: #selector(fbLoginVIewAction(_:)))
fbLoginContainerView.addGestureRecognizer(fbContainerTapGesture)
in action getFBUserData
#objc func fbLoginVIewAction(_ sender: UITapGestureRecognizer) {
let loginManager = LoginManager()
loginManager.logIn(permissions: ["public_profile", "email"] , viewController: self) { loginResult in
switch loginResult {
case .failed(let error):
print(error)
case .cancelled:
print("User cancelled login.")
case .success(let grantedPermissions, let declinedPermissions, let accessToken):
self.getFBUserData()
}
}
}
fileprivate func getFBUserData(){
if((AccessToken.current) != nil){
GraphRequest(graphPath: "me", parameters: ["fields": "id, name, picture.type(large), email"]).start(completionHandler: { (connection, result, error) -> Void in
if (error == nil){
self.dict = result as? [String : AnyObject]
print(result!)
print(self.dict)
self.showProgress(on: self.view)
guard let Username = self.dict["name"]! as? String else { return }
guard let UserId = self.dict["id"]! as? String else { return }
guard let Useremail = self.dict["email"]! as? String else { return }
let params = ["provider_user_id": UserId , "name": Username, "email": Useremail ,"provider": "FacebookProvider"] as! [String: AnyObject]
//Do your sign in network call here with parameter
}
}
See the Button ScreenShot

Login and Logout with Facebook account

I want to add login with Facebook account to my IOS app. I tried installing FacebookCore pod and FacebookLogin pod. My login action was successful but when I try to logout then login again, browser used the previous logged in account. Here is my code for login and logout action:
override func viewDidLoad() {
super.viewDidLoad()
let loginButton = LoginButton(readPermissions: [ .publicProfile ])
loginButton.center = view.center
//adding it to view
view.addSubview(loginButton)
loginButton.delegate = self
}
func loginButtonDidCompleteLogin(_ loginButton: LoginButton, result: LoginResult) {
print("logged in")
self.loginButtonClicked()
}
func loginButtonDidLogOut(_ loginButton: LoginButton) {
print("logged out")
AccessToken.current = nil
UserProfile.current = nil
FBSDKLoginManager().logOut()
}
//when login button clicked
func loginButtonClicked() {
let loginManager = LoginManager()
loginManager.loginBehavior = .web
loginManager.logIn(readPermissions: [.publicProfile], viewController: self, completion: {loginResult in
switch loginResult {
case .failed(let error):
print("loginnnnnn errrooorrr",error)
case .cancelled:
print("User cancelled login.")
case .success(let grantedPermissions, let declinedPermissions, let accessToken):
self.getFBUserData()
}
})
}
//function is fetching the user data
func getFBUserData(){
if((FBSDKAccessToken.current()) != nil){
FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "id, name, picture.type(large), email"]).start(completionHandler: { (connection, result, error) -> Void in
if (error == nil){
self.dict = result as! [String : AnyObject]
self.isLogin = true
print(result!)
print(self.dict)
}
})
}
}
How to login with another account?
I had similar issue, I was receiving
you previously logged in to [app name] with Facebook. Would you like
to continue?
every time I wanted to log in into my app even after logout.
I solved it by using of Facebook native app (ver. 177).
You can 'log out' from your app here:
Menu Tab -> Settings -> 'Apps and Websites' -> 'Logged in with Facebook' -> Active -> select app -> tap Remove

Facebook login and App Invites for iOS in Swift

I am stuck after coding the Facebook login.....!
After the login procedure I just end up with a logout button.
I would like to make use of the App Invites for iOS but I cant figure out how to integrate it...
The documentations on "Facebook.developer" is hard to understand.
This is my code so far in the view controller:
import UIKit
import FacebookLogin
import FBSDKLoginKit
class ViewController: UIViewController, FBSDKLoginButtonDelegate {
var dict : [String : AnyObject]!
override func viewDidLoad() {
super.viewDidLoad()
//creating button
let loginButton = LoginButton(readPermissions: [ .publicProfile, .userFriends, .email ])
loginButton.center = view.center
//adding it to view
view.addSubview(loginButton)
//if the user is already logged in
if let accessToken = FBSDKAccessToken.current(){
getFBUserData()
}
}
//when login button clicked
#objc func loginButtonClicked() {
let loginManager = LoginManager()
loginManager.logIn([ .publicProfile, .userFriends, .email ], viewController: self) { loginResult in
switch loginResult {
case .failed(let error):
print(error)
case .cancelled:
print("User cancelled login.")
case .success(let grantedPermissions, let declinedPermissions, let accessToken):
self.getFBUserData()
}
}
}
//function is fetching the user data
func getFBUserData(){
if((FBSDKAccessToken.current()) != nil){
FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "id, name, picture.type(large), email"]).start(completionHandler: { (connection, result, error) -> Void in
if (error == nil){
self.dict = result as! [String : AnyObject]
print(result!)
print(self.dict)
}
})
}
}
func loginButton(_ loginButton: FBSDKLoginButton!, didCompleteWith result: FBSDKLoginManagerLoginResult!, error: Error!) {
/*
Check for successful login and act accordingly.
Perform your segue to another UIViewController here.
*/
let viewController = self.storyboard?.instantiateInitialViewController("StoryBoardID") as? UITableViewController
self.presentViewController(viewController, animated: true, completion: nil)
}
func loginButtonDidLogOut(_ loginButton: FBSDKLoginButton!) {
// Actions for when the user logged out goes here
}
}
Any help?

facebook swift sdk 0.2.0 swift 3.1 login page stays open

I'm using;
swift sdk 0.2.0 and
swift 3.1
So, on my viewcontroller (initial.swift) I've
import UIKit
import FacebookCore
import FacebookLogin
...
override func viewDidLoad() {
if let accessToken = AccessToken.current {
print("User is already logged in")
...
}
else {
let loginButton = LoginButton(readPermissions: [ .publicProfile, .email ])
loginButton.center = view.center
//AFTER ACTION
let loginManager = LoginManager()
loginManager.logIn([ .publicProfile, .email ], viewController: self)
{ loginResult in
switch loginResult {
case .failed(let error):
print(error)
break
case .cancelled:
print("User cancelled login.")
case .success(let grantedPermissions, let declinedPermissions, let accessToken):
print("Logged in!")
}
}
}
Login button shows up as expected but after permission page, white page stays open, when I click "done" manually app thinks that user cancelled login.
There are similar threads suggesting missing app delegate functions (34734885), but I guess it's a solution for FBSDKCoreKit and FBSDKLoginKit, not for new facebook swift sdk. Again, I guess. Any suggestion?
EDIT
I've also tried this one;
let myLoginButton = UIButton()
myLoginButton.backgroundColor = UIColor.darkGray
myLoginButton.frame = CGRect(0, 0, 180, 40);
myLoginButton.center = view.center;
myLoginButton.setTitle("My Login Button", for: UIControlState.normal)
// Add the button to the view
view.addSubview(myLoginButton)
// Handle clicks on the button
myLoginButton.addTarget(self, action: #selector(self.FBloginButtonClicked), for: UIControlEvents.touchUpInside)
#objc func FBloginButtonClicked() {
//AFTER ACTION
let loginManager = LoginManager()
loginManager.logIn([ .publicProfile, .email ], viewController: self)
{ loginResult in
switch loginResult {
case .failed(let error):
print(error)
break
case .cancelled:
print("User cancelled login.")
case .success(let grantedPermissions, let declinedPermissions, let accessToken):
print("Logged in!")
}
}
}
As I understand you did everything accordingly to facebook guide?
Including this part:
// Handle clicks on the button
myLoginButton.addTarget(self, action: #selector(self.loginButtonClicked) forControlEvents: .TouchUpInside)
And
// Once the button is clicked, show the login dialog
#objc func loginButtonClicked() {
let loginManager = LoginManager()
...
}
As per understanding your question, You seems to write one method into appdelegate. This will return to your application after successful login.
import FacebookCore // (FBSDKCore's alternative for swift)
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
let isFBOpenUrl = SDKApplicationDelegate.shared.application(application, open: url, sourceApplication: sourceApplication, annotation: annotation)
if isFBOpenUrl { return true }
return false
}
SWIFT 4:
func application(_ application: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey:Any] = [:]) -> Bool {
return SDKApplicationDelegate.shared.application(application, open: url, options: options)
}
Hope this thing will help you.

"Not Now" button handling in **Facebook-SDK**

In Facebook-Login when user enters "Username & Password" then on safari Facebook-SDK showing below screen.
If the user clicks on Not Now rather that on Continue as User. Facebook-SDK returns always Success
Is this Facebook-SDK bug or I am missing something.
I am adding below code:
func fbLoginWithViewController(viewController :UIViewController, completion : #escaping (_ loginResult: AnyObject, _ error: NSError?) -> Void) {
let loginManager = LoginManager()
loginManager.loginBehavior = .systemAccount
loginManager.logIn([ .publicProfile,.userFriends,.email ], viewController: viewController) { loginResult in
switch loginResult {
case .failed(let error):
print(error)
// show error method
self.isCancelled = false
completion(loginResult as AnyObject,error as NSError?)
case .cancelled:
print("User cancelled login.")
// show cancelled error
self.isCancelled = true
completion(loginResult as AnyObject,nil)
case .success(let grantedPermissions, let declinedPermissions, let accessToken):
print("Logged in!")
//add api
self.isCancelled = false
if((FBSDKAccessToken.current()) != nil){
FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "id, name, first_name, last_name, email, picture.width(180).height(180)"]).start(completionHandler: { (connection, result, error) -> Void in
if (error == nil){
self.fbAccesstoken = FBSDKAccessToken.current().tokenString
completion(result as AnyObject,nil)
}
})
}
}
}
}
Let me know if I am doing wrong!

Resources