Newbie question: getting statuses works, but trying to stream fails with a security error


#1

I’m brand new to Twitter’s APIs, so this is probably going to be a trivially easy thing to answer, but I’m confused. :slight_smile: I’m using the Swifter library
to access Twitter in a Mac app, and am currently using all tokens, rather than username/password or the Twitter account in my Mac’s keychain. It’s early days, and I just want the basics working before I go for the advanced stuff.

I’ve successfully posted tweets and pulled them using “statuses/home_timeline.json”. That gives me no problems at all.

The next logical step seemed to be enabling streaming instead of manually pulling tweets every so often, so I tried that. I got an error, though, saying that a secure connection couldn’t be made. Why this would happen when non-streaming requests work perfectly is my first question. My second, if it’s not too off topic here, is how I might go about fixing this? I imagine the problem is somewhere in Swifter, but all the URLs used in that library are HTTPS and, from what I can tell (again though, NOOB) things seem to be in order. Does anyone have any ideas about why streaming would error out when non-streaming requests work, and how I might fix this? Thank you in advance!


#2

Are you able to share any code (not including app tokens) and the specific error message? I’m not super familiar with Swifter, but maybe that will provide some clues.


#3

Thanks for the quick reply. I won’t post my app’s code, because all I do is call Swifter’s methods anyway. I’ll instead post the relevant functions Swifter uses. It relies on a lot of internal functions, and I’m not sure where the cause of the problem might be, but at least this will be a start.

Getting statuses of Home timeline, without streaming (this works perfectly):

    public func getStatusesHomeTimelineWithCount(count: Int? = nil, sinceID: String? = nil, maxID: String? = nil, trimUser: Bool? = nil, contributorDetails: Bool? = nil, includeEntities: Bool? = nil, success: ((statuses: [JSONValue]?) -> Void)? = nil, failure: FailureHandler? = nil) {
        self.getTimelineAtPath("statuses/home_timeline.json", parameters: [:], count: count, sinceID: sinceID, maxID: maxID, trimUser: trimUser, contributorDetails: contributorDetails, includeEntities: includeEntities, success: success, failure: failure)
    }

getTimelineAtPath method (failureHandler here is a predefined closure that takes an NSError as an argument):

    private func getTimelineAtPath(path: String, parameters: Dictionary<String, Any>, count: Int? = nil, sinceID: String? = nil, maxID: String? = nil, trimUser: Bool? = nil, contributorDetails: Bool? = nil, includeEntities: Bool? = nil, success: ((statuses: [JSONValue]?) -> Void)? = nil, failure: FailureHandler? = nil) {
        var params = parameters

        if count != nil {
            params["count"] = count!
        }
        if sinceID != nil {
            params["since_id"] = sinceID!
        }
        if maxID != nil {
            params["max_id"] = maxID!
        }
        if trimUser != nil {
            params["trim_user"] = Int(trimUser!)
        }
        if contributorDetails != nil {
            params["contributor_details"] = Int(!contributorDetails!)
        }
        if includeEntities != nil {
            params["include_entities"] = Int(includeEntities!)
        }

        self.getJSONWithPath(path, baseURL: self.apiURL, parameters: params, uploadProgress: nil, downloadProgress: nil, success: {
            json, response in

            success?(statuses: json.array)
            return

            }, failure: failure)
    }

getJSONWithPath method:

    internal func getJSONWithPath(path: String, baseURL: NSURL, parameters: Dictionary<String, Any>, uploadProgress: SwifterHTTPRequest.UploadProgressHandler?, downloadProgress: JSONSuccessHandler?, success: JSONSuccessHandler?, failure: SwifterHTTPRequest.FailureHandler?) -> SwifterHTTPRequest {
        return self.jsonRequestWithPath(path, baseURL: baseURL, method: "GET", parameters: parameters, uploadProgress: uploadProgress, downloadProgress: downloadProgress, success: success, failure: failure)
    }

Hopefully that’s deep enough into the path of functions, but let me know if not. Now, the streaming, which fails. First, here’s how Swifter defines the URL it bases streams off of:
self.userStreamURL = NSURL(string: "https://userstream.twitter.com/1.1/")!

The method called when I ask to start streaming is this one:

    public func getUserStreamDelimited(delimited: Bool? = nil, stallWarnings: Bool? = nil, includeMessagesFromFollowedAccounts: Bool? = nil, includeReplies: Bool? = nil, track: [String]? = nil, locations: [String]? = nil, stringifyFriendIDs: Bool? = nil, progress: ((status: Dictionary<String, JSONValue>?) -> Void)? = nil, stallWarningHandler: ((code: String?, message: String?, percentFull: Int?) -> Void)? = nil, failure: FailureHandler? = nil) -> SwifterHTTPRequest {
        let path = "user.json"

        var parameters = Dictionary<String, Any>()
        if delimited != nil {
            parameters["delimited"] = delimited!
        }
        if stallWarnings != nil {
            parameters["stall_warnings"] = stallWarnings!
        }
        if includeMessagesFromFollowedAccounts != nil {
            if includeMessagesFromFollowedAccounts! {
                parameters["with"] = "user"
            }
        }
        if includeReplies != nil {
            if includeReplies! {
                parameters["replies"] = "all"
            }
        }
        if track != nil {
            parameters["track"] = (track!).joinWithSeparator(",")
        }
        if locations != nil {
            parameters["locations"] = (locations!).joinWithSeparator(",")
        }
        if stringifyFriendIDs != nil {
            parameters["stringify_friend_ids"] = stringifyFriendIDs!
        }

        return self.getJSONWithPath(path, baseURL: self.userStreamURL, parameters: parameters, uploadProgress: nil, downloadProgress: {
            json, response in

            if let stallWarning = json["warning"].object {
                stallWarningHandler?(code: stallWarning["code"]?.string, message: stallWarning["message"]?.string, percentFull: stallWarning["percent_full"]?.integer)
            }
            else {
                progress?(status: json.object)
            }

            }, success: {
                json, response in

                progress?(status: json.object)
                return

            }, failure: failure)
    }

The getJSONWithPath is one I pasted above, so it looks like the request made is somehow causing the problem. What that problem is or how to diagnose it, I’m not as certain of. Please let me know what else I can provide that might help you, or anyone, see what’s going on. Thanks!