I am try to get my UILabel text to equal the url text I am not getting any errors but it just is not doing anything not sure what I am doing wrong?
let tasks = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print("error")
} else {
if let content = data {
do {
let Json = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
if let data = Json as? NSString {
DispatchQueue.main.async {
self.myLabel.text = "\(data)"
}
}
} catch {
}
}
}
}
tasks.resume()
Your API Response is not String , It's Dictionary Dictionary in json is {}
API Response:
{
quote: "Travel is the most private of pleasures. There is no greater bore than the travel bore. We do not in the least want to hear what he has seen in Hong Kong.",
author: "Vita Sackville-West",
cat: "travel"
}
so you will not pass this condition at this line if let data = Json as? NSString because data is Dictionary not String
Correct solution :
let tasks = URLSession.shared.dataTask(with: URL(string: "https://talaikis.com/api/quotes/random/")!) { (data, response, error) in
if error != nil {
print("error")
} else {
if let content = data {
do {
let Json = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
if let data = Json as? [AnyHashable:Any] {
if let quote = data["quote"] as? String {
print(quote)
}
if let author = data["author"] as? String {
print(author)
}
if let cat = data["cat"] as? String {
print(cat)
}
DispatchQueue.main.async {
self.myLabel.text = "\(quote)"
}
}
} catch {
}
}
}
}
tasks.resume()
}
Related
To get a direct link to YouTube video I made request to get the get_video_info file, in this file that link, but I have to parse it, I find the solution to parse it by PHP but I want to parse it directly from my App
I get the data from my code like this:
let youtubeContentID = "sZz7tiToK1U"
if let infoURL = URL(string:"https://www.youtube.com/get_video_info?video_id=\(youtubeContentID)") {
let request = URLRequest(url: infoURL)
let session = URLSession(configuration: .default)
let task = session.dataTask(with: request, completionHandler: { (data, response, error) -> Void in
if let error = error {
print(error)
} else if let data = data, let result = NSString(data: data, encoding: String.Encoding.utf8.rawValue) {
print(result)
}
})
task.resume()
}
}
I got data with many Ascii symbols like this:
c=WEB&vss_host=s.youtube.com&innertube_api_version=v1&xhr_apiary_host=youtubei.youtube.com&apiary_host_firstparty=&status=ok&t=1&enabled_engage_types=3%2C6%2C4%2C5%2C17%2C1&ssl=1&adaptive_fmts=type%3Dvideo%252Fwebm%253B%2Bcodecs%253D%2522vp9%2522%26eotf%3Dbt709%26projection_type%3D1%26lmt%3D1550114115045036%26bitrate%3D16962786%26size%3D3840x2160%26index%3D221-1771%26quality_label%3D2160p%26xtags%3D%26url%3Dhttps%253A%252F%252Fr2---sn-nuj-wxqek.googlevideo.com%252Fvideoplayback%253Fexpire%253D1551500605%2526usequic%253Dno%2526gir%253Dyes%2526mime%253Dvideo%25252Fwebm%2526requiressl%253Dyes%2526keepalive%253Dyes%2526fvip%253D2%2526clen%253D547103152%2526source%253Dyoutube%2526aitags%253D133%25252C134%25252C135%25252C136%25252C137%25252C160%25252C242%25252C243%25252C244%25252C247%25252C248%25252C271%25252C278%25252C313%25252C394%25252C395%25252C396%25252C397%2526signature%253D9C28A5C103FA95701CD3655795DDB7F2C0954828.55534AC9C7BEE5D644C3A34C2CD4A4EEC9E2FD38%2526lmt%253D1550114115045036%2526ip%253D129.208.30.232%2526key%253Dyt6%2526c%253DWEB%2526ei%253D3bB5XKeFFNWd1wbBpqjgAg%2526txp%253D5531432%2526id%253Do-AIOVogCB8KFe32o_VgxSx-LqaEjNBZxiZ1jl81VTXZhF%2526sparams%253Daitags%25252Cclen%25252Cdur%25252Cei%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Ckeepalive%25252Clmt%25252Cmime%25252Cmm%25252Cmn%25252Cms%25252Cmv%25252Cpl%25252Crequiressl%25252Csource%25252Cusequic%25252Cexpire%2526initcwndbps%253D296250%2526itag%253D313%2526ms%253Dau%25252Crdu%2526mt%253D1551478917%2526mv%253Dm%2526dur%253D428.933%2526pl%253D19%2526ipbits%253D0%2526mm%253D31%25252C29%2526mn%253Dsn-nuj-wxqek%25252Csn-hgn7yn7l%26clen%3D547103152%26init%3D0-220%26itag%3D313%26primaries%3Dbt709%26fps%3D30%2Ctype%3Dvideo%252Fwebm%253B%2Bcodecs%253D%2522vp9%2522%26eotf%3Dbt709%26projection_type%3D1%26lmt%3D1550113771564979%26bitrate%3D6318874%26size%3D2560x1440%26index%3D220-1763%26quality_label%3D1440p%26xtags%3D%26url%3Dhttps%253A%252F%252Fr2---sn-nuj-wxqek.googlevideo.com%252Fvideoplayback%253Fexpire%253D1551500605%2526usequic%253Dno%2526gir%253Dyes%2526mime%253Dvideo%25252Fwebm%2526requiressl%253Dyes%2526keepalive%253Dyes%2526fvip%253D2%2526clen%253D155020170%2526source%253Dyoutube%2526aitags%253D133%25252C134%25252C135%25252C136%25252C137%25252C160%25252C242%25252C243%25252C244%25252C247%25252C248%25252C271%25252C278%25252C313%25252C394%25252C395%25252C396%25252C397%2526signature%253D4D7BF761EE4A6DFD048DE3D48550FCE80E61B7D0.D625BCC9645471ABA478F79C197B2208F354E15F%2526lmt%253D1550113771564979%2526ip%253D129.208.30.232%2526key%253Dyt6%2526c%253DWEB%2526ei%253D3bB5XKeFFNWd1wbBpqjgAg%2526txp%253D5531432%2526id%253Do-AIOVogCB8KFe32o_VgxSx- .... ext
I find code can deal with this YouTube request, it created on 2015 so it can't run on the new Swift, so I edited it to work on Swift 4, just copy this code in side a new Swift file then call function h264videosWithYoutubeID(youtubeID: your TouTube ID) and you will get the correct url:
import UIKit
public extension NSURL {
func dictionaryForQueryString() -> [String: AnyObject]? {
if let query = self.query {
return query.dictionaryFromQueryStringComponents()
}
let result = absoluteString?.components(separatedBy: "?")
if result!.count > 1 {
return result!.last?.dictionaryFromQueryStringComponents()
}
return nil
}
}
public extension NSString {
func stringByDecodingURLFormat() -> String {
let result = self.replacingOccurrences(of: "+", with: " ")
return result.removingPercentEncoding!
}
func dictionaryFromQueryStringComponents() -> [String: AnyObject] {
var parameters = [String: AnyObject]()
for keyValue in components(separatedBy: "&") {
let keyValueArray = keyValue.components(separatedBy: "=")
if keyValueArray.count < 2 {
continue
}
let key = keyValueArray[0].stringByDecodingURLFormat()
let value = keyValueArray[1].stringByDecodingURLFormat()
parameters[key] = value as AnyObject
}
return parameters
}
}
public class YoutubeUrlReciver: NSObject {
static let infoURL = "http://www.youtube.com/get_video_info?video_id="
static var userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.79 Safari/537.4"
public static func youtubeIDFromYoutubeURL(youtubeURL: NSURL) -> String? {
if let
youtubeHost = youtubeURL.host,
let youtubePathComponents = youtubeURL.pathComponents {
let youtubeAbsoluteString = youtubeURL.absoluteString
if youtubeHost == "youtu.be" as String? {
return youtubePathComponents[1]
} else if youtubeAbsoluteString?.range(of: "www.youtube.com/embed") != nil {
return youtubePathComponents[2]
} else if youtubeHost == "youtube.googleapis.com" ||
youtubeURL.pathComponents!.first == "www.youtube.com" as String? {
return youtubePathComponents[2]
} else if let
queryString = youtubeURL.dictionaryForQueryString(),
let searchParam = queryString["v"] as? String {
return searchParam
}
}
return nil
}
public static func h264videosWithYoutubeID(youtubeID: String) -> [String: AnyObject]? {
let urlString = String(format: "%#%#", infoURL, youtubeID) as String
let url = NSURL(string: urlString)!
let request = NSMutableURLRequest(url: url as URL)
request.timeoutInterval = 5.0
request.setValue(userAgent, forHTTPHeaderField: "User-Agent")
request.httpMethod = "GET"
var responseString = NSString()
let session = URLSession(configuration: URLSessionConfiguration.default)
let group = DispatchGroup()
group.enter()
session.dataTask(with: request as URLRequest, completionHandler: { (data, response, _) -> Void in
if let data = data as NSData? {
responseString = NSString(data: data as Data, encoding: String.Encoding.utf8.rawValue)!
}
group.leave()
}).resume()
group.wait()
let parts = responseString.dictionaryFromQueryStringComponents()
if parts.count > 0 {
var videoTitle: String = ""
var streamImage: String = ""
if let title = parts["title"] as? String {
videoTitle = title
}
if let image = parts["iurl"] as? String {
streamImage = image
}
if let fmtStreamMap = parts["url_encoded_fmt_stream_map"] as? String {
// Live Stream
if let _: AnyObject = parts["live_playback"]{
if let hlsvp = parts["hlsvp"] as? String {
return [
"url": "\(hlsvp)" as AnyObject,
"title": "\(videoTitle)" as AnyObject,
"image": "\(streamImage)" as AnyObject,
"isStream": true as AnyObject
]
}
} else {
let fmtStreamMapArray = fmtStreamMap.components(separatedBy: ",")
for videoEncodedString in fmtStreamMapArray {
var videoComponents = videoEncodedString.dictionaryFromQueryStringComponents()
videoComponents["title"] = videoTitle as AnyObject
videoComponents["isStream"] = false as AnyObject
return videoComponents as [String: AnyObject]
}
}
}
}
return nil
}
public static func h264videosWithYoutubeURL(youtubeURL: NSURL,completion: ((
_ videoInfo: [String: AnyObject]?, _ error: NSError?) -> Void)?) {
DispatchQueue.global().async {
if let youtubeID = self.youtubeIDFromYoutubeURL(youtubeURL: youtubeURL), let videoInformation = self.h264videosWithYoutubeID(youtubeID: youtubeID) {
DispatchQueue.main.async {
completion?(videoInformation, nil)
}
}else{
DispatchQueue.main.async {
completion?(nil, NSError(domain: "com.player.youtube.backgroundqueue", code: 1001, userInfo: ["error": "Invalid YouTube URL"]))
}
}
}
}
}
I'm having an issue printing data from Weather Underground. My code works with other data sources, just not Weather Underground. I have even tried replacing the URL with actual data (i.e. https://api.wunderground.com/api/APIKEY/forecast/geolookup/forecast/q/94129.json"), but it doesn't print.
Any suggestions on what it could be?
import Foundation
import UIKit
class APIManager {
func weatherJSON(zip: String, completion: #escaping ([Weather]) -> Void) {
let baseUrlString = "https://api.wunderground.com/api/APIKEY/forecast/geolookup/forecast/q/\(zip).json"
guard let url = URL(string: baseUrlString) else { return }
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard error == nil, let data = data else { return }
do {
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] else { return }
// MARK: Print JSON
print(json)
var weatherList = [Weather]()
for item in json {
if let weather = Weather.create(from: item) {
weatherList.append(weather)
}
}
completion(weatherList)
} catch {
print("Uh oh. You have an error with \(zip)!")
}
}
task.resume()
}
}
EDIT: SOLVED
I have used the code posted below and am now seeing errors.
I'd suggest changing this to report errors:
enum WeatherError Error {
case badURL
case invalidJSON
}
func weatherJSON(zip: String, completion: #escaping ([Weather]?, Error?) -> Void) {
let baseUrlString = "https://api.wunderground.com/api/APIKEY/forecast/geolookup/forecast/q/\(zip).json"
guard let url = URL(string: baseUrlString) else {
completion(nil, WeatherError.badURL)
return
}
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard error == nil, let data = data else {
completion(nil, error)
return
}
do {
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] else {
completion(nil, WeatherError.invalidJSON)
return
}
// MARK: Print JSON
print(json)
var weatherList = [Weather]()
for item in json {
if let weather = Weather.create(from: item) {
weatherList.append(weather)
}
}
completion(weatherList, nil)
} catch let parseError {
print("Uh oh. You have an error with \(zip)!")
if let responseString = String(data: data, encoding: .utf8) {
print("responseString = \(responseString)")
}
completion(nil, parseError)
}
}
task.resume()
}
Then, when you call it, you can see what the error was
weatherJSON(zip: something) { weatherReports, error in
guard let weatherReports = weatherReports, error == nil else {
print(error)
return
}
// use weatherReports here
}
This won't solve your problem, but it will help you diagnose what the issue is.
I am trying to get a list of events from the meetup's api. Its crashes when request mulitple times as it states can only request once and is limited to 200 requests. I am getting groups then using the "urlName" api call to get the event coming up for that group. The result would be an array of events from a bunch of meetup groups. Here is my code.
func getEventsFromMeetup(complete: (groups: [Meetup], succes: Bool) -> Void) {
var currentUserInterests = [String]()
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let recommendedBaseUrl = "https://api.meetup.com/recommended/groups?key=\(meetupAPIKey)"
let url = NSURL(string: recommendedBaseUrl)
let request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "GET"
session.dataTaskWithRequest(request) { (data, response, error) in
guard error == nil else {
print(error)
complete(groups: [Meetup](), succes: false)
return
}
guard let data = data else {
print("Error with data")
complete(groups: [Meetup](), succes: false)
return
}
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments) as! [NSDictionary]
var groups = [Meetup]()
CurrentUserFM.sharedInstance.getCurrentUserInterests { (interests) in
currentUserInterests = interests
var ints = [String]()
print(json.count)
for j in json {
let m = Meetup(data: j)
ints.append(m.name!)
if let i = m.interestName {
if currentUserInterests.contains(i) {
groups.append(m)
}
}
print("ints: \(ints.count)")
print("json: \(json.count)")
if Int(ints.count) >= Int(json.count) {
dispatch_async(dispatch_get_main_queue(), {
complete(groups: groups, succes: true)
return
})
}
}
}
}catch {
print(error)
}
}.resume()
}
func getEventsForGroups(completionHandler: (meetupEvents: [MeetupEvent]) -> ()) {
self.getEventsFromMeetup { (groups, success) in
var meetupEvents1 = [MeetupEvent]()
var ints = [String]()
for group in groups {
let eventBaseUrl = "https://api.meetup.com/\(group.urlname!)?key=\(meetupAPIKey)"
let url = NSURL(string: eventBaseUrl)
let request = NSMutableURLRequest(URL: url!)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
session.dataTaskWithRequest(request, completionHandler: { (data, reponse, error) in
guard error == nil else {
print(error)
return
}
guard let data = data else {
print("Error with data")
return
}
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments)
let m = MeetupEvent()
if let name = json["name"] as? String {
m.name = name
}
if let link = json["link"] as? String {
m.link = link
}
if let interestName = group.name {
m.interestName = interestName
}
if let image = json["key_photo"]??["thumb_link"] as? String {
m.image = image
}
meetupEvents1.append(m)
ints.append("1")
if Int(ints.count) >= Int(groups.count) {
dispatch_async(dispatch_get_main_queue(), {
completionHandler(meetupEvents: meetupEvents1)
return
})
}
}catch {
print(error)
}
}).resume()
}
}
}
Any suggestions on how to get the events from the groups so that the app wont crash from meetup calls?
I am doing some swift with web services. I am using alamofire. The code is working but i think that it's not "readable" ( not a clean code). Do you have an idea how i can optimise this ? Thanks
static func checkBookValidity(serialNumber: String, callBack: Result<Bool?> -> ()) {
let router = Router.CheckBookLuggage(serialNumber: serialNumber)
let request = Alamofire.request(router)
request.validate()
request.response { (request, response, data, error) in
if let error = error {
if error.code == NSURLErrorNotConnectedToInternet {
callBack(.Failure(.NoConnection))
}
else {
if let data = data {
do
{
if let json = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments) as? [String : AnyObject] {
let erroType = WSError.errorTypeWithJson(json, httpErroCode: error.code)
callBack(.Failure(erroType))
}
} catch {
callBack(.Failure(.ServerError))
}
}
else {
callBack(.Failure(.ServerError))
}
}
}
else {
if let data = data {
do
{
if let json = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments) as? [String : AnyObject] {
if let resultDic = json["result"] as? [String:AnyObject], let exists = resultDic["exists"] as? Bool {
if exists {
if let owner = resultDic["email"] as? String {
// ...
}
}
else {
callBack(.Success(false))
}
}
}
} catch {
callBack(.Failure(.ServerError))
}
}
}
}
}
Try this
if let error = error, data = data {
do {
if let json = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments) as? [String : AnyObject] {
let erroType = WSError.errorTypeWithJson(json, httpErroCode: error.code)
callBack(.Failure(erroType))
....
....
if let statements can be chained together with comma, as in error and data above.
The same can be done with guard.
I'm a C# developer convert to Swift tvOs and just starting to learn. I've made some progress, but not sure how to handle nested calls to json. The sources are from different providers so I can't just combine the query.
How do I wait for the inner request to complete so the TVSeries has the poster_path? Is there a better way to add the show to the collection and then process the poster path loading in another thread so it doesn't delay the UI Experience?
func downloadTVData() {
let url_BTV = NSURL(string: BTV_URL_BASE)!
let request_BTV = NSURLRequest(URL: url_BTV)
let session_BTV = NSURLSession.sharedSession()
//get series data
let task_BTR = session_BTV.dataTaskWithRequest(request_BTV) { (data_BTV, response_BTV, error_BTV) -> Void in
if error_BTV != nil {
print (error_BTV?.description)
} else {
do {
let dict_BTV = try NSJSONSerialization.JSONObjectWithData(data_BTV!, options: .AllowFragments) as? Dictionary<String, AnyObject>
if let results_BTV = dict_BTV!["results"] as? [Dictionary<String, AnyObject>]{
for obj_BTV in results_BTV {
let tvshow = TVSeries(tvDict: obj_BTV)
//for each tv series try to load a poster_path from secondary provider
if let str = obj_BTV["title"] as? String!{
let escapedString = str?.stringByAddingPercentEncodingWithAllowedCharacters(.URLQueryAllowedCharacterSet())!
if let url = NSURL(string: self.SEARCH_URL_BASE + escapedString!) {
let request = NSURLRequest(URL: url)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
if error != nil {
print (error?.description)
} else {
do {
let dict = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments) as? Dictionary<String, AnyObject>
if let results = dict!["results"] as? [Dictionary<String, AnyObject>] {
//iterate through the poster array
for obj in results {
if let path = obj["poster_path"] as? String {
tvshow.posterPath = path
break
}
}
}
} catch let error as NSError {
print(error.description)
}
}
}
task.resume()
}
}
self.tvSeries.append(tvshow)
}
dispatch_async(dispatch_get_main_queue()){
self.collectionView.reloadData()
}
}
} catch let error as NSError {
print(error.description)
}
}
}
task_BTR.resume()
}
Thanks for your help!
I would recommend breaking things apart into multiple methods, with callbacks to sequence the operations, and utilizing Swift's built-in throws error handling mechanism. Here's an example, not perfect, but might help as a starting point:
class TVSeries
{
let title: String
var posterPath: String?
enum Error: ErrorType {
case MalformedJSON
}
init(tvDict: [String: AnyObject]) throws
{
guard let title = tvDict["title"] as? String else {
throw Error.MalformedJSON
}
self.title = title
}
static func loadAllSeries(completionHandler: [TVSeries]? -> Void)
{
NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: BTV_URL_BASE)!) { data, response, error in
guard let data = data else {
print(error)
completionHandler(nil)
return
}
do {
completionHandler(try fromJSONData(data))
}
catch let error {
print(error)
}
}.resume()
}
static func fromJSONData(jsonData: NSData) throws -> [TVSeries]
{
guard let dict = try NSJSONSerialization.JSONObjectWithData(jsonData, options: .AllowFragments) as? [String: AnyObject] else {
throw Error.MalformedJSON
}
guard let results = dict["results"] as? [[String: AnyObject]] else {
throw Error.MalformedJSON
}
return try results.map {
return try TVSeries(tvDict: $0)
}
}
func loadPosterPath(completionHandler: () -> Void)
{
guard let searchPath = title.stringByAddingPercentEncodingWithAllowedCharacters(.URLQueryAllowedCharacterSet()) else {
completionHandler()
return
}
let url = NSURL(string: SEARCH_URL_BASE)!.URLByAppendingPathComponent(searchPath)
NSURLSession.sharedSession().dataTaskWithURL(url) { [weak self] data, response, error in
defer { completionHandler() }
guard let strongSelf = self else { return }
guard let data = data else {
print(error)
return
}
do {
strongSelf.posterPath = try TVSeries.posterPathFromJSONData(data)
}
catch let error {
print(error)
}
}.resume()
}
static func posterPathFromJSONData(jsonData: NSData) throws -> String?
{
guard let dict = try NSJSONSerialization.JSONObjectWithData(jsonData, options: .AllowFragments) as? [String: AnyObject] else {
throw Error.MalformedJSON
}
guard let results = dict["results"] as? [[String: AnyObject]] else {
throw Error.MalformedJSON
}
for result in results {
if let path = result["poster_path"] as? String {
return path
}
}
return nil
}
}
It might also be worth your time to look into something like RxSwift or Alamofire, which help you with these kinds of data-conversion / sequencing operations.