I'm trying to read remote txt file, which is located at remote hosting. I have a link, like http://www.link.com/file.txt.
I am using this code:
let myURLString = "http://google.com"
guard let myURL = NSURL(string: myURLString) else {
print("Error: \(myURLString) doesn't seem to be a valid URL")
return
}
do {
let myHTMLString = try String(contentsOfURL: myURL)
print("HTML : \(myHTMLString)")
} catch let error as NSError {
print("Error: \(error)")
}
But always getting empty string. File is not empty for sure.
I'm running my app on iOS simulator in Xcode.
What am I doing wrong?
Sorry for my bad English.
Don't use stringWithContentsOfURL and friends to retrieve data from a remote server. That's a synchronous API that was designed solely for use with local files. It isn't even guaranteed to work right for network requests on background threads, much less in your main thread.
The right way to retrieve data from remote URLs is with NSURLSession. I'm not a Swift programmer, so I'm not going to attempt a Swift snippet, but the Objective-C equivalent is:
NSURL *url = ...
NSURLSessionDataTask *task =
[NSURLSession sharedSession dataTaskWithURL:url
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
if (error) {
// Handle client-side errors here
} else if (((NSHTTPURLResponse *)response).statusCode != 200) {
// Handle server-side errors here
} else {
NSString *stock = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
// Do something with data here.
}
}];
[task resume];
Haven't test the code, but this should lead you on the right direction.
var statsRequest: String = "http://www.link.com/file.txt"
var statsRequestURL: NSURL = NSURL(string: statsRequest)!
var error: NSError? = nil
var stockNews: String = try! String.stringWithContentsOfURL(statsRequestURL, encoding: NSUTF8StringEncoding)
If the text file has a different encoding you can just use NSASCIIStringEncoding and then parse the html file.
Related
In my app, I try to download a batch of images from a server.
I'm getting a number of errors, one of which I can't find an explanation for. I checked Apple's URL loading system error codes, but I couldn't fine one for error code 12. I also got HTTP load failed (error code: -999) (which is NSURLErrorCancelled) and Task finished with error - code: -1001(which is NSURLErrorTimedOut).
I recently switched from using deprecated NSURLConnection to NSURLSession. With the former, I also got the 999 and 1001 errors, but I'm trying to find out what error code 12 means.
- (void)loadImage:(LeafletURL*)leafletURLInput isThumbnail:(BOOL)isThumbnailInput isBatchDownload:(BOOL)isBatchDownload isRetina:(BOOL)isRetina
{
isRetina_ = isRetina;
if (session)
{
/*is this the right call here? */
[session invalidateAndCancel];
[session release];
session = nil;
}
if (mImageData)
{
[mImageData release];
mImageData = nil;
}
self.leafletURL = leafletURLInput;
self.isThumbnail = isThumbnailInput;
NSString* location = (self.isThumbnail) ?leafletURL.thumbnailLocation :leafletURL.hiResImageLocation;
//// Check if the image needs to be downloaded from server. If it is a batch download, then override the local resources////
if ( ([location isEqualToString:kLocationServer] || (isBatchDownload && [location isEqualToString:kLocationResource])) && self.leafletURL.rawURL != nil )
{
//NSLog(#"final loadimage called server");
//// tell the delegate to get ride of the old image while waiting. ////
if([delegate respondsToSelector:#selector(leafletImageLoaderWillBeginLoadingImage:)])
{
[delegate leafletImageLoaderWillBeginLoadingImage:self];
}
mImageData = [[NSMutableData alloc] init];
/*download tasks have their data written to a local temp file. It’s the responsibility of the completion handler to move the file from its temporary location to a permanent location.*/
NSURL* url = [NSURL URLWithString:[leafletURL pathForImageOnServerUsingThumbnail:self.isThumbnail isRetina:isRetina]];
NSURLRequest* request = [NSURLRequest requestWithURL:url];
session = [NSURLSession sharedSession];
dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
{
// do something with the data
}];
[dataTask resume];
}
//// if not, tell the delegate that the image is already cached. ////
else
{
if([delegate respondsToSelector:#selector(leafletImageLoaderDidFinishLoadingImage:)])
{
[delegate leafletImageLoaderDidFinishLoadingImage:self];
}
}
}
I have an application which implements remote notifications via firebase messaging api. In this app, I have implemented a notification service extension, which among others, implement UNNotificationActions.
In one of these actions, I've implemented an input field where you can write something, which then should be posted to firestore.
I've tried implementing this, but without success. So my question is how can I write to firestore from a rich notification running in the background - is this even possible?
My implementation looks like this:
let likeAction = UNNotificationAction(identifier: "likeAction", title: "Like", options: [])
let commentAction = UNTextInputNotificationAction(identifier: "commentAction", title: "Comment", options: [UNNotificationActionOptions.authenticationRequired], textInputButtonTitle: "Send", textInputPlaceholder: "Type your message")
let category = UNNotificationCategory(identifier: "posts", actions: [likeAction, commentAction], intentIdentifiers: [], options: [])
UNUserNotificationCenter.current().setNotificationCategories([category])
Then in AppDelegate, I implement the function to run whenever this is triggered like this:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
switch response.actionIdentifier {
case "commentAction":
guard let data = response.notification.request.content.userInfo["data"] as? [String: Any] else { return }
guard
let channelName = data["channelName"],
let postId = data["postId"]
else { return }
if let message = response as? UNTextInputNotificationResponse {
let documentPath = "\(channelName)/\(postId))"
let post = Post()
post.documentPath = documentPath
post.addComment(text: message.userText, postDocumentPath: documentPath)
}
I've debugged the code, and the method post.addComment() does actually get fired, and every field has a value. When I check the database, nothing gets inserted into it. The console prints out this, which I don't know if is related to the problem, I haven't been able to find anything online about these lines:
dnssd_clientstub deliver_request ERROR: write_all(21, 65 bytes) failed
nssd_clientstub read_all(26) DEFUNCT
When running the post method, no error from firebase comes up.
This was the initial information I could think of. I can provide more code or info if need be.
Update
I've discovered if I press the button while on lock screen, but with the iPhone in an unlocked state, nothing happens - as soon as I swipe up, and the app shows, the request gets send. This does indeed seem to be a background issue.
Kind regards Chris
So I finally found a solution / workaround.
It turns out that firebase will not post in the background, instead it stores the data locally, until the app comes into foreground, then it will post it.
The solution was to add another firebase function that listens for HTTP requests. In this function, I added a method to post to the database with the data from the action.
I then do a regular but modified http post request with Alamofire from the action to the url like this:
let headers: HTTPHeaders = [
"Authorization": "Bearer \(token)",
"Content-Type": "application/json"
]
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 15.0
configuration.timeoutIntervalForResource = 15.0
configuration.waitsForConnectivity = true
self.alamofireManager = Alamofire.SessionManager(configuration: configuration)
guard let manager = self.alamofireManager else {
return
}
manager.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: headers).responseString(completionHandler: { (response) in
let _ = manager
print(response)
closure(true)
})
The important part of this, for me, was to set configuration.waitsForConnectivity = true otherwise, it would come back saying no connection to the internet.
This enables the function to comment on a notification from the lock screen.
Hope this information helps others looking for the same.
For anyone using a standard NSURLSession to complete the firestore HTTP REST request (and not Alamofire like in #ChrisEenberg's excellent answer above)
[NB: A previous version of this answer used background tasks, which are not necessary. This edit uses an ordinary NSURLSessionDataTask upload task and appears to work when app is backgrounded or closed, as expected.]
OBJECTIVE-C
Inside didReceiveNotificationResponse:
Prepare request
// Grab authenticated firestore user
FIRUser *db_user = [FIRAuth auth].currentUser;
// Set up the response
NSDictionary *reqFields;
// Fields for firestore object (REST API)
NSDictionary *my_uid_val = [[NSDictionary alloc] initWithObjectsAndKeys: (db_user.uid ?: [NSNull null]), (db_user.uid ? #"stringValue" : #"nullValue"), nil];
NSDictionary *action_val = [[NSDictionary alloc] initWithObjectsAndKeys: response.actionIdentifier, (response.actionIdentifier ? #"stringValue" : #"nullValue"), nil];
// Create object
reqFields = #{
#"my_uid": my_uid_val,
#"action": action_val
};
// Place fields into expected reqBody format (i.e. under 'fields' property)
NSDictionary *reqBody = [[NSDictionary alloc] initWithObjectsAndKeys:
reqFields, #"fields",
nil];
// Confirm...?
NSLog(#"%#", reqBody);
Compose the request
// Grab current user's token (for authenticated firestore REST API call)
[db_user getIDTokenWithCompletion:^(NSString * _Nullable token, NSError * _Nullable error) {
if (!error && token) {
NSLog(#"Successfully obtained getIDTokenWithCompletion: %#", token);
// Compose stringified response
// + (per https://stackoverflow.com/a/44923210/1183749 )
NSError *error;
NSData *postData = [NSJSONSerialization dataWithJSONObject:reqBody options:kNilOptions error:&error];
if(!postData){
NSLog(#"Error creating JSON: %#", [error localizedDescription]);
}else{
NSLog(#"Successfully created JSON.");
}
// Create the request
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:[NSString stringWithFormat:#"https://firestore.googleapis.com/v1beta1/projects/%#/databases/%#/documents/%#", #"project_id", #"(default)", #"collection_id"]]];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setValue:[NSString stringWithFormat:#"Bearer %#", token] forHTTPHeaderField:#"Authorization"]; // 'token' is returned in [[FIRAuth auth].currentUser getIDTokenWithCompletion]. (All of this code resides inside the getIDTokenWithCompletion block so we can pass it along with the request and let firebase security rules take care of authenticating the request.)
[request setHTTPBody:postData];
// Set up the session configuration
NSURLSessionConfiguration *sessionConfig;
sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
// Set up the session
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
Start the upload task
// Start the upload task
NSURLSessionDataTask *uploadTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *uploadTaskResp, NSError *error) {
NSLog(#"dataTask Request reply: %#", uploadTaskResp);
if(error){
NSLog(#"dataTask Request error: %#", error);
}
// Call completion handler
completionHandler();
}
}
}
I'm currently trying to populate a tableView with information stored on my Parse server (username, description, profile picture)
Currently... When I open the tableview I do not get any information showing, but if I click back and re open the table, all of my information shows and I'm unsure why that is.
A first get all information needed from Parse and store them into arrays like so:
let query = PFQuery(className: institutionTitle)
query.findObjectsInBackground {
(objects, error) in
if error == nil {
// The find succeeded.
// Do something with the found objects
if let objects = objects {
for object in objects {
self.postGrabber.append(object)
self.pSenderUsername.append(object["sender"] as! String)
self.pPostBody.append(object["textPost"] as! String)
}
}
} else {
// Log details of the failure
print("Error: \(error!)")
}
}
I'm currently using both the viewDidLoad() and viewWillAppear() to reloadData()
I then set the delegate and datasource of the table view in the viewDidLoad() function.
I then use the cellForRowAtIndexPath method to set up each cell on the table:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UniversityFeedCellTableViewCell = tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! UniversityFeedCellTableViewCell
cell.sender.text = pSenderUsername[indexPath.section]
cell.senderPost.text = pPostBody[indexPath.section]
cell.profilePicture.layer.cornerRadius = cell.profilePicture.frame.size.width / 2
cell.profilePicture.layer.borderColor = UIColor.orange.cgColor
cell.backgroundColor = UIColor.white
return cell
}
I'm looking for some information on how I would make the user wait until all data has successfully loaded and has been populated. Would appreciate some help.
Thanks in advance and feel free to request more information from myself.
You need to reload data in table view after you have fetched all from your server... Something like this should do the trick:
let query = PFQuery(className: institutionTitle)
query.findObjectsInBackground {
(objects, error) in
if error == nil {
// The find succeeded.
// Do something with the found objects
if let objects = objects {
for object in objects {
self.postGrabber.append(object)
self.pSenderUsername.append(object["sender"] as! String)
self.pPostBody.append(object["textPost"] as! String)
}
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData() // Add this line and it should work
})
}
} else {
// Log details of the failure
print("Error: \(error!)")
}
}
If you want to load your data each time your view controller appears then do it in viewWillAppear(), otherWise, if you need to load data only first time, then in viewDidLoad()
You need to set callback method when u fetch json from server and then if result fetch you have to call reload method and mean while you can set indicator. Callback method done with fetch its will reload data
If u need some code example then i can mention the code hear
first you need to add this callback block in you .h file //Callback blocks
void (^successCallback)(id response);
void (^failCallback)(NSError *error);
+(BOOL)CallService3:(NSString*)serviceName postData:(NSString*)params qString:(NSString*)QueryStr callBackBlock:(void (^)(id response))responeBlock;
in you implementation file mean .m file create method for calling web api or web service
+(BOOL)CallService3:(NSString*)serviceName postData:(NSString*)params qString:(NSString*)QueryStr callBackBlock:(void (^)(id response))responeBlock{
NSString *jsonString =params;//if you have dictionary then [params JSONRepresentation];
NSString *post = [NSString stringWithFormat:#"json=%#", jsonString];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:NO];
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfiguration.HTTPAdditionalHeaders = #{
#"api-key" : #"API_KEY"
};
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#%#",baseURLS,serviceName]];
NSLog(#"url %#",[url absoluteString]);
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = #"GET";
NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary* jsonObject = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:&error];
responeBlock(jsonObject);
}];
[postDataTask resume];
return TRUE;
}
After creating this method you have to call this for callback function
-(void)callWebApi{
//here you can call indicator if you need this class i can share with you
// [self startActivityIndicatorInView:self.view withMessage:#"please wait"];
[Connection CallService3:WebApi_Category postData:jsonstring qString:str callBackBlock:^(id response){
// NSLog(#"param .. %#",response);
// NSLog(#"%#",[response allKeys]);
//NSLog(#"param .. %#",response);
jsonArray=[[NSDictionary alloc]initWithDictionary:response];
dispatch_async(dispatch_get_main_queue(), ^(void){
//stop indicator here
// [self stopActivityIndicatorInView:self.view];
[self call_your_method_populating_table:[jsonArray objectForKey:kModel]];
//now call reload tableview
[_tableview reloadData];
});
}];
}
Is it possible to share a video using SLRequest ?
I'm able to share Images using the same
SLRequest *postRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodPOST URL:requestURL parameters:message];
if (isImage)
{
NSData *data = UIImagePNGRepresentation(imgSelected);
[postRequest addMultipartData:data withName:#"media" type:#"image/png" filename:#"TestImage.png"];
}
postRequest.account = account;
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
{
if (!error)
{
NSLog(#"Upload Sucess !");
}
}];
I have been reading through the Twitter Video upload API documentation and its really pretty simple. You basically need to make 3 POST requests to their API. The video you are uploading is also limited to 15 MB in size.
Uploads using this endpoint require at least 3 calls, one to
initialize the request, which returns the media_id, one or more calls
to append/upload binary or base64 encoded data, and one last call to
finalize the upload and make the media_id usable with other resources.
So it works like this:
Request 1: Send a init request with the video size in bytes. This will return a Media ID number which we have to use in request 2 and 3.
Request 2: Use the returned Media ID number from request 1 to upload the video data.
Request 3: Once the video upload has finished, send a "FINALIZE" request back to the Twitter API. This lets the Twitter API know that all the chunks of the video file has finished uploading.
Note The Twitter API accepts video uploads in "chunks". So if your video file is quite big, you may want to split it up into more than one file and thus you will have to repeat "Request 2" more than once (not forgetting to increment the "segment_index" number each time).
I have had a go at coding this below. Try it and experiment around with it. I will update my answer later on to improve it too.
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
// Assign the mediatype to a string
NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
// Check the media type string so we can determine if its a video
if ([mediaType isEqualToString:#"public.movie"]) {
NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL];
NSData *webData = [NSData dataWithContentsOfURL:videoURL];
// Get the size of the file in bytes.
NSString *yourPath = [NSString stringWithFormat:#"%", videoURL];
NSFileManager *man = [NSFileManager defaultManager];
NSDictionary *attrs = [man attributesOfItemAtPath:yourPath error: NULL];
UInt32 result = [attrs fileSize];
//[self tweetVideoStage1:webData :result];
[self tweetVideo:webData :result :1 :#"n/a"];
}
}
-(void)tweetVideo:(NSData *)videoData :(int)videoSize :(int)mode :(NSString *)mediaID {
NSURL *twitterVideo = [NSURL URLWithString:#"https://upload.twitter.com/1.1/media/upload.json"];
// Set the parameters for the first twitter video request.
NSDictionary *postDict;
if (mode == 1) {
postDict = #{#"command": #"INIT",
#"total_bytes" : videoSize,
#"media_type" : #"video/mp4"};
}
else if (mode == 2) {
postDict = #{#"command": #"APPEND",
#"media_id" : mediaID,
#"segment_index" : #"0",
#"media" : videoData };
}
else if (mode == 3) {
postDict = #{#"command": #"FINALIZE",
#"media_id" : mediaID };
}
SLRequest *postRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodPOST URL:requestURL:twitterVideo parameters:postDict];
// Set the account and begin the request.
postRequest.account = account;
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if (!error) {
if (mode == 1) {
// Parse the returned data for the JSON string
// which contains the media upload ID.
NSMutableDictionary *returnedData = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:&error]
NSString *tweetID = [NSString stringWithFormat:#"%#", [returnedData valueForKey:#"media_id_string"]];
[self tweetVideo:videoData :result :2 :tweetID];
}
else if (mode == 2) {
[self tweetVideo:videoData :result :3 :mediaID];
}
}
else {
NSLog(#"Error stage %d - %", mode, error);
}
}];
}
Update - Twitter API errors - https://dev.twitter.com/overview/api/response-codes
In answer to your first comment, error 503 means that the Twitter servers are overloaded and can't handle your request right now.
503 Service Unavailable The Twitter servers are up, but overloaded
with requests. Try again later.
I know how to upload video to twitter use the new API. And I have tried it, it works.
Please check this: https://github.com/liu044100/SocialVideoHelper
You just need to call this class method.
+(void)uploadTwitterVideo:(NSData*)videoData account:(ACAccount*)account withCompletion:(dispatch_block_t)completion;
Hope it can resolve your problem.
Best Regards.
Been looking for sharing video on Twitter solution with below features:
Support chunk upload
Built-in support for user's credential retrieval
Since I couldn't find one meeting my need, so I decided to write one.
https://github.com/mtrung/TwitterVideoUpload
I've been testing for awhile now and it works well for me.
Hope it helps,
Regards.
Try this based in #Dan answer. It not tested, but I think it can work.
Use Cocoa-pods: pod 'TwitterKit'
if you don't use Pods try with fabric
//for Extern call
//Mode is 1
//MediaId is 0
- (void)uploadTwitterVideo:(NSData*)videoData videoTitle:(NSString *)title desc:(NSString *)desc withMode:(int)mode mediaID:(NSString *)mediaID withCompletion:(dispatch_block_t)completion
{
NSString *twitterPostURL = #"https://upload.twitter.com/1.1/media/upload.json";
NSDictionary *postParams;
if (mode == 1) {
postParams = #{#"command": #"INIT",
#"total_bytes" : [NSNumber numberWithInteger: videoData.length].stringValue,
#"media_type" : #"video/mp4"};
} else if (mode == 2) {
postParams = #{#"command": #"APPEND",
#"media_id" : mediaID,
#"segment_index" : #"0"};
} else if (mode == 3) {
postParams = #{#"command": #"FINALIZE",
#"media_id" : mediaID };
} else if (mode == 4) {
postParams = #{#"status": desc,
#"media_ids" : #[mediaID]};
}
TWTRAPIClient *twitterInstance = [[Twitter sharedInstance] APIClient];
NSError *error;
NSURLRequest *requestTw = [twitterInstance URLRequestWithMethod:#"POST" URL:twitterPostURL parameters:postParams error:&error];
[twitterInstance sendTwitterRequest:requestTw completion:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
NSLog(#"HTTP Response: %li, responseData: %#", (long)response, [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
if (error) {
NSLog(#"There was an error:%#", [error localizedDescription]);
} else {
if (mode == 1) {
NSMutableDictionary *returnedData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&connectionError];
NSString *mediaIDResponse = [NSString stringWithFormat:#"%#", [returnedData valueForKey:#"media_id_string"]];
NSLog(#"stage one success, mediaID -> %#", mediaID);
[self uploadTwitterVideo:videoData videoTitle:title desc:desc withMode:2 mediaID:mediaIDResponse withCompletion:completion];
} else if (mode == 2) {
[self uploadTwitterVideo:videoData videoTitle:title desc:desc withMode:3 mediaID:mediaID withCompletion:completion];
} else if (mode == 3) {
[self uploadTwitterVideo:videoData videoTitle:title desc:desc withMode:4 mediaID:mediaID withCompletion:completion];
} else if (mode == 4) {
DispatchMainThread(^(){completion();});
}
}
}];
}
This API Works as follows.
- Login when application (twitter) is installed and when is not installed
- First priority take credential from setting
Check this case
Swift
Its very Simple.
First you need to sign in to your Twitter Account. Go to Phone Setting and click on twitter app and sign in.
Now Just Call this videoUpload func anywhere
Video or Chunked uploads Method Reference
Replace your video type/extension on that code
And Carefully read all twitter requirements.
var twitterAccount = ACAccount()
func videoUpload{
let path = Bundle.main.path(forResource: "file-Name", ofType:"mp4")
let filePath = path
var fileSize = UInt64()
do {
//return [FileAttributeKey : Any]
let attr = try FileManager.default.attributesOfItem(atPath: filePath!)
fileSize = attr[FileAttributeKey.size] as! UInt64
//if you convert to NSDictionary, you can get file size old way as well.
let dict = attr as NSDictionary
fileSize = dict.fileSize()
} catch {
print("Error: \(error)")
}
let accountStore = ACAccountStore()
let twitterAccountType = accountStore.accountType(withAccountTypeIdentifier: ACAccountTypeIdentifierTwitter)
accountStore.requestAccessToAccounts(with: twitterAccountType, options: nil) { (granted, error) in
if granted {
let accounts = accountStore.accounts(with: twitterAccountType)
if (accounts?.count)! > 0 {
self.twitterAccount = accounts?.last as! ACAccount
}}}
twitterAccount = Twitter.sharedInstance().sessionStore.session() as! ACAccount
uploadVideoToTwitter(videoURL: URL(string : path!)! as NSURL, fileSize: UInt32(fileSize))
}
func uploadVideoToTwitter(videoURL:NSURL,fileSize: UInt32) {
if let videoData = NSData(contentsOfFile: videoURL.path!){
self.tweetVideoInit(videoData: videoData, videoSize: Int(fileSize))
}
}
func tweetVideoInit(videoData:NSData,videoSize:Int) {
let uploadURL = NSURL(string:"https://upload.twitter.com/1.1/media/upload.json")
var params = [String:String]()
params["command"] = "INIT"
params["total_bytes"] = String(videoData.length)
params["media_type"] = "video/mp4"
let postRequest = SLRequest(forServiceType: SLServiceTypeTwitter,
requestMethod: SLRequestMethod.POST,
url: uploadURL as URL!,
parameters: params)
postRequest?.account = self.twitterAccount;
postRequest?.perform(handler: { ( responseData, urlREsponse,error) in
if let err = error {
print(error as Any)
}else{
do {
let object = try JSONSerialization.jsonObject(with: responseData! as Data, options: .allowFragments)
if let dictionary = object as? [String: AnyObject] {
if let tweetID = dictionary["media_id_string"] as? String{
self.tweetVideoApped(videoData: videoData, videoSize: videoSize, mediaId: tweetID, chunk: 0)
}
}
}
catch {
print(error)
}
}
})
}
func tweetVideoApped(videoData:NSData,videoSize:Int ,mediaId:String,chunk:NSInteger) {
let uploadURL = NSURL(string:"https://upload.twitter.com/1.1/media/upload.json")
var params = [String:String]()
params["command"] = "APPEND"
params["media_id"] = mediaId
params["segment_index"] = String(chunk)
let postRequest = SLRequest(forServiceType: SLServiceTypeTwitter,
requestMethod: SLRequestMethod.POST,
url: uploadURL as URL!,
parameters: params)
postRequest?.account = self.twitterAccount
postRequest?.addMultipartData(videoData as Data!, withName: "media", type: "video/mov", filename:"mediaFile")
postRequest?.perform(handler: { ( responseData, urlREsponse,error) in
if let err = error {
print(err)
}else{
self.tweetVideoFinalize(mediaId: mediaId)
}
})
}
func tweetVideoFinalize(mediaId:String) {
let uploadURL = NSURL(string:"https://upload.twitter.com/1.1/media/upload.json")
var params = [String:String]()
params["command"] = "FINALIZE"
params["media_id"] = mediaId
let postRequest = SLRequest(forServiceType: SLServiceTypeTwitter,
requestMethod: SLRequestMethod.POST,
url: uploadURL as URL!,
parameters: params)
postRequest?.account = self.twitterAccount;
postRequest?.perform(handler: { ( responseData, urlREsponse,error) in
if let err = error {
print(err)
}else{
do {
let object = try JSONSerialization.jsonObject(with: responseData! as Data, options: .allowFragments)
if let dictionary = object as? [String: AnyObject] {
self.postStatus(mediaId: mediaId)
}
}
catch {
print(error)
}
}
})
}
func postStatus(mediaId:String) {
let uploadURL = NSURL(string:"https://api.twitter.com/1.1/statuses/update.json")
var params = [String:String]()
params["status"] = "my first Video Upload"
params["media_ids"] = mediaId
let postRequest = SLRequest(forServiceType: SLServiceTypeTwitter,
requestMethod: SLRequestMethod.POST,
url: uploadURL as URL!,
parameters: params)
postRequest?.account = self.twitterAccount;
postRequest?.perform(handler: { ( responseData, urlREsponse,error) in
if let err = error {
print(err)
}else{
do {
let object = try JSONSerialization.jsonObject(with: responseData! as Data, options: .allowFragments)
if let dictionary = object as? [String: AnyObject] {
print("video uploaded")
}
}
catch {
print(error)
}
}
})
}
I was able to upload video to twitter successfully!
Below are steps referred from twitter docs:
Request for twitter account
accountStore.requestAccessToAccounts(with: twitterAccountType,options:nil){(granted, error) in
POST media/upload (INIT)
params["command"] = "INIT"
params["total_bytes"] = String(videoData.length)
params["media_type"] = "video/mov"
POST media/upload (APPEND)
params["command"] = "APPEND"
params["media_id"] = mediaId
params["segment_index"] = String(chunk)
POST media/upload (FINALIZE)
params["command"] = "FINALIZE"
params["media_id"] = mediaId
POST media/upload
params["status"] = twitterDescription
params["media_ids"] = mediaId
Here is twitter doc link https://dev.twitter.com/rest/media/uploading-media.html
Please fine the detailed solution for video upload to twitter using SLRequest here.
http://swiftoverflow.blogspot.in/2017/04/upload-video-to-twitter-using-slrequest.html
I already post question How to use Bolts Framework[Facebook+Parse] but Now I've question, Must I use parse webservice if I want to use Bolts-framework?
They provide sample code like below which related(saveAsync:) to Parse webservice. But I've seen in this line "Using these libraries does not require using any Parse services. Nor do they require having a Parse or Facebook developer account" in Boltss' github
[[object saveAsync:obj] continueWithBlock:^id(BFTask *task) {
if (task.isCancelled) {
// the save was cancelled.
} else if (task.error) {
// the save failed.
} else {
// the object was saved successfully.
SaveResult *saveResult = task.result;
}
return nil;
}];
Now I get confusion, Is bolts framework need to use parse webservice?
Note: Don't ask where do you want to use Bolts-framework. see my first line of this question.
Surely it doesn't need Parse webservice. I've the same difficulty in implementing my own task and I'm studying this framework. Take a look at BoltsTest code: you can find some useful code.
I'm trying some experiments in a sample project (https://github.com/giaesp/BoltsFrameworkSample). Basically you need to define your own method returning a BFTask. Here a simple excerpt.
- (BFTask*) parseHTML:(NSURL*)url searchString:(NSString*)searchString {
BFTaskCompletionSource * tcs = [BFTaskCompletionSource taskCompletionSource];
NSURLRequest * request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:30];
NSURLResponse * response;
NSError * error;
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if (!error) {
NSString * receivedData = [NSString stringWithUTF8String:[returnData bytes]];
NSUInteger occurrences = [self countOccurencesOfString:#"iOS" inputString:receivedData];
[tcs setResult:[NSNumber numberWithInt:occurrences]];
}
else {
[tcs setError:error];
}
return tcs.task;
}
Then you can use your method as the docs explains and check the task status.
[[self parseHTML:[NSURL URLWithString:#"http://www.stackoverflow.com"]] continueWithBlock:^id(BFTask *task) {
if (task.isCancelled) {
// the task was cancelled
} else if (task.error) {
// the task failed
} else {
// the task completes
}
return nil;
}];
I know it's been a while since this question was asked but as mani wanted to know if you could use Bolts framework with AFNetworking as well i want to add a quick example that shows usage.
It's written in swift and really just plain and simple.
func taskWithPath(path: String) -> BFTask {
let task = BFTaskCompletionSource()
AFHTTPRequestOperationManager().GET(path, parameters: nil, success: { (operation, response) in
task.setResult(response)
}) { (operation, error) -> Void in
task.setError(error)
}
return task.task
}
Hope this helps :)
The idea with Bolts is to encapsulate any operation using a BFTask. You don't necessarily have to wrap the operation in a method, but it's a good way to imagine how you should structure your code:
- (BFTask*) asynchronousImageProcessOperation;
- (BFTask*) asynchronousNetworkOperation;
...and all of these would follow a similar pattern:
- (BFTask*) asynchronousNetworkOperation {
BFTaskCompletionSource *source = [BFTaskCompletionSource taskCompletionSource];
// ... here's the code that does some asynchronous operation on another thread/queue
[someAsyncTask completeWithBlock:^(id response, NSError *error) {
error ? [source setError:error] : [source setResult:response];
}
return task;
}
The beauty of it is that you can them string these tasks together in some way. For example, if you needed to process an image and then upload it, you could do:
[[object methodReturnImageProcessingTask] continueWithBlock:^(BFTask *task) {
[[anotherObject imageUploadTaskForImage:task.result] continueWithBlock:^(BFTask *task) {
self.label.text = #"Processing and image complete";
}]
}]
Of course you could also encapsulate that two-stage task in its own task:
- (BFTask*) processAndUploadImage:(UIImage* image);
Typing from memory here. It's the sequencing and grouping that's really powerful. Great framework.