Getting 100x100 profile pic using Facebook API, Firebase and Swift - facebook-graph-api

My project had been getting the URL string for the medium sized profile pic using this code:
let downloadMediumPicTask = session.dataTask(with: mediumProfPictureURL) { (data, response, error)
in
// The download has finished.
if let e2 = error {
print("Error downloading profile picture: \(e2)")
} else {
if let res2 = response as? HTTPURLResponse {
print("Downloaded medium profile picture with response code \(res2.statusCode)")
if let imageData2 = data {
mediumProfilePictureUIImageFile = UIImage(data: imageData2)!
print("mediumProfilePictureUIImageFile has now been defined as: \(mediumProfilePictureUIImageFile).")
} else {
print("Couldn't get image: Image is nil")
}
} else {
print("Couldn't get response code for some reason")
}
}
}
downloadMediumPicTask.resume()
It crashes here giving a 403 response code. The URL that is being referenced is an expired signature URL from Facebook. Firebase doesn't adjust to get the new appropriate URL, and it was from Firebase that I had been getting the URL. I can't figure out how to get it directly as tried below:
func getUrlOfMediumProfilePic(){
if (FBSDKAccessToken.current() != nil) {
let graphPathPart2 = "me/picture"
let paramsPart2 = ["type":"medium", "redirect":"false"]
let completionHandlerPart2 = { (connection: FBSDKGraphRequestConnection?, result: Any?, error: Error?) in
if let error = error {
print("Medium picture graph call contained an error: \(error.localizedDescription)")
return
} else {
guard connection != nil else {
print("getURLOfLargeProfilePic() function aborted bc connection failed.")
return
}
let results = result! as! NSDictionary
let dataDict = results["data"] as! NSDictionary
stringOfMediumProfilePicURLaka100x100 = dataDict["url"] as! String
print("medium picture graph call results define stringOfMediumProfilePicURLaka100x100 as: \(stringOfMediumProfilePicURLaka100x100)")
}
}
let graphRequest = FBSDKGraphRequest(graphPath: graphPathPart2, parameters: paramsPart2)!
graphRequest.start(completionHandler: completionHandlerPart2)
}else{
print("User not logged in when getURLOfMediumProfilePic() function was run.")
return
}
}
This code yields an error with code 8.

Have you tried this:
https://graph.facebook.com/{id}/picture?width=100&height=100
I don't know swift, so I can't help about syntax. I think you can make http request to url and get image.
Hope this help :)

Related

Activity Indicator called from another class not Stopping

I am using activity indicator from "https://github.com/erangaeb/dev-notes/blob/master/swift/ViewControllerUtils.swift". I tried using it in my view but indicator doesn't seem to stop.I have included it in my view as below:
ActivityIndicatorView().showActivityIndicator(uiView: self.view)
I using the below code to stop:
ActivityIndicatorView().hideActivityIndicator(uiView: self.view)
But I don't know why the indicator is stoping.
More of my code is as below:
Alamofire.request(diaryViewUrl, method: .get, parameters: [:]).responseJSON {
response in
if response.result.isSuccess{
ActivityIndicatorView().showActivityIndicator(uiView: self.view)
let dataFetched : JSON = JSON(response.result.value!)
self.diaryDateTimeText = dataFetched["diary_datetime"].string
self.diaryLocText = dataFetched["diary_loc"].string
self.diaryText = dataFetched["diary_text"].string
self.diaryTags = dataFetched["tags"].arrayObject as? [String]
ActivityIndicatorView().hideActivityIndicator(uiView: self.view)
self.topBar()
self.showViews()
self.scrollView.addSubview(self.containerView)
self.view.addSubview(self.scrollView)
}else{
print("Error \(String(describing: response.result.error))")
}
}
You have to show the activity indicator before making the Alamofire request and when you get the response with success or error status you can hide it, now show and hide are called at the same time.
It works fine for me. Please try it
ActivityIndicatorView().showActivityIndicator(uiView: self.view)
Alamofire.request(diaryViewUrl, method: .get, parameters: [:]).responseJSON {
response in
ActivityIndicatorView().hideActivityIndicator(uiView: self.view)
if response.result.isSuccess{
let dataFetched : JSON = JSON(response.result.value!)
self.diaryDateTimeText = dataFetched["diary_datetime"].string
self.diaryLocText = dataFetched["diary_loc"].string
self.diaryText = dataFetched["diary_text"].string
self.diaryTags = dataFetched["tags"].arrayObject as? [String]
self.topBar()
self.showViews()
self.scrollView.addSubview(self.containerView)
self.view.addSubview(self.scrollView)
}else{
print("Error \(String(describing: response.result.error))")
}
}

UIWebView: ics and vcard-Links not handled

I do have a UIWebView included where a public URL is loaded; unfortunately, vcard and ical-Links are not handled, i.e. nothing happens when I click on them.
I tried to set all data detectors, no luck unfortunately.
In the Xcode-log, I get this here when clicking on such a link:
2017-07-14 13:43:00.982413+0200 xxx[2208:967973] WF: _userSettingsForUser mobile: {
filterBlacklist = (
);
filterWhitelist = (
);
restrictWeb = 1;
useContentFilter = 0;
useContentFilterOverrides = 0;
whitelistEnabled = 0;
}
In Safari, the same stuff works as expected.
If I use UIApplication.shared.openURL(icsOrVcardUrl) Safari gets opened and from there everything works as expected again, but I don't want the user to leave the app...
EDIT
This doesn't work either:
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if let url = request.url {
if url.absoluteString.contains("=vcard&") || url.absoluteString.contains("/ical/") {
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig)
let request = URLRequest(url:url)
let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
DispatchQueue.main.async {
self.documentController.url = tempLocalUrl
self.documentController.presentPreview(animated: true)
}
}
}
task.resume()
return false
}
}
return true
}
Use a UIDocumentInteractionController to preview without leaving your app.
I tested it quickly with an .ics file and it works fine.
Implement the UIDocumentInteractionControllerDelegate protocol
extension MainViewController: UIDocumentInteractionControllerDelegate {
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
return self;
}
}
Create an instance of the interaction controller:
let documentController = UIDocumentInteractionController()
Intercept the clicks in your UIWebView in shouldStartLoadWithRequest, return false for links you want to handle with the in-app preview and true for all the rest. And finally:
func previewDocument(_ url: URL) {
documentController.url = url
documentController.presentPreview(animated: true)
}
Here it is in the simulator
EDIT:
In response to the comment to this answer:
The reason it doesn't work for you is because the UIDocumentInteractionController depends on the file extension. The extension of the temp file is .tmp
Renaming the file after the download solves the problem. Quick and dirty example:
let task = session.downloadTask(with: url!) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
do {
let filemgr = FileManager.default
let newUrl = tempLocalUrl.appendingPathExtension("ics")
try filemgr.moveItem(at: tempLocalUrl, to: newUrl)
DispatchQueue.main.async {
self.documentController.url = newUrl
self.documentController.presentPreview(animated: true)
}
} catch let error {
print("Error!!!: \(error.localizedDescription)")
}
}
}
task.resume()
In this case it is advisable to clean after yourself, because the file won't be deleted after the task completes although the OS will delete it eventually, when space is needed. If you often access the same urls, Library/Caches/ may be a better place for this files, just come up with good naming schema, and check if the file doesn't exist already.

Try! throwing fatal error in Swift 3, issues updating from Swift 2

I am trying to parse the JSON data from my server and I am getting an error when it hits the try! statement and it is crashing. It is telling me
Code=3840 "Invalid value around character 0.
It my be because I have not updated my code correctly to Swift 3. I was having an issue with if let parse for the longest time until I switched the as to as?
#IBAction func registerButtonTapped(_ sender: Any) {
let userEmail = userEmailTextField.text;
let userPassword = userPasswordTextField.text;
let userRepeatPassword = repeatPasswordTextField.text;
// Check for empty fields
if((userEmail?.isEmpty)! || (userPassword?.isEmpty)! || (userRepeatPassword?.isEmpty)!){
//Display alert message
displayMyAlertMessage(userMessage: "All fields are required");
return;
}
//Check if passwords matech
if(userPassword != userRepeatPassword){
// Display alert message
displayMyAlertMessage(userMessage: "Passwords do not match");
return;
}
// Send user data to server side
let myUrl = URL(string: "http://");
let request = NSMutableURLRequest(url:myUrl!);
request.httpMethod = "Post";
let postString = "email=\(userEmail)&password=\(userPassword)";
//adding the parameters to request body
request.httpBody = postString.data(using: String.Encoding.utf8);
//creating a task to send the post request
let task = URLSession.shared.dataTask(with: request as URLRequest){
data, response, error in
if error != nil{
print("error=\(error)")
return
}
//parsing the reponse
//converting response to Any
let json = try! JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.allowFragments) as? [String:Any]
//parsing JSON
if let parseJSON = json{
let resultValue = parseJSON["status"] as? String
print("result: \resultValue)")
var isUserRegistered:Bool = false;
if(resultValue=="Success") { isUserRegistered = true;}
var messageToDisplay:String = parseJSON["messsage"] as! String;
if(!isUserRegistered)
{
messageToDisplay = parseJSON["message"] as! String;
}
DispatchQueue.main.async {
//Display alert message with confirmation.
let myAlert = UIAlertController(title:"Alert", message:messageToDisplay, preferredStyle: UIAlertControllerStyle.alert);
let okAction = UIAlertAction(title:"ok", style:UIAlertActionStyle.default){ action in
self.dismiss(animated: true, completion:nil);
}
myAlert.addAction(okAction);
self.present(myAlert, animated:true, completion:nil);
};
}
}
task.resume()
}
Please help, thanks
The reason of the error is that you are sending literal "Optional(Foo)" strings to the server via String Interpolation. userEmail and userPassword will never match and the server sends no data back. In Swift 3 you have to explicitly unwrap even implicit unwrapped optional strings.
The solution is a waterproof error handling with optional bindings
#IBAction func registerButtonTapped(_ sender: AnyObject) {
// Check for empty fields
guard let userEmail = userEmailTextField.text, !userEmail.isEmpty,
let userPassword = userPasswordTextField.text, !userPassword.isEmpty,
let userRepeatPassword = repeatPasswordTextField.text, !userRepeatPassword.isEmpty else {
//Display alert message
displayMyAlertMessage(userMessage: "All fields are required")
return
}
...
Now all relevant optionals are safely unwrapped and the server will get the right data.
Further trailing semicolons and parentheses around if conditions are not needed in Swift and use URLRequest rather than NSMutableURLRequest in Swift 3
var request = URLRequest(url:myUrl!) // var is mandatory if properties are going to be changed.
PS: In any case – as already mentioned in the comments – never use carelessly try! when receiving data from a server.

Cannot call value of non-function type HTTPURLResponse?

I am working with Alamofire 4, in Swift 3.
I am getting an error saying
Cannot call value of non-function type HTTPURLResponse?
I'm trying to download an image and store it in cache, here is my code below, can anyone help?
request = Alamofire.request(post.imageUrl!).validate(contentType: ["image/*"]).response(completionHandler: { request,data, err in
if err == nil {
let image = UIImage(data: data!)!
self.showcaseImage.image = image
FeedVC.imageCache.setObject(image, forKey: self.post.imageUrl!)
}
})
i found that i had to change the top part to just .response. but when i do that I cannot go to if err == nil bloc and set image as data. is there away to fix this. code below.
request = Alamofire.request( post.imageUrl!).validate(contentType: ["application/json"]).response { response in
debugPrint(response)
if error == nil {
let image = UIImage(data: data!)!
all the best
Tony
I looked at the documentation and was able to solve this. By changing the code to.
request = Alamofire.request(post.imageUrl!).validate(contentType: ["image/*"])
.response { response in
print(response.error)
if response.error == nil {
let image = UIImage(data: response.data!)!
I wrote response.error and then response.data, this was the same as calling request, response, data, error that solved my problem of moving on with the error and data.

Handling Facebook Graph API result in iOS SDK with Swift

I just want to request data from Facebook's Graph API, e.g. get the current user's basic info.
The Objective-C doc is: https://developers.facebook.com/docs/ios/graph#userinfo
[FBRequestConnection startForMeWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error) {
/* My question: How do I read the contents of "result" in Swift? */
// Success! Include your code to handle the results here
NSLog(#"user info: %#", result);
} else {
// An error occurred, we need to handle the error
// See: https://developers.facebook.com/docs/ios/errors
}
}];
There's no Swift doc yet, and I'm confused about the "result" parameter whose type is "id".
It looks like result contains a dictionary, but it may be nil. In Swift, its type will map to AnyObject?.
So, in Swift, you could do something like:
// Cast result to optional dictionary type
let resultdict = result as? NSDictionary
if resultdict != nil {
// Extract a value from the dictionary
let idval = resultdict!["id"] as? String
if idval != nil {
println("the id is \(idval!)")
}
}
This can be simplified a bit:
let resultdict = result as? NSDictionary
if let idvalue = resultdict?["id"] as? String {
println("the id value is \(idvalue)")
}
Just remember it is not a dictionary all the way down, it is combinations of dictionaries and arrays.
FBRequestConnection.startWithGraphPath("me?fields=feed", completionHandler: { (connection, result, error) -> Void in
if( error == nil){
let fbGraphObject = result as FBGraphObject
let feed = fbGraphObject.objectForKey("feed") as NSMutableDictionary
let data = feed.objectForKey("data") as NSMutableArray
let postDescription = data[0].objectForKey("description") as String
//println( post )
self.fbu.initialUserFeed = feed
self.performSegueWithIdentifier("SelectStreams", sender: self)
}else
{
//TODO Allert to user that something went wrong
println(error)
}
})
I got confused about this in the beginning
This is a simpler way:
let params: [NSObject : AnyObject] = ["redirect": false, "height": 800, "width": 800, "type": "large"]
let pictureRequest = FBSDKGraphRequest(graphPath: "me/picture", parameters: params, HTTPMethod: "GET")
pictureRequest.startWithCompletionHandler({
(connection, result, error: NSError!) -> Void in
if error == nil {
print("\(result)")
let dictionary = result as? NSDictionary
let data = dictionary?.objectForKey("data")
let urlPic = (data?.objectForKey("url"))! as! String
print(urlPic)
} else {
print("\(error)")
}
})
}