how to parse copy link of twitter - swift

I am using this code -:
private static func getJson(_ link: String, completion: #escaping (Json?) -> ()) {
let url = URL(string: "https://twitter.com/BCCI/status/1476041561288822788?s=20")!
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
return completion(nil)
}
if let json = (try? JSONSerialization.jsonObject(with: data)) as? Json {
completion(json)
} else {
completion(nil)
}
}.resume()
}
I want to get json data and download the link

If we take a look at the Twitter docs here you'll see that it requires authentication hence the error when making the call.
Auth: Twitter Oauth 1.0, app-only or app-user
This Twitter getting started link may be useful to get setup to do so, it's fairly self explanatory and goes step by step.

Related

Url works in Postman but not in Swift app

I have a url that works in Postman and in browser, but not in app.
I have an if let url = URL(string: urlString) line, but apparently the URL(string: urlString) is returning nil so it doesn't enter the block. It doesn't actually throw an error so I can't actually search for an error.
I've tried looking at other people's similar problems but haven't found a solution. Any help would be appreciated. If you could point me to another post with a potential solution I'd appreciate that too. Thank you.
Here is my code. I've used this many times before with no problems.
func performRequest<T: Codable>(urlString: String, returnType: T.Type, completion: #escaping (Result<T, Error>) -> Void ) {
print("\n\(#function)")
if let url = URL(string: urlString) { // <--- FAILS RIGHT HERE
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { (data, _, error) in
if let error = error {
completion(.failure(error))
return
}
guard let data = data else { return }
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(T.self, from: data)
completion(.success(decodedData))
} catch let decodingErr {
completion(.failure(decodingErr))
}
}
task.resume()
} else {
print("Something went wrong with the url")
}
}
There was a space in my url that Swift wasn't accepting. Apparently Postman and browsers can still make the call with a space, but not Swift. Thank you to #workingdog for solving this.

Making HTTP GET request with Swift 5

I am obviously missing something very fundamental/naïve/etc., but for the life of me I cannot figure out how to make simple GET requests.
I'm trying to make an HTTP GET request with Swift 5. I've looked at these posts/articles: one, two, but I can't get print() statements to show anything. When I use breakpoints to debug, the entire section within the URLSession.shared.dataTask section is skipped.
I am looking at the following code (from the first link, above):
func HTTP_Request() {
let url = URL(string: "http://www.stackoverflow.com")!
let task = URLSession.shared.dataTask(with: url) {(data: Data?, response: URLResponse?, error: Error?) in
guard let data = data else { return }
print(String(data: data, encoding: .utf8)!)
}
task.resume()
}
HTTP_Request()
I am running this in a MacOS Command Line Project created through XCode.
I would greatly appreciate any help I can get on this, thank you.
Right now, if there is an error, you are going to silently fail. So add some error logging, e.g.,
func httpRequest() {
let url = URL(string: "https://www.stackoverflow.com")! // note, https, not http
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard
error == nil,
let data = data,
let string = String(data: data, encoding: .utf8)
else {
print(error ?? "Unknown error")
return
}
print(string)
}
task.resume()
}
That should at least give you some indication of the problem.
A few other considerations:
If command line app, you have to recognize that the app may quit before this asynchronous network request finishes. One would generally start up a RunLoop, looping with run(mode:before:) until the network request finishes, as advised in the run documentation.
For example, you might give that routine a completion handler that will be called on the main thread when it is done. Then you can use that:
func httpRequest(completion: #escaping () -> Void) {
let url = URL(string: "https://www.stackoverflow.com")! // note, https, not http
let task = URLSession.shared.dataTask(with: url) { data, response, error in
defer {
DispatchQueue.main.async {
completion()
}
}
guard
error == nil,
let data = data,
let string = String(data: data, encoding: .utf8)
else {
print(error ?? "Unknown error")
return
}
print(string)
}
task.resume()
}
var finished = false
httpRequest {
finished = true
}
while !finished {
RunLoop.current.run(mode: .default, before: .distantFuture)
}
In standard macOS apps, you have to enable outgoing (client) connections in the “App Sandbox” capabilities.
If playground, you have to set needsIndefiniteExecution.
By default, macOS and iOS apps disable http requests unless you enable "Allow Arbitrary Loads” in your Info.plist. That is not applicable to command line apps, but you should be aware of that should you try to do this in standard macOS/iOS apps.
In this case, you should just use https and avoid that consideration altogether.
Make sure the response get print before exiting the process, you could try to append
RunLoop.main.run()
or
sleep(UINT32_MAX)
in the end to make sure the main thread won't exit. If you want to print the response and exit the process immediately, suggest using DispatchSemaphore:
let semphare = DispatchSemaphore(value: 0)
func HTTP_Request() {
let url = URL(string: "http://www.stackoverflow.com")!
let task = URLSession.shared.dataTask(with: url) {(data: Data?, response: URLResponse?, error: Error?) in
guard let data = data else { return }
print(String(data: data, encoding: .utf8)!)
semphare.signal()
}
task.resume()
}
HTTP_Request()
_ = semphare.wait(timeout: .distantFuture)
This works for me many times I suggest you snippet for future uses!
let url = URL(string: "https://google.com")
let task = URLSession.shared.dataTask(with: ((url ?? URL(string: "https://google.com"))!)) { [self] (data, response, error) in
do {
let jsonResponse = try JSONSerialization.jsonObject(with: data!, options: [])
print(jsonResponse)
guard let newValue = jsonResponse as? [String:Any] else {
print("invalid format")
}
}
catch let error {
print("Error: \(error)")
}
task.resume()
}

Swift JSON Decoder is NIL but works in web browser - Google Places API

Trying to get the results of a Google Places API call. This code worked until a few weeks ago. I get the results which conform to a Places Struct.
The print url line gives me a url that gives me json data, but when I try and print the response after the JSON decoding I get nil.
let session = URLSession(configuration: .default)
func getGooglePlacesData(forKeyword keyword: String, location: CLLocation, withinMeters radius: Int, using completionHandler: #escaping (GooglePlacesResponse) -> ()) {
let url = googlePlacesDataURL(forKey: googlePlacesKey, location: location, keyword: keyword)
let task = session.dataTask(with: url) { (responseData, _, error) in
if let error = error {
print(error.localizedDescription)
return
}
let decoder = JSONDecoder()
print(url)
guard let data = responseData, let response = try? decoder.decode(GooglePlacesResponse.self, from: data) else {
completionHandler(GooglePlacesResponse(results:[]))
return
}
completionHandler(response)
}
task.resume()
}
Did something change in how we are supposed to decode JSON data?

Making multiple asynchronous HTTP requests in succession and writing with Realm

I'm currently using Alamofire for requesting data and writing to disk with Realm. Specifically, I am fetching 24 source URLS from a Facebook Graph GET request and then making 24 separate requests to retrieve the data for each image. Once the data is retrieved, I am writing to disk with Realm.
here is how I am fetching the 24 sources:
FBAPI
Alamofire.request(.GET, FBPath.photos, parameters: params).responseJSON { response in
guard response.result.error == nil else {
print("error calling GET on \(FBPath.photos)")
print(response.result.error!)
completion(latestDate: nil, photosCount: 0, error: response.result.error)
return
}
if let value = response.result.value {
let json = JSON(value)
if let photos = json[FBResult.data].array {
for result in photos {
let manager = PTWPhotoManager()
manager.downloadAndSaveJsonData(result)
}
As you can see, I have a for loop iterating through each JSON containing the source url for the photo's image in which I then make another network request for each url, like so:
Manager
func downloadAndSaveJsonData(photoJSON : JSON) {
let source = photoJSON[FBResult.source].string
let id = photoJSON[FBResult.id].string
let created_time = photoJSON[FBResult.date.createdTime].string
let imageURL = NSURL(string: source!)
print("image requested")
Alamofire.request(.GET, imageURL!).response() {
(request, response, data, error) in
if (error != nil) {
print(error?.localizedDescription)
}
else {
print("image response")
let photo = PTWPhoto()
photo.id = id
photo.sourceURL = source
photo.imageData = data
photo.createdTime = photo.createdTimeAsDate(created_time!)
let realm = try! Realm()
try! realm.write {
realm.add(photo)
}
print("photo saved")
}
}
}
There seems to be a very long delay between when each image's data is requested and when I receive a response, and it also does not appear to be asynchronous. Is this a threading issue or is there a more efficient way to request an array of data like this? It should also be noted that I am making this network request from the Apple Watch itself.
These requests will happen mostly asynchronous as you wish. But there is some synchronization happening, you might been not aware of:
The response closures for Alamofire are dispatched to the main thread. So your network responses competes against any UI updates you do.
Realm write transactions are synchronous and exclusive, which is enforced via locks which will block the thread where they are executed on.
In combination this both means that you will block the main thread as long as the network requests succeed and keep coming, which would also render your app unresponsive.
I'd recommend a different attempt. You can use GCD's dispatch groups to synchronize different asynchronous tasks.
In the example below, the objects are all kept in memory until they are all downloaded.
A further improvement could it be to write the downloaded data onto disk instead and store just the path to the file in the Realm object. (There are plenty of image caching libraries, which can easily assist you with that.)
If you choose a path, which depends only on the fields of PWTPhoto (or properties of the data, you can get through a quick HEAD request), then you can check first whether this path exists already locally before downloading the file again. By doing that you save traffic when updating the photos or when not all photos could been successfully downloaded on the first attempt. (e.g. app is force-closed by the user, crashed, device is shutdown)
class PTWPhotoManager {
static func downloadAllPhotos(params: [String : AnyObject], completion: (latestDate: NSDate?, photosCount: NSUInteger, error: NSError?)) {
Alamofire.request(.GET, FBPath.photos, parameters: params).responseJSON { response in
guard response.result.error == nil else {
print("error calling GET on \(FBPath.photos)")
print(response.result.error!)
completion(latestDate: nil, photosCount: 0, error: response.result.error)
return
}
if let value = response.result.value {
let json = JSON(value)
if let photos = json[FBResult.data].array {
let group = dispatch_group_create()
var persistablePhotos = [PTWPhoto](capacity: photos.count)
let manager = PTWPhotoManager()
for result in photos {
dispatch_group_enter(group)
let request = manager.downloadAndSaveJsonData(result) { photo, error in
if let photo = photo {
persistablePhotos.add(photo)
dispatch_group_leave(group)
} else {
completion(latestDate: nil, photosCount: 0, error: error!)
}
}
}
dispatch_group_notify(group, dispatch_get_main_queue()) {
let realm = try! Realm()
try! realm.write {
realm.add(persistablePhotos)
}
let latestDate = …
completion(latestDate: latestDate, photosCount: persistablePhotos.count, error: nil)
}
}
}
}
}
func downloadAndSaveJsonData(photoJSON: JSON, completion: (PTWPhoto?, NSError?) -> ()) -> Alamofire.Request {
let source = photoJSON[FBResult.source].string
let id = photoJSON[FBResult.id].string
let created_time = photoJSON[FBResult.date.createdTime].string
let imageURL = NSURL(string: source!)
print("image requested")
Alamofire.request(.GET, imageURL!).response() { (request, response, data, error) in
if let error = error {
print(error.localizedDescription)
completion(nil, error)
} else {
print("image response")
let photo = PTWPhoto()
photo.id = id
photo.sourceURL = source
photo.imageData = data
photo.createdTime = photo.createdTimeAsDate(created_time!)
completion(photo, nil)
}
}
}
}

Making an API call in Swift 2.0

I've been scouring examples looking to pull some ideas together, I've come up with this although I'm not getting any output. It never enters the do which leads me to believe I have an issue with my call.
Can anyone shed some light on this for me or lead me to an appropriate location with more information on API calls in swift 2.0? Examples of this are quite sparse.
let url : String = "http://www.fantasyfootballnerd.com/service/nfl-teams/json/test/"
let request : NSMutableURLRequest = NSMutableURLRequest()
request.URL = NSURL(string: url)
request.HTTPMethod = "GET"
print("Start")
let session = NSURLSession.sharedSession()
session.dataTaskWithRequest(request) { (data, response, error) -> Void in
do {
let jsonResult: NSDictionary! = try NSJSONSerialization.JSONObjectWithData(data!, options:NSJSONReadingOptions.MutableContainers) as? NSDictionary
print("In method")
if (jsonResult != nil) {
// process jsonResult
print("Data added")
} else {
print("No Data")
// couldn't load JSON, look at error
}
}
catch {
print("Error Occured")
}
}
You're missing just one thing. You need to start the request:
// call this after you configure your session
session.dataTaskWithRequest(request) { (data, response, error) -> Void in
// process results
}.resume()