Having trouble unwrapping my variables - ios

EDIT 1: All spots where I define something related to the "author" variable (at bottom)
Can someone tell me why my labels still say "Optional()". My variables should be getting unwrapped through this code, should they not ? I am getting JSON data, parsing it into local objects and then using those objects in a table view (and a subsequent detail view) with labels.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let row = indexPath.row
guard let cell = tableView.dequeueReusableCellWithIdentifier(EditorialTableCellIdentifier, forIndexPath: indexPath) as? EditorialsTableViewCell else {
print ("error: editorialsTableView cell is not of class EditorialsTableViewCell, we will use RandomTableViewCell instead")
return tableView.dequeueReusableCellWithIdentifier(EditorialTableCellIdentifier, forIndexPath: indexPath) as! RandomTableViewCell
}
if let editorialObject = editorialObjects.objectAtIndex(indexPath.row) as? EditorialElement {
// we just unwrapped editorialObject
let title = editorialObject.title ?? "" // if editorialObject.title == nil, then we return an empty string.
let timeStampDateObject = NSDate(timeIntervalSince1970: NSTimeInterval(editorialObject.timeStamp))
let timeStampDateString = dateFormatter.stringFromDate(timeStampDateObject)
let author = editorialObject.author ?? ""
let issueNumber = editorialObject.issueNumber ?? ""
let volumeNumber = editorialObject.volumeNumber ?? ""
let articleContent = editorialObject.articleContent ?? ""
let nodeID = editorialObject.nodeID ?? 0
cell.editorialHeadlineLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
cell.editorialHeadlineLabel.text = title
cell.editorialAuthorLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleSubheadline)
cell.editorialAuthorLabel.text = String(author)
cell.editorialPublishDateLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleSubheadline)
cell.editorialPublishDateLabel.text = timeStampDateString
} else {
}
return cell
}
Class code:
class EditorialElement: NSObject {
var title: String // title
var nodeID: Int // nid
var timeStamp: Int // revision_timestamp
var imageURL: String // image_url
var author: String // author
var issueNumber: String // issue_int
var volumeNumber: String // volume_int
var articleContent: String // html_content
init(title: String, nodeID: Int, timeStamp: Int, imageURL: String, author: String, issueNumber: String, volumeNumber: String, articleContent: String) {
self.title = title
self.nodeID = nodeID
self.timeStamp = timeStamp
self.imageURL = imageURL
self.author = author
self.issueNumber = issueNumber
self.volumeNumber = volumeNumber
self.articleContent = articleContent
}
override func isEqual(object: AnyObject!) -> Bool {
return (object as! EditorialElement).nodeID == self.nodeID
}
override var hash: Int {
return (self as EditorialElement).nodeID
}
}
EDIT 1: Here is everything related to author (after edits)
In my IssueElement class:
var author: String // author
In my populateCurrentIssue function:
issueElement.author = String(node.1["author"])
In my cellForRowAtIndexPath (within the
if let currentIssueObject = currentIssueObjects.objectAtIndex(indexPath.row) as? IssueElement
loop.
let author = currentIssueObject.author
And finally:
cell.currentIssueArticlesAuthorLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleSubheadline)
cell.currentIssueArticlesAuthorLabel.text = author

Looking at your code, editorialHeadlineLabel should not show "Optional()" in any case.
editorialAuthorLabel can be optional because String() can be optional if the constructor fails to initialize a string.
editorialPublishDateLabel can be optional because stringFromDate() will return nil if you pass an invalid date.

Related

Remove duplicate elements in swift array of objects

So I have a section in my app which displays events that your followers are going to. I successfully pull the followers and there corresponding events. I then add those events to a bigger array of event objects. I am currently having trouble removing the duplicates I have tried many extensions but it doesn't seem to be working. I figure it is because the array contains different objects with different memory addresses so when I compare them they aren't the same regardless of the fact that the content is. I have been messing with this for a while now and can't really figure anything out. if anyone could help me I would appreciate it.
This is the method grabs the events and returns them to an array in my main VC.
static func showFollowingEvent(for followerKey: String,completion: #escaping (Event) -> Void) {
//getting firebase root directory
let ref = Database.database().reference()
ref.child("users").child(followerKey).child("Attending").observeSingleEvent(of: .value, with: { (attendingSnapshot) in
print(attendingSnapshot)
guard var eventKeys = attendingSnapshot.children.allObjects as? [DataSnapshot] else{return}
for event in eventKeys{
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
EventService.show(forEventKey: event.key, completion: { (event) in
dispatchGroup.leave()
completion(event!)
})
}
}) { (err) in
print("couldn't grab event info",err)
}
} print("couldn't grab event info",err)
}
}
This is the function that receives the events and reloads the collectionView upon getting them.
#objc func grabFriendsEvents(){
print("Attempting to see where your friends are going")
UserService.following { (user) in
for following in user {
print(following.username as Any)
PostService.showFollowingEvent(for: following.uid, completion: { (event) in
self.friendsEvents.append(event)
// self.friendsEvents.append(contentsOf: event)
// leave here
self.allEvents2["Friends Events"] = self.friendsEvents.removeDuplicates()
self.collectionView?.reloadData()
})
}
}
}
I tried these extensions which I saw in previous questions and nothing worked.
extension Array where Element: Equatable {
func removeDuplicates() -> [Element] {
var result = [Element]()
for value in self {
if result.contains(value) == false {
result.append(value)
}
}
return result
}
mutating func removeDuplicatesTwo() {
var result = [Element]()
for value in self {
if !result.contains(value) {
result.append(value)
}
}
self = result
}
}
import Foundation
import FirebaseDatabase.FIRDataSnapshot
#objc(Event)
class Event:NSObject{
var key: String?
let currentEventName: String
let currentEventImage: String
let currentEventPromo: String?
let currentEventDescription: String
//nested properties
let currentEventStreetAddress: String
let currentEventCity: String
let currentEventState: String
let currentEventDate: String?
let currentEventEndDate: String?
let currentEventTime: String?
let currentEventEndTime: String?
let currentEventZip: Int
var category: String
//nested properties stop
var currentAttendCount: Int
var isAttending = false
var eventDictionary: [String: Any]{
let dateDict = ["start:date":currentEventDate, "start:time": currentEventTime,"end:time":currentEventEndTime, "end:date": currentEventEndDate]
return ["event:name":currentEventName,"event:imageURL" : currentEventImage,
"event:description": currentEventDescription, "attend:count": currentAttendCount,
"event:street:address": currentEventStreetAddress,"event:zip": currentEventZip,
"event:state": currentEventState, "event:city": currentEventCity, "event:promo": currentEventPromo ?? "", "event:date": dateDict, "event:category":category]
}
init(currentEventKey: String, dictionary: [String:Any]) {
self.key = currentEventKey
self.currentEventName = dictionary["event:name"] as? String ?? ""
self.currentEventImage = dictionary["event:imageURL"] as? String ?? ""
self.currentEventDescription = dictionary["event:description"] as? String ?? ""
self.currentEventPromo = dictionary["event:promo"] as? String ?? ""
self.currentAttendCount = dictionary["attend:count"] as? Int ?? 0
self.category = dictionary["event:category"] as? String ?? ""
//nested properties
self.currentEventStreetAddress = dictionary["event:street:address"] as? String ?? ""
self.currentEventCity = dictionary["event:city"] as? String ?? ""
self.currentEventState = dictionary["event:state"] as? String ?? ""
self.currentEventZip = dictionary["event:zip"] as? Int ?? 0
let eventDate = dictionary["event:date"] as? [String: Any]
self.currentEventDate = eventDate?["start:date"] as? String ?? ""
self.currentEventTime = eventDate?["start:time"] as? String ?? ""
self.currentEventEndTime = eventDate?["end:time"] as? String ?? ""
self.currentEventEndDate = eventDate?["end:date"] as? String ?? ""
}
init?(snapshot: DataSnapshot) {
guard let dict = snapshot.value as? [String : Any],
let currentEventName = dict["event:name"] as? String,
let currentEventImage = dict["event:imageURL"] as? String,
let currentEventDescription = dict["event:description"] as? String,
let currentEventPromo = dict["event:promo"] as? String,
let category = dict["event:category"] as? String,
let currentEventStreetAddress = dict["event:street:address"] as? String,
let currentEventCity = dict["event:city"] as? String,
let currentEventState = dict["event:state"] as? String,
let currentEventZip = dict["event:zip"] as? Int,
let currentAttendCount = dict["attend:count"] as? Int,
let eventDate = dict["event:date"] as? [String: Any],
let currentEventDate = eventDate["start:date"] as? String,
let currentEventEndDate = eventDate["end:date"] as? String,
let currentEventTime = eventDate["start:time"] as? String,
let currentEventEndTime = eventDate["end:time"] as? String
else { return nil }
self.key = snapshot.key
self.currentEventName = currentEventName
self.currentEventImage = currentEventImage
self.currentEventDescription = currentEventDescription
self.currentEventStreetAddress = currentEventStreetAddress
self.currentEventCity = currentEventCity
self.currentEventState = currentEventState
self.currentEventZip = currentEventZip
self.currentAttendCount = currentAttendCount
self.currentEventPromo = currentEventPromo
self.currentEventDate = currentEventDate
self.currentEventTime = currentEventTime
self.currentEventEndTime = currentEventEndTime
self.category = category
self.currentEventEndDate = currentEventEndDate
}
static func ==(lhs: Event, rhs: Event) -> Bool {
return lhs.key == rhs.key
}
}
Classes of type NSObject will automatically call isEqual() for the contains() method. You can override the superclass's implementation to fit your logic.
If your class HAS to inherit NSObject, then use:
class Event: NSObject {
var key: String?
init(key: String) {
self.key = key
}
override func isEqual(_ object: Any?) -> Bool {
guard let event = object as? Event else { return false }
return self.key == event.key
}
}
var event = Event(key: "abc")
var eventCopy = Event(key: "abc")
extension Array where Element:Equatable {
func removeDuplicates() -> [Element] {
return reduce(into: []) { result, element in
if !result.contains(element) {
result.append(element)
}
}
}
}
var events = [event, eventCopy]
events = events.removeDuplicates()
print(events.count)
If your class does not inherit NSObject, make it conform to the Equatable protocol.
class Event: Equatable {
var key: String?
init(key: String) {
self.key = key
}
static func ==(lhs: Event, rhs: Event) -> Bool {
return lhs.key == rhs.key
}
}
var event = Event(key: "abc")
var eventCopy = Event(key: "abc")
extension Array where Element:Equatable {
func removeDuplicates() -> [Element] {
var result = [Element]()
for value in self {
if result.contains(value) == false {
result.append(value)
}
}
return result
}
}
var events = [event, eventCopy]
events = events.removeDuplicates()
print(events.count)
You have to make Event class conform to Equatable protocol

How to get text and store it from Text Field inside a Table View Cell

I'm currently developing an app without using a UIStoryboard. I have a simple form with 4 UITextField objects to get input from users (first name, last name etc.).
I want to get the values and store them in my model. However, it always seems to be empty. How can I get the values without using a UIStoryBoard? Here is my model:
class Records: NSObject, NSCoding {
//MARK: Properties
var firstName: String
var lastName:String
var occupation:String
var placeMeet: String
var notes:String
//MARK: Archiving Paths
static let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
static let ArchiveURL = DocumentsDirectory.appendingPathComponent("records")
//MARK: Types
struct PropertyKey {
static let firstName = "firstName"
static let lastName = "lastName"
static let occupation = "occupation"
static let placeMeet = "placeMeet"
static let notes = "notes"
}
//MARK: Initialization
init?(firstName: String, lastName:String, occupation: String, placeMeet: String, notes: String) {
// The name must not be empty
guard !firstName.isEmpty else {
return nil
}
guard !lastName.isEmpty else {
return nil
}
guard !occupation.isEmpty else {
return nil
}
guard !placeMeet.isEmpty else {
return nil
}
guard !notes.isEmpty else {
return nil
}
// Initialization should fail if there is no name or if the rating is negative.
if firstName.isEmpty || lastName.isEmpty {
return nil
}
// Initialize stored properties.
self.firstName = firstName
self.lastName = lastName
self.occupation = occupation
self.placeMeet = placeMeet
self.notes = notes
}
//MARK: NSCoding
func encode(with aCoder: NSCoder) {
aCoder.encode(firstName, forKey: PropertyKey.firstName)
aCoder.encode(lastName, forKey: PropertyKey.lastName)
aCoder.encode(occupation, forKey: PropertyKey.occupation)
aCoder.encode(placeMeet, forKey: PropertyKey.placeMeet)
aCoder.encode(notes, forKey: PropertyKey.notes)
}
required convenience init?(coder aDecoder: NSCoder) {
// The name is required. If we cannot decode a name string, the initializer should fail.
guard let firstName = aDecoder.decodeObject(forKey: PropertyKey.firstName) as? String else {
os_log("Unable to decode the name for a Record object.", log: OSLog.default, type: .debug)
return nil
}
guard let lastName = aDecoder.decodeObject(forKey: PropertyKey.lastName) as? String else {
os_log("Unable to decode the name for a Record object.", log: OSLog.default, type: .debug)
return nil
}
// Because photo is an optional property of Meal, just use conditional cast.
let occupation = aDecoder.decodeObject(forKey: PropertyKey.occupation) as? String
let placeMeet = aDecoder.decodeObject(forKey: PropertyKey.placeMeet) as? String
let notes = aDecoder.decodeObject(forKey: PropertyKey.notes) as? String
// Must call designated initializer.
self.init(firstName:firstName , lastName: lastName, occupation: occupation!, placeMeet: placeMeet! , notes:notes!)
}
}
And here is what Im trying to do
func save() {
let homepage = HomepageController()
let navController = UINavigationController(rootViewController: homepage) // Creating a navigation controller with VC1 at the root of the navigation stack
let myCell = tableView.dequeueReusableCell(withIdentifier: "cellId") as! AddContactCell
let note = tableView.dequeueReusableCell(withIdentifier: "note") as! Note
var occu = ""
var fname = ""
var lname = ""
var place = ""
var noteString = ""
self.navigationController?.present(navController, animated: true)
if myCell.nameLabel.text == items[0] {
occu = myCell.input_field.text!
}
else if myCell.nameLabel.text == items[1] {
fname = myCell.input_field.text!
}
else if myCell.nameLabel.text == items[2] {
lname = myCell.input_field.text!
}
else if myCell.nameLabel.text == "Place" {
place = myCell.input_field.text!
}
else {
noteString = note.input_field.text!
}
record = Records(firstName:fname , lastName: lname, occupation: occu, placeMeet:place, notes: noteString)
}
EDITED:
cellForRow override function
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCell(withIdentifier: "cellId") as! AddContactCell
let note = tableView.dequeueReusableCell(withIdentifier: "note") as! Note
myCell.input_field.delegate = self
note.input_field.delegate = self as? UITextViewDelegate
if indexPath.section == 0 {
myCell.nameLabel.text = items[indexPath.row]
if (myCell.nameLabel.text == "Occupation")
{
myCell.input_field.attributedPlaceholder = NSAttributedString(string: "Occupation",
attributes: [NSForegroundColorAttributeName:UIColor(red:0.81, green:0.81, blue:0.83, alpha:1.0)])
}
else if (myCell.nameLabel.text == "First Name"){
myCell.input_field.attributedPlaceholder = NSAttributedString(string: "First Name",
attributes: [NSForegroundColorAttributeName:UIColor(red:0.81, green:0.81, blue:0.83, alpha:1.0)])
}
else {
myCell.input_field.attributedPlaceholder = NSAttributedString(string: "Last Name",
attributes: [NSForegroundColorAttributeName:UIColor(red:0.81, green:0.81, blue:0.83, alpha:1.0)])
}
myCell.myContactController = self
return myCell
}
else if indexPath.section == 1 {
myCell.nameLabel.text = "Place"
myCell.input_field.attributedPlaceholder = NSAttributedString(string: "Place",
attributes: [NSForegroundColorAttributeName:UIColor(red:0.81, green:0.81, blue:0.83, alpha:1.0)])
return myCell
}
else {
return note
}
}
In func save()
let myCell = tableView.dequeueReusableCell(withIdentifier: "cellId") as! AddContactCell
is wrong...
Use below code
let myCell = tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as! AddContactCell

How to save and fetch data from file in swift?

I want to save the response from JSON in a file and fetch from it when the network is not available. However on trying to fetch idea by disabling the wifi, the app always crashes. Are there any other ways for offline fetching in swift except saving in database??
This is the error I am getting : Could not cast value of type 'Swift._NSContiguousString' (0x109e22320) to 'NSArray'
This is what I have done so far:
Create a model
class Directory : NSObject, NSCoding {
var data : [AnyObject]
var tid : String
var vid : String
var name : String
var imgThumbnail : String
var imgMedium : String
var imgLarge : String
var child : String
// MARK: Archiving Paths
init(data:[AnyObject],tid:String,vid:String,name:String,imgThumbnail:String,imgMedium:String,imgLarge:String,child:String) {
self.data = data ?? []
self.tid = tid ?? ""
self.vid = vid ?? ""
self.name = name ?? ""
self.imgThumbnail = imgThumbnail ?? ""
self.imgMedium = imgMedium ?? ""
self.imgLarge = imgLarge ?? ""
self.child = child ?? ""
}
// MARK: NSCoding
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(data, forKey:Constants.kData)
aCoder.encodeObject(name, forKey:Constants.Directory.kName )
aCoder.encodeObject(tid, forKey: Constants.Directory.tid)
aCoder.encodeObject(vid, forKey: Constants.Directory.vid)
aCoder.encodeObject(imgThumbnail, forKey:Constants.Directory.kImageThumbnail)
aCoder.encodeObject(imgMedium, forKey: Constants.Directory.kImageMedium)
aCoder.encodeObject(imgLarge, forKey: Constants.Directory.kImageLarge)
aCoder.encodeObject(child, forKey: Constants.Directory.kChild)
}
required convenience init?(coder aDecoder: NSCoder) {
let data = aDecoder.decodeObjectForKey(Constants.kData) as! [AnyObject]
let name = aDecoder.decodeObjectForKey(Constants.Directory.kName) as! String
let tid = aDecoder.decodeObjectForKey(Constants.Directory.tid) as! String
let vid = aDecoder.decodeObjectForKey(Constants.Directory.vid) as! String
let imgThumbnail = aDecoder.decodeObjectForKey(Constants.Directory.kImageThumbnail) as! String
let imgMedium = aDecoder.decodeObjectForKey(Constants.Directory.kImageMedium) as! String
let imgLarge = aDecoder.decodeObjectForKey(Constants.Directory.kImageLarge) as! String
let child = aDecoder.decodeObjectForKey(Constants.Directory.kChild) as! String
// Must call designated initializer.
self.init(data:data,tid:tid,vid:vid,name:name,imgThumbnail:imgThumbnail,imgMedium: imgMedium,imgLarge: imgLarge, child: child)
}
}
Code for saving and loading the data from file
class func loadSavedFile(fileName: String) -> AnyObject? {
let pathString: String = Utility.fetchFilePathString(fileName)
print("Here the pathString is \(pathString)")
if NSFileManager.defaultManager().fileExistsAtPath(pathString) {
return NSKeyedUnarchiver.unarchiveObjectWithFile(pathString)!
} else {
return "File doesn't exist"
}
return ""
}
class func saveObject(object: AnyObject, toFile fileName: String) {
let pathString: String = Utility.fetchFilePathString(fileName)
NSKeyedArchiver.archiveRootObject(object, toFile: pathString)
}
class func fetchFilePathString(fileName: String) -> String {
let pathAray: [AnyObject] = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.AllDomainsMask, true)
let pathString = pathAray.last!
return NSURL(fileURLWithPath: pathString as! String).URLByAppendingPathComponent(fileName).absoluteString
}
Checking for network connection in the view controller
var directoryArr = [Directory]()
override func viewDidLoad() {
super.viewDidLoad()
if Utility.isNetworkReachable() {
Utility.saveObject([], toFile: Constants.File.kDirectory)
self.serviceCallDirectory()
} else {
self.directorie = (Utility.loadSavedFile(Constants.File.kDirectory) as? [Directory])!
self.tableView.reloadData()
}
Service Call
func serviceCallDirectory() -> Void {
let stringUrl = Constants.baseUrl + Constants.kDirectoryUrl
WebService.getRequestAPI(stringUrl, withSuccess: {(responseDic, Statusflag,error) in
if Statusflag {
self.tableView.backgroundColor = UIColor.clearColor()
self.tableView.hidden = false
let tempInfo = responseDic![Constants.kData] as! [AnyObject]
var imgthumbnail : String = ""
var imgmedium : String = ""
var imglarge : String = ""
var name : String = ""
var child : String = ""
if tempInfo.count != 0 {
for info in tempInfo {
let tid = info[Constants.Directory.tid] as! String
let vid = info[Constants.Directory.vid] as! String
if let names = info[Constants.Directory.kName] as? String {
name = names
}
if let childs = info[Constants.Directory.kChild] as? String {
child = childs
}
if let imgthumb = info[Constants.Directory.kImageThumbnail] as? String {
imgthumbnail = imgthumb
} else {
imgthumbnail = ""
}
if let imgmediumd = info[Constants.Directory.kImageMedium] as? String {
imgmedium = imgmediumd
} else {
imgmedium = ""
}
if let imglarges = info[Constants.Directory.kImageLarge] as? String {
imglarge = imglarges
}
let myModel = Directory(
data: tempInfo,
tid: tid,
vid: vid,
name: name,
imgThumbnail: imgthumbnail,
imgMedium: imgmedium,
imgLarge: "",
child: child
)
self.directorie.append(myModel)
}
I don't know that this is the only issue, but this code
class func loadSavedFile(fileName: String) -> AnyObject? {
let pathString: String = Utility.fetchFilePathString(fileName)
print("Here the pathString is \(pathString)")
if NSFileManager.defaultManager().fileExistsAtPath(pathString) {
return NSKeyedUnarchiver.unarchiveObjectWithFile(pathString)!
} else {
return "File doesn't exist"
}
return ""
}
Either returns an object or a string. That's not very sensible. It should return a success flag or a tuple or use a completion block. When you call this function your code expects to get back an array of directory, which in a number of cases won't happen
self.directorie = (Utility.loadSavedFile(Constants.File.kDirectory) as? [Directory])!
The error in your question indicates a different kind of data mismatch. You should try not to use AnyObject, let swift help you by type checking what you're doing...

Autocompleting searchBar

I am trying to implement a searchBar in my iOS App. I am searching country and city from the app. Country and city names are coming from the database. There are few problems which I am having is in my code.
First right now my code successfully searches the city but it isn't searching country name and I don't know how can I do a search in country names as well.
func updateSearchResultsForSearchController(searchController: UISearchController) {
let searchWord = searchController.searchBar.text!
getCountriesNamesFromServer(searchWord)
self.filteredKeys.removeAll()
for (key, value) in self.dict {
let valueContainsSearchWord: Bool = (((value as? NSDictionary)?["City"] as? NSDictionary)?["name"] as? String)?.uppercaseString.containsString(searchWord.uppercaseString) ?? false
if valueContainsSearchWord {
self.filteredKeys.append(key as! String)
}
}
self.tableView.reloadData()
}
Second issue Is I want to get the ids of selected country and city as well which user selects on the app. For your information I am getting the ids from the server as well
Here is my full code
class CountryTableViewController: UITableViewController, UISearchResultsUpdating {
var dict = NSDictionary()
var filteredKeys = [String]()
var resultSearchController = UISearchController()
var newTableData = [String]()
override func viewDidLoad() {
super.viewDidLoad()
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
self.tableView.tableHeaderView = controller.searchBar
return controller
})()
self.tableView.reloadData()
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (self.resultSearchController.active) {
return self.filteredKeys.count
} else {
return dict.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CountryTableViewCell
if(self.resultSearchController.active){
let key = self.filteredKeys[indexPath.row]
let dictionary = self.dict[key] as! NSDictionary
if
let cityName = (((self.dict["\(indexPath.row)"] as?NSDictionary)!["City"] as?NSDictionary)!["name"] as?NSString),
let stateName = (((self.dict["\(indexPath.row)"] as?NSDictionary)!["State"] as? NSDictionary)!["name"] as? NSString),
let shortName = (((self.dict["\(indexPath.row)"] as?NSDictionary)!["Country"] as? NSDictionary)!["short_name"] as? NSString)
{
cell.stateNameLabel.text = stateName as String
cell.cityNameLabel.text = cityName as String
cell.shortNameLabel.text = shortName as String
}
return cell
}else{
if let cityName = (((self.dict["\(indexPath.row)"] as?NSDictionary)!["City"] as?NSDictionary)!["name"] as?NSString){
cell.cityNameLabel.text = cityName as String
}
return cell
}
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
let searchWord = searchController.searchBar.text!
getCountriesNamesFromServer(searchWord)
self.filteredKeys.removeAll()
for (key, value) in self.dict {
let valueContainsSearchWord: Bool = (((value as? NSDictionary)?["City"] as? NSDictionary)?["name"] as? String)?.uppercaseString.containsString(searchWord.uppercaseString) ?? false
if valueContainsSearchWord {
self.filteredKeys.append(key as! String)
}
}
self.tableView.reloadData()
}
func getCountriesNamesFromServer(searchWord:String){
let url:String = "http://localhost"
let params = ["keyword":searchWord]
ServerRequest.postToServer(url, params: params) { result, error in
if let result = result {
print("result is\(result)")
self.dict = result
}
}
}
}
dictionary structure:
{
0 = {
City = {
code = 10409;
"country_id" = 244;
id = 8727;
iso = "ZA-05";
lat = "-32.7833000";
longi = "26.8333000";
name = Alice;
"state_id" = 4379;
};
Country = {
id = 244;
name = "South Africa";
"short_name" = ZA;
};
State = {
"country_id" = 244;
id = 4379;
name = "Eastern Cape";
};
};
You are only searching for ["City"] key in dicitionay, should add ["Country"] key too.
for (key, value) in self.dict {
let valueContainsCity: Bool = (((value as? NSDictionary)?["City"] as? NSDictionary)?["name"] as? String)?.uppercaseString.containsString(searchWord.uppercaseString) ?? false
let valueContainsCountry: Bool = (((value as? NSDictionary)?["Country"] as? NSDictionary)?["name"] as? String)?.uppercaseString.containsString(searchWord.uppercaseString) ?? false
if valueContainsCity || valueContainsCountry{ self.filteredKeys.append(key as! String) }
}
uppercaseString - is your city and country names in uppercaseString? if not do a lowerCaseString
this will get you city id and country id.
(((self.dict["\(indexPath.row)"] as?NSDictionary)!["City"] as?NSDictionary)!["id"] as?NSString)
(((self.dict["\(indexPath.row)"] as?NSDictionary)!["Country"] as?NSDictionary)!["id"] as?NSString)

Swift dictionary deletes previous data when appending new data

I have a swift dictionary with custom objects and I use it to fill my UITableview. However, when I fill the dictionary, I realize that it deletes previous data when I append a new data to it. The related code blocks like below:
var dict = Dictionary<ListItem, [ListItem]>()
func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return self.dict.keys.array.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
var cell: CategoryCell! = tableView.dequeueReusableCellWithIdentifier("categoryCell", forIndexPath: indexPath) as? CategoryCell
if(cell == nil)
{
cell = CategoryCell(style: UITableViewCellStyle.Default, reuseIdentifier: "categoryCell")
}
else
{
cell.ClearCurrentCellContext(cell.image, labelTitle: cell.lblCategory, labelDescription: cell.lblDescription)
}
if let ip = indexPath as NSIndexPath?
{
let key = Array(self.dict.keys)[ip.row] as ListItem
cell.SetCell(key, image: cell.image, labelTitle: cell.lblCategory, labelDescription: cell.lblDescription)
}
return cell
}
And I fill the dictionary inside of this block:
Alamofire.request(.POST, WSConstants.WebServiceAddress, parameters: nil)
.responseJSON
{(request, response, JSON, error) in
if let jsonResult: NSDictionary = JSON as? NSDictionary
{
if let categories = jsonResult[WSConstants.CATEGORIES] as? NSDictionary
{
if let wsStatus = categories.valueForKey(WSConstants.WS_STATUS) as? String
{
if(wsStatus == "ok")
{
var mainObj: NSDictionary!
var mainItem: ListItem!
for(var i=0; i<mainJSONArray.count; i++)
{
mainObj = mainJSONArray.objectAtIndex(i) as! NSDictionary
mainItem = ListItem()
mainItem.itemId = mainObj.valueForKey(WSConstants.ID) as! Int
mainItem.itemName = mainObj.valueForKey(WSConstants.TITLE) as! String
mainItem.itemDescription = mainObj.valueForKey(WSConstants.DESCRIPTION) as! String
mainItem.itemIcon = mainObj.valueForKey(WSConstants.ICON) as! String
mainItem.parentId = mainObj.valueForKey(WSConstants.PARENT) as! Int
mainItem.postCount = mainObj.valueForKey(WSConstants.POST_COUNT) as! Int
if let subJSONArray = mainObj.valueForKey(WSConstants.SUB) as? NSArray
{
var midCategoryList: [ListItem]! = [ListItem]()
var subObj: NSDictionary!
var subItem: ListItem!
for(var i=0; i<subJSONArray.count; i++)
{
subObj = subJSONArray.objectAtIndex(i) as! NSDictionary
subItem = ListItem()
subItem.itemId = subObj.valueForKey(WSConstants.ID) as! Int
subItem.itemName = subObj.valueForKey(WSConstants.TITLE) as! String
subItem.itemDescription = subObj.valueForKey(WSConstants.DESCRIPTION) as! String
subItem.itemIcon = subObj.valueForKey(WSConstants.ICON) as! String
subItem.parentId = subObj.valueForKey(WSConstants.PARENT) as! Int
subItem.postCount = subObj.valueForKey(WSConstants.POST_COUNT) as! Int
midCategoryList.append(subItem)
subItem = nil
subObj = nil
}
// The code below line fills the dictionary with key and its values
self.dict[mainItem] = midCategoryList
midCategoryList = nil
}
mainItem = nil
mainObj = nil
}
Utility.ReloadTableViewDataWithAnimations(self.categoryTableView)
}
}
}
}
ListItem class:
class ListItem: Serializable, Hashable, NSCoding
{
var uniqueID: Int = 0
override var hashValue: Int { return uniqueID.hashValue }
var itemId: Int
var itemName: String!
var itemIcon: String!
var itemDescription: String!
var parentId: Int!
var postCount: Int!
var content: String!
override init()
{
self.itemId = 0
self.itemName = ""
self.itemIcon = ""
self.parentId = 0
self.postCount = 0
self.content = ""
self.itemDescription = ""
}
init(itemId: Int, itemName: String!, itemIcon: String!, itemDescription: String!, parentId: Int!, postCount: Int, content: String!, date: String!, url: String!)
{
self.itemId = itemId
self.itemName = itemName
self.itemIcon = itemIcon
self.parentId = parentId
self.postCount = postCount
self.content = content
self.itemDescription = itemDescription
}
deinit
{
}
required init(coder aDecoder: NSCoder)
{
self.itemId = aDecoder.decodeIntegerForKey("itemId") as Int
self.itemName = aDecoder.decodeObjectForKey("itemName") as! String
self.itemIcon = aDecoder.decodeObjectForKey("itemIcon") as! String
self.itemDescription = aDecoder.decodeObjectForKey("itemDescription") as! String
}
func encodeWithCoder(aCoder: NSCoder)
{
aCoder.encodeInteger(self.itemId, forKey: "itemId")
aCoder.encodeObject(self.itemName, forKey: "itemName")
aCoder.encodeObject(self.itemIcon, forKey: "itemIcon")
aCoder.encodeObject(self.itemDescription, forKey: "itemDescription")
}
}
func ==(lhs: ListItem, rhs: ListItem) -> Bool
{
return lhs.uniqueID == rhs.uniqueID
}
Is this a swift bug or I make some mistakes to fill it ?
Thank you for your answers
King regards
Solution:
The reason of the problem is that using == instead of === in ListItem class. Daniel T.'s solution is the right answer. Thank you all.
Without seeing more code, my guess is that the problem lies in your ListItem's == operator. The following code does not exhibit the problem you are describing:
//: Playground - noun: a place where people can play
class ListItem: Hashable {
var itemId: Int = 0
var itemName: String = ""
var itemDescription: String = ""
var itemIcon: String = ""
var parentId: Int = 0
var postCount: Int = 0
var hashValue: Int {
return itemId.hashValue
}
}
func ==(lhs: ListItem, rhs: ListItem) -> Bool {
return lhs === rhs // simple identity
}
var dict = Dictionary<ListItem, [ListItem]>()
let arr = [ListItem(), ListItem()]
dict[ListItem()] = arr
dict[ListItem()] = arr
dict.keys.array.count

Resources