Show Facebook Events in Swift 3 - ios

How can I store Facebook events in an array using Swift 3? I have the following code which I pretty much copied from The Swift Guy but it doesn't work for this code. The following is in my viewDidLoad() function:
let url = URL(string: "https://www.facebook.com/events/upcoming")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print("ERROR")
} else {
if let content = data {
do {
let myJson = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
print(myJson)
} catch {
print("error")
}
}
}
}
task.resume()
How can I get the information from the Facebook Events page? I should mention that I'm good at coding, but I'm a beginner when it comes to Swift so some explanation as to what each line does would be very helpful. Thanks!

The URL you're requesting returns an HTML page that you're trying to parse as if it was a JSON resource. You'll have to use the Facebook Graph API and/or the Facebook Swift SDK to get the information as JSON.
Try reading the Facebook developer documentation for more information:
https://developers.facebook.com/docs/swift
https://developers.facebook.com/docs/graph-api
Also, Swift Error objects contain information that can help you understand what went wrong. You can print them to the console. Try this:
if error != nil {
print(error)
} else {
// ...
The catch statement also sets its own error variable inside its block, so you can use:
} catch {
print(error)
}
Make sure you understand the steps involved in creating a Facebook app: registering the app with Facebook, downloading the SDK, adding the SDK to your project, configuring the SDK for your app, logging in to Facebook in your app and then calling the Facebook Graph API to get the information. These steps are all described in the Facebook documentation mentioned above. I'd start with the iOS SDK (Objective-C) instructions to setup your project and then change your app delegate and view controller to the following:
AppDelegate.swift:
import UIKit
import FacebookCore
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
SDKApplicationDelegate.shared.application(application, didFinishLaunchingWithOptions: launchOptions)
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
return SDKApplicationDelegate.shared.application(app, open: url, options: options)
}
}
ViewController.swift:
import UIKit
import FacebookCore
import FacebookLogin
class ViewController: UIViewController, LoginButtonDelegate {
override func viewDidLoad() {
super.viewDidLoad()
if let _ = AccessToken.current {
loadEvents()
}
let loginButton = LoginButton(readPermissions: [ .publicProfile, .userEvents ])
loginButton.center = view.center
loginButton.delegate = self
view.addSubview(loginButton)
}
func loginButtonDidCompleteLogin(_ loginButton: LoginButton, result: LoginResult) {
if let _ = AccessToken.current {
loadEvents()
}
}
func loginButtonDidLogOut(_ loginButton: LoginButton) {
// Logout handling code here
}
func loadEvents() {
let connection = GraphRequestConnection()
connection.add(GraphRequest(graphPath: "/me/events")) { httpResponse, result in
switch result {
case .success(let response):
print("Graph Request Succeeded: \(response)")
case .failed(let error):
print("Graph Request Failed: \(error)")
}
}
connection.start()
}
}
The response object will contain the Facebook Events information, already parsed.

Related

Handling dynamic link from Firebase in swift

I have made a dynamic link in Firebase, which op my iOS app, when I click it.
The problem is, that I can't print information about the link.
I use this function in the AppDelegate file to handle the dynamic link:
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
print("Handeling dynamic link")
if let incomingURL = userActivity.webpageURL {
print("Incoming URL is \(incomingURL)")
let linkHandled = DynamicLinks.dynamicLinks().handleUniversalLink(incomingURL) { (dynamicLink, error) in
guard error == nil else {
print("Found an error! \(error!.localizedDescription)")
return
}
if let dynamicLink = dynamicLink {
self.handleIncomingDynamicLink(dynamicLink)
}
}
if linkHandled {
return true
} else {
// Maybe do other things with our incoming url
return false
}
}
return false
}
When I click on a dynamic link and open the app, none of the print statements is printed to the console.
It seems like this function is never tapped into.
The handleDynamicLink function is:
func handleIncomingDynamicLink(_ dynamicLink: DynamicLink) {
guard let url = dynamicLink.url else {
print("That is weird. My dynamic link object has no url")
return
}
print("Your incoming link parameter is \(url.absoluteString)")
}
I want to print information about the url so I can debug and use the information in the url to redirect to pages in the app.
When I'm testing this, I run the app on an iPhone connected to my mac.
I don't run it on the iOS simulator.
The code in your UIApplication delegate method looks good. The only difference I see with the way you have yours compared to mine is I'm returning true in the last line & you're returning false.
By the way, put breakpoints in that UIApplication delegate method & let me know if you're ever even getting to the first line in that delegate method. Are you able to hit any breakpoints in your handleIncomingDynamicLink() method?
Also, try adding this in applicationDidBecomeActive:
func applicationDidBecomeActive(_ application: UIApplication) {
guard let url = self.launchURL else { return }
self.launchURL = nil
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: { // wait to init. notifs.
DynamicLinks.dynamicLinks().handleUniversalLink(url) { (dynamiclink, error) in
if let dynamiclink = dynamiclink {
self.handleIncomingDynamicLink(dynamiclink)
}
}
})
}
And add this:
func application(_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
print("RECEIVED A URL THROUGH A CUSTOM SCHEME: \(url.absoluteString)")
if let dynamicLink = DynamicLinks.dynamicLinks().dynamicLink(fromCustomSchemeURL: url) {
self.handleIncomingDynamicLink(dynamicLink)
return true
} else { // Maybe handle Google or Facebook sign-in here
return false
}
}

Checking If user is logged in or not

I'm new to iOS development and to the AWS Amplify framework. I am currently working my way through the Authentication documentation, but it isn't clear how to check the logged-in status of a given user. I only want to display the login form if the user not already logged in. How do I achieve this? There doesn't seem to be any information listed in the docs, and the only resource I found from google applied to a different platform (react).
You need to listen to Auth events and update the state for a flag Eg:isSignedIn which would be initially signed off.
final class UserData: ObservableObject {
#Published var isSignedIn : Bool = false
}
import UIKit
import Amplify
import AmplifyPlugins
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
public let userData = UserData()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
do {
try Amplify.add(plugin: AWSCognitoAuthPlugin())
try Amplify.configure()
// load data when user is signedin
self.checkUserSignedIn()
// listen to auth events
_ = Amplify.Hub.listen(to: .auth) { (payload) in
switch payload.eventName {
case HubPayload.EventName.Auth.signedIn:
self.updateUI(forSignInStatus: true)
case HubPayload.EventName.Auth.signedOut:
self.updateUI(forSignInStatus: false)
case HubPayload.EventName.Auth.sessionExpired:
self.updateUI(forSignInStatus: false)
default:
break
}
}
} catch {
print("Failed to configure Amplify \(error)")
}
return true
}
func updateUI(forSignInStatus : Bool) {
DispatchQueue.main.async() {
self.userData.isSignedIn = forSignInStatus
}
}
// when user is signed in, fetch its details
func checkUserSignedIn() {
// every time auth status changes, let's check if user is signedIn or not
// updating userData will automatically update the UI
_ = Amplify.Auth.fetchAuthSession { (result) in
do {
let session = try result.get()
self.updateUI(forSignInStatus: session.isSignedIn)
} catch {
print("Fetch auth session failed with error - \(error)")
}
}
}
See the full code here.

Spotify token swap : unsupported URL error

I'm trying to implement the token swap and refresh for the Spotify sdk. I'm using the Heroku app as a server. Here is my code.
In my Sign-in view controller:
var auth = SPTAuth.defaultInstance()!
var session: SPTSession!
var player: SPTAudioStreamingController?
SPTAuth.defaultInstance().clientID = "********************************"
SPTAuth.defaultInstance().redirectURL = URL(string: "viraj-project2://callback" )
SPTAuth.defaultInstance().tokenSwapURL = URL(string: "https://viraj-project2.herokuapp.com/v1/swap")
SPTAuth.defaultInstance().tokenRefreshURL = URL(string: "https://viraj-project2.herokuapp.com/v1/refresh")
SPTAuth.defaultInstance().requestedScopes = [SPTAuthStreamingScope, SPTAuthPlaylistReadPrivateScope, SPTAuthPlaylistModifyPublicScope, SPTAuthPlaylistModifyPrivateScope, SPTAuthUserLibraryReadScope, SPTAuthUserLibraryModifyScope]
loginUrl = SPTAuth.defaultInstance().spotifyWebAuthenticationURL()
#IBAction func signIn(_ sender: Any) {
self.performSegue(withIdentifier: "toNewsFeed", sender: self)
if SPTAuth.supportsApplicationAuthentication() {
UIApplication.shared.open(loginUrl!, options: [:], completionHandler: nil)
} else {
if UIApplication.shared.openURL(loginUrl!) {
if auth.canHandle(auth.redirectURL) {
// To do - build in error handling
}
}
}
}
and in my AppDelegate
class AppDelegate: UIResponder, UIApplicationDelegate{
var window: UIWindow?
var auth = SPTAuth()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
auth.redirectURL = URL(string: "viraj-project2")
auth.sessionUserDefaultsKey = "current session"
// Override point for customization after application launch.
return true
}
func application(_ application: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
// 2- check if app can handle redirect URL
if auth.canHandle(auth.redirectURL) {
// 3 - handle callback in closure
print (url)
auth.handleAuthCallback(withTriggeredAuthURL: url, callback: { (error, session) in
// 4- handle error
if error != nil {
print(error)
print("error!")
}
NotificationCenter.default.post(name: Notification.Name(rawValue: "loggedinperformsegue"), object: nil)
// 5- Add session to User Defaults
let userDefaults = UserDefaults.standard
let sessionData = NSKeyedArchiver.archivedData(withRootObject: session!)
userDefaults.set(sessionData, forKey: "SpotifySession")
userDefaults.synchronize()
// 6 - Tell notification center login is successful
NotificationCenter.default.post(name: Notification.Name(rawValue: "loginSuccessfull"), object: nil)
})
return true
}
return false
}
The authentication works fine without adding the swap and refresh URLs. But whenever I run it with the two URLS added to the SPTAuth.defaultInstance() I get a unsupported URL error like so:
viraj-project2://callback/?code=AQDhKLE9s5GQGITEn**********5_y9aKZM6_nSlzA
2018-09-14 16:55:08.258063-0400 Project2[17606:3197471] NSURLConnection finished with error - code -1002
Optional(Error Domain=NSURLErrorDomain Code=-1002 "unsupported URL" UserInfo={NSLocalizedDescription=unsupported URL, NSUnderlyingError=0x109385130 {Error Domain=kCFErrorDomainCFNetwork Code=-1002 "unsupported URL" UserInfo={NSLocalizedDescription=unsupported URL}}})
error!
Can someone help?
Update on further digging:
When I include add the tokenSwapURL and tokenRefreshURL to my default instance the loginUrl formed looks like so
"https://accounts.spotify.com/authorize?nolinks=true&nosignup=true&response_type=code&scope=streaming%20playlist-read-private%20playlist-modify-public%20playlist-modify-private%20user-library-read%20user-library-modify&utm_source=spotify-sdk&utm_medium=ios-sdk&utm_campaign=ios-sdk&redirect_uri=viraj-project2%3A%2F%2Fcallback&show_dialog=true&client_id=****************"
The response type is "code" which as per the documentation is the right one according to Authorization Flow. But this is what returns the unsupported URL error.
Without the swap and refresh URLs the loginURL looks the same except that the response_type says "token". And this works fine. Is that right though? Doesn't response_type = token mean implicit grant flow?
https://developer.spotify.com/documentation/general/guides/authorization-guide/#client-credentials-flow

How to set the default SyncConfiguration for Realm, so I can get it in multiple ViewControlllers without redundant code?

According to the:
Proper Realm usage patterns/best practices
What is the best practice or design pattern to maintain sync activity across multiple views
Design Pattern for Realm Database Swift 3.1 - Singleton
my approach is like:
AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
DispatchQueue.main.async {
let username = "test#test.com"
let password = "Test123"
let serverUrl = URL(string: "http://test.com:9080")
let realmUrl = URL(string: "realm://test.com:9080/~/realmtest")
if let user = SyncUser.current {
Realm.Configuration.defaultConfiguration.syncConfiguration = SyncConfiguration(user: user, realmURL: realmUrl!)
} else {
SyncUser.logIn(with: .usernamePassword(username: username, password: password, register: false), server: serverUrl!, onCompletion: { (user, error) in
guard let user = user else {
print("Error: \(String(describing: error?.localizedDescription))")
return
}
Realm.Configuration.defaultConfiguration.syncConfiguration = SyncConfiguration(user: user, realmURL: realmUrl!)
})
}
}
return true
}
ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
print("SyncConfiguration: \(String(describing: Realm.Configuration.defaultConfiguration.syncConfiguration))")
self.realm = try! Realm()
}
When I open app for the first time nothing happens but when I open app the second time, Realm works fine.
Whenever I open app, the printed SyncConfiguration is nil. No errors!
Searched here and there and can't find an answer...
The problem is that you are using an async method to configure your Realm, but you don't call the print inside the completion handler of your method. You should only present your viewcontoller once your asynchronous call has finished.

Trying to connect Facebook login via FirebaseUI Auth but having an issue with authUI.delegate = self

I am attempting to connect Facebook login via FirebaseUI Auth however I cannot get the Facebook connect button to appear in the instanced authViewController. Platform: iOS, Language: Swift 2.0, Latest stable Xcode release
Everything is working as it should as far as pulling up an instanced authViewController. The e-mail/password option is present and actively creates a new user in my Firebase database.
The problem I am having occurs when trying to implement Facebook login functionality;
In my AppDelegate.swift file I have the following:
import Firebase
import FirebaseAuthUI
...
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
FIRApp.configure()
let authUI = FIRAuthUI.authUI()!
authUI.delegate = self
return true
}
The error I'm getting is "Cannot assign value of type 'AppDelegate' to type 'FIRAuthUIDelegate'" - basically I can't get the following line to work; authUI.delegate = self
I have read all documentation and browsed open/closed issues on implementation at the project's gitHub repo here:
https://github.com/firebase/FirebaseUI-iOS/tree/master/FirebaseUI
I have looked over a previous StackOverflow question that is similar in nature here;
How to use FirebaseUI for Google authentication on iOS in Swift?
I have attempted to copy the code from the above Stack Overflow since he states he did get more options to appear using his code (his question is about an error further down the line regarding authentication) but I still run into the same self delegation error. I have tried assigning FIRAuthUIDelegate to AppDelegate but it does not conform. What am I missing?
I'm also not familiar with FirebaseUI but here is a working example of authorizing a user with Facebook using regular Firebase and the FBSDKs
#IBAction func fbButtonTapped(sender: UIButton) {
let facebookReadPermissions = ["email", "public_profile", "user_photos"]
FBSDKLoginManager().logInWithReadPermissions(facebookReadPermissions, fromViewController: self, handler: { (result:FBSDKLoginManagerLoginResult?, error:NSError?) -> Void in
if error != nil {
Helper.displayAlert("Error Logging into Facebook", message: error!.localizedDescription, viewController: self)
} else {
let credential = FIRFacebookAuthProvider.credentialWithAccessToken(FBSDKAccessToken.currentAccessToken().tokenString)
FIRAuth.auth()?.signInWithCredential(credential) { (user, error) in
if error != nil {
Helper.displayAlert("Error Logging into Facebook", message: error!.localizedDescription, viewController: self)
} else {
let request = FBSDKGraphRequest(graphPath:"me", parameters: ["fields": "id, first_name, last_name, email, age_range, gender, verified, timezone, picture"])
request.startWithCompletionHandler {
(connection, result, error) in
if error != nil {
print (error)
} else if let userData = result as? [String : AnyObject] {
guard let userID = FIRAuth.auth()?.currentUser?.uid else { return }
let userInfo = ["firstName": userData["first_name"] as! String, "lastName": userData["last_name"] as! String, "email": userData["email"] as! String,
"gender": userData["gender"] as! String, "id": userData["id"] as! String, "verified": userData["verified"]?.description as! AnyObject, "key": userID]
FirebaseData.fbData.createFirebaseUser(userID, user: userInfo)
self.performSegueWithIdentifier(self.loginSucessIdentifier, sender: nil)
}
}
}
}
}
})
}
func createFirebaseUser(uid: String, user: [String : AnyObject]) {
REF_USERS.child(uid).setValue(user)
}
The code could be cleaned up a bit to get rid of all the if let statements but it should get you going with a working authorization for facebook login.
I haven't worked with Firebase, but it seems like you should put the code in your view controller...
import UIKit
import Firebase
import FirebaseAuthUI
class ViewController: UIViewController, FIRAuthUIDelegate {
override func viewDidLoad() {
super.viewDidLoad()
FIRApp.configure()
let authUI = FIRAuthUI.authUI()!
authUI.delegate = self
}
Again, I have not used Firebase before, but this is how delegates usually work.

Resources