How can I use swift version of SLRequest

I try to use SLRequest to post both text and meida to twitter. But somehow only text can be posted. I barely find any information that how to use this class. What's more since twitter change its API in terms of posting media all informaiton I can find in the internet dosn't work anymore. I post the same question on twitter's forum but no body answer me. Is there anybody who can help me.




func twitterSender(photoImported: UIImage) ->Void {

let account = ACAccountStore()

let accountType = account.accountTypeWithAccountTypeIdentifier(

ACAccountTypeIdentifierTwitter)

account.requestAccessToAccountsWithType(accountType, options: nil,

completion: {(success: Bool, error: NSError!) -> Void in

if success {

let arrayOfAccounts =

account.accountsWithAccountType(accountType)

if arrayOfAccounts.count > 0 {

let twitterAccount = arrayOfAccounts.last as! ACAccount

var message = Dictionary<String, AnyObject>()

message["status"] = "My app test 5"

let imageData = UIImageJPEGRepresentation(photoImported, 0.9)

let imageString = imageData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.allZeros)

message["media_ids"] = imageString

let requestURL = NSURL(string:

"https:/

let postRequest = SLRequest(forServiceType:

SLServiceTypeTwitter,

requestMethod: SLRequestMethod.POST,

URL: requestURL,

parameters: message)

postRequest.addMultipartData(imageData, withName: "oauth_*", type: "application/octet-stream", filename: "image.jpg")

postRequest.account = twitterAccount

postRequest.performRequestWithHandler({

(responseData: NSData!,

urlResponse: NSHTTPURLResponse!,

error: NSError!) -> Void in

if let err = error {

println("Error : \(err.localizedDescription)")

}

println("Twitter HTTP response \(urlResponse.statusCode)")

})

}

}

})

}

Replies

The SLRequest class is just a simple wrapper for url requests, and setting up the HTTP request correctly to match the service's API is left up to the developer. This way, when the service updates their API, the developers can update their code to match without Apple needing to make changes to the Social Framework.


It looks like the Twitter API now requires two requests to post a tweet with text and media.


The first request uploads the media/images to Twitter, and returns a media ID for the uploaded resources.

(google: twitter "POST media/upload")


The second request is the actual tweet, which includes the media ID from the first request.

(google: twitter "POST statuses/update")

Thanks for your answer. I have looked at the two APIs you mention. But I am still not too sure how to wrap my request. I think addMultipartData needs to be used, but not too sure either. I have found a lot of objective-c code, they use addMultipartData to add media stuff and then send it with text.

This is untested (other than making sure it compiles w/o error), but it should look something like the following code.

I didn't include the part where you get a valid reference to the account, but if your code is posting the text portion of the status update you should have that working already.

You'll also need to fix the urls, since they need to be invalid or else the forum requires a moderator to approve the post.


Swift 1.2 (Xcode 6.4)

import Social
import Accounts

func updateTwitterStatus(tweet: String, withPhoto photo: UIImage, forAccount account: ACAccount) -> Void
{
    // use Resource URL from (google: twitter "POST media/upload")
    let uploadURL = NSURL(string: "htt ps:/ / upload . twitter . com/1.1/media/upload.json")

    let mediaKey = "media" as NSString
    let imageData = UIImageJPEGRepresentation(photo, 0.9)
    if (imageData == nil) {print("error: could not convert photo to JPG"); return}

    let uploadRequest = SLRequest(forServiceType:SLServiceTypeTwitter, requestMethod: .POST, URL: uploadURL, parameters: [mediaKey : imageData!])

    uploadRequest.account = account

    uploadRequest.performRequestWithHandler()
    {   (responseData: NSData!, urlResponse: NSHTTPURLResponse!, error: NSError!) -> Void in
         
        let mediaIDString = twit_extractStringForKey("media_id_string", fromJSONData:responseData)
        if (mediaIDString == nil) {print("error: failed to get media id from response \(urlResponse.statusCode)"); return}
     
        let statusKey = "status" as NSString
        let mediaIDKey = "media_ids" as NSString
     
        // use Resource URL from (google: twitter "POST statuses/update")
        let statusURL = NSURL(string: "htt ps:/ / api . twitter . com/1.1/statuses/update.json")
     
        let statusRequest = SLRequest(forServiceType:SLServiceTypeTwitter, requestMethod: .POST, URL: statusURL, parameters: [statusKey : tweet, mediaIDKey : mediaIDString!])
     
        statusRequest.account = account
     
        statusRequest.performRequestWithHandler()
        {   (responseData: NSData!, urlResponse: NSHTTPURLResponse!, error: NSError!) -> Void in
             
            if let err = error {print("error : \(err.localizedDescription)")}
            print("Twitter HTTP response \(urlResponse.statusCode)")
        }
    }
}

private func twit_extractStringForKey(key: String, fromJSONData data: NSData?) -> String?
{
    if (data == nil) {return nil}

    let response = NSJSONSerialization.JSONObjectWithData(data!, options: nil, error: nil) as? NSDictionary
    let result = response?.objectForKey(key) as? String
    return result
}


or


Swift 2.0 (Xcode 7 beta 2)

import Social
import Accounts

func updateTwitterStatus(tweet: String, withPhoto photo: UIImage, forAccount account: ACAccount) -> Void
{
    // use Resource URL from (google: twitter "POST media/upload")
    let uploadURL = NSURL(string: "htt ps:/ / upload . twitter . com/1.1/media/upload.json")

    let mediaKey = "media" as NSString
    let imageData = UIImageJPEGRepresentation(photo, 0.9)
    guard (imageData != nil) else {print("error: could not convert photo to JPG"); return}

    let uploadRequest = SLRequest(forServiceType:SLServiceTypeTwitter, requestMethod: .POST, URL: uploadURL, parameters: [mediaKey : imageData!])

    uploadRequest.account = account

    uploadRequest.performRequestWithHandler()
    {   (responseData: NSData!, urlResponse: NSHTTPURLResponse!, error: NSError!) -> Void in

        let mediaIDString = twit_extractStringForKey("media_id_string", fromJSONData:responseData)
        guard (mediaIDString != nil) else {print("error: failed to get media id from response \(urlResponse.statusCode)"); return}

        let statusKey = "status" as NSString
        let mediaIDKey = "media_ids" as NSString

        // use Resource URL from (google: twitter "POST statuses/update")
        let statusURL = NSURL(string: "htt ps:/ / api . twitter . com/1.1/statuses/update.json")

        let statusRequest = SLRequest(forServiceType:SLServiceTypeTwitter, requestMethod: .POST, URL: statusURL, parameters: [statusKey : tweet, mediaIDKey : mediaIDString!])

        statusRequest.account = account

        statusRequest.performRequestWithHandler()
        {   (responseData: NSData!, urlResponse: NSHTTPURLResponse!, error: NSError!) -> Void in
     
            if let err = error {
                print("error : \(err.localizedDescription)")
            }
            print("Twitter HTTP response \(urlResponse.statusCode)")
        }
    }
}

private func twit_extractStringForKey(key: String, fromJSONData data: NSData?) -> String?
{
    guard (data != nil) else {return nil}
    do
    {
        let response = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary
        let result = response?.objectForKey(key) as? String
        return result
    }
    catch {return nil}
}
Post not yet marked as solved Up vote reply of LCS Down vote reply of LCS

Really appreciate. Thank you very much. You remind me how to send one post with two requests. There is just a small mistake in the above code which is in the first request "media_data" parameter is also required along with "media". After I add that paratmenter in dictionary it works perfectly. Again thank you very much.

I'm able to use one request as long as update_with_media.json is specified, like so:


func tweetWithImage(data:NSData)
    {
      
        let account = ACAccountStore()
        let accountType = account.accountTypeWithAccountTypeIdentifier(
            ACAccountTypeIdentifierTwitter)
      
        account.requestAccessToAccountsWithType(accountType, options: nil,
            completion: {(success: Bool, error: NSError!) -> Void in
                if success {
                    let arrayOfAccounts =
                    account.accountsWithAccountType(accountType)
                  
                    if arrayOfAccounts.count > 0 {
                        let twitterAccount = arrayOfAccounts.first as! ACAccount
                        var message = Dictionary<String, AnyObject>()
                        message["status"] = "Test Tweet with image"
                      
                        let requestURL = NSURL(string:
                            "https://api.twitter.com/1.1/statuses/update_with_media.json")
                        let postRequest = SLRequest(forServiceType:
                            SLServiceTypeTwitter,
                            requestMethod: SLRequestMethod.POST,
                            URL: requestURL,
                            parameters: message)
                      
                        postRequest.account = twitterAccount
                        postRequest.addMultipartData(data, withName: "media", type: nil, filename: nil)
                      
                        postRequest.performRequestWithHandler({
                            (responseData: NSData!,
                            urlResponse: NSHTTPURLResponse!,
                            error: NSError!) -> Void in
                            if let err = error {
                                println("Error : \(err.localizedDescription)")
                            }
                            println("Twitter HTTP response \(urlResponse.statusCode)")
                          
                        })
                    }
                }
                else
                {
                    // do what you want here
                  
                }
        })
    }

This is great guys. I've been bashing my head-in trying to get a specific user ID's follower count to no avail. I'm using Twitter's Fabric Kit for login but apart from that nothing seems to work. Any advice? I know this is vague.