Cast NSArray to NSDictionary - swift

I want to cast jsonResult to NSDicitonary to be able to do a callback of jsonResult. Is this possible?
func request(url:String,callback:(NSDictionary)->()) {
let nsURL = NSURL(string: url)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(nsURL!, completionHandler: { (data, response, error) -> Void in
if error != nil {
print(error)
} else {
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSArray
print(jsonResult[0])
} catch {
print("my error")
}
}
})

If I understand your question, you can use optional binding to cast your object:
if let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSArray {
if let dict = jsonResult[0] as? NSDictionary {
callback(dict)
}
}

Related

How to properly cancel and restart an URLSessionDataTask Swift

This is my request funtion:
func receiptValidation(completion: #escaping(_ isPurchaseSchemeActive: Bool, _ error: Error?) -> ()) {
let receiptFileURL = Bundle.main.appStoreReceiptURL
guard let receiptData = try? Data(contentsOf: receiptFileURL!) else {
//This is the First launch app VC pointer call
completion(false, nil)
return
}
let recieptString = receiptData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
let jsonDict: [String: AnyObject] = ["receipt-data" : recieptString as AnyObject, "password" : "7bb160f1c8ec4d929fbc751c507d24fd" as AnyObject]
do {
let requestData = try JSONSerialization.data(withJSONObject: jsonDict, options: JSONSerialization.WritingOptions.prettyPrinted)
let storeURL = URL(string: self.verifyReceiptURL)!
var storeRequest = URLRequest(url: storeURL)
storeRequest.httpMethod = "POST"
storeRequest.httpBody = requestData
let session = URLSession(configuration: URLSessionConfiguration.default)
let task = session.dataTask(with: storeRequest, completionHandler: { [weak self] (data, response, error) in
do {
if let jsonResponse = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary {
print("json response \(jsonResponse)")
if let expiresDate = self?.getPurchaseAndExpirationDateFromResponse(jsonResponse, keyString: "expires_date") {
//print("expiresDate \(expiresDate)")
let purchaseStatus = self?.isSubscriptionActive(expireDate: expiresDate)
if let purchaseStatus = purchaseStatus {
completion(purchaseStatus, nil)
}
}
}
} catch let parseError {
completion(false, parseError)
}
})
task.resume()
} catch let parseError {
completion(false, parseError)
}
}
This is how I am calling it:
func callForVal() {
receiptValidation() { isPurchaseSchemeActive, error in
if let err = error {
self.onBuyProductHandler?(.failure(err))
} else {
self.onBuyProductHandler?(.success(isPurchaseSchemeActive))
}
}
}
But sometimes It takes a long time to give a response back. Now I want to call it with a 60 seconds timer If do not get any response within these 60 seconds. How can I do it?

Value of type 'NSArray.Element' (aka 'Any') has no subscripts

I openned an old ios project in Xcode with warnings about swift 4 updates.
During some fixing an error I could not find solution.
The error occours while looping jsonArray, passing values to variables...
let url=NSURL(string:"http://webserver.com/json")
let data = NSData(contentsOf: url! as URL)
do {
let jsonResult = try JSONSerialization.jsonObject(with: data! as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
let jsonArray = jsonResult.value(forKey: "person") as! NSArray
for json in jsonArray {
let id = json["id"] as? Int?
let name = json["name"] as? String
let age = json["age"] as? String
tableID.append(String(id!))
tableName.append(name!)
tableAge.append(age!)
tableView.reloadData()
}
} catch {
}
There are many don'ts in the code.
NSURL, NSData
NSArray, NSDictionary (which causes the error)
(NS)Data(contentsOf)
value(forKey:)
.mutableContainers
ignoring the error in the catch block
The actual native Swift syntax is
let url = URL(string:"http://webserver.com/json")!
let task = URLSession.shared.dataTask(with: url) { [unowned self] (data, response, error) in
if let error = error { print(error); return }
do {
if let jsonResult = try JSONSerialization.jsonObject(with: data!) as? [String:Any],
let jsonArray = jsonResult["person"] as? [[String:Any]] {
for json in jsonArray {
let id = json["id"] as! Int
let name = json["name"] as! String
let age = json["age"] as! String
self.tableID.append(String(id))
self.tableName.append(name)
self.tableAge.append(age)
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
} catch {
print(error)
}
}
task.resume()
There is one don't left: Don't use multiple arrays as data source.

Fatal error: found nil - In calling Web API with JSON

Full error: Fatal error: Unexpectedly found nil while unwrapping an Optional value.
I am trying to get data from Web API service and am not sure where in the program it is getting nil value from.
Program crashes and getting error at line when declaring jsonResult
let urlAsString = "http://api.geonames.org/earthquakesJSON?north="+northString+"&south="+southString+"&east="+eastString+"&west="+westString+"&username=test"
let url = URL(string: urlAsString)!
let urlSession = URLSession.shared
let jsonQuery = urlSession.dataTask(with: url, completionHandler: { data, response, error -> Void in
if (error != nil) {
print(error!.localizedDescription)
}
var err: NSError?
let jsonResult = (try! JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers)) as! NSDictionary //program crashes and gets error here
if (err != nil) {
print("JSON Error \(err!.localizedDescription)")
}
print(jsonResult)
let setOne:NSArray? = jsonResult["earthquakes"] as? NSArray
print(setOne?[0]);
let y = setOne?[0] as? [String: AnyObject]
let dateTime: String = (y!["datetime"] as? NSString)! as String
DispatchQueue.main.async{
self.date.text = String(dateTime)
}
})
jsonQuery.resume()
Seems like data is nil. You forcibly tried to set nil value.
if let data = data {
if let jsonResult = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as NSDictionary {
if let setOne = jsonResult["earthquakes"] as? [NSDictionary] {
let y = setOne[0] as? [String: AnyObject]
let dateTime: String = (y!["datetime"] as? String)! as String
DispatchQueue.main.async{
self.date.text = String(dateTime)
}
}
}
}
It crashes because you force unwrap a nil value. So try with optional instead
replace this line let jsonResult = (try! JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers)) as! NSDictionary with:
do {
if let jsonResult = try? JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers)) as? NSDictionary {
// Rest of your code here
}
} catch let error {
}

Nested dataTaskWithRequest in Swift tvOS

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.

NSString to expected argument NSData

I have this code:
let urls : String = Baseurl+"lat="+latitude+"&lon="+longitude+"&APPID="+apiKey
if let url = NSURL(string: urls) {
do {
let jsonResult = try NSString(contentsOfURL: url, usedEncoding: nil)
do {
if let jsonRes = try NSJSONSerialization.JSONObjectWithData(jsonResult, options: []) as? NSDictionary {
print(jsonRes)
}
} catch let error as NSError {
print(error.localizedDescription)
}
But there is an error on this line: if let jsonRes = try.... The error is: cannot convert value of type 'NSString' to expected argument type 'NSData'.
When I print the jsonResult I get this:
{
"coord":{
"lon":-0.13,
"lat":51.51
},
"weather":[
{
"id":501,
"main":"Rain",
"description":"moderate rain",
"icon":"10d"
}
],
"base":"stations",
"main":{
"temp":284.46,
"pressure":1023,
"humidity":76,
"temp_min":283.15,
"temp_max":285.15
},
"visibility":10000,
"wind":{
"speed":7.7,
"deg":220
},
"clouds":{
"all":75
},
"dt":1449755400,
"sys":{
"type":1,
"id":5093,
"message":0.0414,
"country":"GB",
"sunrise":1449734113,
"sunset":1449762690
},
"id":2643743,
"name":"London",
"cod":200
}
How can I acces those values?
NSJSONSerialization.JSONObjectWithData() expects an NSData object, not a String.
Replace
let jsonResult = try NSString(contentsOfURL: url, usedEncoding: nil)
with
let jsonResult = NSData(contentsOfURL: url)
and remove the try.
I should also note that this isn't really the best way to make a web request, you should use NSURLSession to make an async request:
NSURLSession().dataTaskWithURL(NSURL(string: urls)!) { (data, response, error) -> Void in
if let data = data {
do {
let dict = try NSJSONSerialization.JSONObjectWithData(data, options: [])
print(dict)
} catch {
print(error)
}
}
}.resume()