Getting Cell Count after network operation - ios

I am trying to get the collectionViewCell count from the network request but the value turns out to be always 0 (to which I initialised the count variable) I want the view to load the cells after I get it's count from the get request.What is it I'm doing wrong I have written this code after super.viewDidLoad().
DispatchQueue.global(qos:.background).async {
let token = "---------------------------"
let url = URL(string: "https:----------home.json?token="+token)!
let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
guard let data = data else { return }
// print(String(data: data, encoding: .utf8)!)
let jsonWithObjectRoot = try? JSONSerialization.jsonObject(with: data, options: [])
// print(json!)
if let dictionary = jsonWithObjectRoot as? [String: Any] {
if let data = dictionary["data"] as? [String:Any]{
if let posts = data["posts"] as? [Any]{
count = posts.count
//print(count) //the value here is 2
for object in posts{
if let contentString = object as? [String: Any] {
print(contentString["title"] as! String)
// print(contentString["entered"]as! String)
}
}
}
}
}
}
task.resume()
/* end Request */
DispatchQueue.main.async{
self.collectionView.reloadData()
self.collectionView.collectionViewLayout.invalidateLayout()
}
}

Classic async case. You network calls are async, so your reload should happen inside the completion of the network call.
DispatchQueue.global(qos:.background).async {
let token = "---------------------------"
let url = URL(string: "https:----------home.json?token="+token)!
let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
guard let data = data else { return }
// print(String(data: data, encoding: .utf8)!)
let jsonWithObjectRoot = try? JSONSerialization.jsonObject(with: data, options: [])
// print(json!)
if let dictionary = jsonWithObjectRoot as? [String: Any] {
if let data = dictionary["data"] as? [String:Any]{
if let posts = data["posts"] as? [Any]{
count = posts.count
//print(count) //the value here is 2
DispatchQueue.main.async{
self.collectionView.reloadData()
self.collectionView.collectionViewLayout.invalidateLayout()
}
for object in posts {
if let contentString = object as? [String: Any] {
print(contentString["title"] as! String)
// print(contentString["entered"]as! String)
}
}
}
}
}
}
task.resume()
/* end Request */
}

You have to reload after getting data from the network
count = posts.count
//print(count) //the value here is 2
DispatchQueue.main.async{
self.collectionView.reloadData()
}

Related

Converting Data from http request from JSON Dictionary of Dictionaries into one Array of Dictionaries in Swift

When Running my http request I get returned the data in the following way.
I am requesting as follows
do {
if let file = URL(string: "https://myhttprequest....") {
let data = try Data(contentsOf: file)
let json = try JSONSerialization.jsonObject(with: data, options: [])
if let object = json as? [String: Any] {
// json is a dictionary
print(object)
} else {
print("JSON is invalid")
}
} else {
print("no file")
}
} catch {
print(error.localizedDescription)
}
The http request shows like this for example
{"created":[{"id":"-LVEAdIk2KwDmxBj25pK","caption":"Cool watch bro ","creationDate":1546442937.5934439,"imageHeight":1000,"imageUrl":"https://firebasestorage.googleapis.com/..."}],"reshared":[{"id":"-LVEAdIk2KwDmxBj25pK","caption":"Cool watch bro ","creationDate":1546442937.5934439,"imageHeight":1000,"imageUrl":"https://firebasestorage.googleapis.com/..."}]}
I want to be able to put the value of object["created"] and object["reshared"] together to have one array of two dictionaries [[caption:"", creationDate:""...],[caption:"", creationDate:""...]]
I have tried by accessing them individually like object["created"] but its not of type dictionary and I cant seem to figure out how to get it to be one.
UPDATE: So I am now doing the following
guard let uid = Auth.auth().currentUser?.uid else { return }
let url = URL(string: "https://us-central1-flashtrend-bdcd3.cloudfunctions.net/getFeed/\(uid)")!
let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
guard let firstData = data else { return }
let jsonStr = String(data: firstData, encoding: .utf8)!
guard let data = jsonStr.data(using: .utf8) else {
return
}
do {
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
return
}
guard let created = json["created"], let reshared = json["reshared"] else {
return
}
let result = [created, reshared]
print(result)
} catch {
print(error.localizedDescription)
}
}
task.resume()
But when i print it looks weird
[<__NSSingleObjectArrayI 0x600002de2290>(
{
caption = "Cool watch bro ";
creationDate = "1546442937.593444";
id = "-LVEAdIk2KwDmxBj25pK";
imageHeight = 1000;
imageUrl = "https://firebasestorage.googleapis.com/v0/b/flashtrend-bdcd3.appspot.com/o/posts%2FE346E4B7-31D8-4E9E-89F2-DA7C426C0537?alt=media&token=4936ce58-64bb-4d5a-b913-c3b87705614f";
imageWidth = 750;
swipes = 0;
userid = U9097gARoXOus96vT1uBHAcNPs03;
views = 1;
}
)
, <__NSArray0 0x600002df40c0>(
)
]
I have changed the http request result to json string, sample code for you as follow, works fine on my xcode:
let jsonStr = "{\"created\":{\"caption\":\"Cool watch bro \",\"creationDate\":\"1546442937.593444\",\"id\":\"-LVEAdIk2KwDmxBj25pK\",\"imageHeight\":1000,\"imageUrl\":\"https://firebasestorage.googleapis.com/v0/b/flashtrend-bdcd3.appspot.com/o/posts/E346E4B7-31D8-4E9E-89F2-DA7C426C0537?alt:media&token:4936ce58-64bb-4d5a-b913-c3b87705614f\",\"imageWidth\":750,\"swipes\":0,\"userid\":\"U9097gARoXOus96vT1uBHAcNPs03\",\"views\":1},\"reshared\":{\"caption \":\"Cool watch bro\",\"creationDate \":\"1546442937.593444\",\"id \":\"-LVEAdIk2KwDmxBj25pK \",\"imageHeight\":1000,\"imageUrl\":\"https://firebasestorage.googleapis.com/v0/b/flashtrend-bdcd3.appspot.com/o/posts/E346E4B7-31D8-4E9E-89F2-DA7C426C0537?alt:media&token:4936ce58-64bb-4d5a-b913-c3b87705614f\",\"imageWidth\":750,\"swipes\":0,\"userid\":\"U9097gARoXOus96vT1uBHAcNPs03\",\"views\":1}}"
guard let data = jsonStr.data(using: .utf8) else {
return
}
do {
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
return
}
guard let created = json["created"], let reshared = json["reshared"] else {
return
}
let result = [created, reshared]
print(result)
} catch {
print(error.localizedDescription)
}

Json data to label

I have been struggling with this for a while and just need some quick help with this basic question.
let url = URL(string: "https://api.coindesk.com/v1/bpi/currentprice.json")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil
{
print ("Error!")
}
else
{
if let content = data
{
do
{
// Array
let myJson = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
if let rates = myJson["bpi"] as? NSDictionary
{
if let currency = rates["USD"] as NSDictionary
{
if let money = currency["rate"]
{
print(money)
}
}
}
}
catch
{
}
}
}
}
task.resume()
self.label.text = (self.money as String?)
I am trying to pass the numerical value for "money" to a label in my view controller.
The main issue is that you are trying to update the label from outside the completion handler of your asynchronous network request. However, there are several other issues with your code. You shouldn't use NSDictionary in Swift, use [String:Any] when parsing JSON dictionaries. .mutableContainers also doesn't have any effect in Swift. Also make sure that you dispatch any UI related updates to the main thread.
let url = URL(string: "https://api.coindesk.com/v1/bpi/currentprice.json")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
guard let data = data else {
print(error!); return
}
do {
if let myJson = try JSONSerialization.jsonObject(with: content) as? [String:Any],
let rates = myJson["bpi"] as? [String:Any],
let currency = rates["USD"] as? [String:Any],
let money = currency["rate"] as? String {
DispatchQueue.main.async{
self.label.text = money
}
}
} catch{
print(error)
}
}
task.resume()

How to parse a api for swift 3?

Have been researching on the parsing for quite a bit. With plethora of information avilable for JSON nothing seems to explain how to do in a sensible way to extract information with swift 3.
This is what got so far
func getBookDetails() {
let scriptUrl = "https://www.googleapis.com/books/v1/volumes?q=isbn:9781451648546" .
let myurl = URL(string:scriptUrl)
let request = NSMutableURLRequest(url: myurl!)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: myurl! ) { (data, response, error) in
if error != nil{
print("THIS ERROR",error!)
return
} else{
if let mydata = data{
do{
let myJson = try (JSONSerialization.jsonObject(with: mydata, options: JSONSerialization.ReadingOptions.mutableContainers)) as AnyObject
// print("this is the MY JSON",myJson) ---> prints out the json
if let dictonary = myJson["items"] as AnyObject? {
print("the DICTONARY",dictonary) // ----> OUTPUT
if let dictonaryAA = dictonary["accessInfo"] as AnyObject? {
print("the accessInfo",dictonaryAA)
}
}
} catch{
print("this is the in CATCH")
}
} //data
}
}
task.resume()
}
}
OUTPUT :
the DICTONARY (
{
accessInfo = {
accessViewStatus = SAMPLE;
country = US;
=============
RELEVANT DATA as in https://www.googleapis.com/books/v1/volumes?
q=isbn:9781451648546"
==========================
title = "Steve Jobs";
};
}
)
Just need to parse through the json data to get the name, author and title of the book with reference to isbn.
Know there should be a better way to do things that is easily understandable to someone new into the language
You can parse the api in two ways
Using URLSession:
let rawDataStr: NSString = "data={\"mobile\":\"9420....6\",\"password\":\"56147180..1\",\"page_no\":\"1\"}"
self.parsePostAPIWithParam(apiName: "get_posts", paramStr: rawDataStr){ ResDictionary in
// let statusVal = ResDictionary["status"] as? String
self.postsDict = (ResDictionary["posts"] as! NSArray!) as! [Any]
print("\n posts count:",self.postsDict.count)
}
func parsePostAPIWithParam(apiName:NSString, paramStr:NSString,callback: #escaping ((NSDictionary) -> ())) {
var convertedJsonDictResponse:NSDictionary!
let dataStr: NSString = paramStr
let postData = NSMutableData(data: dataStr.data(using: String.Encoding.utf8.rawValue)!)
let request = NSMutableURLRequest(url: NSURL(string: "http://13.12..205.248/get_posts/")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = nil
request.httpBody = postData as Data
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error as Any)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse as Any)
do{
if let convertedJsonIntoDict = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary {
convertedJsonDictResponse = convertedJsonIntoDict.object(forKey: apiName) as? NSDictionary
// callback for response
callback(convertedJsonDictResponse)
}
} catch let error as NSError {
print(error)
}
}
Using Alamofire
func AlamofirePOSTRequest() {
let urlString = "http://13.12..205.../get_posts/"
let para = ["data": "{\"mobile\":\"9420....6\",\"password\":\"56147180..1\",\"page_no\":\"1\"}"]
Alamofire.request(urlString, method: .post, parameters: para , headers: nil).responseJSON {
response in
switch response.result {
case .success:
print("response: ",response)
let swiftyJsonVar = JSON(response.result.value!)
if let resData = swiftyJsonVar["posts"].arrayObject {
self.postsDict = resData as! [[String:AnyObject]]
}
print("\n \n alomafire swiftyJsonVar: ",swiftyJsonVar)
break
case .failure(let error):
print(error)
}
}
}
})
dataTask.resume()
}
First of all, all JSON types are value types in Swift 3 so the most unspecified type is Any, not AnyObject.
Second of all, there are only two collection types in the JSON type set, dictionary ([String:Any]) and array ([Any], but in most cases [[String:Any]]). It's never just Any nor AnyObject.
Third of all, the given JSON does not contain a key name.
For convenience let's use a type alias for a JSON dictionary:
typealias JSONDictionary = [String:Any]
The root object is a dictionary, in the dictionary there is an array of dictionaries for key items. And pass no options, .mutableContainers is nonsense in Swift.
guard let myJson = try JSONSerialization.jsonObject(with: mydata) as? JSONDictionary,
let items = myJson["items"] as? [JSONDictionary] else { return }
Iterate through the array and extract the values for title and authors which is an array by the way. Both values are in another dictionary for key volumeInfo.
for item in items {
if let volumeInfo = item["volumeInfo"] as? JSONDictionary {
let title = volumeInfo["title"] as? String
let authors = volumeInfo["authors"] as? [String]
print(title ?? "no title", authors ?? "no authors")
The ISBN information is in an array for key industryIdentifiers
if let industryIdentifiers = volumeInfo["industryIdentifiers"] as? [JSONDictionary] {
for identifier in industryIdentifiers {
let type = identifier["type"] as! String
let isbn = identifier["identifier"] as! String
print(type, isbn)
}
}
}
}
You are doing wrong in this line
if let dictonaryAA = dictonary["accessInfo"] as AnyObject?
because dictonary here is an array not dictionary. It is array of dictionaries. So as to get first object from that array first use dictonary[0], then use accessInfo key from this.
I am attaching the code for your do block
do{
let myJson = try (JSONSerialization.jsonObject(with: mydata, options: JSONSerialization.ReadingOptions.mutableContainers)) as AnyObject
// print("this is the MY JSON",myJson) ---> prints out the json
if let array = myJson["items"] as AnyObject? {
print("the array",array) // ----> OUTPUT
let dict = array.object(at: 0) as AnyObject//Master Json
let accessInf = dict.object(forKey: "accessInfo") //Your access info json
print("the accessInfo",accessInf)
}
}
Hope this helps you.

Passing data from JSON to table view cell in Swift 3

I'm trying to pass data from a JSON response to a table view cell. I'm having problems with capturing the response values that I'm extracting in URLSession.shared.dataTask.
func callYouTubeAPIToGetAllVideos() {
let url = URL(string: "https://www.googleapis.com/youtube/v3/search?part=snippet&channelId=XYZ&maxResults=50&order=date&key=ABC")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print(error!)
} else {
if let usableData = data {
let json = try? JSONSerialization.jsonObject(with: usableData, options: [])
if let dictionary = json as? [String: Any?] {
if let array = dictionary["items"] as? [Any] {
for object in array {
if let objectAsDictionary = object as? [String: Any?] {
if let objectWithKindAndVideoId = objectAsDictionary["id"] as? [String: String] {
if let videoId = objectWithKindAndVideoId["videoId"] {
//pass data to table cell
}
}
if let snippet = objectAsDictionary["snippet"] as? [String: Any] {
if let description = snippet["description"] {
//pass data to table cell
}
}
}
}
}
}
}
}
}
task.resume()
}
I tried appending the values to an instance variable but it didn't work.
Sorry about the messy code, this is my 1st time working with JSON in Swift.
First of all never declare a received JSON dictionary as [String:Any?]. A received dictionary value can't be nil.
Declare a custom struct Video.
struct Video {
let videoId : String
let description : String
}
Declare a data source array.
var videos = [Video]()
Parse the JSON into the array and reload the table view on the main thread.
func callYouTubeAPIToGetAllVideos() {
let url = URL(string: "https://www.googleapis.com/youtube/v3/search?part=snippet&channelId=XYZ&maxResults=50&order=date&key=ABC")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print(error!)
} else {
do {
if let dictionary = try JSONSerialization.jsonObject(with: data!) as? [String: Any],
let array = dictionary["items"] as? [[String: Any]] {
for object in array {
if let objectWithKindAndVideoId = object["id"] as? [String: String],
let snippet = object["snippet"] as? [String: Any] {
let videoId = objectWithKindAndVideoId["videoId"] ?? ""
let description = snippet["description"] as? String ?? ""
let video = Video(videoId: videoId, description: description)
self.videos.append(video)
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
} catch {
print(error)
}
}
}
task.resume()
}
In cellForRow assign the values to the text properties
let video = videos[indexPath.row]
cell.textLabel!.text = video.videoId
cell.detailTextLabel?.text = video.description

Json and variable scope

My error should be quite obvious but I can't find it;
I've a global variable initialized a the beginning of my class:
class InscriptionStageViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
var lesSemaines = [String]()
I try to populate this array with a distant json file using that function
func getSemainesStages(){
let url = URL(string: "http://www.boisdelacambre.be/ios/json/semaines.json")
let task = URLSession.shared.dataTask(with: url!){ (data, response, error) in
if let content = data {
do {
let myJson = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
let listeSemaines = myJson["semaine"] as! [[String:AnyObject]]
//print(listeSemaines)
for i in 0...listeSemaines.count-1 {
var tabSem = listeSemaines[i]
let intituleSemaine:String = tabSem["intitule"] as! String
//let dateSemaine:String = tabSem["date"] as! String
DispatchQueue.main.sync
{
self.lesSemaines.append(intituleSemaine)
}
}
} catch
{
print("erreur Json")
}
}
}
task.resume()
}
When I call my function in the viewDidLoad and then I print my global array, it's empty (the URL is correct, the json data is read correctly and when I read the data appended in the array in the loop, it print the (so) needed value...)
Thanks in advance
The download takes time. Introduce another methode:
func updateUi() {
print(lesSemaines)
//pickerView.reloadAllComponents()
}
And call it after the download finished:
func getSemainesStages(){
// ... your code
let task = URLSession.shared.dataTask(with: url!){ (data, response, error) in
// ... your code
for tabSem in listeSemaines{
guard let intituleSemaine = tabSem["intitule"] as? String else {
print("erreur Json")
continue
}
self.lesSemaines.append(intituleSemaine)
}
// update UI *after* for loop
DispatchQueue.main.async {
updateUi()
}
// ... your code
}
}
I have updated your code to Swift 3. Please replace it with below code.
func getSemainesStages(){
let url = URL(string: "http://www.boisdelacambre.be/ios/json/semaines.json")
let task = URLSession.shared.dataTask(with: url!){ (data, response, error) in
if let content = data {
do {
let myJson = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as! [String: Any]
let listeSemaines = myJson["semaine"] as! [[String: Any]]
for i in 0...listeSemaines.count-1 {
var tabSem = listeSemaines[i]
let intituleSemaine:String = tabSem["intitule"] as! String
self.lesSemaines.append(intituleSemaine)
}
print(self.lesSemaines)
} catch {
print("erreur Json")
}
}
}
task.resume()
}

Resources