I have ios project written in swift 2.3 that is integrated with AWS by following the instructions from AWS mobile hub integration page. On the page that explains about downloading file from s3 bucket, it provides a function, and that function takes AWSContent type parameter. I'd like to use that function to download a particular file from S3 bucket. I tried using contentWithKey member of AWSUserFileManager with my file name and path but getting "found nil while unwrapping optional value" error. Could anyone point me to the right direction? Thanks.
private var manager: AWSUserFileManager!
class S3Access: NSObject {
func setupS3Acess() {
let x = manager.contentWithKey("public/GMG.csv")
downloadContent(x, pinOnCompletion: false)
}
// This code is from Amazon MobileHub integration page
private func downloadContent(content: AWSContent, pinOnCompletion: Bool) {
content.downloadWithDownloadType(
.IfNewerExists,
pinOnCompletion: pinOnCompletion,
progressBlock: {[weak self](content: AWSContent?, progress: NSProgress?) -> Void in
guard self != nil else { return }
/* Show progress in UI. */
},
completionHandler: {[weak self](content: AWSContent?, data: NSData?, error: NSError?) -> Void in
guard self != nil else { return }
if let error = error {
print("Failed to download a content from a server. \(error)")
return
}
print("Object download complete.")
})
}
}
I found the culprit. I need to assign manager variable with default user file manager like "manager = AWSUserFileManager.defaultUserFileManager()". I'm just posting this for dev like myself who already integrated with AWS libraries to the project and does not require additional authentication setup.
private var manager: AWSUserFileManager!
class S3Access: NSObject {
func setupS3Acess() {
manager = AWSUserFileManager.defaultUserFileManager()
let x = manager.contentWithKey("public/GMG.csv")
downloadContent(x, pinOnCompletion: false)
}
// This code is from Amazon MobileHub integration page
private func downloadContent(content: AWSContent, pinOnCompletion: Bool) {
content.downloadWithDownloadType(
.IfNewerExists,
pinOnCompletion: pinOnCompletion,
progressBlock: {[weak self](content: AWSContent?, progress: NSProgress?) -> Void in
guard self != nil else { return }
/* Show progress in UI. */
},
completionHandler: {[weak self](content: AWSContent?, data: NSData?, error: NSError?) -> Void in
guard self != nil else { return }
if let error = error {
print("Failed to download a content from a server. \(error)")
return
}
print("Object download complete.")
})
}
}
Related
I've centralized API calls for my App in a class called APIService.
Calls look like the one below:
// GET: Attempts getconversations API call. Returns Array of Conversation objects or Error
func getConversations(searchString: String = "", completion: #escaping(Result<[Conversation], APIError>) -> Void) {
{...} //setting up URLRequest
let dataTask = URLSession.shared.dataTask(with: request) { data, response, error in
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200, let _ = data
else {
print("ERROR: ", error ?? "unknown error")
completion(.failure(.responseError))
return
}
do {
{...} //define custom decoding strategy
}
let result = try decoder.decode(ResponseMultipleElements<[Conversation]>.self, from: data!)
completion(.success(result.detailresponse.element))
}catch {
completion(.failure(.decodingError))
}
}
dataTask.resume()
}
I'm executing API calls from anywhere in the Application like so:
func searchConversations(searchString: String) {
self.apiService.getConversations(searchString: searchString, completion: {result in
switch result {
case .success(let conversations):
DispatchQueue.main.async {
{...} // do stuff
}
case .failure(let error):
print("An error occured \(error.localizedDescription)")
}
})
}
What I would like to achieve now is to execute func searchConversations for each character tapped by the user when entering searchString.
This would be easy enough by just calling func searchConversations based on a UIPressesEvent being fired. Like so:
override func pressesEnded(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
guard let key = presses.first?.key else { return }
switch key.keyCode {
{...} // handle special cases
default:
super.pressesEnded(presses, with: event)
searchConversations(searchString: SearchText.text)
}
}
My problem is this now:
Whenever a new character is entered, I'd like to cancel the previous URLSession and kick-off a new one. How can I do that from inside the UIPressesEvent handler?
The basic idea is to make sure the API returns an object that can later be canceled, if needed, and then modifying the search routine to make sure to cancel any pending request, if any:
First, make your API call return the URLSessionTask object:
#discardableResult
func getConversations(searchString: String = "", completion: #escaping(Result<[Conversation], APIError>) -> Void) -> URLSessionTask {
...
let dataTask = URLSession.shared.dataTask(with: request) { data, response, error in
...
}
dataTask.resume()
return dataTask
}
Have your search routine keep track of the last task, canceling it if needed:
private weak var previousTask: URLSessionTask?
func searchConversations(searchString: String) {
previousTask?.cancel()
previousTask = apiService.getConversations(searchString: searchString) { result in
...
}
}
We frequently add a tiny delay so that if the user is typing quickly we avoid lots of unnecessary network requests:
private weak var previousTask: URLSessionTask?
private weak var delayTimer: Timer?
func searchConversations(searchString: String) {
previousTask?.cancel()
delayTimer?.invalidate()
delayTimer = Timer.scheduledTimer(withTimeInterval: 0.25, repeats: false) { [weak self] _ in
guard let self = self else { return }
self.previousTask = self.apiService.getConversations(searchString: searchString) {result in
...
}
}
}
The only other thing is that you probably want to change your network request error handler so that the “cancel” of a request isn’t handled like an error. From the URLSession perspective, cancelation is an error, but from our app’s perspective, cancelation is not an error condition, but rather an expected flow.
You can achieve this by using a timer,
1) Define a timer variable
var requestTimer: Timer?
2) Update searchConversations function
#objc func searchConversations() {
self.apiService.getConversations(searchString: SearchText.text, completion: {result in
switch result {
case .success(let conversations):
DispatchQueue.main.async {
{...} // do stuff
}
case .failure(let error):
print("An error occured \(error.localizedDescription)")
}
})
}
3) Update pressesEnded
override func pressesEnded(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
guard let key = presses.first?.key else { return }
switch key.keyCode {
{...} // handle special cases
default:
super.pressesEnded(presses, with: event)
self.requestTimer?.invalidate()
self.requestTimer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(searchConversations), userInfo: nil, repeats: false)
}
}
We have .zip file in app, tagged as on demand resource. On downloading it we get success in NSBundleResourceRequest completion handler, but unable to find path of downloaded file(.zip). It works fine for png and jpg file but fails for .zip files. Also .zip file download works fine in our testing devices and fails only on App Reviewer devices.
Any alternative for .zip in iOS will work on ODR?
Are you using conditionalyBeginAccessingResources method before beginAccessingResources ?
Resources:
Check this nice ODR ios tutorial from Ray, and this book from Vandad (it contains a section for propper ODR fetching).
From the Ray's tutorial:
class ODRManager {
// MARK: - Properties
static let shared = ODRManager()
var currentRequest: NSBundleResourceRequest?
// MARK: - Methods
func requestFileWith(tag: String,
onSuccess: #escaping () -> Void,
onFailure: #escaping (NSError) -> Void) {
currentRequest = NSBundleResourceRequest(tags: [tag])
guard let request = currentRequest else { return }
request.endAccessingResources()
request.loadingPriority =
NSBundleResourceRequestLoadingPriorityUrgent
request.beginAccessingResources { (error: Error?) in
if let error = error {
onFailure(error as NSError)
return
}
onSuccess()
}
}
}
In use:
ODRManager.shared.requestFileWith(tag: "<#Your tag#>", onSuccess: {
// load it through Bundle
}, onFailure: { (error) in
let controller = UIAlertController(title: "Error", message: "There was a problem.", preferredStyle: .alert)
switch error.code {
case NSBundleOnDemandResourceOutOfSpaceError:
controller.message = "You don't have enough space available to download this resource."
case NSBundleOnDemandResourceExceededMaximumSizeError:
controller.message = "The bundle resource was too big."
case NSBundleOnDemandResourceInvalidTagError:
controller.message = "The requested tag does not exist."
default:
controller.message = error.description
}
controller.addAction(UIAlertAction(title: "Dismiss", style: .default, handler: nil))
guard let rootViewController = self.view?.window?.rootViewController else { return }
rootViewController.present(controller, animated: true)
})
From the book:
let tag = "<#tagString#>"
var currentResourcePack: NSBundleResourceRequest? = NSBundleResourceRequest(tags: [tag])
guard let req = currentResourcePack else { return }
req.conditionallyBeginAccessingResources { available in
if available {
self.displayImagesForResourceTag(tag)
} else {
// this usualy means that the resources are not downloaded so you need to download them first
req.beginAccessingResources { error in
guard error == nil else {
<#/* TODO: you can handle the error here*/#>
return
}
self.displayImagesForResourceTag(tag)
}
}
}
func displayImagesForResourceTag(_ tag: String) {
OperationQueue.main.addOperation {
for n in 0..<self.imageViews.count {
self.imageViews[n].image = UIImage(named: tag + "-\(n+1)")
}
}
}
So, maybe you can dig out the source of zip there?
Alternative way
Another solution is to download the zip, extract it and start using resources from the extract sandbox destination folder by using the FileManager, and use Bundle only when ODR are not or can't be downloaded.
GL
I'm using AWSAppSyncClient to upload files but I'm struggling to connect the upload progress hook with the view.
AWSAppSyncClient is a property of the the application delegate initialized with an S3ObjectManager. The object manager method upload has access to the upload progress via the AWSTransferUtilityUplaodExpression:
expression.progressBlock = {(task, progress) in
DispatchQueue.main.async(execute: {
// Can we update the controller's progress bar here?
print("Progress: \(Float(progress.fractionCompleted))")
})
}
My controller invokes the upload by calling perform:
var appSyncClient: AWSAppSyncClient? // retrieved from the app delegate singleton
appSyncClient?.perform(mutation: CreatePostMutation(input: input)) { (result, error) in ...
What I am struggling with: how do I provide the S3ObjectManager a reference to the controller? I thought of instantiating the AWSAppSyncClient in each controller, and maybe using some sort of delegate pattern?
It's probably overkill to instantiate a new client on each view controller. Setup & teardown take a bit of time & system resources to perform, and you'd probably prefer to keep those activities separate from the view controller in any case, just for separation of responsibilities.
There isn't really a good way of registering a per-object listener, since mutations are queued for eventual, asynchronous delivery. Your delegate idea seems like the best approach at this point.
NOTE: Code below is untested, and not thread-safe.
For example, you could declare a singleton delegate that manages watchers for individual views that need to report progress:
class AppSyncS3ObjectManagerProgressWatcher {
typealias ProgressSubscription = UUID
static let shared = AppSyncS3ObjectManagerProgressWatcher()
private var watchers = [UUID: AppSyncS3ObjectManagerProgressDelegate?]()
func add(_ watcher: AppSyncS3ObjectManagerProgressDelegate) -> ProgressSubscription {
let subscription = UUID()
weak var weakWatcher = watcher
watchers[subscription] = weakWatcher
return subscription
}
func remove(_ subscription: ProgressSubscription?) {
guard let subscription = subscription else {
return
}
watchers[subscription] = nil
}
}
extension AppSyncS3ObjectManagerProgressWatcher: AppSyncS3ObjectManagerProgressDelegate {
func progressReportingExpression(forDownloadingObject object: AWSS3ObjectProtocol) -> AWSS3TransferUtilityDownloadExpression {
let expression = AWSS3TransferUtilityDownloadExpression()
expression.progressBlock = { _, progress in
self.didReportProgress(forDownloadingObject: object, progress: progress)
}
return expression
}
func progressReportingExpression(forUploadingObject object: AWSS3ObjectProtocol & AWSS3InputObjectProtocol) -> AWSS3TransferUtilityUploadExpression {
let expression = AWSS3TransferUtilityUploadExpression()
expression.progressBlock = { _, progress in
self.didReportProgress(forUploadingObject: object, progress: progress)
}
return expression
}
func didReportProgress(forDownloadingObject object: AWSS3ObjectProtocol, progress: Progress) {
for watcher in watchers.values {
watcher?.didReportProgress(forDownloadingObject: object, progress: progress)
}
}
func didReportProgress(forUploadingObject object: AWSS3ObjectProtocol & AWSS3InputObjectProtocol, progress: Progress) {
for watcher in watchers.values {
watcher?.didReportProgress(forUploadingObject: object, progress: progress)
}
}
}
Wherever you conform S3TransferUtility to S3ObjectManager, you would do something like:
extension AWSS3TransferUtility: AWSS3ObjectManager {
public func download(s3Object: AWSS3ObjectProtocol, toURL: URL, completion: #escaping ((Bool, Error?) -> Void)) {
let completionBlock: AWSS3TransferUtilityDownloadCompletionHandlerBlock = { task, url, data, error -> Void in
if let _ = error {
completion(false, error)
} else {
completion(true, nil)
}
}
let progressReportingExpression = AppSyncS3ObjectManagerProgressWatcher
.shared
.progressReportingExpression(forDownloadingObject: s3Object)
let _ = self.download(
to: toURL,
bucket: s3Object.getBucketName(),
key: s3Object.getKeyName(),
expression: progressReportingExpression,
completionHandler: completionBlock)
}
public func upload(s3Object: AWSS3ObjectProtocol & AWSS3InputObjectProtocol, completion: #escaping ((_ success: Bool, _ error: Error?) -> Void)) {
let completionBlock : AWSS3TransferUtilityUploadCompletionHandlerBlock = { task, error -> Void in
if let _ = error {
completion(false, error)
} else {
completion(true, nil)
}
}
let progressReportingExpression = AppSyncS3ObjectManagerProgressWatcher
.shared
.progressReportingExpression(forUploadingObject: s3Object)
let _ = self.uploadFile(
s3Object.getLocalSourceFileURL()!,
bucket: s3Object.getBucketName(),
key: s3Object.getKeyName(),
contentType: s3Object.getMimeType(),
expression: progressReportingExpression,
completionHandler: completionBlock
).continueWith { (task) -> Any? in
if let err = task.error {
completion(false, err)
}
return nil
}
}
}
And then in the progress reporting view:
override func awakeFromNib() {
super.awakeFromNib()
progressSubscription = AppSyncS3ObjectManagerProgressWatcher.shared.add(self)
}
func didReportProgress(forUploadingObject object: AWSS3InputObjectProtocol & AWSS3ObjectProtocol, progress: Progress) {
// TODO: Filter by object local URI/key/etc to ensure we're updating the correct progress
print("Progress received for \(object.getKeyName()): \(progress.fractionCompleted)")
self.progress = progress
}
As I noted, this code is untested, but it should outline a general approach for you to start from. I'd welcome your feedback and would like to hear what approach you eventually settle on.
Finally, please feel free to open a feature request on our issues page: https://github.com/awslabs/aws-mobile-appsync-sdk-ios/issues
Okay, so I'm trying to build an iOS app that relies on Firebase (To work with its android version)
I started with creating a repository for each actor in my app and a general repository to manage them all
Each repository manages the observers of this actor. An example:
Inside the PagesRepository, this is a function that retrieves all the pages from Firebase and returns it inside a completionHandler:
//MARK: Gets the whole pages list
func getPagesList(completionHandler: #escaping (_ pages: [Page]?, _ error: NSError?) -> Void) {
func displayError(error: String) {
print(error)
completionHandler(nil, self.getErrorFromString(error))
}
pagesRef.observe(DataEventType.value) { pagesSnapshot in
guard pagesSnapshot.exists() else {
displayError(error: "Pages snapshot doesn't exist")
return
}
var pagesList = [Page]()
for pageSnapshot in pagesSnapshot.children {
pagesList.append(Page(snapshot: pageSnapshot as! DataSnapshot))
}
completionHandler(pagesList, nil)
}
}
And then I call it from the ViewController like this:
repository.getPagesList { (pages, error) in
guard error == nil else {
return
}
//Do processing
}
I know this may be a lot to take in, but my problem is that every time I call the function, it creates a new observer but doesn't cancel the old one... So, the completionHandler is called multiple times with different values
How should I manage this problem?
(Sorry for being complicated and a little unclear, I'm just really lost)
It seems like you only want to observe the value once so I would use this instead:
func getPagesList(completionHandler: #escaping (_ pages: [Page]?, _ error: NSError?) -> Void) {
func displayError(error: String) {
print(error)
completionHandler(nil, self.getErrorFromString(error))
}
pagesRef.observeSingleEvent(of: .value, with: { (pagesSnapshot) in
var pagesList = [Page]()
for pageSnapshot in pagesSnapshot.children {
pagesList.append(Page(snapshot: pageSnapshot as! DataSnapshot))
}
completionHandler(pagesList, nil)
}) { (error) in
// Display error
}
}
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.")
}