I'm loading json data from webservice using Alamofire, one of my request return a json like this:
"lorem ipsum...",
"http://site.ed/image.jpg"
Then I create a var texto: [String] = [] to receive all texts.
Now I need to adapt to receive image to show in UITableView
What I already do:
func loadPosts() {
let url = "http://site.ed"
Alamofire.request(.GET, url)
.responseJSON { response in
if let value: AnyObject = response.result.value {
let post = JSON(value)
for (_, subJson) in post {
if(self.verifyUrl(subJson.stringValue)){
print("Valide URL \(subJson.stringValue)")
}
self.texto.append(subJson.stringValue)
}
}
}
}
func verifyUrl (urlString: String?) -> Bool {
if let urlString = urlString {
if let url = NSURL(string: urlString) {
return UIApplication.sharedApplication().canOpenURL(url)
}
}
return false
}
In this print("Valide URL") i need to get the real image and put into a table, how can i do that ?
Why not try some image library like KingFisher
What you need to do is just set imageView with url
imageView.kf_setImageWithURL(NSURL(string: "http://your_image_url.png")!)
Related
I created a reusable Alamofire request which works smoothly. I am trying to get the data from the request Decode it(works fine too) and append it to one of my arrays to display in tableView/collectionView.
I am using MVVM and I append my data in viewModel(you can see below). The thing is I have tableView in my viewController and inside my tableView methods( viewForSupplementaryElementOfKind for instance) the 'stories'(from viewModel) are always empty.
In my viewDidLoad method I call getMainPageData(from viewModel) first and then create my tableView. I assure you the request itself is a success, the only problem is displaying the data.
Please keep in mind that the project has many API calls so I need a solution which will work in all cases when I have to deal with "lists". Thank you in advance
class NetworkManager {
let keychain = KeychainManager()
let base = "SomeBase"
let storageManager = StorageManager()
func setupRequest(path: Paths, method: RequestMethod, body: Encodable? = nil, params: [String: Any]? = nil, header: HeaderType, completion: #escaping((Result<Data,NetworkError>) -> Void)) {
var queries = ""
if let params = params {
queries = params.passingQuery()
}
let url = URL(string: base + path.rawValue + queries)
var request = URLRequest(url: url!)
request.httpMethod = method.rawValue
request.setValue(header.value[0].value, forHTTPHeaderField: "Content-Type")
if let userToken = keychain.getAccessToken(), userToken.count > 0 {
request.setValue("Bearer " + userToken, forHTTPHeaderField: "Authorization")
}
if let body = body {
if let jsonData = body.toJSONData() {
request.httpBody = jsonData
}
}
AF.request(request).validate().responseJSON { response in
if (200...299) ~= response.response?.statusCode ?? -1 {
self.handlingHeaders(response: response)
completion(.success(response.data!))
} else {
do {
if let data = response.data {
let json = try JSONDecoder().decode(ErrorResponse.self, from: data)
completion(.failure(.responseError(json.message)))
}
} catch {
completion(.failure(.serverError))
}
}
}
}
private func handlingHeaders(response: AFDataResponse<Any>) {
let headers = response.response?.headers
if let accessToken = headers?.dictionary["Authorization"] {
keychain.saveToken(token: accessToken)
}
}
}
extension Encodable {
func toJSONData() -> Data? { try? JSONEncoder().encode(self) }
}
var stories = [Story]()
func getMainPageData(completion: #escaping(Result<Void, NetworkError>) -> ()) {
networkManager.setupRequest(path: .mainPageData, method: .get, body: nil, params: nil, header: .application_json) { [self] result in
switch result {
case .success(let data):
do {
let homePageData = try JSONDecoder().decode(MainPageResponse.self, from: data)
stories.append(contentsOf: homePageData.model.stories)
I'm totally new to swift and iOS programming so I'm a little lost on how to do this and even in what files I should be doing this too.
I'm trying to do a http post request to get calendar events and save them in the app to later use and display.
I made a model class with this code.
import UIKit
class Event {
var id: Int
var init_date: String
var end_date: String
var title: String
var description: String
var color_code: String
var all_day: Int
init?(id: Int, init_date: String, end_date: String, title: String, description: String, color_code: String, all_day: Int) {
//Initialization should fail if these are false
if id < 0 || init_date.isEmpty || end_date.isEmpty || title.isEmpty {
return nil
}
//Initialize stored properties
self.id = id
self.init_date = init_date
self.end_date = end_date
self.title = title
self.description = description
self.color_code = color_code
self.all_day = all_day
}
}
But now I don't know what the next step would be. I need this to be downloaded immediately once the app is opened for the first time and not when it's not being opened for the first time. Do I create a new method in the ViewController.swift for the download?
Right now I haven't added anything to the ViewController
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
What should I do next?
At this point you need to create a function that handles the POST request you are making.
Once completed, place this function inside your appDelegate main function didFinishLaunchingWithOptions. This is the function that executes on appStart
On a successful function call save the data (presumably json) into a Global Variable or whatever you need for you app.
TIP:
On you class
class Event: Codable {
}
make sure to add Codable like above
Below is an example of what your post request will look like
func myPostRequest(completionHandler: #escaping (Bool?, String?) -> Void){
guard let url = URL(string:"") else { return }
let parameters = ["": ""]
var request: URLRequest = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("", forHTTPHeaderField: "Authorization")
do {
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard
error == nil
else {
print(error as Any)
return
}
if let httpResponse = response as? HTTPURLResponse {
if (httpResponse.statusCode == 200) {
if let data = data {
let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]]
//print("^^^^^^^^^^^^^^",json)
for x in json ?? [] {
//here is where you will parse your data from the post request
}
completionHandler(true, nil)
return
}
} else {
completionHandler(false, "No Response From Server")
print("Failure response: STATUS CODE != 200")
}
} else {
completionHandler(false, "Database Connection Error")
print("Error \(error!)")
}
}
task.resume()
} catch let error {
completionHandler(false, "failure")
print("POSTERROR: \(error.localizedDescription)")
}
}
I use Alamofire, you can add it to your project via:
Pods
Swift Package Manager
When you add the framework you can use it:
import Alamofire
Then you need to make your class with the protocol Codable to pass the data to your class.
class Event: Codable { }
Then you need to call the url and store the response in a variable:
override func viewDidAppear(_ animated: Bool) {
AF.request("your API rest url").responseData { (resData) in
guard let data = resData.data else { return }//Check if the data is valid
do {
let decoder = JSONDecoder()//Initialize a Json decoder variable
let decodedData = try decoder.decode(Event.self, from: data)//Decode the response data to your decodable class
//Print the values
print(decodedData.headers)
print(decodedData.id)
print(decodedData.init_date)
print(decodedData.end_date)
} catch {
print(error.localizedDescription)
}
}
}
Using regular expressions, I extracted the html string, the desired images and put them in an array.
What does the array look like at the moment:
var collectionPhotoLinks = [
"http:static-cdn3.vigbo.tech/u65463/78125/blog/5162255/4423863/57114004/500-codleto-889a051259ba894edee37b8da63ddbe9.jpg",
"http:static-cdn3.vigbo.tech/u65463/78125/blog/5162255/4423863/57114004/500-codleto-9516ab3514e07bde176f117e70c7ba85.jpg"]
I can upload one image
func downloadImages () {
guard let url = URL(string:
"http:static-cdn3.vigbo.tech/u65463/78125/blog/5162255/4423863/57114004/500-codleto-889a051259ba894edee37b8da63ddbe9.jpg")
else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let data = data, let image = UIImage(data: data) {
DispatchQueue.main.async {
self.imageView.image = image
}
}
} .resume()
}
If the "url" is replaced with an array "collectionPhotoLinks", then respectively xcode requires string not array
So the question follows, how do I load all the images from the array? In the future I will need to send them to tableview or collectionview, but I would like to resolve the issue with this first.
The usual is that you download it inside cellForRowAt with say SDWebImage , but if you need to pre-download all then you can try
let arr = ["url1","urls"]
func downloadImages () {
let g = DispatchGroup()
for item in arr {
guard let url = URL(string:item)
else { return }
g.enter()
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let data = data, let image = UIImage(data: data) {
//
}
g.leave()
} .resume()
}
g.notify(queue: .main) {
// done
}
}
if let jsonObj = jsonObj as? [String: Any],
let weatherDictionary = jsonObj["weather"] as? [String: Any],
let weather = weatherDictionary["description", default: "clear sky"] as?
NSDictionary {
print("weather")
DispatchQueue.main.async {
self.conditionsLabel.text = "\(weather)"
}
}
// to display weather conditions in "name" from Open Weather
"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}]
//No errors, but code is not printing or displaying in App.
I'm not sure how to help with your exact question unless you can provide some more code for context. However,
You might try using the built-in decoding that comes with Swift 4. Check it out here. Basically, you make a class that models the response object, like this:
struct Weather: Decodable {
var id: Int
var main: String
var description: String
var icon: String
}
Then decode it like so:
let decoder = JSONDecoder()
let weather = try decoder.decode(Weather.self, from: jsonObj)
And it magically decodes into the data you need! Let me know if that doesn't work, and comment if you have more code context for your problem that I can help with.
I put the complete demo here to show how to send a HTTP request and parse the JSON response.
Note, Configure ATS if you use HTTP request, rather than HTTPS request.
The demo URL is "http://samples.openweathermap.org/data/2.5/forecast?q=M%C3%BCnchen,DE&appid=b6907d289e10d714a6e88b30761fae22".
The JSON format is as below, and the demo shows how to get the city name.
{
cod: "200",
message: 0.0032,
cnt: 36,
list: [...],
city: {
id: 6940463,
name: "Altstadt",
coord: {
lat: 48.137,
lon: 11.5752
},
country: "none"
}
}
The complete demo is as below. It shows how to use URLSessionDataTask and JSONSerialization.
class WeatherManager {
static func sendRequest() {
guard let url = URL(string: "http://samples.openweathermap.org/data/2.5/forecast?q=M%C3%BCnchen,DE&appid=b6907d289e10d714a6e88b30761fae22") else {
return
}
// init dataTask
let dataTask = URLSession.shared.dataTask(with: url) { (data, response, error) in
let name = WeatherManager.cityName(fromWeatherData: data)
print(name ?? "")
}
// send the request
dataTask.resume()
}
private static func cityName(fromWeatherData data: Data?) -> String? {
guard let data = data else {
print("data is nil")
return nil
}
do {
// convert Data to JSON object
let jsonObject = try JSONSerialization.jsonObject(with: data, options: [])
print(jsonObject)
if let jsonObject = jsonObject as? [String: Any],
let cityDic = jsonObject["city"] as? [String: Any],
let name = cityDic["name"] as? String {
return name
} else {
return nil
}
} catch {
print("failed to get json object")
return nil
}
}
}
To parse json i have following function
func single_news(userid: Int) {
var request = URLRequest(url: URL(string: news_url)!)
request.httpMethod = "POST"
//Pass your parameter here
let postString = "userid=\(userid)"
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print("error=(error)")
return
}
let json: Any?
do
{
json = try JSONSerialization.jsonObject(with: data, options: [])
print("abcnews")
//here is your JSON
print(json)
let jsonValue : NSDictionary = json as! NSDictionary
self.results = jsonValue.object(forKey: "data") as! [[String:String]]
self.DiscoveryNewsTableView.delegate = self
self.DiscoveryNewsTableView.dataSource = self
self.DiscoveryNewsTableView.reloadData()
// let _ = getData.shared.getDataForTableView(dict: json)
}
catch
{
return
}
guard let server_response = json as? NSDictionary else
{
return
}
}
task.resume()
}
To get data the class is created
class getData: NSObject {
var descriptionn : String = ""
var image : String = ""
// static let shared = getData()
func getDataForTableView(results: [[String:String]], index : Int){
var productArray = [String:String]()
productArray = results[index]
descriptionn = productArray["description"]!
image = productArray["images"]!
}
}
To display data in table view
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "discoveryNewscell") as! DiscoveryNewsTableViewCell
// if results.count > 0{
classObject.getDataForTableView(results: results, index: indexPath.row)
cell.sneakerImageView.image=filteredsneakernews[indexPath.row].image
print("abc image"+classObject.image)
cell.newsTitle.text = classObject.descriptionn
// }
return cell
}
How to display the image .Image(classObject.image) in string format how to display image view on table view ?you can download the code from this link .https://drive.google.com/file/d/1bVQsuSQINSa6YRwZe2QwEjPpU_m7S3b8/view?usp=sharing
You're wanting to display an image but you only have the URL to that image and not the image itself so you'll need to download it, then display it. I have a class I use a lot that allows you to simply call one line to download AND cache the image so you'll be able to do something like this:
classObject.getDataForTableView(results: results, index: indexPath.row)
let image_url = filteredsneakernews[indexPath.row].image
cell.sneakerImageView.loadImageUsingCacheWithUrlString(urlString: image_url!)
To do this, you'll have to copy the class below and inside your cell class, you’ll want to change the imageView type from a standard UIImageView to a CustomImageView for example:
let imageView: CustomImageView!
//
import UIKit
let imageCache = NSCache<NSString, UIImage>()
class CustomImageView: UIImageView {
var imageUrlString: String?
func loadImageUsingCacheWithUrlString(urlString: String) {
imageUrlString = urlString
if let cachedImage = imageCache.object(forKey: urlString as NSString) {
self.image = cachedImage
return
}
self.image = nil
let url = URL(string: urlString)
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
if error != nil { return }
DispatchQueue.main.async {
if let downloadedImage = UIImage(data: data!) {
if self.imageUrlString == urlString {
if self.imageUrlString != "" {
self.image = downloadedImage
} else {
self.image = nil
}
}
imageCache.setObject(downloadedImage, forKey: urlString as NSString)
}
}
}).resume()
}
}