I have a class that conform to the NSURLSession delegates for downloading data and have an issue when more than one service is called when finished they call the method func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?), the results are parsed and handled here to be returned to the correct view controller.
How do I know from what session or task the results are so I can call the correct view controller?
import Foundation
class Support{
// MARK - Properties
var ID: Int!
var SoftekID: String!
var Subject: String!
var LastUpdate: String!
var LastUpdatedBy: String!
var Priority: Int!
var Impact: Int!
var SupportType: String!
var Importance: Int!
// MARK: General
init() {
self.ID = 0
self.SoftekID = ""
self.Subject = ""
self.LastUpdate = ""
self.LastUpdatedBy = ""
self.Priority = 0
self.Impact = 0
self.SupportType = ""
self.Importance = 0
}
func getSupportTickets(){
let sp = SuppportProvider()
sp.getSupportTickets()
}
}
class SuppportProvider: NSObject, NSURLSessionDelegate, NSURLSessionDataDelegate, NSURLSessionTaskDelegate{
// MARK: - Properties
var mData: NSMutableData?
var session: NSURLSession!
override init(){
super.init()
prepareConnection()
}
// MARK: - Methods
func prepareConnection(){
session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), delegate: self, delegateQueue:nil)
}
func getSupportTickets(){
var request = NSMutableURLRequest(URL: NSURL(string: "http://10.0.58.137/ISOWeb.UI/api/CSMobile/GetSupportTickets?PageNumber=0&PagingSize=10&TicketStatus=Priority")!,
cachePolicy: NSURLRequestCachePolicy.UseProtocolCachePolicy,
timeoutInterval: 20.0)
let task = session.dataTaskWithRequest(request)
mData = NSMutableData()
task.resume()
}
func getHelpInformation(){
var request = NSMutableURLRequest(URL: NSURL(string: "http://10.0.58.137/ISOWeb.UI/api/CSMobile/GetHelpInformation")!,
cachePolicy: NSURLRequestCachePolicy.UseProtocolCachePolicy,
timeoutInterval: 20.0)
let task = session.dataTaskWithRequest(request)
mData = NSMutableData()
task.resume()
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: (NSURLSessionResponseDisposition) -> Void) {
mData!.length = 0
completionHandler(NSURLSessionResponseDisposition.Allow)
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
mData!.appendData(data)
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
if error != nil{
println("An error has occured completing the request")
}else{
//Result for method: getSupportTickets
var value = NSString(bytes: mData!.mutableBytes, length: mData!.length, encoding: NSUTF8StringEncoding)
var jError: NSError?
if let JSONResult: Array<NSDictionary> = NSJSONSerialization.JSONObjectWithData(mData!, options: NSJSONReadingOptions.AllowFragments, error: &jError) as? Array<NSDictionary> {
if JSONResult.count > 0 {
var arr = Array<Support>()
for dict in JSONResult{
let item = Support()
if (dict["ID"] as? Int != nil) {
item.ID = dict["ID"] as! Int
}else {
item.ID = 0
}
if (dict["SoftekID"] as? String != nil) {
item.SoftekID = dict["SoftekID"] as! String
}else {
item.SoftekID = ""
}
if (dict["Subject"] as? String != nil) {
item.Subject = dict["Subject"] as! String
}else {
item.Subject = ""
}
if (dict["LastUpdate"] as? String != nil) {
item.LastUpdate = dict["LastUpdate"] as! String
}else {
item.LastUpdate = ""
}
if (dict["LastUpdatedBy"] as? String != nil) {
item.LastUpdatedBy = dict["LastUpdatedBy"] as! String
}else {
item.LastUpdatedBy = ""
}
if (dict["Priority"] as? Int != nil) {
item.Priority = dict["Priority"] as! Int
}else {
item.Priority = 0
}
if (dict["Impact"] as? Int != nil) {
item.Impact = dict["Impact"] as! Int
}else {
item.Impact = 0
}
if (dict["SupportType"] as? String != nil) {
item.SupportType = dict["SupportType"] as! String
}else {
item.SupportType = ""
}
if (dict["Importance"] as? Int != nil) {
item.Importance = dict["Importance"] as! Int
}else {
item.Importance = 0
}
arr.append(item)
}
}
}
//Result for method: getHelpInformation
//How to know to which task holds the result?
}
}
}
UPDATE
import Foundation
class CSSupport{
// MARK - Properties
var ID: Int!
var SoftekID: String!
var Subject: String!
var LastUpdate: String!
var LastUpdatedBy: String!
var Priority: Int!
var Impact: Int!
var SupportType: String!
var Importance: Int!
// MARK: General
init() {
self.ID = 0
self.SoftekID = ""
self.Subject = ""
self.LastUpdate = ""
self.LastUpdatedBy = ""
self.Priority = 0
self.Impact = 0
self.SupportType = ""
self.Importance = 0
}
}
class Support:NSObject, NSURLSessionDelegate, NSURLSessionDataDelegate, NSURLSessionTaskDelegate{
// MARK: - Properties
var mData: NSMutableData?
var session: NSURLSession!
override init(){
super.init()
var configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
session = NSURLSession(configuration:configuration, delegate: self, delegateQueue:nil)
}
// MARK: - Methods
func getSupportTickets(){
var request = NSMutableURLRequest(URL: NSURL(string: "http://10.0.58.137/ISOWeb.UI/api/CSMobile/GetSupportTickets?PageNumber=0&PagingSize=10&TicketStatus=Priority")!,
cachePolicy: NSURLRequestCachePolicy.UseProtocolCachePolicy,
timeoutInterval: 20.0)
let task = session.dataTaskWithRequest(request)
mData = NSMutableData()
task.resume()
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: (NSURLSessionResponseDisposition) -> Void) {
mData!.length = 0
completionHandler(NSURLSessionResponseDisposition.Allow)
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
mData!.appendData(data)
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
if error != nil{
println("An error has occured completing the request")
}else{
var value = NSString(bytes: mData!.mutableBytes, length: mData!.length, encoding: NSUTF8StringEncoding)
var jError: NSError?
switch task.taskIdentifier {
case 1:
if let JSONResult: Array<NSDictionary> = NSJSONSerialization.JSONObjectWithData(mData!, options: NSJSONReadingOptions.AllowFragments, error: &jError) as? Array<NSDictionary> {
if JSONResult.count > 0 {
var arr = Array<CSSupport>()
for dict in JSONResult{
let item = CSSupport()
if (dict["ID"] as? Int != nil) {
item.ID = dict["ID"] as! Int
}else {
item.ID = 0
}
if (dict["SoftekID"] as? String != nil) {
item.SoftekID = dict["SoftekID"] as! String
}else {
item.SoftekID = ""
}
if (dict["Subject"] as? String != nil) {
item.Subject = dict["Subject"] as! String
}else {
item.Subject = ""
}
if (dict["LastUpdate"] as? String != nil) {
item.LastUpdate = dict["LastUpdate"] as! String
}else {
item.LastUpdate = ""
}
if (dict["LastUpdatedBy"] as? String != nil) {
item.LastUpdatedBy = dict["LastUpdatedBy"] as! String
}else {
item.LastUpdatedBy = ""
}
if (dict["Priority"] as? Int != nil) {
item.Priority = dict["Priority"] as! Int
}else {
item.Priority = 0
}
if (dict["Impact"] as? Int != nil) {
item.Impact = dict["Impact"] as! Int
}else {
item.Impact = 0
}
if (dict["SupportType"] as? String != nil) {
item.SupportType = dict["SupportType"] as! String
}else {
item.SupportType = ""
}
if (dict["Importance"] as? Int != nil) {
item.Importance = dict["Importance"] as! Int
}else {
item.Importance = 0
}
arr.append(item)
}
}
}
break
case 2:
break
default:
println("No task was found.")
break
}
}
}
}
Yes, the lack of any sort of customizable storage is kind of annoying, particularly because the identifier is only unique on a per-session basis. There are a few ways to handle this:
If you're using background sessions or a mixture of foreground and background sesions, I think the best thing you can do is a two-level lookup, in which you associate a mutable dictionary with each session and look up the session first, then look up the task by its identifier within the per-session dictionary:
For background sessions, provide an identifier to use as the key in the top-level mutable dictionary.
For other sessions, use the session object itself as the key in the top-level mutable dictionary.
If you're using only foreground tasks (not in a background session, that is), you should be able to just use the tasks as dictionary keys and look up additional data related to the tasks.
I think that both of those techniques should work correctly. You might also be able to use associated objects on the task to store additional data (so long as you aren't working with background sessions).
One thing you definitely can't do (I already filed a bug about this) is subclass NSURLRequest and store additional data there, because NSURLSession returns its own copy of the NSURLRequest (rather than your custom subclass) when you ask for currentRequest, and returns a seemingly random instance of your subclass (unrelated to the actual original request) when you ask for orginalRequest.
It's amazing how much effort everyone has to expend creating external dictionaries to work around the lack of a simple refCon object property on each task and on each session. It is definitely worth filing a bug to ask for that feature.
Why not using your own custom string identifier in taskDescription which is a member of NSURLSessionTask ?
Maybe taskIdentifier property could help you: https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLSessionTask_class/index.html#//apple_ref/occ/instp/NSURLSessionTask/taskIdentifier
Related
I have run into a problem where I can save and load into and from CoreData in Swift for my iOS app, but I run into a problem where I have tried to guard for duplicate entries, but it does not seem to work. can anyone tell me where I went wrong? Thanks!
My ViewController class:
import UIKit
import CoreData
class ViewController: UIViewController, UITableViewDelegate,
UITableViewDataSource {
#IBOutlet weak var headerLabel:UILabel!
#IBOutlet weak var myTableView: UITableView!
var lenders = [LenderData]()
var lendersTemp = [LenderData]()
override func viewDidLoad() {
super.viewDidLoad()
self.myTableView.rowHeight = 90
myTableView.delegate = self
myTableView.dataSource = self
let fetchRequest: NSFetchRequest<LenderData> = LenderData.fetchRequest()
do {
let lenders = try PersistenceService.context.fetch(fetchRequest)
self.lenders = lenders
} catch {
// Who cares....
}
downloadJSON {
for tempLender in self.lendersTemp {
if !self.lenders.contains(where: {$0.id == tempLender.id}) {
self.lenders.append(tempLender)
}
}
self.lendersTemp.removeAll()
PersistenceService.saveContext()
self.myTableView.reloadData()
}
}
func downloadJSON(completed: #escaping () -> ()) {
let url = URL(string: "https://api.kivaws.org/v1/loans/newest.json")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print("JSON not downloaded")
} else {
if let content = data {
do {
let myJSONData = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
var imageID:Int64 = -1
var country:String = "N/A"
var latLongPair:String = "0.000000 0.000000"
var town:String = "N/A"
if let loans = myJSONData["loans"] as? NSArray {
for i in 0...loans.count-1 {
if let lender = loans[i] as? NSDictionary {
if let imageData = lender["image"] as? NSDictionary { imageID = imageData["id"] as! Int64 }
if let countryData = lender["location"] as? NSDictionary {
country = countryData["country"] as! String
town = countryData["town"] as! String
if let geo = countryData["geo"] as? NSDictionary {
latLongPair = geo["pairs"] as! String
}
}
let newLender = LenderData(context: PersistenceService.context)
newLender.id = lender["id"] as! Int64
newLender.name = lender["name"] as? String
newLender.image_id = imageID
newLender.activity = lender["activity"] as? String
newLender.use = lender["use"] as? String
newLender.loan_amount = lender["loan_amount"] as! Int32
newLender.funded_amount = lender["funded_amount"] as! Int32
newLender.country = country
newLender.town = town
newLender.geo_pairs = latLongPair
self.lendersTemp.append(newLender)
}
}
}
DispatchQueue.main.async {
completed()
}
} catch {
print("Error occured \(error)")
}
}
}
}
task.resume()
}
}
EDIT
Added the part of the code where I populate the lendersTemp array
I quote matt on this one from the comments:
So... You are appending to self.lendersTemp on a background thread but reading it on the main thread. Instead, get rid of it and just pass the data right thru the completed function.
Which is exactly what I did. And this worked
Im New to Swift and I'm Integrating PayUmoney IOS SDK in swift, Im getting trouble When I'm doing in live its showing hash mismatch (Hash mismatch1) If I'm doing in test its showing invalid merchant key (Invalid key) I struck here from 2 weeks Did so many things and didn't get any solution for this can any one help it would be awesome. Below is my code, Thank you in Advance.
var params : PUMRequestParams = PUMRequestParams.shared()
var utils : Utils = Utils()
params.environment = PUMEnvironment.test;
params.firstname = txtFldName.text;
params.key = "bZf4AOjj";
params.merchantid = "5745303";
params.logo_url = "";
params.productinfo = "Product Info";
params.email = txtFldEmail.text;
params.phone = "";
params.surl = "https://www.payumoney.com/mobileapp/payumoney/success.php";
params.furl = "https://www.payumoney.com/mobileapp/payumoney/failure.php";
if(params.environment == PUMEnvironment.test){
generateHashForProdAndNavigateToSDK()
}
else{
calculateHashFromServer()
}
// assign delegate for payment callback.
params.delegate = self;
}
func generateHashForProdAndNavigateToSDK() -> Void {
let txnid = params.txnid!
let hashSequence : NSString = "\(params.key)|\(txnid)|\(params.amount)|\(params.productinfo)|\(params.firstname)|\(params.email)|||||||||||2uIsGhXWVw" as NSString
let data :NSString = utils.createSHA512(hashSequence as String!) as NSString
params.hashValue = data as String!;
startPaymentFlow();
}
// MARK:HASH CALCULATION
func prepareHashBody()->NSString{
return "SHA-512key=\(params.key!)&amount=\(params.amount!)&txnid=\(params.txnid!)&productinfo=\(params.productinfo!)&email=\(params.email!)&firstname=\(params.firstname!)" as NSString;
}
func calculateHashFromServer(){
let config = URLSessionConfiguration.default // Session Configuration
let session = URLSession(configuration: config) // Load configuration into Session
let url = URL(string: "https://test.payumoney.com/payment/op/v1/calculateHashForTest")!
var request = URLRequest(url: url)
request.httpBody = prepareHashBody().data(using: String.Encoding.utf8.rawValue)
request.httpMethod = "POST"
let task = session.dataTask(with: request, completionHandler: {
(data, response, error) in
if error != nil {
print(error!.localizedDescription)
} else {
do {
if let json = try JSONSerialization.jsonObject(with: data!, options: []) as? [String: Any]{
print(json)
let status : NSNumber = json["status"] as! NSNumber
if(status.intValue == 0)
{
self.params.hashValue = json["result"] as! String!
OperationQueue.main.addOperation {
self.startPaymentFlow()
}
}
else{
OperationQueue.main.addOperation {
self.showAlertViewWithTitle(title: "Message", message: json["message"] as! String)
}
}
}
} catch {
print("error in JSONSerialization")
}
}
})
task.resume()
}
Hello Vinny do with webview its working for me. Before I also used this PayUmoney IOS SDK but faced so many problems so based on objective-c I did this so I think its useful to you. create a weak var webview and create class UIwebviewdelegate
class PayumoneyViewController: UIViewController, UIWebViewDelegate, UIAlertViewDelegate {
#IBOutlet weak var Webview: UIWebView!
and for test use below credentials
//test
var merchantKey = "40747T"
var salt = "ur salt"
var PayUBaseUrl = "https://test.payu.in"
For live
//Production
var merchantKey = “xxxxxx”
var salt = “xxxxx”
var PayUBaseUrl = "https://secure.payu.in"
let productInfo = “Myapp” //It can be Project name or anything else
let firstName = “Santoshi” //Details of user whose is purchasing order
let email = “santoshi#app.com" //Details of user whose is purchasing order
let phone = "xxxxxxxxx" //Details of user whose is purchasing order
let sUrl = "www.google.com" //By this URL we match whether payment got success or failure
let fUrl = "www.google.com" //By this URL we match whether payment got success or failure
let service_provider = "payu_paisa"
var txnid1: String! = "" //Its an unique id which can give order a specific order number.
let totalPriceAmount = "1.0"
Above viewdidload do like this
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
initPayment()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
}
In viewdidload do like this
override func viewDidLoad() {
super.viewDidLoad()
Webview.delegate = self
// Do any additional setup after loading the view.
}
Create payment and Generate Hash key
func initPayment() {
txnid1 = “Myapp\(String(Int(NSDate().timeIntervalSince1970)))"
//Generating Hash Key
let hashValue = String.localizedStringWithFormat("%#|%#|%#|%#|%#|%#|||||||||||%#",merchantKey,txnid1,totalPriceAmount,productInfo,firstName,email,salt)
let hash = self.sha1(string: hashValue)
let postStr = "txnid="+txnid1+"&key="+merchantKey+"&amount="+totalPriceAmount+"&productinfo="+productInfo+"&firstname="+firstName+"&email="+email+"&phone="+phone+"&surl="+sUrl+"&furl="+fUrl+"&hash="+hash+"&service_provider="+service_provider
let url = NSURL(string: String.localizedStringWithFormat("%#/_payment", PayUBaseUrl))
let request = NSMutableURLRequest(url: url! as URL)
do {
let postLength = String.localizedStringWithFormat("%lu",postStr.characters.count)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Current-Type")
request.setValue(postLength, forHTTPHeaderField: "Content-Length")
request.httpBody = postStr.data(using: String.Encoding.utf8)
Webview.loadRequest(request as URLRequest)
}
catch let error as NSError
{
print(error)
}
}
Finally Do this
func sha1(string:String) -> String {
let cstr = string.cString(using: String.Encoding.utf8)
let data = NSData(bytes: cstr, length: string.characters.count)
var digest = [UInt8](repeating: 0, count:Int(CC_SHA512_DIGEST_LENGTH))
CC_SHA512(data.bytes, CC_LONG(data.length), &digest)
let hexBytes = digest.map { String(format: "%02x", $0) }
return hexBytes.joined(separator: "")
}
func webViewDidFinishLoad(_ webView: UIWebView) {
let requestURL = self.Webview.request?.url
let requestString:String = (requestURL?.absoluteString)!
if requestString.contains("https://www.payumoney.com/mobileapp/payumoney/success.php") {
print("success payment done")
}else if requestString.contains("https://www.payumoney.com/mobileapp/payumoney/failure.php") {
print("payment failure")
}
}
func webView(_ webView: UIWebView, didFailLoadWithError error: Error) {
let requestURL = self.Webview.request?.url
print("WebView failed loading with requestURL: \(requestURL) with error: \(error.localizedDescription) & error code: \(error)")
if error._code == -1009 || error._code == -1003 {
showAlertView(userMessage: "Please check your internet connection!")
}else if error._code == -1001 {
showAlertView(userMessage: "The request timed out.")
}
}
func showAlertView(userMessage:String){
}
I have Faced Same problem and i have got solution of this problem.
In my code this line generate optional values --> let hashSequence : NSString = "(params.key!)|(txnid)|(params.amount!)|(params.productinfo!)|(params.firstname!)|(params.email!)|||||||||||(params.merchantid!)" as NSString
remove optional from the values.
I have created a project that will retrieve the extracted JSON data and display it in UITableview. I don't want to burden the app by downloading everything. So, only when user selected a row, will it retrieve the employee details. I'm using a page view controller so that the user is able to navigate each page by sliding the page. How can I sent the value I sent for page in dispatch_sync to detailviewcontroller page?
This is my code from managePageviewController
func viewDetailViewController(index: Int) -> DetailViewController? {
if let storyboard = storyboard,
page = storyboard.instantiateViewControllerWithIdentifier("DetailViewController") as? DetailViewController {
let currentEmployee = employeeStore.searchEmployee[index]
getJson().testsearchJSON(currentEmployee.id, handler: {(employeeDetails) -> Void in
dispatch_sync(dispatch_get_main_queue(), {
page.employee = employeeDetails
page.employeeIndex = index
return page //fail here
})
})
}
return nil
}
This is my getJSON().testSearchJSON fund
func testsearchJSON(id:String, handler: (Employee) -> Void) {
let requestURL: NSURL = NSURL(string: (favUrl + id))!
let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(urlRequest) {
(data, response, error) -> Void in
let httpResponse = response as! NSHTTPURLResponse
let statusCode = httpResponse.statusCode
//retrieve data successfully
if (statusCode == 200) {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments)
if data!.length > 0 && error == nil {
guard let name = json["firstName"] as? String,
let title = json["title"] as? String,
let id = json["id"]!,
let manager = json["managerName"] as? String,
let oa = json["oa"] as? String,
let email = json["email"] as? String,
let department = json["department"] as? String,
let division = json["division"] as? String,
let company = json["company"] as? String
else {
return;
}
let newEmployee = Employee(id: String(id), name: name, title: title, manager: manager, oa: oa, email: email, department: department, division: division, company: company)
//test
handler(newEmployee)
}
} catch {
print("Error with JSON: \(error)")
}
}
}
task.resume()
}
}
This is my page for DetailviewController
class DetailViewController: UIViewController, UITextFieldDelegate {
// MARK:- Propertise
#IBOutlet var employeePic: UIImageView! //employee picture
#IBOutlet var employeeName: UILabel! // name
#IBOutlet var employeeTitle: UILabel! //job title
#IBOutlet var dateCreated: UILabel!
#IBOutlet var managerName: UITextField!
#IBOutlet var oaName: UITextField!
#IBOutlet var emailField: UITextField!
#IBOutlet var departmentField: UITextField!
#IBOutlet var divisionField: UITextField!
#IBOutlet var companyField: UITextField!
var employee: Employee! {
//add applicataion name
didSet {
navigationItem.title = employee.name
}
}
//current employee index
var employeeIndex: Int!
let dateFormatter: NSDateFormatter = {
let formatter = NSDateFormatter()
formatter.dateStyle = .MediumStyle
formatter.timeStyle = .NoStyle
return formatter
}()
//MARK:- assign values
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
employeeName.text = employee.name
employeeTitle.text = "( " + employee.title + " )"
emailField.text = employee.email
managerName.text = employee.manager
dateCreated.text = dateFormatter.stringFromDate(employee.dateCreated)
oaName.text = employee.oa
departmentField.text = employee.department
divisionField.text = employee.division
companyField.text = employee.company
//retrieve image
employeePic.thumbnails()
employeePic.image = UIImage(named: "Default Image")
}
I think is this case it would be better to write data fetching in viewDidLoad or viewWillAppear function in DetailViewController. Something like that:
In MainViewController:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
viewDetailViewController(index : indexPath.row, employee: employeeStore.searchEmployee[indexPath.row])
}
func viewDetailViewController(index: Int, employee: Employee) {
let detailController = storyboard.instantiateViewControllerWithIdentifier("DetailViewController") as? DetailViewController
detailController.currentEmployee = employee
// present/push/etc detail controller
present(detailViewController, animated: true, completion: nil)
}
In DetailViewController:
var employee : Employee?
...
override func viewDidLoad() {
super.viewDidLoad()
if let employee = currentEmployee {
getJson().testsearchJSON(employee.id, handler: {(employeeDetails) -> Void in
dispatch_sync(dispatch_get_main_queue(), {
//reload UI for employeeDetails
})
})
}
}
Also you can use GCD to wait for block loading, for example GCD groups.
Try to like this
func viewDetailViewController(index: Int) -> DetailViewController? {
if let storyboard = storyboard,
page = storyboard.instantiateViewControllerWithIdentifier("DetailViewController") as? DetailViewController {
let currentEmployee = employeeStore.searchEmployee[index]
getJson().testsearchJSON(currentEmployee.id, handler: {(employeeDetails) -> Void in
page.employee = employeeDetails
page.employeeIndex = index
return page //fail here
})
}
return nil
}
func testsearchJSON(id:String, handler: (Employee) -> Void) {
let requestURL: NSURL = NSURL(string: (favUrl + id))!
let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL)
let semaphore = dispatch_semaphore_create(0);
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(urlRequest) {
(data, response, error) -> Void in
let httpResponse = response as! NSHTTPURLResponse
let statusCode = httpResponse.statusCode
//retrieve data successfully
if (statusCode == 200) {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments)
if data!.length > 0 && error == nil {
guard let name = json["firstName"] as? String,
let title = json["title"] as? String,
let id = json["id"]!,
let manager = json["managerName"] as? String,
let oa = json["oa"] as? String,
let email = json["email"] as? String,
let department = json["department"] as? String,
let division = json["division"] as? String,
let company = json["company"] as? String
else {
dispatch_semaphore_signal(semaphore);
return;
}
let newEmployee = Employee(id: String(id), name: name, title: title, manager: manager, oa: oa, email: email, department: department, division: division, company: company)
//test
handler(newEmployee)
dispatch_semaphore_signal(semaphore);
}
} catch {
dispatch_semaphore_signal(semaphore);
print("Error with JSON: \(error)")
}
}
}
task.resume()
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
}
}
I have the following two functions in my first ViewController. They load a UITableView with over 300 rows. I call the loadRemoteData function inside the ViewDidLoad. Everything works fine in the first ViewController.
// MARK: - parseJSON
func parseJSON(data: NSData) {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers)
if let rootDictionary = json as? [NSObject: AnyObject], rootResults = rootDictionary["results"] as? [[NSObject: AnyObject]] {
for childResults in rootResults {
if let firstName = childResults["first_name"] as? String,
let lastName = childResults["last_name"] as? String,
let bioguideId = childResults["bioguide_id"] as? String,
let state = childResults["state"] as? String,
let stateName = childResults["state_name"] as? String,
let title = childResults["title"] as? String,
let party = childResults["party"] as? String {
let eachLegislator = Legislator(firstName: firstName, lastName: lastName, bioguideId: bioguideId, state: state, stateName: stateName, title: title, party: party)
legislators.append(eachLegislator)
}
}
}
} catch {
print(error)
}
}
// MARK: - Remote Data configuration
func loadRemoteData() {
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let url = "https://somedomain.com/legislators?order=state_name__asc,last_name__asc&fields=first_name,last_name,bioguide_id"
if let url = NSURL(string: url) {
let task = session.dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in
if let error = error {
print("Data Task failed with error: \(error)")
return
}
if let http = response as? NSHTTPURLResponse, data = data {
if http.statusCode == 200 {
dispatch_async(dispatch_get_main_queue()) {
self.parseJSON(data)
self.tableView.reloadData()
}
}
}
})
task.resume()
}
}
In the second ViewController, I want to display more information about the individual listed in the cell that is tapped, for that I use a different URL such as https://somedomain.com/legislators?bioguide_id=\"\(bioguideId)\" which provides me with a lot more detail. (The data being requested from the JSON Dictionary is different)
The code I use in the second ViewController is just like shown above with the only difference being the URL. I can print the url coming from the previous ViewController and it is displayed in the console log but no json data is shown.
I would appreciate any help.
Thanks
Below is the code for my second ViewController:
import UIKit
class DetailViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var bioguideId: String?
var currentLegislator: Legislator? = nil
var currentLegislatorUrl: String?
let reuseIdentifier = "Cell"
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var tableView: UITableView!
// MARK: - parseJSON
private func parseJSON(data: NSData) {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers)
if let rootDictionary = json as? [NSObject: AnyObject],
rootResults = rootDictionary["results"] as? [[NSObject: AnyObject]] {
for childResults in rootResults {
if let firstName = childResults["first_name"] as? String,
let lastName = childResults["last_name"] as? String,
let bioguideId = childResults["bioguide_id"] as? String,
let state = childResults["state"] as? String,
let stateName = childResults["state_name"] as? String,
let title = childResults["title"] as? String,
let party = childResults["party"] as? String {
currentLegislator = Legislator(firstName: firstName, lastName: lastName, bioguideId: bioguideId, state: state, stateName: stateName, title: title, party: party)
}
}
}
} catch {
print(error)
}
}
// MARK: - Remote Data configuration
func loadRemoteData(url: String) {
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let url = currentLegislatorUrl
if let url = NSURL(string: url!) {
let task = session.dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in
if let error = error {
print("Data Task failed with error: \(error)")
return
}
print("Success")
if let http = response as? NSHTTPURLResponse, data = data {
if http.statusCode == 200 {
dispatch_async(dispatch_get_main_queue()) {
self.parseJSON(data)
self.tableView.reloadData()
}
}
}
})
task.resume()
}
}
func loadImage(urlString:String) {
let imgURL: NSURL = NSURL(string: urlString)!
let request: NSURLRequest = NSURLRequest(URL: imgURL)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request){
(data, response, error) -> Void in
if (error == nil && data != nil) {
func display_image() {
self.imageView.image = UIImage(data: data!)
}
dispatch_async(dispatch_get_main_queue(), display_image)
}
}
task.resume()
}
override func viewDidLoad() {
super.viewDidLoad()
print(currentLegislatorUrl!)
loadRemoteData(currentLegislatorUrl!)
loadImage("https://theunitedstates.io/images/congress/225x275/\(bioguideId!).jpg")
self.title = bioguideId
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier, forIndexPath: indexPath)
cell.textLabel!.text = currentLegislator?.firstName
return cell
}
}
Thanks to Adam H. His comment made me reevaluate the URL I was using and by adding additional operators, now the data is shown in my second ViewController.
I am using following Class to receive data from an external database:
import Foundation
protocol HomeModelProtocal: class {
func itemsDownloaded(items: NSArray)
}
class HomeModel: NSObject, NSURLSessionDataDelegate {
//properties
weak var delegate: HomeModelProtocal!
var data : NSMutableData = NSMutableData()
var mi_movil: String = ""
let misDatos:NSUserDefaults = NSUserDefaults.standardUserDefaults()
var urlPath: String = "http:...hidden here.."
let parametros = "?id="
func downloadItems() {
mi_movil = misDatos.stringForKey("ID_IPHONE")!
print ("mi_movil en HOMEMODEL:",mi_movil)
urlPath = urlPath + parametros + mi_movil
let url: NSURL = NSURL(string: urlPath)!
var session: NSURLSession!
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
print ("LA URL ES: ",url)
session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: nil)
let task = session.dataTaskWithURL(url)
task.resume()
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
self.data.appendData(data);
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
if error != nil {
print("Failed to download data")
}else {
print("Data downloaded")
self.parseJSON()
}
}
func parseJSON() {
var jsonResult: NSMutableArray = NSMutableArray()
do{
jsonResult = try NSJSONSerialization.JSONObjectWithData(self.data, options:NSJSONReadingOptions.AllowFragments) as! NSMutableArray
} catch let error as NSError {
print(error)
}
var jsonElement: NSDictionary = NSDictionary()
let locations: NSMutableArray = NSMutableArray()
for(var i = 0; i < jsonResult.count; i++)
{
jsonElement = jsonResult[i] as! NSDictionary
print (jsonElement)
let location = MiAutoModel()
//the following insures none of the JsonElement values are nil through optional binding
if let id_mis_autos = jsonElement["id_mis_autos"] as? String,
let modelo = jsonElement["modelo"] as? String,
let ano = jsonElement["ano"] as? String,
let id_movil = jsonElement["id_movil"] as? String
{
location.id_mis_autos = id_mis_autos
location.modelo = modelo
location.ano = ano
location.id_movil = id_movil
}
locations.addObject(location)
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.delegate.itemsDownloaded(locations)
})
}
}
If there are received data, it works fine but if there are no data an exception is thrown:
Could not cast value of type '__NSArray0' (0x1a0dd2978) to 'NSMutableArray' (0x1a0dd3490)
What should I change to detect if there are no data to avoid the exception?
Since you don't seem to be modifying jsonResult anywhere, the obvious choice is to make it an NSArray instead of an NSMutableArray, and change the downcasting to match that.
I'm not sure why you're using NSDictionary and NSMutableArray but this is how I would do it:
for result in jsonResult {
guard let jsonElement = result as? [String:AnyObject] else { return }
let locations: [MiAutoModel] = []
let location = MiAutoModel()
//the following insures none of the JsonElement values are nil through optional binding
let id_mis_autos = jsonElement["id_mis_autos"] as? String ?? ""
let modelo = jsonElement["modelo"] as? String ?? ""
let ano = jsonElement["ano"] as? String ?? ""
let id_movil = jsonElement["id_movil"] as? String ?? ""
location.id_mis_autos = id_mis_autos
location.modelo = modelo
location.ano = ano
location.id_movil = id_movil
locations.append(location)
}
You might have to change some of the code depending on your situation.