need your support I've been struggling with this for days. I'll appreciate any help
hi
I'm trying to learn IOS Development & No SQL Database
using Firebase
by creating a Chat app using Firebase real time database & swift
my problem is UI is not updated unless i rebuild The App
sum up
-the log out functionality will not work unless i rebuild the App again ( if i sign out i can login but i cant see the Chat Messages unless i rebuild the App)
if i want to login (to see the Chat Messages) this is my Process
A- log out from the APP
B -login to the APP (here i need to be logged in ) i rebuild the App again
so every time i add a message i need to rebuild because so the signin method will get activates
the Login Method
extension FCViewController : FUIAuthDelegate
{
func authUI(_ authUI: FUIAuth, didSignInWith authDataResult: AuthDataResult?, error: Error?)
{
if (error != nil)
{
return
}
login()
}
}
func login(
let authUI = FUIAuth.defaultAuthUI()
let googleAuthProvider = FUIGoogleAuth(authUI: authUI!)
let provider : [FUIAuthProvider] = [googleAuthProvider , FUIEmailAuth()]
authUI?.providers = provider
_authHandle = Auth.auth().addStateDidChangeListener
{
(auth : Auth , user : User?) in
self.messages.removeAll(keepingCapacity: false)
self.messagesTable.reloadData()
if let activeUser = user
{
if (self.user != activeUser)
{
self.user = activeUser
self.signedInStatus(isSignedIn: true)
let name = user!.email?.components(separatedBy: "#")[0]
self.displayName = name!
print("The first call",Auth.auth().currentUser as Any)
}
else
{
// user must Sign in
self.signedInStatus(isSignedIn: false
self.loginSession()
}
}
}
}
the library
import UIKit
import Firebase
import FirebaseEmailAuthUI
import FirebaseAuthUI
import FirebaseGoogleAuthUI
the signout Method
#IBAction func signOut
try Auth.auth().signOut()
the UI
func showAlert(title: String, message: String) {
DispatchQueue.main.async {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
let dismissAction = UIAlertAction(title: "Dismiss", style: .destructive, handler: nil)
alert.addAction(dismissAction)
self.present(alert, animated: true, completion: nil)
}
}
When I tap the "signOut" button, the method gets called and gets executed. after the try Auth.auth().signOut() line of code gets executed, the current user is nil
(lldb) po Auth.auth().currentUser
nil
And if i login again
the Message will not Appear unless i rebuild the App
Related
I want the getUserToken function and userLogin function to run before the next line which is the Firebase Authentication. For it to run ansynchronous
#IBAction func loginButtonPressed(_ sender: UIButton) {
self.showSpinner(onView: self.view)
guard var phoneNumber = phoneTextField.getRawPhoneNumber() else { return }
phoneNumber = "+234\(phoneNumber)"
guard var userPhoneNumber = phoneTextField.getRawPhoneNumber() else { return }
userPhoneNumber = "234\(userPhoneNumber)"
guard let userName = nameTextField.text else {return}
print(phoneNumber)
getUserAcessToken()
userLogin()
//Validate Required fields are mnot empty
if nameTextField.text == userName {
//Firebase Manipulation
PhoneAuthProvider.provider().verifyPhoneNumber(phoneNumber, uiDelegate: nil) { (verificationId, error) in
if error == nil {
print(verificationId!)
//UserDefaults Database manipulation for Verification ID
guard let verifyid = verificationId else {return}
self.defaults.set(verifyid, forKey: "verificationId")
self.defaults.synchronize()
self.removeSpinner()
}else {
print("Unable to get secret verification code from Firebase", error?.localizedDescription as Any)
let alert = UIAlertController(title: "Please enter correct email and phone number", message: "\n", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
return
}
}
}
let OTPRequestVC = storyboard?.instantiateViewController(withIdentifier: "OTPRequestViewController") as! OTPRequestViewController
OTPRequestVC.userId = userId
OTPRequestVC.userEmailData = userEmail
self.present(OTPRequestVC, animated: true)
}
I want the two functions to run asynchronously before the firebase auth.
Its not a good idea to run the time consuming functions on the main thread.
My suggestions would be.
getUserAcessToken() and userLogin() functions Should have a callback. Which will make those functions run on a different thread (I believe those functions are making api call which is done in the background thread)
You could call userLogin() in the completion handler of getUserAcessToken() and then firebaseAuth in the completion handler of getUserAcessToken().
This will make sure that the UI is not hanged till you make those api calls and the user will know that something is going on in the app and the app is not hanged.
Without reproducing the entire intended functionality, the pattern you want to follow is:
func loginButtonPressed(_ sender: UIButton) {
// Any immediate changes to the UI here
// ...
// Start time consuming task in background
DispatchQueue.global(qos: .userInitiated).async {
getUserAccessToken()
userLogin()
// Make your Firebase call
PhoneAuthProvider.provider().verifyPhoneNumber(phoneNumber, uiDelegate: nil) { (verificationId, error) in
// Any response validation here
// ...
DispatchQueue.main.async {
// Any UI updates here
}
}
}
}
I implemented a button in my app that allows the user to change their email using Firebase.
#IBAction func resetEmail(_ sender: Any) {
let alertController = UIAlertController(title: "Change Email", message: "", preferredStyle: .alert)
alertController.addTextField { (textField : UITextField!) -> Void in
textField.placeholder = "Enter New Email Address"
let saveAction = UIAlertAction(title: "Save", style: .default, handler: { (action : UIAlertAction!) -> Void in
//Reset Email
let currentUser = Auth.auth().currentUser
if Auth.auth().currentUser != nil{
currentUser?.updateEmail(to: textField.text!) { error in
if let error = error {
print(error)
} else {
print("CHANGED")
let user = Auth.auth().currentUser
let name = user?.displayName!
let ref = Database.database().reference().child("main").child("users_sen").child(name!).child("email")
ref.setValue(textField.text!)
}
}
}
})
alertController.addAction(saveAction)
}
self.present(alertController, animated: true, completion: {
alertController.view.superview?.isUserInteractionEnabled = true
alertController.view.superview?.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.alertClose(gesture:))))
})
}
However, when I run it and I try to change the email it gives me this error:
UserInfo={NSLocalizedDescription=This operation is sensitive and requires
recent authentication. Log in again before retrying this request.
and tells me to re-sign in order to change the email. How do I avoid this? How do I change the email without re-signing in?
This is how I change the password:
// Password updated.
let currentUser = Auth.auth().currentUser
currentUser?.updatePassword(to: textField.text!) { error in
if let error = error {
} else {
// Password updated.
print("success")
}
}
let userEmail = Auth.auth().currentUser?.email
self.currentPassword = textField.text!
let credential = EmailAuthProvider.credential(withEmail: userEmail!, password: textField.text!)
currentUser?.reauthenticate(with: credential) { error in
if let error = error {
// An error happened.
} else {
// User re-authenticated.
}
}
Base on Firebase's documentation, you need to re-authenticate the user when performing this type of action.
Re-authenticate a user Some security-sensitive actions—such as
deleting an account, setting a primary email address, and changing a
password—require that the user has recently signed in. If you perform
one of these actions, and the user signed in too long ago, the action
fails with an error. When this happens, re-authenticate the user by
getting new sign-in credentials from the user and passing the
credentials to reauthenticateWithCredential.
let user = Auth.auth().currentUser
let credential = EmailAuthProvider.credential(withEmail: "email", password: "password")
user?.reauthenticate(with: credential)
{ error in
if let error = error {
// An error happened.
} else {
// User re-authenticated.
user?.updateEmail(to: "newemail")
{ error in
}
}
}
To change user email without re-authentication you can also leverage Cloud Functions. An example course of action could be:
Create a function that accepts user access token and new email address as parameters
In the function, verify access token and get the user ID from it
In the function, call
admin.auth().updateUser(userId, { email: newEmail })
Call the new function from the client
Note: This solution is less secure because the user intent is not verified by additional authentication. Therefore anyone getting hold of the user's device could change their email address.
If you use email and password to authenticate a user you should to do something like this.
You have to re-authenticate user using credential
Re-authenticate user
Update email
Before don't forget to get current user in your class and import Firebase like this :
...
import Firebase
class Blabla {
...
var currentUser: User? {
return Auth.auth().currentUser
}
Then :
func updateUserEmail(newEmail: String, password: String) {
// 1. Get the credential
guard let currentEmail = currentUser?.email else {return}
var credential = EmailAuthProvider.credential(withEmail: currentEmail, password: password)
You can't get directly password, so you must ask to the user his password by a textfield or other.
// 2. Re-authenticate the user
//(To change mail or password, the user must to be authentificate a short time ago !!!)
self.currentUser?.reauthenticate(with: credential, completion: { (result, error) in
if error != nil {
print("ERROR: ", error?.localizedDescription)
return
}
//3. Update email
self.currentUser?.updateEmail(to: newEmail, completion: { (error) in
if error != nil {
print("ERROR: ", error?.localizedDescription)
}else {
//Do something, for example present an alert of confirmation..
}
})
})
All of the code in the same function from the step 1.
I'm currently working on an application that requires the use of the Google Calendar API in Swift. Unfortunately, the progress of the project is at a standstill as I'm unable to determine how I need to proceed in order to get past the error shown in the title. It appears just after performing the google sign in function. Circumventing the firebase client ID in favor of a different one generated through the Google Developer Console also did not seem to resolve the problem. On multiple occassions in the code, I added scope values as well as the addition of "https://www.googleapis.com/auth/userinfo.profile" to the scope based on another stackoverflow user's solution but none of it has resolved my problem. I do have a Google-Info.plist file who has been properly configured with the reverse ID as well as url scheme, and also attempted to use the client id from that plist file to perform the function, but it still doesn't seem to work. Below I have included code from the two dominant parts of my program that include references to the calendar api or authorization functions. Let me know if there's anything you see that may be incorrectly implemented or if I missed a step in getting this to work. Most of my code is based on the quickstart code provided here: https://developers.google.com/google-apps/calendar/quickstart/ios?ver=swift
with only slight alterations to allow the additional implementation of Firebase.
Note: Sign-In does appear to work and is successfully communicating data to firebase, it simply isn't allowing further progress once calling Google Calendar. In adition to what I provided below, there is also a separate viewcontroller that houses the google sign in button, but I don't believe the issues I'm having reside in that class, hence its exclusion.
APPDELEGATE
import UIKit
import Firebase
import GoogleSignIn
import FirebaseDatabase
import GoogleAPIClientForREST
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate {
var scopes = [kGTLRAuthScopeCalendarReadonly, "https://www.googleapis.com/auth/userinfo.profile"]
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
GIDSignIn.sharedInstance().delegate = self
GIDSignIn.sharedInstance().scopes = scopes
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = UINavigationController(rootViewController: ViewController())
window?.makeKeyAndVisible()
return true
}
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
if let err = error {
print("Failed to log into Google: ", err)
return
}
print("Successfully logged into Google", user)
//Allows information to be stored in Firebase Database
let ref = Database.database().reference(fromURL: "https://tesseractscheduler.firebaseio.com/")
guard let idToken = user.authentication.idToken else { return }
guard let accessToken = user.authentication.accessToken else { return }
LoginController().service.authorizer = user.authentication.fetcherAuthorizer()
let credentials = GoogleAuthProvider.credential(withIDToken: idToken, accessToken: accessToken)
Auth.auth().signIn(with: credentials, completion: { (user, error) in
if let err = error {
print("Failed to create a Firebase User with Google account: ", err)
return
}
//successfully authenticated user
guard let uid = user?.uid else { return }
print("Successfully logged into Firebase with Google", uid)
//Creates database entry with users unique identifier, username, and email. "null" provided to prevent errors.
ref.updateChildValues(["UID": uid, "username": user?.displayName ?? "null", "Email": user?.email ?? "null"])
//Pushes to next screen if login has occurred.
if GIDSignIn.sharedInstance().hasAuthInKeychain(){
//GIDSignIn.sharedInstance().clientID = "197204473417-56pqo0dhn8v2nf5v64aj7o64ui414rv7.apps.googleusercontent.com"
self.window?.rootViewController = UINavigationController(rootViewController: LoginController())
}
LoginController().fetchEvents()
})
}
func application(_ application: UIApplication, open url: URL, options:
[UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
GIDSignIn.sharedInstance().handle(url,
sourceApplication: options[
UIApplicationOpenURLOptionsKey.sourceApplication] as! String!, annotation : options[UIApplicationOpenURLOptionsKey.annotation])
return true
}
LOGINCONTROLLER
import UIKit
import GoogleSignIn
import Firebase
import GoogleAPIClientForREST
class LoginController: UIViewController {
let service = GTLRCalendarService()
let output = UITextView()
let appDelegate = (UIApplication.shared.delegate as? AppDelegate)
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Logout", style: .plain, target: self, action: #selector(didTapSignOut))
view.backgroundColor = UIColor(red: 61/255, green: 91/255, blue: 151/255, alpha: 1)
output.frame = view.bounds
output.isEditable = false
output.contentInset = UIEdgeInsets(top: 20, left: 0, bottom: 20, right: 0)
output.autoresizingMask = [.flexibleHeight, .flexibleWidth]
output.isHidden = true
view.addSubview(output);
}
func fetchEvents() {
let query = GTLRCalendarQuery_EventsList.query(withCalendarId: "primary")
query.maxResults = 10
query.timeMin = GTLRDateTime(date: Date())
query.singleEvents = true
query.orderBy = kGTLRCalendarOrderByStartTime
service.executeQuery(
query,
delegate: self,
didFinish: #selector(displayResultWithTicket(ticket:finishedWithObject:error:)))
}
#objc func displayResultWithTicket(
ticket: GTLRServiceTicket,
finishedWithObject response : GTLRCalendar_Events,
error : NSError?) {
if let error = error {
showAlert(title: "Error", message: error.localizedDescription)
return
}
var outputText = ""
if let events = response.items, !events.isEmpty {
for event in events {
let start = event.start!.dateTime ?? event.start!.date!
let startString = DateFormatter.localizedString(
from: start.date,
dateStyle: .short,
timeStyle: .short)
outputText += "\(startString) - \(event.summary!)\n"
}
} else {
outputText = "No upcoming events found."
}
output.text = outputText
}
private func presentViewController(alert: UIAlertController, animated flag: Bool, completion: (() -> Void)?) -> Void {
UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: flag, completion: completion)
}
//Shows an error alert with a specified output from the log.
func showAlert(title : String, message: String) {
let alert = UIAlertController(
title: title,
message: message,
preferredStyle: UIAlertControllerStyle.alert
)
let ok = UIAlertAction(
title: "OK",
style: UIAlertActionStyle.default,
handler: nil
)
alert.addAction(ok)
presentViewController(alert: alert, animated: true, completion: nil)
}
//Communicated to navigation button to perform logout function.
#objc func didTapSignOut(_ sender: AnyObject) {
GIDSignIn.sharedInstance().signOut()
print("Successfully logged out of Google.")
showAlert(title: "Log Off", message: "Logout Successful!")
appDelegate?.window?.rootViewController = UINavigationController(rootViewController: ViewController())
}
// Do any additional setup after loading the view.
}
Please let me know if there's anything else you would need provided to be able to help identify the issue. I've searched quite extensively and been unable to find a solution for my particular issue. I apologize if it's a silly question: I'm still fairly new to the incorporation of APIs.
I can't say for sure if there is a more effective workaround yet, but for now I have been able to avoid this error by stripping the code of all references to Firebase and ignoring its implementation. Something about the way I was authenticating Google Sign In with Firebase was causing this error to occur. It is no longer an issue after Firebase's removal. El Tomato's question led me to trying this method and so far I have at least been able to progress. If I have time later in the project, I may come back to see if it's possible to implement them both without the error occurring, but for now it's a non-vital feature.
In my app i have a option to login to the app using google sign in. Login is working fine. Once i click the Logout Button, I am not able to logout from google. When ever I click on login button it's not showing login page as image given below:
Instead its redirecting to authentication dialog page as image given below:
Code :
override func viewDidLoad()
{
super.viewDidLoad()
GIDSignIn.sharedInstance().uiDelegate = self
let button = GIDSignInButton(frame: CGRectMake(0, 0, 100, 100))
button.center = view.center
view.addSubview(button)
}
#IBAction func signOutButton(sender: AnyObject) {
GIDSignIn.sharedInstance().signOut()
}
1.import below
import GoogleAPIClient
import GTMOAuth2
2.declare below variable
let kKeychainItemName = "your app name"
let kClientID = "your app clinet id"
let scopes = [kGTLAuthScopeDrive]
let service = GTLServiceDrive()
3.replace this methods to your existing one
override func viewDidLoad() {
super.viewDidLoad()
if let auth = GTMOAuth2ViewControllerTouch.authForGoogleFromKeychainForName(
kKeychainItemName,
clientID: kClientID,
clientSecret: nil) {
service.authorizer = auth
}
self.tblView.tableFooterView=UIView()
// Do any additional setup after loading the view.
}
override func viewDidAppear(animated: Bool) {
if let authorizer = service.authorizer,
canAuth = authorizer.canAuthorize where canAuth {
fetchFiles()
} else {
presentViewController(
createAuthController(),
animated: true,
completion: nil
)
}
}
func fetchFiles() {
let query = GTLQueryDrive.queryForFilesList()
query.pageSize = 10
query.fields = "nextPageToken, files(id, name)"
service.executeQuery(
query,
delegate: self,
didFinishSelector: #selector(GoogleDriveVC.displayResultWithTicket(_:finishedWithObject:error:))
)
}
// Parse results and display
func displayResultWithTicket(ticket : GTLServiceTicket,
finishedWithObject response : GTLDriveFileList,
error : NSError?) {
if let error = error {
showAlert("Error", message: error.localizedDescription)
return
}
if let files = response.files where !files.isEmpty {
for file in files as! [GTLDriveFile] {
self.arrayOfNames.append(file.name)
self.arrayOfIdentifier.append(file.identifier)
}
}
self.tblView.reloadData()
}
// Creates the auth controller for authorizing access to Drive API
private func createAuthController() -> GTMOAuth2ViewControllerTouch {
let scopeString = scopes.joinWithSeparator(" ")
return GTMOAuth2ViewControllerTouch(
scope: scopeString,
clientID: kClientID,
clientSecret: nil,
keychainItemName: kKeychainItemName,
delegate: self,
finishedSelector: #selector(GoogleDriveVC.viewController(_:finishedWithAuth:error:))
)
}
// Handle completion of the authorization process, and update the Drive API
// with the new credentials.
func viewController(vc : UIViewController,
finishedWithAuth authResult : GTMOAuth2Authentication, error : NSError?) {
if let error = error {
service.authorizer = nil
showAlert("Authentication Error", message: error.localizedDescription)
return
}
service.authorizer = authResult
dismissViewControllerAnimated(true, completion: nil)
}
// Helper for showing an alert
func showAlert(title : String, message: String) {
let alert = UIAlertController(
title: title,
message: message,
preferredStyle: UIAlertControllerStyle.Alert
)
let ok = UIAlertAction(
title: "OK",
style: UIAlertActionStyle.Default,
handler: nil
)
alert.addAction(ok)
presentViewController(alert, animated: true, completion: nil)
}
at last logout using
func logout(){
//logout code
GTMOAuth2ViewControllerTouch.removeAuthFromKeychainForName(kKeychainItemName)
navigationController?.popViewControllerAnimated(true)
}
this is the full implementation
If OP is still looking (doubt it since it's been 3 years) and if anyone is still working on this I think I figured it out.
I'm using Objective C but the methodology is still the same.
I have an app that uses Google Sign In to authenticate. This work as OP has described. For our sign out I have the following:
(IBAction)didTapSignOut:(id)sender
{
GIDSignIn *gidObject = [GIDSignIn sharedInstance];
[gidObject signOut];
[gidObject disconnect];
NSString *logOutUrl = #"https://www.google.com/accounts/Logout";
[[UIApplication sharedApplication] openURL:[NSURL URLWithString: logOutUrl] options:#{} completionHandler:nil];
}
Make sure signOut is before disconnect (i originally had those two reversed and it did not sign the user out).
For our workflow I have it so that the logout url appears. This works because if the user wants to sign in again then they need to authenticate. If the user is signed in already it bypasses the authentication and goes directly into application.
I have made a simple game using the Game template in Xcode, coded in swift. I created a shapeNode, and when it is touched, I would like this code to run:
if SLComposeViewController.isAvailableForServiceType(SLServiceTypeFacebook){
var controller = SLComposeViewController(forServiceType: SLServiceTypeFacebook)
controller.setInitialText("Testing Posting to Facebook")
//self.presentViewController(controller, animated:true, completion:nil)
}
This code is run in the GameViewController.swift file, but gives this error. This error occurs on the commented line.
Could not cast value of type 'UIView' (0x379480d0) to 'SKView' (0x37227ad0).
Update: If you are targeting iOS 9 or above there are some small changes to make this work. You will need to add the correct URL schemes to your info.plist otherwise the check to see if the app is installed will not work.
NOTE: Its is a better idea to now use UIActivityController for sharing. This allows you to only use 1 button and you can share to all sorts of services.
http://useyourloaf.com/blog/querying-url-schemes-with-canopenurl/
To present a viewController in a SKScene you need to use the rootViewController
self.view?.window?.rootViewController?.presentViewController(...
I use a little helper for this using swift 2 protocol extensions, so you can use it anywhere you like in your app. The Facebook part looks like this, twitter is basically the same.
import SpriteKit
import Social
/// URLString
private struct URLString {
static let iTunesApp = URL(string: "Your iTunes app link")
static let facebookApp = URL(string: "Your Facebook app link")
static let facebookWeb = URL(string: "Your Facebook web link")
}
/// Text strings
private struct TextString {
static let shareSheetText = "Your share sheet text"
static let error = "Error"
static let enableSocial = "Please sign in to your account first"
static let settings = "Settings"
static let ok = "OK"
}
/// Social
protocol Social {}
extension Social where Self: SKScene {
/// Open facebook
func openFacebook() {
guard let facebookApp = URLString.facebookApp else { return }
guard let facebookWeb = URLString.facebookWeb else { return }
if UIApplication.shared.canOpenURL(facebookApp){
UIApplication.shared.openURL(facebookApp)
} else {
UIApplication.shared.openURL(facebookWeb)
}
}
/// Share to facebook
func shareToFacebook() {
guard SLComposeViewController.isAvailable(forServiceType: SLServiceTypeFacebook) else {
showAlert()
return
}
guard let facebookSheet = SLComposeViewController(forServiceType: SLServiceTypeFacebook) else { return }
facebookSheet.completionHandler = { result in
switch result {
case .cancelled:
print("Facebook message cancelled")
break
case .done:
print("Facebook message complete")
break
}
}
let text = TextString.shareSheetText
//facebookSheet.setInitialText(text)
facebookSheet.setInitialText(String.localizedStringWithFormat(text, "add your score property")) // same as line above but with a score property
facebookSheet.addImage(Your UIImage)
facebookSheet.add(URLString.iTunesApp)
self.view?.window?.rootViewController?.present(facebookSheet, animated: true, completion: nil)
}
// MARK: - Private Methods
/// Show alert
private func showAlert() {
let alertController = UIAlertController(title: TextString.error, message: TextString.enableSocial, preferredStyle: .alert)
let okAction = UIAlertAction(title: TextString.ok, style: .cancel) { _ in }
alertController.addAction(okAction)
let settingsAction = UIAlertAction(title: TextString.settings, style: .default) { _ in
if let url = URL(string: UIApplicationOpenSettingsURLString) {
UIApplication.shared.openURL(url)
}
}
alertController.addAction(settingsAction)
self.view?.window?.rootViewController?.present(alertController, animated: true, completion: nil)
}
}
To use the helper you simply go to the SKScene you need to call the methods and implement the protocol
class YourScene: SKScene, Social {....
Now when the Facebook node/button is pressed you can call the methods as if they are part of the scene itself.
openFacebook() // opens app or safari
shareToFacebook() // opens share sheet textField
all thanks to swift 2 and protocol extensions. The cool bit about this is say you want to use this helper in a regular UIKit app, than all you have to do is import UIKit instead of spriteKit
import UIKit
and change the protocol extension to this
extension Social where Self: UIViewController {....
Its quite nice and very flexible I think
Hope this helps.