No Data in array github Api Swift - json

How do I get the "names" from the if let statement into my tableview? The code triggers the else block right now. I am trying to parse the name data from the github api. Here's the code:
import UIKit
import Foundation
class ViewController: UITableViewController {
var tableArray = [String] ()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.dataSource = self
self.tableView.delegate = self
parseJSON()
}
func parseJSON () {
let url = URL(string: "https://api.github.com")
let task = URLSession.shared.dataTask(with: url!) {(data, response, error ) in
guard error == nil else {
print("returned error")
return
}
guard let content = data else {
print("No data")
return
}
guard let json = (try? JSONSerialization.jsonObject(with: content,
options: JSONSerialization.ReadingOptions.mutableContainers)) as?
[String: Any] else {
print("Not containing JSON")
return
}
if let array = json["name"] as? [String] {
self.tableArray = array
} else {
print("Name is blank")
}
print(self.tableArray)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
task.resume()
}
}
extension ViewController {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as UITableViewCell
cell.textLabel?.text = self.tableArray[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.tableArray.count
}
}
Right now it's displaying "name is blank" in the console. I am trying to get the names of the users and display them in a tableview. Other url's seem to work, but I can't seem to figure out Github. Thanks for the help.

Your mistakes is.
1. Used wrong url.
2. Wrong mapping response
https://api.github.com Return Api list.
https://api.github.com/users/ Return user list.
Function fetchNameOfUsers
1. Implement request by use "user list" url.
2. Mapping the response by use "user list" structure.
func fetchNameOfUsers() {
guard let url = URL(string: "https://api.github.com/users") else {
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
guard error == nil else {
return // Error
}
guard let data = data, let _ = response else {
return // No data
}
guard let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] else {
return // Not [String: Any] list
}
let nameOfUsers = json.compactMap {
$0["login"] as? String
}
let displayedNameOfUsers = nameOfUsers.joined(separator: ", ")
print(displayedNameOfUsers)
}.resume()
}
To see result call function fetchNameOfUsers.
fetchNameOfUsers()

Related

How to return an array of objects using SearchBar and for loop

I am wanting the search bar to return the title for each object in the Array. I believe the problem is in my for loop but I am not completed sure. Hopefully, you guys are able to tell me what I am doing wrong.
I am searching through an API. This is the array I am attempting to search through
struct ProductResponse: Decodable {
let results: [SearchResults]
}
struct SearchResults: Decodable {
let title: String
let id:Int
}
I created a for loop to run through each object in the array and get the title and id.
func fetchProduct(productName: String) {
let urlString = "\(searchURL)&query=\(productName)&number=25"
performRequest(urlString: urlString)
}
func performRequest(urlString: String) {
// Create a URL
if let url = URL(string: urlString) {
//Create a URLSession
let session = URLSession(configuration: .default)
// Give the session a task
let task = session.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
return
}
if let safeData = data {
self.parseJSON(productTitle: safeData)
}
}
// Start the task
task.resume()
}
}
func parseJSON(productTitle: Data) {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(ProductResponse.self, from: productTitle)
print(decodedData.results)
} catch {
print(error)
}
}
}
I created this function to reload the data in my tableview. When I search for the product results in the Search Bar, my tableview doesn't return anything. The goal is to have my tableview return each result in a tableview cell
var listOfProducts = [SearchResults]() {
didSet {
DispatchQueue.main.async {
self.tableView.reloadData()
self.navigationItem.title = "\(self.listOfProducts.count) Products found"
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
productSearch.delegate = self
}
func downloadJSON() {
guard let downloadURL = url else { fatalError("Failed to get URL")}
URLSession.shared.dataTask(with: downloadURL) { (data, Response, error) in
print("downloaded")
}.resume()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return listOfProducts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let product = listOfProducts[indexPath.row].title
cell.textLabel?.text = product
return cell
}
}
extension ProductsTableViewController: UISearchBarDelegate {
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
if let searchBarText = searchBar.text {
searchRequest.fetchProduct(productName: searchBarText)
}
}
}
This is the result
enter image description here
If everything goes well and You got the data under "decodedData.results" of "parseJSON" method, And I saw "decodedData.results" and "listOfProducts" both variables are the same type of SearchResults. So you can just add the one line of under "parseJSON" method as follows:-
func parseJSON(productTitle: Data) {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(ProductResponse.self, from: productTitle)
print(decodedData.results)// it have some array data of type SearchResults
self.listOfProducts = decodedData.results
self.tableView.reloadData()
} catch {
print(error)
}
}

I'm having troubles displaying an image from JSON in a Table View

I'm trying to display images that comes from an API. The images are inside an URL and I want to fill a Table View with all the array, but it shows only one image at the Table View.
Here's my code:
struct Autos {
let Marca:String
let Modelo:String
let Precio:String
let RutaImagen:String
init?(_ dict:[String:Any]?){
guard let _dict = dict,
let marca=_dict["Marca"]as?String,
let modelo=_dict["Modelo"]as?String,
let precio=_dict["Precio"]as?String,
let rutaImagen=_dict["RutaImagen"]as?String
else { return nil }
self.Marca = marca
self.Modelo = modelo
self.Precio = precio
self.RutaImagen = rutaImagen
}
}
var arrAutos = [Autos]()
func getImage(from string: String) -> UIImage? {
// Get valid URL
guard let url = URL(string: string)
else {
print("Unable to create URL")
return nil
}
var image: UIImage? = nil
do {
// Get valid data
let data = try Data(contentsOf: url, options: [])
// Make image
image = UIImage(data: data)
}
catch {
print(error.localizedDescription)
}
return image
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 9
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "carsCell", for: indexPath) as! CarsDetailTableViewCell
let url = URL(string: "http://ws-smartit.divisionautomotriz.com/wsApiCasaTrust/api/autos")!
let task = URLSession.shared.dataTask(with: url) {
(data, response, error) in
guard let dataResponse = data, error == nil else {
print(error?.localizedDescription ?? "Response Error")
return
}
do {
let jsonResponse = try JSONSerialization.jsonObject(with: dataResponse, options: []) as? NSArray
self.arrAutos = jsonResponse!.compactMap({ Autos($0 as? [String:String])})
DispatchQueue.main.async {
// Get valid string
let string = self.arrAutos[indexPath.row].RutaImagen
if let image = self.getImage(from: string) {
// Apply image
cell.imgCar.image = image
}
cell.lblBrand.text = self.arrAutos[indexPath.row].Marca
cell.lblPrice.text = self.arrAutos[indexPath.row].Precio
cell.lblModel.text = self.arrAutos[indexPath.row].Modelo
}
} catch let parsingError {
print("Error", parsingError)
}
}
task.resume()
return cell
}
The JSON serialization is working fine, because the other data is showed correctly at the table view, the issue is with the image, because in the table view only appears one image, the other rows are empty. Does anyone have an advise?
I think you should download your full data before loading tableview and reload tableview in the completion handler. Call loadData() method in your viewDidLoad().
fileprivate func loadData() {
let url = URL(string: "http://ws-smartit.divisionautomotriz.com/wsApiCasaTrust/api/autos")!
let task = URLSession.shared.dataTask(with: url) {
(data, response, error) in
guard let dataResponse = data, error == nil else {
print(error?.localizedDescription ?? "Response Error")
return
}
do {
let jsonResponse = try JSONSerialization.jsonObject(with: dataResponse, options: []) as? NSArray
self.arrAutos = jsonResponse!.compactMap({ Autos($0 as? [String:String])})
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch let parsingError {
print("Error", parsingError)
}
}
task.resume()
}
For loading images in tableView cell, download the image in background thread and then update the imageView in the main thread.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "carsCell", for: indexPath) as! CarsDetailTableViewCell
// Get valid string
let string = self.arrAutos[indexPath.row].RutaImagen
//print(string)
cell.lblBrand.text = self.arrAutos[indexPath.row].Marca
cell.lblPrice.text = self.arrAutos[indexPath.row].Precio
cell.lblModel.text = self.arrAutos[indexPath.row].Modelo
let url = URL(string: string)
if url != nil {
DispatchQueue.global().async {
let data = try? Data(contentsOf: url!)
DispatchQueue.main.async {
if data != nil {
cell.imgCar.image = UIImage(data:data!)
}
}
}
}
return cell
}
Hope this will work.

Display JSON data in table view

I am not able to display the JSON data on my table view I don't know why. I tried to get the JSON data but I am not able of displaying it on screen on table format.
import UIKit
class User{
var userId : Int
var id : Int
var title : String
var completed : Bool
init (userId : Int , id: Int, title : String, completed : Bool){
self.userId = userId
self.id = id
self.title = title
self.completed = completed
}
}
class ViewController: UITableViewController{
var users = [User]()
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
makeGetCall()
//makePostCall()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
let user = users[indexPath.row]
cell.textLabel?.text = user.title
cell.detailTextLabel?.text = user.title
return cell
}
func makeGetCall(){
// users = []
let todoEndpoint: String = "http://jsonplaceholder.typicode.com/todos"
guard let url = URL(string: todoEndpoint) else {
print("Error: cannot create URL")
return
}
let urlRequest = URLRequest(url: url)
// set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// make the request
let task = session.dataTask(with: urlRequest) {
(data, response, error) in
// check for any errors
guard error == nil else {
print("error calling GET on /todos/1")
print(error!)
return
}
// make sure we got data
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
let todo = try JSONSerialization.jsonObject(with: responseData, options: .mutableLeaves)
as? NSArray
for eachUser in todo! {
let eachUsers = eachUser as! [String : AnyObject]
let userId = eachUsers["userId"] as! Int
let id = eachUsers["id"] as! Int
let title = eachUsers["title"] as! String
let completed = eachUsers["completed"] as! Bool
self.users.append(User(userId: userId, id: id, title: title, completed: completed))
print(eachUser)
DispatchQueue.main.async {
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: self.cellId)
}
}
} catch {
print("error trying to convert data to JSON")
return
}
}
task.resume()
}
func makePostCall() {
let todosEndpoint: String = "https://jsonplaceholder.typicode.com/todos"
guard let todosURL = URL(string: todosEndpoint) else {
print("Error: cannot create URL")
return
}
var todosUrlRequest = URLRequest(url: todosURL)
todosUrlRequest.httpMethod = "POST"
let newTodo: [String: Any] = ["title": "First todo", "completed": false, "userId": 1]
let jsonTodo: Data
do {
jsonTodo = try JSONSerialization.data(withJSONObject: newTodo, options: [])
todosUrlRequest.httpBody = jsonTodo
} catch {
print("Error: cannot create JSON from todo")
return
}
let session = URLSession.shared
let task = session.dataTask(with: todosUrlRequest) {
(data, response, error) in
guard error == nil else {
print("error calling POST on /todos/1")
print(error!)
return
}
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
guard let receivedTodo = try JSONSerialization.jsonObject(with: responseData,options: []) as? [String: Any] else {
print("Could not get JSON from responseData as dictionary")
return
}
print("The todo is: " + receivedTodo.description)
guard let todoID = receivedTodo["id"] as? Int else {
print("Could not get todoID as int from JSON")
return
}
print("The ID is: \(todoID)")
} catch {
print("error parsing response from POST on /todos")
return
}
}
task.resume()
}
}
You need to reload the table after appending the data
DispatchQueue.main.async {
self.tableView.reloadData()
}
And register line should be in viewDidLoad
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: self.cellId)
A good way also is to use
struct User :Decodable{
let userId , id : Int
let title : String
let completed : Bool
}
users = try? JSONDecoder().decode([User].self,from:data)

How to reload data in UITableview after making second Json call Swift 3

I am making JSON request and if its completed so I am navigating page with JSON data to my table view controller and everything works fine but when I am making second call to load more cells I am not able to reload data and here is the code of my table view controller there I am making second call.
var listData:[String] = []
var videoIDData:[String] = []
var valueKeyData:[String] = []
var nextPageToken:String?
var titleNamee:[String] = []
var videoIDD :[String] = []
var valueKeyy :[String] = []
var PrevPageToken:String?
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return listData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "VideoCell", for: indexPath) as! VideoTableViewCell
cell.videoTitle.text = listData[indexPath.row]
let url = NSURL(string: valueKeyData[indexPath.row])
let dataaa = NSData(contentsOf: url! as URL)
let image = UIImage(data: dataaa! as Data)
cell.videoThumnailImageView.image = image
if (indexPath.row == listData.count - 1)
{
makeGetCall()
}
return cell
}
func makeGetCall() {
// Set up the URL request
var pageToken:String = (pageToken)
let todoEndpoint: String = "https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50\(pageToken)&playlistId=\(id)_ItOZ8WBF5_SI_SrSN3_F&\(key)"
guard let url = URL(string: todoEndpoint) else {
print("Error: cannot create URL")
return
}
let urlRequest = URLRequest(url: url)
// set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// make the request
let task = session.dataTask(with: urlRequest) {
(data, response, error) in
// check for any errors
guard error == nil else {
print("error calling GET on /todos/1")
print(error)
return
}
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
guard let jsonObject = try JSONSerialization.jsonObject(with: responseData, options: []) as? [String : AnyObject] else {
print("error trying to convert data to JSON")
return
}
self.PrevPageToken = jsonObject["prevPageToken"] as? String
self.nextPageToken = jsonObject["nextPageToken"] as? String
if let itemsArray = jsonObject["items"] as? [[String:AnyObject]]{
for snippetArray in itemsArray{
if var snippet = snippetArray["snippet"] as? [String : AnyObject]{
if let titleItems = snippet["title"] as? String{
self.titleNamee += [titleItems]
}
if let thumbnail = snippet["thumbnails"] as? [String : AnyObject]{
if let highValue = thumbnail["high"] as? [String : AnyObject]{
if let urlValueKey = highValue ["url"] as? String{
self.valueKeyy += [urlValueKey]
}
}
}
if let resource = snippet["resourceId"] as? [String : AnyObject]{
if let videoId = resource["videoId"] as? String{
// self.videoIDD.append(videoId)
self.videoIDD += [videoId]
}
}
}
}
}
} catch {
print("error trying to convert data to JSON")
return
}
}
task.resume()
DispatchQueue.main.async{
self.tableView.reloadData()
}
}
}
You are reloading tableView at wrong place you need to reload it inside completion block after the for loop because completion block will call async, so remove your current reload code of tableView and put it after the for loop.
for snippetArray in itemsArray{
if var snippet = snippetArray["snippet"] as? [String : AnyObject]{
if let titleItems = snippet["title"] as? String{
self.titleNamee += [titleItems]
}
if let thumbnail = snippet["thumbnails"] as? [String : AnyObject]{
if let highValue = thumbnail["high"] as? [String : AnyObject]{
if let urlValueKey = highValue ["url"] as? String{
self.valueKeyy += [urlValueKey]
}
}
}
if let resource = snippet["resourceId"] as? [String : AnyObject]{
if let videoId = resource["videoId"] as? String{
// self.videoIDD.append(videoId)
self.videoIDD += [videoId]
}
}
}
}
//Reload your table here
DispatchQueue.main.async{
self.tableView.reloadData()
}
Note: Instead of creating multiple array that you are doing currently what you need to do is make one custom class or struct with all these properties and make array of that custom class or struct objects.
Edit: I haven't looked at that you are calling this method in cellForRowAt don't do that as of cell will be reused and that is the reason you UI getting stuck so remove that calling code from cellForRowAt, if you want to make something like pagination than you can use scrollViewDelegate method like this way.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if((scrollView.contentOffset.y + scrollView.frame.size.height) == scrollView.contentSize.height)
{
self.makeGetCall()
}
}
Also it its better that you show some process indicator while you are making API request.

How do I download json file from remote saver and save to bundle for offline access

I am able to download a json file from a server and put it into a TableView each time the App is opened. (see code below)
However, I also want to allow offline access using the last file downloaded. So essentially I am having trouble working out how to save the downloaded file for local access offline. Any help (especially examples) much appreciated.
class KalkanTableController: UITableViewController {
var TableData:Array< String > = Array < String >()
override func viewDidLoad() {
super.viewDidLoad()
get_data_from_url("http://ftp.MY FILE LOCATION/kalkanInfoTest.json")
if let path = Bundle.main.path(forResource: "kalkanInfoTest", ofType: "json") {
do {
let jsonData = try NSData(contentsOfFile: path, options: NSData.ReadingOptions.mappedIfSafe)
do {
let jsonResult: NSDictionary = try JSONSerialization.jsonObject(with: jsonData as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
} catch {}
} catch {}
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return TableData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = TableData[indexPath.row]
return cell
}
func get_data_from_url(_ link:String)
{
let url:URL = URL(string: link)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "GET"
request.cachePolicy = URLRequest.CachePolicy.reloadIgnoringCacheData
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(
data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
return
}
self.extract_json(data!)
})
task.resume()
}
func extract_json(_ data: Data)
{
let json: Any?
do
{
json = try JSONSerialization.jsonObject(with: data, options: [])
}
catch
{
return
}
guard let data_list = json as? NSArray else
{
return
}
if let item_list = json as? NSArray
{
for i in 0 ..< data_list.count
{
if let item_obj = item_list[i] as? NSDictionary
{
if let item_name = item_obj["kalkanInfo"] as? Stri
{
TableData.append(item_name)
print(item_name)
}
}
}
}
DispatchQueue.main.async(execute: {self.do_table_refresh()})
}
func do_table_refresh()
{
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
return
})
}
}
You can try to save your json locally using these methods to save on disk
override func viewDidLoad() {
super.viewDidLoad()
if readKalkanDataFromDisk() {
extract_json(readKalkanDataFromDisk())
} else {
get_data_from_url("http://ftp.MY FILE LOCATION/kalkanInfoTest.json")
}
}
func get_data_from_url(_ link:String)
{
let url:URL = URL(string: link)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "GET"
request.cachePolicy = URLRequest.CachePolicy.reloadIgnoringCacheData
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(
data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
return
}
saveKalkanDataOnDisk(kalkanData: data!)
self.extract_json(data!)
})
task.resume()
}
func saveKalkanDataOnDisk(kalkanData: Data) {
do {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = documentsURL.appendingPathComponent("Kalkan.json")
try kalkanData.write(to: fileURL, options: .atomic)
} catch { }
}
func readKalkanDataFromDisk() -> Data? {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let filePath = documentsURL.appendingPathComponent("Kalkan.json").path
if FileManager.default.fileExists(atPath: filePath), let data = FileManager.default.contents(atPath: filePath) {
return data
}
return nil
}