I am using Alamofire to download data and parse it with JSON. I know that in order to announce that data is available we should use closures and no the NotificationCenter. I do not understand closures though. How would I use a closure to reload a table view once the request has completed?
Here is the code.
func downloadEvents() {
let coreDataObject = CoreDataMethods()
Alamofire.request(URL(string:URL)!)
.responseJSON { response in
switch response.result {
case .success:
// we create the model object
let downloadedEvent = EventModel()
/* we have new data remove old data from core data*/
coreDataObject.deleteData(entityArgument: "Event")
events.removeAll() // remove the data from array this is no longer needed FIX
for JSONData in response.result.value as! [Dictionary<String, Any>] {
/* get the data from JSON and store in the event model*/
downloadedEvent.eTitle = JSONData[downloadedEvent.titleString] as? String
downloadedEvent.eDate = JSONData[downloadedEvent.dateString] as? String
downloadedEvent.eDescription = JSONData[downloadedEvent.descriptionString] as? String
downloadedEvent.eLocation = JSONData[downloadedEvent.locationline1String] as? String
downloadedEvent.eLocation2 = JSONData[downloadedEvent.locationline2String] as? String
/* if the event has an image save the url*/
if let image = JSONData[downloadedEvent.imageString] as? String {
downloadedEvent.eImageURL = image
} else {
/* else we save the default image url */
downloadedEvent.eImageURL = downloadedEvent.defaultImageURL
}
coreDataObject.save(eventParam: downloadedEvent)
}
/* post notification to reload table view FIX */
NotificationCenter.default.post(name: RELOAD_NOTIFICATION, object: nil)
case .failure(let error):
print("ALAMO REQUEST FIALED: \(error)")
}
}
}
Here is the downloadEvents function with the ability to notify the caller it was successful:
func downloadEvents(completion: #escaping (Bool, String?)-> Void) {
let coreDataObject = CoreDataMethods()
Alamofire.request(URL(string:URL)!)
.responseJSON { response in
switch response.result {
case .success:
// we create the model object
let downloadedEvent = EventModel()
/* we have new data remove old data from core data*/
coreDataObject.deleteData(entityArgument: "Event")
events.removeAll() // remove the data from array this is no longer needed FIX
for JSONData in response.result.value as! [Dictionary<String, Any>] {
/* get the data from JSON and store in the event model*/
downloadedEvent.eTitle = JSONData[downloadedEvent.titleString] as? String
downloadedEvent.eDate = JSONData[downloadedEvent.dateString] as? String
downloadedEvent.eDescription = JSONData[downloadedEvent.descriptionString] as? String
downloadedEvent.eLocation = JSONData[downloadedEvent.locationline1String] as? String
downloadedEvent.eLocation2 = JSONData[downloadedEvent.locationline2String] as? String
/* if the event has an image save the url*/
if let image = JSONData[downloadedEvent.imageString] as? String {
downloadedEvent.eImageURL = image
} else {
/* else we save the default image url */
downloadedEvent.eImageURL = downloadedEvent.defaultImageURL
}
coreDataObject.save(eventParam: downloadedEvent)
}
completion(true, nil)
/* post notification to reload table view FIX */
//NotificationCenter.default.post(name: RELOAD_NOTIFICATION, object: nil)
case .failure(let error):
print("ALAMO REQUEST FIALED: \(error)")
completion(false, "ALAMO REQUEST FIALED: \(error)")
}
}
}
You would then call the function like this:
func reloadTable(){
downloadEvents { (success, errMsg) in
if success{
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
else{
let alertMessage: String
if let err = errMsg{
alertMessage = err
}
else{
alertMessage = "An unknown error occurred."
}
let alert = UIAlertController.init(title: "Request Failed", message: alertMessage, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.cancel, handler: nil))
DispatchQueue.main.async {
self.present(alert, animated: true, completion: nil)
}
}
}
}
Related
UPDATED WITH PROPOSED SOLUTION AND ADDITIONAL QUESTION
I'm officially stuck and also in callback hell. I have a call to Firebase retrieving all articles in the FireStore. Inside each article object is a an Image filename that translates into a storage reference location that needs to be passed to a function to get the absolute URL back. I'd store the URL in the data, but it could change. The problem is the ArticleListener function is prematurely returning the closure (returnArray) without all the data and I can't figure out what I'm missing. This was working fine before I added the self.getURL code, but now it's returning the array back empty and then doing all the work.
If anyone has some bonus tips here on chaining the methods together without resorting to PromiseKit or GCD that would be great, but open to all suggestions to get this to work as is
and/or refactoring for more efficiency / readability!
Proposed Solution with GCD and updated example
This is calling the Author init after the Article is being created. I am trying to transform the dataDict dictionary so it get's used during the Author init for key ["author"]. I think I'm close, but not 100% sure if my GCD enter/leave calls are happening in the right order
public func SetupArticleListener(completion: #escaping ([Article]) -> Void) {
var returnArray = [Article]()
let db = FIRdb.articles.reference()
let listener = db.addSnapshotListener() { (querySnapshot, error) in
returnArray = [] // nil this out every time
if let error = error {
print("Error in setting up snapshot listener - \(error)")
} else {
let fireStoreDispatchGrp = DispatchGroup() /// 1
querySnapshot?.documents.forEach {
var dataDict = $0.data() //mutable copy of the dictionary data
let id = $0.documentID
//NEW EXAMPLE WITH ADDITIONAL TASK HERE
if let author = $0.data()["author"] as? DocumentReference {
author.getDocument() {(authorSnapshot, error) in
fireStoreDispatchGrp.enter() //1
if let error = error {
print("Error getting Author from snapshot inside Article getDocumentFunction - leaving dispatch group and returning early")
fireStoreDispatchGrp.leave()
return
}
if let newAuthor = authorSnapshot.flatMap(Author.init) {
print("Able to build new author \(newAuthor)")
dataDict["author"] = newAuthor
dataDict["authorId"] = authorSnapshot?.documentID
print("Data Dict successfully mutated \(dataDict)")
}
fireStoreDispatchGrp.leave() //2
}
}
///END OF NEW EXAMPLE
if let imageURL = $0.data()["image"] as? String {
let reference = FIRStorage.articles.referenceForFile(filename: imageURL)
fireStoreDispatchGrp.enter() /// 2
self.getURL(reference: reference){ result in
switch result {
case .success(let url) :
dataDict["image"] = url.absoluteString
case .failure(let error):
print("Error getting URL for author: \n Error: \(error) \n forReference: \(reference) \n forArticleID: \(id)")
}
if let newArticle = Article(id: id, dictionary: dataDict) {
returnArray.append(newArticle)
}
fireStoreDispatchGrp.leave() ///3
}
}
}
//Completion block
print("Exiting dispatchGroup all data should be setup correctly")
fireStoreDispatchGrp.notify(queue: .main) { ///4
completion(returnArray)
}
}
}
updateListeners(for: listener)
}
Original Code
Calling Setup Code
self.manager.SetupArticleListener() { [weak self] articles in
print("πππππππIn closure function to update articlesπππππππ")
self?.articles = articles
}
Article Listener
public func SetupArticleListener(completion: #escaping ([Article]) -> Void) {
var returnArray = [Article]()
let db = FIRdb.articles.reference()
let listener = db.addSnapshotListener() { (querySnapshot, error) in
returnArray = [] // nil this out every time
if let error = error {
printLog("Error retrieving documents while adding snapshotlistener, Error: \(error.localizedDescription)")
} else {
querySnapshot?.documents.forEach {
var dataDict = $0.data() //mutable copy of the dictionary data
let id = $0.documentID
if let imageURL = $0.data()["image"] as? String {
let reference = FIRStorage.articles.referenceForFile(filename: imageURL)
self.getURL(reference: reference){ result in
switch result {
case .success(let url) :
print("Success in getting url from reference \(url)")
dataDict["image"] = url.absoluteString
print("Dictionary XFORM")
case .failure(let error):
print("Error retrieving URL from reference \(error)")
}
if let newArticle = Article(id: id, dictionary: dataDict) {
printLog("Success in creating Article with xformed url")
returnArray.append(newArticle)
}
}
}
}
print("πππππππ sending back completion array \(returnArray)πππππππ")
completion(returnArray)
}
}
updateListeners(for: listener)
}
GetURL
private func getURL(reference: StorageReference, _ result: #escaping (Result<URL, Error>) -> Void) {
reference.downloadURL() { (url, error) in
if let url = url {
result(.success(url))
} else {
if let error = error {
print("error")
result(.failure(error))
}
}
}
}
You need dispatch group as the for loop contains multiple asynchronous calls
public func SetupArticleListener(completion: #escaping ([Article]) -> Void) {
var returnArray = [Article]()
let db = FIRdb.articles.reference()
let listener = db.addSnapshotListener() { (querySnapshot, error) in
returnArray = [] // nil this out every time
if let error = error {
printLog("Error retrieving documents while adding snapshotlistener, Error: \(error.localizedDescription)")
} else {
let g = DispatchGroup() /// 1
querySnapshot?.documents.forEach {
var dataDict = $0.data() //mutable copy of the dictionary data
let id = $0.documentID
if let imageURL = $0.data()["image"] as? String {
let reference = FIRStorage.articles.referenceForFile(filename: imageURL)
g.enter() /// 2
self.getURL(reference: reference){ result in
switch result {
case .success(let url) :
print("Success in getting url from reference \(url)")
dataDict["image"] = url.absoluteString
print("Dictionary XFORM")
case .failure(let error):
print("Error retrieving URL from reference \(error)")
}
if let newArticle = Article(id: id, dictionary: dataDict) {
printLog("Success in creating Article with xformed url")
returnArray.append(newArticle)
}
g.leave() /// 3
}
}
}
g.notify(queue:.main) { /// 4
print("πππππππ sending back completion array \(returnArray)πππππππ")
completion(returnArray)
}
}
}
updateListeners(for: listener)
}
I need some assistance with my codes. The json below was the original response I got from the postman.
OLD Format
{
"totalCreditedAmount": 2898.3000,
"periodId": 566,
"periodDate": "4/26/2019"
}
So I created the API service code below. It runs smoothly.
APIService
struct DoctorLatestCreditedAmount {
typealias getLatestCreditedAmountTaskCompletion = (_ latestCreditedAmount: CurrentRemittance?, _ error: NetworkError?) -> Void
static func getLatestCreditedAmount(doctorNumber: String, completion: #escaping getLatestCreditedAmountTaskCompletion) {
guard let latestCreditedAmountURL = URL(string: "\(Endpoint.LatestCreditedAmount.latestCreditedAmount)/\(doctorNumber)") else {
completion(nil, .invalidURL)
return
}
let sessionManager = Alamofire.SessionManager.default
sessionManager.session.getAllTasks { (tasks) in
tasks.forEach({ $0.cancel() })
}
Alamofire.request(latestCreditedAmountURL, method: .get, encoding: JSONEncoding.default).responseJSON { (response) in
guard HelperMethods.reachability(responseResult: response.result) else {
completion(nil, .noNetwork)
return
}
guard let statusCode = response.response?.statusCode else {
completion(nil, .noStatusCode)
return
}
switch(statusCode) {
case 200: guard let jsonData = response.data else {
completion(nil, .invalidJSON)
return
}
let decoder = JSONDecoder()
do {
let currentCreditedAmount = try decoder.decode(CurrentRemittance.self, from: jsonData)
completion(currentCreditedAmount, nil)
} catch {
completion(nil, .invalidJSON)
}
case 400: completion(nil, .badRequest)
case 404: completion(nil, .noRecordFound)
default:
print("**UNCAPTURED STATUS CODE FROM (getLatestCreditedAmount)\nSTATUS CODE: \(statusCode)")
completion(nil, .uncapturedStatusCode)
}
}
}
But when the json side had some changes regarding the response. The json format changed and now using the APIService above I encountered error. It says invalidjson since the new json format is below.
NEW Format
{
"responseMessage": "Request successful",
"data": {
"totalCreditedAmount": 2898.3000,
"periodId": 566,
"periodDate": "4/26/2019"
}
}
Edited: getTotalCreditedAmount
var currentRemittance: CurrentRemittance!
func getTotalCreditedAmount(doctorNumber: String) {
windlessSetup()
APIService.DoctorLatestCreditedAmount.getLatestCreditedAmount(doctorNumber: doctorNumber) { (remittanceDetails, error) in
guard let creditedAmountDetails = remittanceDetails, error == nil else {
if let networkError = error {
switch networkError {
case .noRecordFound:
let alertController = UIAlertController(title: βNo Record Foundβ, message: βYou donβt have current payment remittanceβ, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: βOKβ, style: .default))
self.present(alertController, animated: true, completion: nil)
case .noNetwork:
let alertController = UIAlertController(title: βNo Networkβ, message: β\(networkError.rawValue)β, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: βOKβ, style: .default))
self.present(alertController, animated: true, completion: nil)
default:
let alertController = UIAlertController(title: βErrorβ, message: βThere is something went wrong. Please try againβ, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: βOKβ, style: .default))
self.present(alertController, animated: true, completion: nil)
}
}
self.creditedView.windless.end()
self.remittanceView.windless.end()
return
}
self.currentRemittance = creditedAmountDetails
self.showLatestTotalCreditedAmount()
self.creditedView.windless.end()
self.remittanceView.windless.end()
return
}
}
Encountered Error
My problem is, how can I alter my codes for APIService so it will match to the proper NEW json format I got. I am having a hard time since I get to used to pulling the same "OLD" format. I am really new to swift and I really need assistance. Hope you can give me some of your time.
You need
// MARK: - Welcome
struct Root: Codable {
let responseMessage: String
let data: CurrentRemittance
}
// MARK: - DataClass
struct CurrentRemittance: Codable {
let totalCreditedAmount: Double
let periodId: Int
let periodDate: String
}
Decode
let res = try decoder.decode(Root.self, from: jsonData)
print(res.data)
Try the code below, it works to me in array of data:
Alamofire.request(url, method: .get).responseJSON {
response in
if response.result.isSuccess {
let dataJSON = JSON(response.result.value!)
if let datas = dataJSON["data"].arrayObject {
print(datas)
}
}
}
This forEach loop works sometimes and sometimes it skips. I am not sure what I am doing wrong here. The loop will skip the last item and will never exit. So the completion block does not get fired at all.
I am using firebase, Eureka forms and it's ImageRow extension.
I would appreciate some help here.
//MARK: - Get Form Values
var returnedValues: [String: Any] = [:]
fileprivate func getFormValues(values: [String: Any], completion: #escaping ([String:Any])->()) {
if let name = values["name"] as? String,
let description = values["description"] as? String,
let images = values["images"] as? [UIImage],
let category = values["category"] as? String,
let price = values["price"] as? Double,
let deliveryFee = values["deliveryFee"] as? Double,
let deliveryAreas = values["deliveryArea"] as? Set<String>,
let deliveryTime = values["deliveryTime"] as? String {
guard let uid = Auth.auth().currentUser?.uid else { return }
var imagesData = [[String: Any]]()
var counter = 0
images.forEach({ (image) in
let imageName = NSUUID().uuidString
let productImageStorageRef = Storage.storage().reference().child("product_images").child(uid).child("\(imageName).jpg")
var resizedImage = UIImage()
if image.size.width > 800 {
resizedImage = image.resizeWithWidth(width: 800)!
}
if let uploadData = UIImageJPEGRepresentation(resizedImage, 0.5) {
productImageStorageRef.putData(uploadData, metadata: nil, completion: { (metadata, error) in
if error != nil {
print("Failed to upload image: \(error?.localizedDescription ?? "")")
return
}
//Successfully uploaded product Image
print("Successfully uploaded product Image")
if let productImageUrl = metadata?.downloadURL()?.absoluteString {
counter += 1
let imageData: [String: Any] = [imageName: productImageUrl]
imagesData.append(imageData)
if counter == images.count {
let deliveryAreasArr = Array(deliveryAreas)
self.returnedValues = ["name": name, "description": description, "images": imagesData , "category": category, "price": price, "deliveryFee": deliveryFee, "deliveryArea": deliveryAreasArr, "deliveryTime": deliveryTime, "creationDate": Date().timeIntervalSince1970, "userId": uid]
completion(self.returnedValues)
}
}
})
}
})
} else {
let alert = UIAlertController(title: "Missing Information", message: "All fields are required. Please fill all fields.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (_) in
alert.dismiss(animated: true, completion: nil)
}))
UIActivityIndicatorView.stopActivityIndicator(indicator: self.activityIndicator, container: self.activityIndicatorContainer, loadingView: self.activityIndicatorLoadingView)
self.present(alert, animated: true, completion: nil)
}
}
There are a number of if statements inside your for loop that can result in counter not being incremented. If any of these fail then you will never call the completion handler.
I understand that you are using the counter in an attempt to know when all of the asynchronous tasks are complete, but a dispatch group is a better solution for this.
It is also important that your completion handler is called in all paths; such as when the initial guard fails or in the else clause of the initial if - Your completion handler should probably accept an Error parameter so that it knows that there was a problem.
//MARK: - Get Form Values
fileprivate func getFormValues(values: [String: Any], completion: #escaping ([String:Any]?)->()) {
var returnedValues: [String: Any] = [:]
if let name = values["name"] as? String,
let description = values["description"] as? String,
let images = values["images"] as? [UIImage],
let category = values["category"] as? String,
let price = values["price"] as? Double,
let deliveryFee = values["deliveryFee"] as? Double,
let deliveryAreas = values["deliveryArea"] as? Set<String>,
let deliveryTime = values["deliveryTime"] as? String {
guard let uid = Auth.auth().currentUser?.uid else {
completion(nil)
return
}
var imagesData = [[String: Any]]()
let dispatchGroup = DispatchGroup() // Create a Dispatch Group
images.forEach({ (image) in
let imageName = NSUUID().uuidString
let productImageStorageRef = Storage.storage().reference().child("product_images").child(uid).child("\(imageName).jpg")
var resizedImage = UIImage()
if image.size.width > 800 {
resizedImage = image.resizeWithWidth(width: 800)!
}
if let uploadData = UIImageJPEGRepresentation(resizedImage, 0.5) {
dispatchGroup.enter() // Enter the group
productImageStorageRef.putData(uploadData, metadata: nil, completion: { (metadata, error) in
guard error == nil else {
print("Failed to upload image: \(error?.localizedDescription ?? "")")
dispatchGroup.leave() // Leave the dispatch group if there was an error
return
}
//Successfully uploaded product Image
print("Successfully uploaded product Image")
if let productImageUrl = metadata?.downloadURL()?.absoluteString {
let imageData: [String: Any] = [imageName: productImageUrl]
imagesData.append(imageData)
}
dispatchGroup.leave() // Leave the dispatch group in normal circumstances
})
}
})
// Schedule a notify closure for execution when the dispatch group is empty
dispatchGroup.notify(queue: .main) {
let deliveryAreasArr = Array(deliveryAreas)
returnedValues = ["name": name, "description": description, "images": imagesData , "category": category, "price": price, "deliveryFee": deliveryFee, "deliveryArea": deliveryAreasArr, "deliveryTime": deliveryTime, "creationDate": Date().timeIntervalSince1970, "userId": uid]
completion(self.returnedValues)
}
} else {
completion(nil)
let alert = UIAlertController(title: "Missing Information", message: "All fields are required. Please fill all fields.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (_) in
alert.dismiss(animated: true, completion: nil)
}))
UIActivityIndicatorView.stopActivityIndicator(indicator: self.activityIndicator, container: self.activityIndicatorContainer, loadingView: self.activityIndicatorLoadingView)
self.present(alert, animated: true, completion: nil)
}
}
Some other points:
It would be better to pass structs rather than dictionaries. Using a struct for your input would get rid of that massive if let at the start of your function since you would know the types of the values and by making them non-optional properties of the struct you would know that the values were present.
It is unusual for a function such as this to present an alert; it would normally just return an error via the completion or perhaps throw back to the caller to indicate that there was a problem and let the caller handle it
I don't see why imagesData needs to be an array of dictionaries. Each dictionary in the array only has one entry, so you could just use a dictionary of [String:String] (There is no need to use Any when you know what the type will be.
My app has a search bar that sends a query to a MySQL database and then receives a JSON response and displays it in a view. This works great when the response is success (there's an actual array and rows in MySQL). However, when the response is empty I would like to display an alertbox with a simple error.
My PHP code on the server is set to echo No rows when the query response is empty, how can I achieve this in my app?
This is what I have going right now:
func searchBarSearchButtonClicked(searchBar: UISearchBar)
{
if(searchBar.text!.isEmpty)
{
return
}
doSearch(searchBar.text!)
}
func doSearch(searchWord: String)
{
mySearchBar.resignFirstResponder()
//created NSURL
if let requestURL = NSURL(string: URL_GET_coffees) {
//creating NSMutableURLRequest
let request = NSMutableURLRequest(URL: requestURL)
//setting the method to post
request.HTTPMethod = "POST"
//getting values from search bar text field
let searchText=mySearchBar.text
//creating the post parameter from search bar text field
let postParameters = "searchQuery="+searchText!;
//adding the parameters to request body
request.HTTPBody = postParameters.dataUsingEncoding(NSUTF8StringEncoding)
//creating a task to send the post request
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){
data, response, error in
//exiting if there is some error
if error != nil{
print("error is \(error)")
self.displayAlertMessage(error!.localizedDescription)
return;
}
//parsing the response
do {
guard let coffees = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSArray else {
//Doesn't exist, or isn't an NSArray
return
}
var newcoffees=[face]()
for coffee in coffees {
//getting the data at each index
let coffeeName = coffee["name"] as! String
let coffeeDirectLink = coffee["calculation_toemail_"] as! String
let coffeeImage = coffee["calculation_toemail_"] as! String
let newface = face(name:coffeeName,
directLink: coffeeDirectLink,
image:coffeeImage)
newcoffees.append(newface)
//displaying the data
print("name -> ", coffeeName)
print("direct_link -> ", coffeeDirectLink)
print("image -> ", coffeeImage)
print("===================")
print()
}
dispatch_async(dispatch_get_main_queue(),{
self.faces = newcoffees
self.collectionview.reloadData()
})
let errorMessage = newcoffees["No rows"] as? String
if(errorMessage != nil)
{
// display an alert message
self.displayAlertMessage(errorMessage!)
}
} catch {
print(error)
}
}
//executing the task
task.resume()
}
}
func displayAlertMessage(userMessage: String)
{
let myAlert = UIAlertController(title: "Alert", message: userMessage, preferredStyle: UIAlertControllerStyle.Alert);
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil)
myAlert.addAction(okAction);
self.presentViewController(myAlert, animated: true, completion: nil)
}
But what's going on is I'm getting an Cannot subscript a value of type '[face]' with an index of type 'String' where I tell it what the error response is. What can I be doing wrong?
I am trying to return a result from a JSON object but unable to do so. I am new to Swift so kindly explain me how to do so. In the below code I want to return json_level_number in the return of function fetchNumberOfSections () where i have hard coded as 5 right now return 5. If i declare a variable json_level_number just above the reachability code it sort of solves the problem but then it is returning '0' for the first time. The API returns 2 each time.
Code as below:
func fetchNumberOfSections () -> Int {
if Reachability.isConnectedToNetwork() == true {
// Below code to fetch number of sections
var urlAsString = "http://themostplayed.com/rest/fetch_challenge_sections.php"
urlAsString = urlAsString+"?apiKey="+apiKey
print (urlAsString)
let url = NSURL(string: urlAsString)!
let urlSession = NSURLSession.sharedSession()
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
if (error != nil) {
print(error!.localizedDescription)
}
do {
let jsonResult = (try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as! NSDictionary
let json_level_number: String! = jsonResult["level_number"] as! String
//
dispatch_async(dispatch_get_main_queue(), {
// self.dateLabel.text = jsonDate
// self.timeLabel.text = jsonTime
print(json_level_number)
// self.activityIndicatorStop()
})
}
catch let errorJSON {
print (errorJSON)
// alert box code below
let alert = UIAlertController(title: "JSON Error!", message:"Error processing JSON.", preferredStyle: .Alert)
let action = UIAlertAction(title: "OK", style: .Default) { _ in
// Put here any code that you would like to execute when
self.dismissViewControllerAnimated(true, completion: {})
}
alert.addAction(action)
self.presentViewController(alert, animated: true, completion: nil)
// alert box code end
}
})
jsonQuery.resume()
// End
}
else {
print("Internet connection FAILED")
self.performSegueWithIdentifier("nointernet", sender: nil)
}
return 5
}