I am making app with swift and getting data from my Wordpress Website and using Rest Api to display data in CollectionView, all works fine but the problem is when i add new posts in website it doesn’t automatically shows in app , and when i refresh data then also can’t see the newest post . this is my code on refresh
#objc
private func didPullToRefresh(_ sender: Any) {
sortBy = "&orderby=date"
page = 1
self.fetchPostData { (posts) in
self.newsData = posts }
SVProgressHUD.show()
self.collectionView.setContentOffset(CGPoint(x:0,y:0), animated: true)
refreshControl.endRefreshing()
}
and this is the code to fetch data
func fetchPostData(completionHandler: #escaping ([Post]) -> Void ) {
DispatchQueue.main.asyncAfter(deadline: .now()) {
SVProgressHUD.show()
}
let url = URL(string: "https://www.pbkalam.com/wp-json/wp/v2/posts/?page=\(page)\(sortBy)" )!
print(url)
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else {return}
do {
let postsData = try JSONDecoder().decode([Post].self, from: data)
completionHandler(postsData)
DispatchQueue.main.async {
self.collectionView.isUserInteractionEnabled = true
self.collectionView.reloadData()
SVProgressHUD.dismiss()
}
}
catch {
let error = error
print(String(describing: error))
SVProgressHUD.dismiss()
}
}
task.resume()
}
Here is my cell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "postcell", for: indexPath) as! ViewCell
cell.setup(with: newsData[indexPath.row-(indexPath.row/4)])
when i use
sortBy = "&orderby=rand"
then it loads the random posts but not the newest.. please help
i have tried this code
#objc
private func didPullToRefresh(_ sender: Any) {
sortBy = "&orderby=date&order=desc"
page = 1
self.fetchPostData { (posts) in
self.newsData = posts }
self.collectionView.reloadData()
SVProgressHUD.show()
self.collectionView.setContentOffset(CGPoint(x:0,y:0), animated: true)
refreshControl.endRefreshing()
}
Related
My JSON request works fine but every time I do a request the network monitor shows me there are two requests made. What is wrong here? How can I optimize it to just one?
I tried to find the reason for this and it looks like it has to happen here with this request.
struct networkGoogleApi {
static var testVariable = [Items]()
static var gewählteKategorie = 0
mutating func fetchPoi(geoCordinate: String) {
var baseUrlToApiConnection = ""
baseUrlToApiConnection = {
switch networkGoogleApi.gewählteKategorie {
case 0:
return
"first url"
case 1:
return "other url"
default:
return "error"
}
}()
let urlString = "\(baseUrlToApiConnection)?at=\(geoCordinate)"
print(urlString)
performRequest(urlString: urlString)
}
func performRequest(urlString: String) {
if let url = URL(string: urlString) {
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { data, response, error in
if error != nil {
print(error!)
return
}
if let safeData = data {
parseJSON(poiData: safeData)
}
}
//4. Start the task
task.resume()
}
}
}
func parseJSON(poiData: Data) {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(POIData.self, from: poiData)
networkGoogleApi.testVariable = decodedData.items
//print(networkGoogleApi.testVariable)
} catch {
print(error)
}
}
Breakpoint Event added.
For some reason the JSON object from parsing doesnt update after network calls to and api we built. I check the endpoint and now for a fact it updates right away. I have a timer being called every 10 sec to make the call but the parsed json doesnt update until after a minute or so. I have tried putting it on the main thread and that still doesnt work. Here is my code:
#objc func getLaunches() {
let simulator = UserDefaults.standard.string(forKey: self.launchSimulator)
if(simulator == self.password){
print("they are the same")
}
guard let launchUrl = URL(string: launchesURL) else {
return
}
let request = URLRequest(url: launchUrl)
DispatchQueue.main.async { [weak self] in
let task = URLSession.shared.dataTask(with: request, completionHandler: {
(data, response, error) -> Void in
if let error = error {
print(error)
return
}
// Parse JSON data
if let data = data {
self?.launches.removeAll()
self?.launches = (self!.parseJsonData(data: data))
let nextlaunch = self?.launches[0]
// Reload table view
self?.hours = nextlaunch?.time
self?.yearMonth = nextlaunch?.date
var fulltime = self?.yearMonth
fulltime!.insert("-", at: fulltime!.index(fulltime!.startIndex, offsetBy: 4))
fulltime!.insert("-", at: fulltime!.index(fulltime!.startIndex, offsetBy: 7))
fulltime = fulltime! + " "
fulltime = fulltime! + self!.hours
let fullFormatter = DateFormatter()
fullFormatter.dateFormat = "YYYY-MM-dd HH:mm"
fullFormatter.timeZone = TimeZone(abbreviation: "EST")
self?.launchDate = fullFormatter.date(from: fulltime!)
self?.getCountdown()
}
})
task.resume()
}
}
//parse launch info from json to dictionary into launches object
func parseJsonData(data: Data) -> [NextLaunch] {
var launches = [NextLaunch]()
do {
let jsonResult = try JSONSerialization.jsonObject(with: data, options:
JSONSerialization.ReadingOptions.allowFragments) as? NSDictionary
let jsonLaunches = jsonResult?["launches"] as! [NSDictionary]
for jsonLaunch in jsonLaunches {
let launch = NextLaunch()
launch.date = jsonLaunch["date"] as! String
launch.time = jsonLaunch["time"] as! String
if(launch.time == ""){
launch.time = "00:00"
}
launch.mission = jsonLaunch["mission"] as! String
launch.launchpad = jsonLaunch["launch_pad"] as! String
launch.image = jsonLaunch["image"] as! String
launch.delay = jsonLaunch["delayed"] as! String
//show delay image if it is delayed
if(launch.delay == "1"){
self.delayed()
}else{
self.notDelayed()
}
launches.append(launch)
}
} catch {
print(error)
}
return launches
}
You need
DispatchQueue.main.async {
self?.getCountdown()
}
As the response of URLSession.shared.dataTask(with: occurs in a background thread
The code in my viewcontroller-class is executed before the JSON-download-process is ready even though there is a completion handler in the func for downloading JSON an a DispatchGroup(). I store the JSON-data in an array called "fetchedModules" and this is filled with 11 items in this case. Why does this happen?
result in console:
---> in Class PostCell - func numberOfSections: 0
JSON call finished
// ViewController
override func viewDidLoad() {
super.viewDidLoad()
let group = DispatchGroup()
group.enter()
self.fetchJSON()
// here calling downloadJSONasync
group.leave()
group.notify(queue: .main) {
print("JSON call finished")
}
...
// networkService with completion
func downloadJSONasync(searchItem: String, completion: #escaping ([NSDictionary]) -> Void) {
//request.cachePolicy = URLRequest.CachePolicy.reloadIgnoringLocalCacheData
request.httpMethod = "GET"
let configuration = URLSessionConfiguration.default
//let session = URLSession(configuration: configuration, delegate: nil)
let session = URLSession(configuration: configuration)
let task = session.dataTask(with: request, completionHandler: {(data, response, error) in
guard let data = data, error == nil else { return }
if (error != nil) {
print("error!")
}
else{
do {
let fetchedData = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as! [NSDictionary]
completion(fetchedData)
}
catch {
print("error")
}
}
})
task.resume()
}
// call in viewController
override func numberOfSections(in tableView: UITableView) -> Int {
print("---> in Class PostCell - func numberOfSections: \(String(describing: fetchedModules.count))")
return fetchedModules.count
// code of fetchJSON
func fetchJSON()
{
let baseurl = AppConstants.Domains.baseurl // https://m.myapp2go.de
let compositURL = baseurl + "getmodules_noItems.php?id=\(AppConstants.appString.startString)"
let encodedUrl : String! = compositURL.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) // remove the spaces in the url string for safty reason
let JSONurl = URL(string: encodedUrl)! // convert the string into url
var JSONrequest = URLRequest(url: JSONurl) // make request
JSONrequest.httpMethod = "GET"
//JSONrequest.cachePolicy = .reloadIgnoringCacheData
let networkService = NetworkService(request: JSONrequest)
networkService.downloadJSONasync(searchItem: AppConstants.appString.startString, completion: { (fetchedData) in
fetchedModules.removeAll()
DispatchQueue.main.async {
for eachFetchedModul in fetchedData {
let eachModul = eachFetchedModul
if
let custid = eachModul["custid"] as? String,
let modulcat = eachModul["modulcat"] as? String,
let modulname = eachModul["modulname"] as? String,
let comment = eachModul["comment"] as? String
{
fetchedModules.append(CModules(custid: custid, modulcat: modulcat, modulname: modulname, comment: comment))
print(custid)
print(modulcat)
print(modulname)
print(comment)
print("---------------------")
}
}// for end
// ... somehow set data source array for your table view
self.tableView.reloadData()
}// dispatch
}
)} // func end
Because fetchJSON returns immediately, before the JSON is downloaded. The effect is that the DispatchGroup is entereed and left right away, without waiting for the JSON:
group.enter()
self.fetchJSON() // returns immediately
group.leave() // the JSON has yet to be downloaded
To wait until the JSON has arrived, add a completion handler to fetchJSON:
override func viewDidLoad() {
group.enter()
self.fetchJSON {
group.notify(queue: .main) {
print("JSON call finished")
}
group.leave()
}
}
// Change the data type of the completion handler accordingly
func fetchJSON(completionHandler: #escaping (Data?) -> Void) {
// ...
networkService.downloadJSONasync(searchItem: AppConstants.appString.startString) { fetchedData in
defer { completionHandler(fetchedData) }
// ...
}
)
Using defer ensures that the completion handler will always be called, no matter how the outer closure returns. I'm not clear why you use a DispatchGroup here as there is no waiting, but I kept it in place to answer your question.
Your table view doesn't have any data from the beginning because any data hasn't been fetched yet. So it's ok, that table view has no cells. You just need to reloadData of your table view since now you appended elements to table view's data source array and now you should show this data.
Please, don't use DispatchGroup for this, just use your completion parameter of your method and inside of the completion closure after data are received set data source array for table view and then ... reload data of table view
downloadJSONasync(searchItem: "someString") { dictionary in
DispatchQueue.main.async { // don't forget that your code doesn't run on the main thread
// ... somehow set data source array for your table view
self.tableView.reloadData()
}
}
Note that you should avoid using NSDictonary and you should rather use Dictionary. Also from Swift 4+ you can use Codable instead of JSONSerialization.
I've been trying to figure out why my JSON data did not get appended into my global variables. I know that it is because it takes time for my functions to get fetch and process the JSON data. So I've tried to using a completion block to check to see if there's anything in it. However, I still got nothing inside. I am wondering where am I doing it worng?
Here's the code:
#IBAction func dismissButton(_ sender: UIButton) {
yelpAPIRequest(completion: {
print("WE are done")
print(self.businessName)
})
}
func yelpAPIRequest(completion:#escaping ()->()){
let coordinate = YLPCoordinate(latitude: userLatitude , longitude: userLongitude)
let query = YLPQuery(coordinate: coordinate)
query.term = "dessert"
query.limit = 5
query.radiusFilter = 16094
YLPClient.authorize(withAppId: clientId, secret: clientSecret).flatMap { client in
client.search(withQuery: query)
}.onSuccess { search in
if search.businesses.isEmpty == false {
let topBusiness = search.businesses
for i in topBusiness{
print(i.identifier)
self.retrieveBusinessFromAPI(id: i.identifier)
}
completion()
} else {
print("No businesses found")
}
}.onFailure { error in
print("Search errored: \(error)")
}
}
func retrieveBusinessFromAPI(id: String){
let url = URL(string: "https://api.yelp.com/v3/businesses/\(id)")
var request: URLRequest = URLRequest(url: url!)
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
let businessData = JSON(data)
self.businessName.append(businessData["name"].string)
self.businessYelpURL.append(businessData["url"].string)
self.businessImageURL.append(businessData["image_url"].string)
self.businessIsOpenNow.append(businessData["hours"][0]["is_open_now"].bool)
self.businessPrice.append(businessData["price"].string)
self.businessRating.append(businessData["rating"].int)
self.businessAddress.append([businessData["location"]["display_address"].string])
})
task.resume()
}
EDIT: Yes ive looked at other posts about this same topic but the makority of them all pointed towards the completion: and I've done that but my data is still not saved
Here is Stripe's example code for retrieving a customer (https://github.com/stripe/stripe-ios/blob/master/Example/Stripe%20iOS%20Example%20(Simple)/MyAPIClient.swift):
#objc func retrieveCustomer(_ completion: #escaping STPCustomerCompletionBlock) {
guard let key = Stripe.defaultPublishableKey() , !key.contains("#") else {
let error = NSError(domain: StripeDomain, code: 50, userInfo: [
NSLocalizedDescriptionKey: "Please set stripePublishableKey to your account's test publishable key in CheckoutViewController.swift"
])
completion(nil, error)
return
}
guard let baseURLString = baseURLString, let baseURL = URL(string: baseURLString) else {
// This code is just for demo purposes - in this case, if the example app isn't properly configured, we'll return a fake customer just so the app works.
let customer = STPCustomer(stripeID: "cus_test", defaultSource: self.defaultSource, sources: self.sources)
completion(customer, nil)
return
}
let path = "/customer"
let url = baseURL.appendingPathComponent(path)
let request = URLRequest.request(url, method: .GET, params: [:])
let task = self.session.dataTask(with: request) { (data, urlResponse, error) in
DispatchQueue.main.async {
let deserializer = STPCustomerDeserializer(data: data, urlResponse: urlResponse, error: error)
if let error = deserializer.error {
completion(nil, error)
return
} else if let customer = deserializer.customer {
completion(customer, nil)
}
}
}
task.resume()
}
Stripe has a customer deserializer that specifies that "STPCustomerDeserializer expects the JSON response to be in the exact same format as the Stripe API." The Stripe API is here in Nodejs:
// Retrieve Stripe Customer
app.get('/customer', function(request, response) {
// Load the Stripe Customer ID for your logged in user
var customer = 'cus_abc...';
stripe.customers.retrieve(customerId, function(err, customer) {
if (err) {
response.status(402).send('Error retrieving customer.');
} else {
response.json(customer);
}
});
The response I get is an error: The data could not be read because it isn't in the correct format. I think it wants me to return JSON but I tried that several different ways to no avail. Such as:
func retrieveCustomer(_ completion: #escaping STPCustomerCompletionBlock) {
guard let key = Stripe.defaultPublishableKey() , !key.contains("#") else {
let error = NSError(domain: StripeDomain, code: 50, userInfo: [
NSLocalizedDescriptionKey: "Set PubKey"])
completion(nil, error)
return
}
guard let baseURLString = baseURLString, let baseURL = URL(string: baseURLString) else {
let customer = STPCustomer(stripeID: "", defaultSource: self.defaultSource, sources: self.sources)
completion(customer, nil)
return
}
let path = "/customer"
let url = baseURL.appendingPathComponent(path)
let request = URLRequest.request(url, method: .GET, params: [:])
let task = self.session.dataTask(with: request) { (data, urlResponse, error) in
DispatchQueue.main.async {
let deserializer = STPCustomerDeserializer(data: data, urlResponse: urlResponse, error: error)
if let error = deserializer.error {
completion(nil, error)
return
} else if let customer = deserializer.customer {
do {
let parser = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as Any
print(parser)
} catch {
print(error)
}
completion(customer, nil)
}
}
}
task.resume()
}
What am I missing!?