I have a page that show messages from a database. The message is getting loaded as it should but when i try to reload it to show the new messages after a interval of one minute i get the message out of range. It looks like the array is empty for some reason. I have read so many of the solutions but non of them explain what is happening in my code.
This is the complete code.
import UIKit
import Foundation
class LeftSideViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var answerPMTapped: UIButton!
#IBOutlet weak var senderProfileImage: UIImageView!
#IBOutlet weak var profileImageView: UIImageView!
#IBOutlet weak var BodyMessage: UITextView!
#IBOutlet weak var TitleMessage: UILabel!
#IBOutlet weak var totalKm: UILabel!
#IBOutlet weak var ranking: UILabel!
#IBOutlet weak var totalCollected: UILabel!
#IBOutlet weak var profileName: UILabel!
#IBOutlet weak var euroSign: UILabel!
var MessageId = [String]()
var MessageSender = [String]()
var MessageReceiver = [String]()
var MessageBody = [String]()
var MessageTime = [String]()
var MessageDate = [String]()
var MessageStatus = [String]()
var MessageTitle = [String]()
var SenderUsername = [String]()
var ReplyMessageId = [String]()
var T_message_Title:String!
var menuItems:[String] = ["Home","Profiel", "Goede Doelen", "Berichten", "Verstuur bericht", "Instellingen","Uitloggen"];
var menuImage: [String] = ["home", "profile", "charity", "message", "reply", "settings", "logout"]
var urlString = String()
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBarHidden = true
load_message()
}
override func viewDidLoad() {
super.viewDidLoad()
get_user_data()
get_my_message_list()
/* if (TitleMessage != nil){
var title_convert = MessageTitle[1]
var message_convert = MessageBody[1]
TitleMessage.text = title_convert
BodyMessage.text = message_convert
} else {
BodyMessage.text = "Er zijn geen berichten op dit moment."
} */
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
var userEmail = prefs.valueForKey("USERNAME") as! String
let newUsername = userEmail.stringByReplacingOccurrencesOfString(".", withString: "")
let NewUserName = newUsername.stringByReplacingOccurrencesOfString("#", withString: "")
let imgString = NewUserName
var urlString = "xxxxxxxx/profile/image/\(imgString).jpg"
euroSign.hidden = true
if (totalCollected != nil) {
euroSign.hidden = false
}
self.profileImageView.layer.cornerRadius = self.profileImageView.frame.size.width / 2;
self.profileImageView.clipsToBounds = true;
load_profile_image(urlString)
var reload_messages = NSTimer.scheduledTimerWithTimeInterval(60.0, target: self, selector: Selector("message_Reloader"), userInfo: nil, repeats: true)
}
func message_Reloader(){
self.MessageId.removeAll()
self.MessageSender.removeAll()
self.MessageReceiver.removeAll()
self.MessageBody.removeAll()
self.MessageTime.removeAll()
self.MessageDate.removeAll()
self.MessageStatus.removeAll()
self.MessageTitle.removeAll()
self.SenderUsername.removeAll()
self.ReplyMessageId.removeAll()
get_my_message_list()
load_message()
}
func load_profile_image(urlString: String){
var imgURL: NSURL = NSURL(string: urlString)!
let request: NSURLRequest = NSURLRequest(URL: imgURL)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: {(response: NSURLResponse!, data: NSData!,error: NSError!) -> Void in
if error == nil {
self.profileImageView.image = UIImage(data: data)
println(imgURL)
}
})
}
func get_user_data(){
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
var userEmail = prefs.valueForKey("USERNAME") as! String
let url = NSURL(string:"xxxxxx/fetch-user-profile.php")
let cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData
var request = NSMutableURLRequest(URL: url!, cachePolicy: cachePolicy, timeoutInterval: 2.0)
request.HTTPMethod = "POST"
// set Content-Type in HTTP header
let boundaryConstant = "----------V2ymHFg03esomerandomstuffhbqgZCaKO6jy";
let contentType = "multipart/form-data; boundary=" + boundaryConstant
NSURLProtocol.setProperty(contentType, forKey: "Content-Type", inRequest: request)
// set data
var dataString = "username=\(userEmail)"
let requestBodyData = (dataString as NSString).dataUsingEncoding(NSUTF8StringEncoding)
request.HTTPBody = requestBodyData
// set content length
//NSURLProtocol.setProperty(requestBodyData.length, forKey: "Content-Length", inRequest: request)
var response: NSURLResponse? = nil
var error: NSError? = nil
let dataReply = NSURLConnection.sendSynchronousRequest(request, returningResponse:&response, error:&error)
if(dataReply != nil){
let res = response as! NSHTTPURLResponse!;
NSLog("Response code: %ld", res.statusCode);
if (res.statusCode >= 200 && res.statusCode < 300)
{
var results = NSJSONSerialization.JSONObjectWithData(dataReply!, options: nil, error: &error) as! NSDictionary
NSLog("PostData: %#",dataReply!);
NSLog("ResultData: %#",results);
var jsonOutput = JSON(data: dataReply!)
totalKm.text = jsonOutput["total_km"].stringValue
totalCollected.text = jsonOutput["collected"].stringValue
ranking.text = jsonOutput["rank"].stringValue
profileName.text = jsonOutput["username"].stringValue
} else {
println( "Fetching data failed.")
}
} else {
println( "Fetching data failed.")
}
}
func load_message(){
println("\(MessageBody[0])")
var messagebody = MessageBody[0]
var messagetitle = MessageTitle[0]
var messagesender = MessageSender[0]
let newUsername = messagesender.stringByReplacingOccurrencesOfString(".", withString: "")
let NewUserName = newUsername.stringByReplacingOccurrencesOfString("#", withString: "")
let imgString = NewUserName
var urlString = "xxxxxxxxx/profile/image/\(imgString).jpg"
self.senderProfileImage.layer.cornerRadius = self.senderProfileImage.frame.size.width / 2;
self.senderProfileImage.clipsToBounds = true;
var imgURL: NSURL = NSURL(string: urlString)!
let request: NSURLRequest = NSURLRequest(URL: imgURL)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: {(response: NSURLResponse!, data: NSData!,error: NSError!) -> Void in
if error == nil {
self.senderProfileImage.image = UIImage(data: data)
println(imgURL)
}
})
if (messagetitle.isEmpty){
BodyMessage.text = "Er zijn geen berichten op dit moment."
}
else {
TitleMessage.text = messagetitle
BodyMessage.text = messagebody
}
}
#IBAction func answerPM(sender: AnyObject) {
var messageid = MessageId[0]
var messagesender = MessageSender[0]
var messagereceiver = MessageReceiver[0]
var senderusername = SenderUsername[0]
var replymessageid = ReplyMessageId[0]
// send over the orignal message so we can use that info to show the message again when they click the back button
var messagedate = MessageDate[0]
var messagebody = MessageBody[0]
var messagetitle = MessageTitle[0]
var messagetime = MessageTime[0]
var messagesendeR = MessageSender[0]
var centerViewController = self.storyboard?.instantiateViewControllerWithIdentifier("answerMessage") as! answerMessageView
centerViewController.MessageId2 = messageid
centerViewController.MessageSender2 = messagesender
centerViewController.MessageReceiver2 = messagereceiver
centerViewController.SenderUsername2 = senderusername
centerViewController.ReplyMessageId2 = replymessageid
println("sender username: \(senderusername) reply message id: \(replymessageid)")
centerViewController.A_messagedate = messagedate
centerViewController.A_messagebody = messagebody
centerViewController.A_messagetitle = messagetitle
centerViewController.A_messagetime = messagetime
centerViewController.A_messagesendeR = messagesendeR
var centerNavController = UINavigationController(rootViewController: centerViewController)
var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.centerContainer!.centerViewController = centerNavController
appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Left, animated: true, completion: nil)
}
#IBAction func viewAllMessages(sender: AnyObject) {
var userProfiles = self.storyboard?.instantiateViewControllerWithIdentifier("messagesList") as! MessagesView
var userProfileNav = UINavigationController(rootViewController: userProfiles)
var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.centerContainer!.centerViewController = userProfileNav
appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Left, animated: true, completion: nil)
}
#IBAction func markMGSRead(sender: AnyObject) {
}
func get_my_message_list(){
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
var receiverEmail = prefs.valueForKey("USERNAME") as! String
println("userEmail: \(receiverEmail)")
let request = NSMutableURLRequest(URL: NSURL(string: "xxxxxxxxxx/user/fetchmymessages.php")!)
//let urlEncodedString = urlString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
request.HTTPMethod = "POST"
var postData = "receiveremail=\(receiverEmail)"
request.HTTPBody = postData.dataUsingEncoding(NSUTF8StringEncoding)
NSLog("data: \(request)")
var task = NSURLSession.sharedSession().dataTaskWithRequest(request) {(data, response, innerError) in
NSLog("response: \(response)")
let json = JSON(data: data)
if json != nil {
let mysocialArray = json.arrayValue
NSLog("\(json)")
dispatch_async(dispatch_get_main_queue(), {
for mysociallist in mysocialArray
{
let messageId = mysociallist["message_id"].stringValue
let sender = mysociallist["sender"].stringValue
let receiver = mysociallist["receiver"].stringValue
let messageBody = mysociallist["message"].stringValue
let time = mysociallist["time"].stringValue
let date = mysociallist["date"].stringValue
let messageStatus = mysociallist["message_read"].stringValue
let messagesubject = mysociallist["message_title"].stringValue
let senderusername = mysociallist["sender_username"].stringValue
let replymessageid = mysociallist["reply_message_id"].stringValue
println( "message id: \(messageId)" )
println( "message sender: \(sender)" )
self.MessageId.append(messageId)
self.MessageSender.append(sender)
self.MessageReceiver.append(receiver)
self.MessageBody.append(messageBody)
self.MessageTime.append(time)
self.MessageDate.append(date)
self.MessageStatus.append(messageStatus)
self.MessageTitle.append(messagesubject)
self.SenderUsername.append(senderusername)
self.ReplyMessageId.append(replymessageid)
}
})
}else {
var alertView:UIAlertView = UIAlertView()
alertView.title = "Berichten ophalen mislukt"
alertView.message = "Er is een fout opgetreden, daardoor kunnen de berichten niet tonen."
alertView.delegate = self
alertView.addButtonWithTitle("OK")
alertView.show()
}
}
task.resume()
}// end of get my message list
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return menuItems.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
var mycell = tableView.dequeueReusableCellWithIdentifier("MyCell") as! MyCustomTableViewCell
mycell.menuItemLabel?.text = menuItems[indexPath.row]
mycell.menuItemImage?.image = UIImage(named: menuImage[indexPath.row])
return mycell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
switch(indexPath.row)
{
case 0:
var centerViewController = self.storyboard?.instantiateViewControllerWithIdentifier("memberArea") as! userOverview
var centerNavController = UINavigationController(rootViewController: centerViewController)
var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.centerContainer!.centerViewController = centerNavController
appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Left, animated: true, completion: nil)
break;
case 1:
var userProfiles = self.storyboard?.instantiateViewControllerWithIdentifier("userProfile") as! userProfile
var userProfileNav = UINavigationController(rootViewController: userProfiles)
var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.centerContainer!.centerViewController = userProfileNav
appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Left, animated: true, completion: nil)
break;
case 2:
var userProfiles = self.storyboard?.instantiateViewControllerWithIdentifier("userProfile") as! userProfile
var userProfileNav = UINavigationController(rootViewController: userProfiles)
var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.centerContainer!.centerViewController = userProfileNav
appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Left, animated: true, completion: nil)
break;
case 3:
var userProfiles = self.storyboard?.instantiateViewControllerWithIdentifier("messagesList") as! MessagesView
var userProfileNav = UINavigationController(rootViewController: userProfiles)
var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.centerContainer!.centerViewController = userProfileNav
appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Left, animated: true, completion: nil)
break;
case 4:
var userProfiles = self.storyboard?.instantiateViewControllerWithIdentifier("answerMessage") as! answerMessageView
var userProfileNav = UINavigationController(rootViewController: userProfiles)
var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.centerContainer!.centerViewController = userProfileNav
appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Left, animated: true, completion: nil)
break;
case 5:
var userProfiles = self.storyboard?.instantiateViewControllerWithIdentifier("answerMessage") as! answerMessageView
var userProfileNav = UINavigationController(rootViewController: userProfiles)
var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.centerContainer!.centerViewController = userProfileNav
appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Left, animated: true, completion: nil)
break;
case 6:
let appDomain = NSBundle.mainBundle().bundleIdentifier
NSUserDefaults.standardUserDefaults().removePersistentDomainForName(appDomain!)
self.performSegueWithIdentifier("loggin_out", sender: self)
break;
default:
println("\(menuItems[indexPath.row]) is selected");
}
}
}
this is the full code but the part that is giving the error is when the following is being called,
override func viewDidLoad() {
super.viewDidLoad()
var reload_messages = NSTimer.scheduledTimerWithTimeInterval(60.0, target: self, selector: Selector("message_Reloader"), userInfo: nil, repeats: true)
}
func message_Reloader(){
self.MessageId.removeAll()
self.MessageSender.removeAll()
self.MessageReceiver.removeAll()
self.MessageBody.removeAll()
self.MessageTime.removeAll()
self.MessageDate.removeAll()
self.MessageStatus.removeAll()
self.MessageTitle.removeAll()
self.SenderUsername.removeAll()
self.ReplyMessageId.removeAll()
get_my_message_list()
load_message()
}
i have left out a little part of viewdidload to make it not too long.
I am not a pro so i can really use some help here to figure out how to called the function and store it in the array so i can show the new message.
Hope some one can give me some code examples or can point me into the right direction, i am really stuck here and it keeps crashing my app.
Thanks
You have a race condition in your code: get_my_message_list() method is asynchronous, so load_message() can be called before get_my_message_list() is completed. In load_message() you're trying to access array members at index 0:
...
var messagebody = MessageBody[0]
var messagetitle = MessageTitle[0]
...
And that's the problem: those arrays can be empty (if get_my_message_list() still haven't updated them).
Related
The application uses iCloud to store the objects conformed to Codable protocol using NSKeyedArchiver/NSKeyedUnarchiver.
Synchronization between devices is OK, except when the application is reinstalled on the device (or installed on a new device) and the file with data exists – in this case
NSKeyedUnarchiver.unarchiveObject(withFile: filePathe) return nil.
How to get data from an existing file from the iCloud when I install the application on a new device (reinstall on the same device)?
class ViewController: UIViewController {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var weightLabel: UILabel!
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var weightTextField: UITextField!
var iCloudContainer: URL? {
return FileManager().url(forUbiquityContainerIdentifier: nil)
}
func getFilePath(container: URL, fileName: String) -> String {
let filePath = container.appendingPathComponent(fileName).path
return filePath
}
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func fetchButtonPressed(_ sender: UIButton) {
let container = self.iCloudContainer
let filePathe = getFilePath(container: container!, fileName: "Person")
if let jsonData = NSKeyedUnarchiver.unarchiveObject(withFile: filePathe) as? Data {
if let person = try? JSONDecoder().decode(Person.self, from: jsonData) {
nameLabel.text = person.name
weightLabel.text = String(person.weight)
} else {
nameLabel.text = "No data loaded"
weightLabel.text = "No data loaded"
}
} else {
nameLabel.text = "No data loaded"
weightLabel.text = "No data loaded"
}
}
#IBAction func saveButtonPressed(_ sender: UIButton) {
let container = self.iCloudContainer
let filePathe = getFilePath(container: container!, fileName: "Person")
let person = Person(name: nameTextField.text!, weight: Double(weightTextField.text!)!)
let jsonData = try? JSONEncoder().encode(person)
NSKeyedArchiver.archiveRootObject(jsonData!, toFile: filePathe)
}
To get data from iCloud container to your local UbiquityContainer you should use NSMetadata to find and download items from iCloud to device.
lazy var metadataQuery : NSMetadataQuery = {
let query = NSMetadataQuery()
query.searchScopes = [NSMetadataQueryUbiquitousDocumentsScope]
query.predicate = NSPredicate(format: "%K CONTAINS %#", NSMetadataItemFSNameKey, "List")
NotificationCenter.default.addObserver(self, selector: #selector(didFinishGathering), name: NSNotification.Name.NSMetadataQueryDidUpdate, object: query)
NotificationCenter.default.addObserver(self, selector: #selector(didFinishGathering), name: NSNotification.Name.NSMetadataQueryDidFinishGathering, object: query)
return query
}()
override func viewDidLoad() {
super.viewDidLoad()
self.metadataQuery.start()
}
deinit {
NotificationCenter.default.removeObserver(self)
}
#objc func didFinishGathering(notification: Notification?) {
let query = notification?.object as? NSMetadataQuery
query?.enumerateResults { (item: Any, index: Int, stop: UnsafeMutablePointer<ObjCBool>) in
let metadataItem = item as! NSMetadataItem
if isMetadataItemDownloaded(item: metadataItem) == false {
let url = metadataItem.value(forAttribute: NSMetadataItemURLKey) as! URL
try? FileManager.default.startDownloadingUbiquitousItem(at: url)
}
}
guard let queryresultsCount = query?.resultCount else { return }
for index in 0..<queryresultsCount {
let item = query?.result(at: index) as? NSMetadataItem
let itemName = item?.value(forAttribute: NSMetadataItemFSNameKey) as! String
let container = filesCoordinator.iCloudContainer
let filePath = filesCoordinator.getFilePath(container: container!, fileName: "TaskList")
let addressPath = filesCoordinator.getFilePath(container: container!, fileName: "CategoryList")
if itemName == "TaskList" {
if let jsonData = NSKeyedUnarchiver.unarchiveObject(withFile: filePath) as? Data {
if let person = try? JSONDecoder().decode(Person.self, from: jsonData) {
nameLabel.text = person.name
weightLabel.text = String(person.weight)
} else {
nameLabel.text = "NOT decoded"
weightLabel.text = "NOT decoded"
}
} else {
nameLabel.text = "NOT unarchived"
weightLabel.text = "NOT unarchived"
}
} else if itemName == "CategoryList" {
if let jsonData = NSKeyedUnarchiver.unarchiveObject(withFile: addressPath) as? Data {
if let address = try? JSONDecoder().decode(Address.self, from: jsonData) {
streetLabel.text = address.street
houseLabel.text = String(address.house)
} else {
streetLabel.text = "NOT decoded"
houseLabel.text = "NOT decoded"
}
} else {
streetLabel.text = "NOT unarchived"
houseLabel.text = "NOT unarchived"
}
}
}
}
func isMetadataItemDownloaded(item : NSMetadataItem) -> Bool {
if item.value(forAttribute: NSMetadataUbiquitousItemDownloadingStatusKey) as? String == NSMetadataUbiquitousItemDownloadingStatusCurrent {
return true
} else {
return false
}
}
#IBAction func saveButtonPressed(_ sender: UIButton) {
let container = filesCoordinator.iCloudContainer
let personPath = filesCoordinator.getFilePath(container: container!, fileName: "TaskList")
let addressPath = filesCoordinator.getFilePath(container: container!, fileName: "CategoryList")
let person = Person(name: nameTextField.text!, weight: Double(weightTextField.text!)!)
let jsonPersonData = try? JSONEncoder().encode(person)
NSKeyedArchiver.archiveRootObject(jsonPersonData!, toFile: personPath)
let address = Address(street: streetTextField.text!, house: Int(houseTextField.text!)!)
let jsonAddressData = try? JSONEncoder().encode(address)
NSKeyedArchiver.archiveRootObject(jsonAddressData!, toFile: addressPath)
}
}// end of the class
I'm making an app that parses JSON data & populates a tableview. In this app i want to make a SearchBar to search the id's. Everything is fine with parsing and populating the tableview but I am having problem with the searching. When I run the app I am gettin the following error: terminating with uncaught exception of type NSException
I have two arrays, first one is the main one and the other one is filtered one.
Requirement: I want to search array by "DesenNo" which i declared in my model.
Here are my codes:
ViewController:
import UIKit
import Foundation
import os.log
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchResultsUpdating, HomeModelProtocol {
//Properties
var feedItems: NSArray = NSArray()
var detailViewController: detailsVC? = nil
var searchActive : Bool = false
var filteredData:[String] = []
var resultSearchController:UISearchController!
#IBOutlet weak var listTableView: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
resultSearchController = UISearchController(searchResultsController: nil)
resultSearchController.searchResultsUpdater = self
resultSearchController.hidesNavigationBarDuringPresentation = false
resultSearchController.dimsBackgroundDuringPresentation = false
resultSearchController.searchBar.searchBarStyle = UISearchBarStyle.prominent
resultSearchController.searchBar.sizeToFit()
self.listTableView.tableHeaderView = resultSearchController.searchBar
self.listTableView.dataSource = self
let homeModel = HomeModel()
homeModel.delegate = self
homeModel.downloadItems()
}
func itemsDownloaded(items: NSArray) {
feedItems = items
self.listTableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if resultSearchController.isActive {
return filteredData.count
}
else {
return feedItems.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Retrieve cell
let cellIdentifier: String = "BasicCell"
let myCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! MyTableCell
// Get the location to be shown
// Get references to labels of cell
//Label1
if resultSearchController.isActive {
let item: LocationModel = feedItems[indexPath.row] as! LocationModel
let desenNoToString = item.DesenNo
let desenString = "\(desenNoToString)"
let regex = try! NSRegularExpression(pattern:"\"(.*)\"")
if let match = regex.firstMatch(
in: desenString, range:NSMakeRange(0,desenString.utf16.count)) {
let result = (desenString as NSString).substring(with: match.range(at:1))
myCell.label1!.text = result
}
//Label2
let dolarToString = item.Dolar
let dolarString = "\(dolarToString)"
if let match = regex.firstMatch(
in: dolarString, range:NSMakeRange(0,dolarString.utf16.count)) {
let result = (dolarString as NSString).substring(with: match.range(at:1))
myCell.label2!.text = result
}
//Label3
let zeminToString = item.Zemin
let zeminString = "\(zeminToString)"
if let match = regex.firstMatch(
in: zeminString, range:NSMakeRange(0,zeminString.utf16.count)) {
let result = (zeminString as NSString).substring(with: match.range(at:1))
myCell.label3!.text = result
}
//Label4
let renkToString = item.Renk
let renkString = "\(renkToString)"
if let match = regex.firstMatch(
in: renkString, range:NSMakeRange(0,renkString.utf16.count)) {
let result = (renkString as NSString).substring(with: match.range(at:1))
myCell.label4!.text = result
}
//Label5
let enToString = item.En
let enString = "\(enToString)"
if let match = regex.firstMatch(
in: enString, range:NSMakeRange(0,enString.utf16.count)) {
let result = (enString as NSString).substring(with: match.range(at:1))
myCell.label5!.text = result
}
//Label6
let euroToString = item.Euro
let euroString = "\(euroToString)"
if let match = regex.firstMatch(
in: euroString, range:NSMakeRange(0,euroString.utf16.count)) {
let result = (euroString as NSString).substring(with: match.range(at:1))
myCell.label6!.text = result
}
}else {
let item: LocationModel = feedItems[indexPath.row] as! LocationModel
let desenNoToString = item.DesenNo
let desenString = "\(desenNoToString)"
let regex = try! NSRegularExpression(pattern:"\"(.*)\"")
if let match = regex.firstMatch(
in: desenString, range:NSMakeRange(0,desenString.utf16.count)) {
let result = (desenString as NSString).substring(with: match.range(at:1))
myCell.label1!.text = result
}
//Label2
let dolarToString = item.Dolar
let dolarString = "\(dolarToString)"
if let match = regex.firstMatch(
in: dolarString, range:NSMakeRange(0,dolarString.utf16.count)) {
let result = (dolarString as NSString).substring(with: match.range(at:1))
myCell.label2!.text = result
}
//Label3
let zeminToString = item.Zemin
let zeminString = "\(zeminToString)"
if let match = regex.firstMatch(
in: zeminString, range:NSMakeRange(0,zeminString.utf16.count)) {
let result = (zeminString as NSString).substring(with: match.range(at:1))
myCell.label3!.text = result
}
//Label4
let renkToString = item.Renk
let renkString = "\(renkToString)"
if let match = regex.firstMatch(
in: renkString, range:NSMakeRange(0,renkString.utf16.count)) {
let result = (renkString as NSString).substring(with: match.range(at:1))
myCell.label4!.text = result
}
//Label5
let enToString = item.En
let enString = "\(enToString)"
if let match = regex.firstMatch(
in: enString, range:NSMakeRange(0,enString.utf16.count)) {
let result = (enString as NSString).substring(with: match.range(at:1))
myCell.label5!.text = result
}
//Label6
let euroToString = item.Euro
let euroString = "\(euroToString)"
if let match = regex.firstMatch(
in: euroString, range:NSMakeRange(0,euroString.utf16.count)) {
let result = (euroString as NSString).substring(with: match.range(at:1))
myCell.label6!.text = result
}
}
return myCell
}
func updateSearchResults(for searchController: UISearchController) {
if searchController.isActive {
filteredData.removeAll(keepingCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (feedItems as NSArray).filtered(using: searchPredicate)
filteredData = array as! [String]
listTableView.reloadData()
}
else {
filteredData.removeAll(keepingCapacity: false)
filteredData = feedItems as! [String]
listTableView.reloadData()
}
}
}
LocationModel.Swift
import UIKit
class LocationModel: NSObject {
//properties
var DesenNo: String?
var Dolar: String?
var Zemin: String?
var En: String?
var Euro: String?
var Renk: String?
//empty constructor
override init()
{
}
//construct with #name, #address, #latitude, and #longitude parameters
init(DesenNo: String, Dolar: String, Zemin: String, En: String, Euro: String, Renk: String) {
self.DesenNo = DesenNo
self.Dolar = Dolar
self.Zemin = Zemin
self.En = En
self.Euro = Euro
self.Renk = Renk
}
//prints object's current state
override var description: String {
return "Desen: \(DesenNo), Dolar: \(Dolar), Zemin: \(Zemin), Renk: \(Renk), En: \(En), Euro: \(Euro) "
}
}
HomeModel.Swift
import UIKit
protocol HomeModelProtocol: class {
func itemsDownloaded(items: NSArray)
}
class HomeModel: NSObject {
//properties
weak var delegate: HomeModelProtocol!
let urlPath = "http://aktul.com/den.php" //this will be changed to the path where service.php lives
func downloadItems() {
let url: URL = URL(string: urlPath)!
let defaultSession = Foundation.URLSession(configuration: URLSessionConfiguration.default)
let task = defaultSession.dataTask(with: url) { (data, response, error) in
if error != nil {
print("Failed to download data")
}else {
print("Data downloaded")
self.parseJSON(data!)
}
}
task.resume()
}
func parseJSON(_ data:Data) {
var jsonResult = NSArray()
do{
jsonResult = try JSONSerialization.jsonObject(with: data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSArray
} catch let error as NSError {
print(error)
}
var jsonElement = NSDictionary()
let urunler = NSMutableArray()
for i in 0 ..< jsonResult.count
{
jsonElement = jsonResult[i] as! NSDictionary
let urun = LocationModel()
//the following insures none of the JsonElement values are nil through optional binding
if let desen = jsonElement["DesenNo"] as? String,
let dolar = jsonElement["Dolar"] as? String,
let zemin = jsonElement["Zemin"] as? String,
let renk = jsonElement["Renk"] as? String,
let en = jsonElement["En"] as? String,
let euro = jsonElement["Euro"] as? String
//let longitude = jsonElement["Longitude"] as? String
{
urun.DesenNo = desen
urun.Dolar = dolar
urun.Zemin = zemin
urun.Renk = renk
urun.En = en
urun.Euro = euro
}
urunler.add(urun)
}
DispatchQueue.main.async(execute: { () -> Void in
self.delegate.itemsDownloaded(items: urunler)
})
}
}
Update with following code in updatesearchResults function
func updateSearchResults(for searchController: UISearchController) {
if searchController.isActive {
filteredData.removeAll(keepingCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = self.feedItems.filter { searchPredicate.evaluate(with: ($0 as! LocationModel).DesenNo) }
let filterArray = array.map {($0 as! LocationModel).DesenNo}
filteredData = filterArray as! [String]
tabelView.reloadData()
}
else {
filteredData.removeAll(keepingCapacity: false)
tabelView.reloadData()
}
}
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 am trying to display a Json result (temperature and Humidity) on my view controller (respectively temperatureDisp and humidityDisp), but it does not seem to work.
class HomeVC: UIViewController {
#IBOutlet var usernameLabel: UILabel!
#IBOutlet var temperatureDisp: UILabel!
#IBOutlet var humidityDisp: UILabel!
#IBAction func logoutTapped(sender: AnyObject) {
let appDomain = NSBundle.mainBundle().bundleIdentifier
NSUserDefaults.standardUserDefaults().removePersistentDomainForName(appDomain!)
self.performSegueWithIdentifier("goto_login", sender: self)
}
override func viewDidAppear(animated: Bool) {
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
let isLoggedIn:Int = prefs.integerForKey("ISLOGGEDIN") as Int
if (isLoggedIn != 1) {
self.performSegueWithIdentifier("goto_login", sender: self)
} else {
self.usernameLabel.text = prefs.valueForKey("USERNAME") as! NSString as String
}
}
override func viewDidLoad() {
super.viewDidLoad()
var url2 : String = "http://admin:xxxxxxx#xxxxxx/xxxxx.fr/metrics2.php"
var request2 : NSMutableURLRequest = NSMutableURLRequest()
request2.URL = NSURL(string: url2)
request2.HTTPMethod = "GET"
NSURLConnection.sendAsynchronousRequest(request2, queue: NSOperationQueue(), completionHandler: {(response: NSURLResponse!,data: NSData!,error: NSError!) -> Void in
var error: AutoreleasingUnsafeMutablePointer<NSError?> = nil
let jsonResult : NSArray! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: error) as! NSArray
if (jsonResult != nil) {
println(jsonResult)
} else {
println("There is a problem")
}
var temperature = jsonResult[0].valueForKey("temperature") as! String
var humidity = jsonResult[0].valueForKey("humidite") as! String
println(temperature)
println(humidity)
self.humidityDisp.text = temperature
})
}
}}
That is how the variable jsonResult looks :
(
{
Id = 117;
date = "2015-04-06";
humidite = "45.3";
login = raspberrypi;
luminosite = "\U00e9teinte";
temperature = "18.4";
time = "16:25:21";
}
)
I'm not sure what you mean by "it does not seem to work", but from your code I can assume at least that you are not getting the values you expect from your JSON result, but more likely your app is crashing like crazy. If you want to write applications in Swift you absolutely must understand optionals and learn how to properly work with them. Read The Swift Programming Language—and thoroughly. As your code is now, by force unwrapping using as! and using implicitly unwrapped types (those followed by !) you are ignoring the entire concept of optionals and opening yourself up to crashes.
So, assuming that there is no network or parsing errors, and assuming that the JSON string you're parsing has an array as its root object, the following should work. I've taken the liberty of typing and unwrapping your variables appropriately, as well as cleaning up some of the cruft.
class HomeVC: UIViewController {
#IBOutlet var usernameLabel: UILabel!
#IBOutlet var temperatureDisp: UILabel!
#IBOutlet var humidityDisp: UILabel!
#IBAction func logoutTapped(sender: AnyObject) {
if let appDomain = NSBundle.mainBundle().bundleIdentifier {
NSUserDefaults.standardUserDefaults().removePersistentDomainForName(appDomain)
}
self.performSegueWithIdentifier("goto_login", sender: self)
}
override func viewDidAppear(animated: Bool) {
let prefs = NSUserDefaults.standardUserDefaults()
let isLoggedIn = prefs.boolForKey("ISLOGGEDIN")
if isLoggedIn {
self.performSegueWithIdentifier("goto_login", sender: self)
} else {
if let username = prefs.stringForKey("USERNAME") {
self.usernameLabel.text = username
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
var url2 = "http://admin:xxxxxxx#xxxxxx/xxxxx.fr/metrics2.php"
var request2 = NSMutableURLRequest()
request2.URL = NSURL(string: url2)
request2.HTTPMethod = "GET"
NSURLConnection.sendAsynchronousRequest(request2, queue: NSOperationQueue()) { (response: NSURLResponse!,data: NSData!,error: NSError!) in
var parseError:NSError?
if let data = data, let jsonResults = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &parseError) as? NSArray {
println( jsonResults )
if let result = jsonResults.firstObject as? [String : AnyObject] {
if let humidity = result["humidite"] as? String {
self.humidityDisp.text = humidity
}
if let temperature = result["temperature"] as? String {
self.temperatureDisp.text = temperature
}
}
} else {
println("There is a problem: \(parseError)" )
}
}
}
}
The following Swift code has to run twice in order to display the JSON data in a label. On the first run the label simply remains blank. It seems to me that the issue could be something along the lines of the JSON part of the code runs last, but I can't figure out why. I have included the code below. I'm still a rookie, so be gentle :D
class ViewController: UIViewController {
#IBOutlet weak var labelDegrees: UILabel!
#IBOutlet weak var labelSpeed: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Set logo in nav bar
navigationItem.titleView = UIImageView(image: UIImage(named: "logo"))
// Global save values
var appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
var context: NSManagedObjectContext = appDel.managedObjectContext!
// JSON Fetching
let urlPath = "http://api.openweathermap.org/data/2.5/weather?lat=55.564120&lon=12.568605"
let url = NSURL(string: urlPath)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
if (error != nil) {
println(error)
}
else {
// Delete old entries in CoreData
var request = NSFetchRequest(entityName: "WindData")
request.returnsObjectsAsFaults = false
var results = context.executeFetchRequest(request, error: nil)
for result in results! {
context.deleteObject(result as NSManagedObject)
context.save(nil)
}
// Start fetching JSON
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
var item = jsonResult["wind"] as NSDictionary
var degrees:Float = item["deg"] as Float
var speed:Float = item["speed"] as Float
// Start saving JSON
var newItem = NSEntityDescription.insertNewObjectForEntityForName("WindData", inManagedObjectContext: context) as NSManagedObject
var speedValue:Float = speed as Float
var degreesValue:Float = degrees as Float
newItem.setValue(speedValue, forKey: "speed")
newItem.setValue(degreesValue, forKey: "degrees")
context.save(nil)
}
})
task.resume()
// Start fetching from CoreData
var request = NSFetchRequest(entityName: "WindData")
request.returnsObjectsAsFaults = false
var results = context.executeFetchRequest(request, error: nil)
if results!.count > 0 {
for result in results as [NSManagedObject] {
let degrees:Float = result.valueForKey("degrees")! as Float
let speed:Float = result.valueForKey("speed")! as Float
if speed > 6.0 {
self.labelDegrees.text = "Go kitesurf: \(speed) m/s"
}
else {
self.labelDegrees.text = "Stay in: \(speed) m/s"
}
}
}
else {
println("No data")
}
}
You are actually fetching the data on another thread with dataTaskWithURL.So you need to write // Start fetching from CoreData code in dataTaskWithURL.As dataTaskWithURL will not immediately executed so // Start fetching from CoreData code will get called first while dataTaskWithURL is fetching the data.So replece the code with below
class ViewController: UIViewController {
#IBOutlet weak var labelDegrees: UILabel!
#IBOutlet weak var labelSpeed: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Set logo in nav bar
navigationItem.titleView = UIImageView(image: UIImage(named: "logo"))
// Global save values
var appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
var context: NSManagedObjectContext = appDel.managedObjectContext!
// JSON Fetching
let urlPath = "http://api.openweathermap.org/data/2.5/weather?lat=55.564120&lon=12.568605"
let url = NSURL(string: urlPath)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
dispatch_async(dispatch_get_main_queue(),{
if (error != nil) {
println(error)
}
else {
// Delete old entries in CoreData
var request = NSFetchRequest(entityName: "WindData")
request.returnsObjectsAsFaults = false
var results = context.executeFetchRequest(request, error: nil)
for result in results! {
context.deleteObject(result as NSManagedObject)
context.save(nil)
}
// Start fetching JSON
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
var item = jsonResult["wind"] as NSDictionary
var degrees:Float = item["deg"] as Float
var speed:Float = item["speed"] as Float
// Start saving JSON
var newItem = NSEntityDescription.insertNewObjectForEntityForName("WindData", inManagedObjectContext: context) as NSManagedObject
var speedValue:Float = speed as Float
var degreesValue:Float = degrees as Float
newItem.setValue(speedValue, forKey: "speed")
newItem.setValue(degreesValue, forKey: "degrees")
context.save(nil)
}
// Start fetching from CoreData
var request = NSFetchRequest(entityName: "WindData")
request.returnsObjectsAsFaults = false
var results = context.executeFetchRequest(request, error: nil)
if results!.count > 0 {
for result in results as [NSManagedObject] {
let degrees:Float = result.valueForKey("degrees")! as Float
let speed:Float = result.valueForKey("speed")! as Float
if speed > 6.0 {
self.labelDegrees.text = "Go kitesurf: \(speed) m/s"
}
else {
self.labelDegrees.text = "Stay in: \(speed) m/s"
}
}
}
else {
println("No data")
}
});
})
task.resume()
}