iOS Swift: How to post a video to twitter with TwitterKit?

前端 未结 2 1529
不思量自难忘°
不思量自难忘° 2020-12-10 22:24

I haven\'t found relevant and up to date answer on SO.

Here is the code I\'m currently using, all requests are handled correctly but no video is posted ?

<         


        
2条回答
  •  野趣味
    野趣味 (楼主)
    2020-12-10 23:19

    If you want to convert @Trung's code to Swift 4, and if you want to USE TwitterKit, you can use this enum to handle all upload stages:

    enum UploadStage {
        case initial(size: String, videoDuration: Int?) // if your video duration is <= 30s, you can pass nil here
        case append(mediaId: String, videoData: Data, segment: Int)
        case finalize(mediaId: String)
        case status(status: String, mediaId: String)
    
        static let videoChunkMaxSize = 5 * 1000 * 1000
    
        var parameters: [String: Any] {
            get {
                switch self {
    
                case .initial(let size, let videoDuration):
                    var params = ["command":stageName, "total_bytes": size, "media_type": "video/mp4"]
                    if let videoDuration = videoDuration, videoDuration > 30 {
                        params["media_category"] = "tweet_video"
                    }
                    return params
                case .append(let mediaId, _ , let segment):
                    let videoChunkString = self.videoChunk!.base64EncodedString(options: [])
                    return ["command":stageName, "media_id": mediaId, "segment_index": "\(segment)", "media": videoChunkString]
                case .finalize(let mediaId):
                    return ["command":stageName, "media_id": mediaId]
                case .status(let status, let mediaId):
                    return ["status": status, "wrap_links": "true", "media_ids": mediaId]
                }
            }
        }
    
        var stageName: String {
            get {
                switch self {
                case .initial:
                    return "INIT"
                case .append:
                    return "APPEND"
                case .finalize:
                    return "FINALIZE"
                case .status:
                    return "STATUS"
    
                }
            }
        }
    
        var videoChunk: Data? {
            switch self {
            case .append(_ , let videoData, let segment):
                if videoData.count > UploadStage.videoChunkMaxSize {
                    let maxPos = segment * UploadStage.videoChunkMaxSize + UploadStage.videoChunkMaxSize
                    let range: Range = segment * UploadStage.videoChunkMaxSize..<(maxPos >= videoData.count ? videoData.count : maxPos)
                    return videoData.subdata(in: range)
    
                }
                return videoData
            default:
                return nil
            }
        }
    
        var urlString: String {
            switch self {
            case .initial, .append, .finalize:
                return "https://upload.twitter.com/1.1/media/upload.json"
            case .status:
                return "https://api.twitter.com/1.1/statuses/update.json"
            }
        }
    }
    

    UploadStage enum can be used in the method with recursive calls by passing next stage enum value like this:

    func uploadTwitterVideo(videoData: Data, status: String, stage: UploadStage, success: @escaping () -> Void, failure: @escaping (Error?) -> Void) {
    
        let client = TWTRAPIClient.withCurrentUser()
    
        var clientError: NSError?
        let urlRequest = client.urlRequest(withMethod: "POST", urlString: stage.urlString, parameters: stage.parameters, error: &clientError)
        if clientError == nil {
            client.sendTwitterRequest(urlRequest) { (urlResponse, responseData, connectionError) in
    
                guard connectionError == nil else {
                    print("There was an error: \(connectionError!.localizedDescription)")
                    failure(connectionError)
                    return
                }
    
                self.handleError(urlResponse, failure: failure)
                if let data = responseData, let dataString = String(data: data, encoding: .utf8), let urlResponse = urlResponse {
                    print("Twitter stage \(stage.stageName) URL response : \(urlResponse), response data: \(dataString)")
    
    
                    var nextStage: UploadStage?
                    do {
                        switch stage {
                        case .initial:
                            let returnedJSON = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! [String:Any]
                            if let mediaId = returnedJSON["media_id_string"] as? String {
                                print("stage one success, mediaID -> \(mediaId)")
                                nextStage = .append(mediaId: mediaId, videoData:videoData, segment: 0)
                            }
                        case .append(let mediaId, let videoData, let segment):
                            if ((segment + 1) * UploadStage.videoChunkMaxSize < videoData.count) {
                                nextStage = .append(mediaId: mediaId, videoData: videoData, segment: segment + 1)
                            } else {
                                nextStage = .finalize(mediaId: mediaId)
                            }
                        case .finalize(let mediaId):
                            nextStage = .status(status: status, mediaId: mediaId)
                        case .status:
                            success()
                        }
    
                        if let nextStage = nextStage {
                            self.uploadTwitterVideo(videoData: data, status: status, stage: nextStage, success: success, failure: failure)
                        }
                    } catch let error as NSError {
                        failure(error)
                    }
                }
            }
        }
    }
    

提交回复
热议问题