Jorge Luis Jiménez Jorge Luis Jiménez - 2 months ago 11
iOS Question

Correct way to update UIPageViewController after an API request

I have a problem when I try to do a GET request with Alamofire. In my viewDidLoad method. I already checked that my request works and my arrays are populated but when I run the code I get a fatal error: Index out of range because when I called getViewControllerAtIndex method the arrays are empty at that moment.
How can I do if I want to create my UIPageViewController with the data coming from my API.
This my code in ViewController.swift

import UIKit
import Foundation
import Alamofire


class ViewController: UIPageViewController, UIPageViewControllerDataSource {

var arrPageTitle = [String]()
var arrPagePhoto = [String]()

override func viewDidLoad() {
super.viewDidLoad()

let guidesEndpoint: String = "http://testapi.eu-gb.mybluemix.net/guides"

Alamofire.request(guidesEndpoint).responseJSON { (response) in

if let JSON = response.result.value {

//print("JSON: \(JSON)")

if let guides = JSON as? [String : AnyObject] {

if let guideResourceList = guides["_embedded"] as? [String : AnyObject] {

//print(guideResourceList)
if let guidesArray = guideResourceList["guideResourceList"] as? [AnyObject] {

for guide in guidesArray {

if let guideGuide = guide["guide"] as? [String : AnyObject] {
if let textItem = guideGuide["text"] as? String {
// Populate arrPageTitle
self.arrPageTitle.append(textItem)
print(textItem)
}

}

if let guideLinks = guide["_links"] as? [String : AnyObject] {
if let linksObj = guideLinks["imageUrl"] as? [String : String] {
if let link = linksObj["href"] {
// Populate arrPagePhoto
self.arrPagePhoto.append(link)
print(link)
}
}
}
}
}
}
}
}
}

self.dataSource = self
self.setViewControllers([getViewControllerAtIndex(0)] as [UIViewController], direction: UIPageViewControllerNavigationDirection.forward, animated: false, completion: nil)
}

// Use this method to return to previous view. In this I have put a condition such that if it is first view then return nil otherwise return ViewController.
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {

let pageContent: PageContentViewController = viewController as! PageContentViewController
var index = pageContent.pageIndex
if index == 0 || index == NSNotFound {
return nil
}
index -= 1

return getViewControllerAtIndex(index)
}

// MARK:- UIPageViewControllerDataSource Methods

// Use this method to return next view. In this I have put a condition such that if it is last view then return nil otherwise return ViewController.
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {

let pageContent: PageContentViewController = viewController as! PageContentViewController
var index = pageContent.pageIndex
if index == NSNotFound { return nil }
index += 1
if index == arrPageTitle.count { return nil }

return getViewControllerAtIndex(index)
}



// MARK:- Custom Methods

func getViewControllerAtIndex(_ index: Int) -> PageContentViewController {

// Create a new view controller and pass suitable data.
let pageContentViewController = self.storyboard?.instantiateViewController(withIdentifier: "PageContentViewController") as! PageContentViewController

// fatal error: Index out of range
pageContentViewController.strTitle = "\(arrPageTitle[index])"
pageContentViewController.strPhotoName = "\(arrPagePhoto[index])"
pageContentViewController.pageIndex = index
pageContentViewController.numberOfPages = arrPagePhoto.count
print("\(arrPagePhoto.count)")

return pageContentViewController

}

}


and this is my PageContentViewController.swift

import UIKit

class PageContentViewController: UIViewController {

@IBOutlet weak var lblTitle: UILabel!
@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var pageControl: UIPageControl!

var pageIndex: Int = 0
var strTitle: String!
var strPhotoName: String!
var numberOfPages: Int!

override func viewDidLoad() {
super.viewDidLoad()

imageView.image = UIImage(named: strPhotoName)
lblTitle.text = strTitle
pageControl.currentPage = pageIndex
pageControl.numberOfPages = numberOfPages

}

}


any ideas???

Answer
self.setViewControllers([getViewControllerAtIndex(0)] as [UIViewController], direction: UIPageViewControllerNavigationDirection.forward, animated: false, completion: nil)

Needs to be called in the alamofire callback not outside of that scope. The array you are populating from (arrPageTitle) would be empty at the point you are calling setViewControllerAtIndex and you are accessing values in the array with index giving you the out of bounds error.

You should be calling the function in the end of this if-scope

if let guidesArray = guideResourceList["guideResourceList"] as? [AnyObject] {
     //parsing code...
    self.setViewControllers([getViewControllerAtIndex(0)] as [UIViewController], direction: UIPageViewControllerNavigationDirection.forward, animated: false, completion: nil)

}

or inside the for loop, depending on what you want to do.

Comments