struct not keeping sorted order (swift4) - ios

My code has 2 textfields one for int and the other for string. The way the code is supposed to be sorted is by ascending alphabetical order for the string (a-z)then by descending order for the int (9-1).So i entered 2 entries in a,2 a,1 order but the way the list is being displayed is a,2 a,1 which is not the sorted order. How can I keep the sorted order?
VIEWCONTROLLER
#IBAction func move(_ sender: Any) {
yourArray.append((textA.text!))
number.append(Int(textB.text!)!)
let tuples = zip(yourArray,number)
let sorted = tuples.sorted(by: { this, next in
if this.0 < next.0 {
return true
} else if this.0 == next.0 {
return this.1 < next.1
} else {
return false
}})
bad.mm.append(String(describing: sorted.map { " \($0)" }.joined(separator:"\n")))
}
struct bad {
static var mm = [String]()}
VIEWCONTROLLER2
override func viewDidLoad() {
super.viewDidLoad()
let defaults = UserDefaults.standard
defaults.set(benCarson.text, forKey: "SavedStringArray2")
defaults.synchronize()
benCarson.text = String(describing: bad.mm)
benCarson.numberOfLines = 5000
}

Related

Not Repeating Word in Swift when clicking button - IOS

How can I not repeat the array when I click on the button in swift? I'm trying to generate fruits without them repeating.
import UIKit
class fruitrandomViewController: UIViewController {
#IBOutlet weak var nextfruitButton: UIButton!
#IBOutlet weak var fruitbox: UILabel!
#IBAction func fruitbutton(_ sender: UIButton) {
let array = ["Apple","Banana","Orange","Pinapple", "Plum", "Pear",]
let randomFruitgenerator = Int(arc4random_uniform(UInt32(array.count)))
fruitbox.text = array[randomFruitgenerator]
}
}
My suggestion is to use a Set and remove the random item from the set
var set = Set(["Apple","Banana","Orange","Pinapple", "Plum", "Pear"])
#IBAction func fruitbutton(_ sender: UIButton) {
if let fruit = set.randomElement() {
fruitbox.text = fruit
set.remove(fruit)
} else {
fruitbox.text = "" // or whatever to indicate that the set is empty
}
}
My suggest:
You can random each item 1 time
let originFruits = ["Apple","Banana","Orange","Pinapple", "Plum", "Pear"]
let array = originFruits
#IBAction func fruitbutton(_ sender: UIButton) {
...
let fruitRandom = array random
array delete fruitRandom
if (array empty) {
array = originFruits
}
}
You can check and remove in next time
let originFruits = ["Apple","Banana","Orange","Pinapple", "Plum", "Pear"]
let array = originFruits
let skipFruit = ""
#IBAction func fruitbutton(_ sender: UIButton) {
...
array = originFruits
array delete skipFruit
let fruitRandom = array random
skipFruit = fruitRandom
}
I think this will work, but I think it's Time Complexity can be O(n) as I am assuming that there is a possibility of randomElement return Apple every time and savedFruit is also Apple, so in that case, the Time Complexity will be O(n). A better workaround will be to remove that element from that array so, the next time the randomElement will be different for sure. Then, once it is different you can append the old one and remove the current one. I hope it makes sense to you, but for now this will work I think:
let array = ["Apple","Banana","Orange","Pinapple", "Plum", "Pear"]
var savedFruit = String()
func fetchRandomFruit() {
if let fruit = array.randomElement() {
if fruit != savedFruit { //Here it will check if the last element is same as the new randomElement
savedFruit = fruit
fruitbox.text = savedFruit
} else {
fetchRandomFruit()
}
}
}
#IBAction func fruitbutton(_ sender: UIButton) {
fetchRandomFruit()
}
Use fruitbox.text = array.randomElement() ?? "default value for if array is empty". This is not guaranteed not to repeat, but is random.
If you want to ensure it does not repeat, use this
var new = array.randomElement() ?? fruitbox.text
while new == fruitbox.text {
new = array.randomElement() ?? fruitbox.text
}
fruitbox.text = new
This WILL loop infinitely (until it crashes) if the array is empty.

How to edit realm object and save editing?

I have 2 Controllers: TableViewController and ViewController. TableViewController is responsible for displaying all data, View Controller is responsible for creating new data.
Now I want to make it possible to edit the current data also in ViewController. When we click on data, we need to switch to the ViewController and replace all default values with the current values. When we change and click save, we go back to TableViewController, where we already see the change.
class OperationsViewController: UITableViewController {
// MARK: - Stored Properties
var transactions: Results<Transaction>!
var sections = [(date: Date, items: Results<Transaction>)]()
// MARK: - UITableViewController Methods
override func viewDidLoad() {
super.viewDidLoad()
transactions = realm.objects(Transaction.self)
}
override func viewWillAppear(_ animated: Bool) {
super .viewWillAppear(animated)
assembleGroupedTransactions()
tableView.reloadData()
}
}
// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let indexPath = tableView.indexPathForSelectedRow {
let section = sections[indexPath.section]
let item = section.items[indexPath.row]
print(item)
if segue.identifier == "editOrDeleteOperationCell" {
let addTableViewController = segue.destination as! AddTableViewController
addTableViewController.defaultTransaction = item
}
}
}
}
// MARK: - User Interface
extension OperationsViewController {
#discardableResult private func assembleGroupedTransactions() -> Array<Any> {
// fetch all Items sorted by date
let results = realm.objects(Transaction.self).sorted(byKeyPath: "date", ascending: false)
sections = results
.map { item in
// get start of a day
return Calendar.current.startOfDay(for: item.date)
}
.reduce([]) { dates, date in
// unique sorted array of dates
return dates.last == date ? dates : dates + [date]
}
.compactMap { startDate -> (date: Date, items: Results<Transaction>) in
// create the end of current day
let endDate = Calendar.current.date(byAdding: .day, value: 1, to: startDate)!
// filter sorted results by a predicate matching current day
let items = results.filter("(date >= %#) AND (date < %#)", startDate, endDate)
// return a section only if current day is non-empty
return (date: startDate, items: items)
}
return sections
}
But when I trying to send current data to next ViewController I get error:
*** Terminating app due to uncaught exception 'RLMException', reason: 'Attempting to modify object outside of a write transaction - call beginWriteTransaction on an RLMRealm instance first.'
I guess that I have problem with Category. Look on my model:
class Transaction: Object {
#objc dynamic var controlStatus = 0
#objc dynamic private var privateCategory: String = Category.consumption.rawValue
var category: Category {
get { return Category(rawValue: privateCategory)! }
set { privateCategory = newValue.rawValue }
}
#objc dynamic var amount = "0"
#objc dynamic var date = Date()
#objc dynamic var note = ""
}
controlStatus needs for monitors the status of the transaction that will be needed in the future. Where 0 is the expense, 1 is the income.
The big problem I suppose is that I created categories by enum.
I need to change the arrays with categories depending on the controlStatus.
Now is this my model of Category:
indirect enum Category: String {
case income = "+"
case consumption = "-"
case salary = "salary"
case billingInterest = "billingInterest"
case partTimeJob = "partTimeJob"
etc.
}
extension Category: RawRepresentable {
typealias RawValue = String
init?(rawValue: RawValue) {
switch rawValue {
case "+": self = .income
case "-": self = .consumption
case "salary": self = .salary
case "billingInterest": self = .billingInterest
case "partTimeJob: self = .partTimeJob
case "pleasantFinds": self = .pleasantFinds
case "debtRepayment": self = .debtRepayment
case "noCategories": self = .noCategories
case "food": self = .food
etc.
default:
return nil
}
}
var rawValue: RawValue {
switch self {
case .salary:
return "salary"
case .billingInterest:
return "billingInterest"
case .partTimeJob:
return "partTimeJob"
case .pleasantFinds:
return "pleasantFinds"
case .debtRepayment:
return "debtRepayment"
case .noCategories:
return "noCategories"
case .food:
return "food"
case .cafesAndRestaurants:
return "cafesAndRestaurants"
etc.
}
}
}
You need to enclose your realm write transactions in write block
try realm.write({ () -> Void in
realm.add(object, update: true)
})
From Realm doc
try! realm.write {
realm.add(myDog)
}

Error upon core data change processing

I am working on a coin collection app, and I have recently started integrating core data into my app. I have a core data database which stores custom CoinCategory objects and each CoinCategory object has an array of Coin objects. This way, my app stores everything in terms of categories.
When I integrated the core data, I can add my first category without any errors and delete it without any problems, but when I add in a second coin category, I experience the following error:
2017-06-26 09:55:37.218 CoinCollection[18889:12839563] -[CoinCollection.CoinCategory compare:]: unrecognized selector sent to instance 0x608000236cc0
2017-06-26 09:55:37.219215-0400 CoinCollection[18889:12839563] [error] error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. -[CoinCollection.CoinCategory compare:]: unrecognized selector sent to instance 0x608000236cc0 with userInfo (null)
CoreData: error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. -[CoinCollection.CoinCategory compare:]: unrecognized selector sent to instance 0x608000236cc0 with userInfo (null)
Could anyone please advise me on how to fix this? Thank you very much!
I am attaching my code below for my viewcontroller and the app delegate that is running the core data.
AppDelegate.swift
// We customize the app, system-wide
import UIKit
import CoreData
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "CoinCollection")
container.loadPersistentStores(completionHandler: { (storeDescription,
error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext ()
{
let context = persistentContainer.viewContext
if context.hasChanges
{
do
{
try context.save()
}
catch
{
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate.
//You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}
And then for my view controller. Notice that I get a Coin object from a sub-viewcontroller that invokes this method, and that we decide whether the Coin object fits into the existing categories. If not, then we add the new Coin.
// Controls the table view controller showing the general coins (one per each category)
import UIKit
import CoreData
class CoinTableViewController: UITableViewController, NSFetchedResultsControllerDelegate {
//this is an array of all the coins in the collection
//each row of this two-dimensional array represents a new category
var coinsByCategory: [CoinCategoryMO] = []
var fetchResultController: NSFetchedResultsController<CoinCategoryMO>!
//other attributes....
////////////////////////////////////////////////////////////////////////
override func viewDidLoad()
{
super.viewDidLoad()
//////////////////////////////////////////////////////////////////////////////////
//we now fetch the data
let fetchRequest : NSFetchRequest<CoinCategoryMO> = CoinCategoryMO.fetchRequest()
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate)
{
let context = appDelegate.persistentContainer.viewContext
let sortDescriptor = NSSortDescriptor(key: "coinCategory", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
fetchResultController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
fetchResultController.delegate = self
do
{
try fetchResultController.performFetch()
if let fetchedObjects = fetchResultController.fetchedObjects
{
self.coinsByCategory = fetchedObjects
}
}
catch
{
print(error)
}
}
//configure even more....
}
// MARK: - Table view data soure
func deleteCoinCategory(rowPath: IndexPath)
{
if 0 <= rowPath.row && rowPath.row < self.coinsByCategory.count
{
//we have just tested that the rowPath index is valid
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate)
{
let context = appDelegate.persistentContainer.viewContext
let coinCategoryToDelete = self.fetchResultController.object(at: rowPath)
context.delete(coinCategoryToDelete)
appDelegate.saveContext()
}
}
}
func deleteCoin(c: Coin, indexOfSelectedCategory: IndexPath) -> Bool
{
//we have a coin that we want to delete from this viewcontroller
//and the data contained in it.
//
//the parameter indexOfSelectedCategory refers to the IndexPath of the
//row in the TableView contained in THIS viewcontroller whose category
//of coins we are modifying in this method
//
//Return value: a boolean that indicates whether a single coin has
//been deleted - meaning that the user should return to the parentviewcontroller
if 0 < indexOfSelectedCategory.row && indexOfSelectedCategory.row < self.coinsByCategory.count && self.coinsByCategory[indexOfSelectedCategory.row].coinCategory?.hasCoin(c: c) == true
{
//the index is valid as it refers to a category in the coinsByCategory array
//and the examined category has the coin in question
if self.coinsByCategory[indexOfSelectedCategory.row].coinCategory?.count == 1
{
//the coin "c" that we are going to delete is the only coin in the entire category
self.deleteCoinCategory(rowPath: indexOfSelectedCategory)
return true
}
else
{
//more than one coin in the category
self.coinsByCategory[indexOfSelectedCategory.row].coinCategory?.removeCoin(c: c)
//we save the changes in the database...
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate)
{
appDelegate.saveContext()
}
return false
}
}
return false
}
func addCoin(coinToAdd: Coin)
{
//we check over each category to see if the coin can be added
for category in self.coinsByCategory
{
if category.coinCategory?.coinFitsCategory(aCoin: coinToAdd) == true
{
//we can add the coin to the category
category.coinCategory?.addCoin(newCoin: coinToAdd)
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate)
{
//we save changes to the database
appDelegate.saveContext()
//we are DONE with this function
return
}
}
}
//since the coinToAdd does not fall in the existing categories, we create a new one
if let appDelegate = (UIApplication.shared.delegate as? AppDelegate)
{
let newCategory = CoinCategoryMO(context: appDelegate.persistentContainer.viewContext)
newCategory.coinCategory = CoinCategory(coins: [coinToAdd], categoryType: CoinCategory.CategoryTypes.COUNTRY_VALUE_AND_CURRENCY)
print("Saving data to context ...")
appDelegate.saveContext()
}
}
//delegate methods control the core data database
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>)
{
tableView.beginUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?)
{
switch type
{
case .insert :
if let newIndexPath = newIndexPath
{
tableView.insertRows(at: [newIndexPath], with: .fade)
}
case .delete:
if let indexPath = indexPath
{
tableView.deleteRows(at: [indexPath], with: .fade)
}
case .update:
if let indexPath = indexPath
{
tableView.reloadRows(at: [indexPath], with: .fade)
}
default:
tableView.reloadData()
}
if let fetchedObjects = controller.fetchedObjects
{
self.coinsByCategory = fetchedObjects as! [CoinCategoryMO]
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>)
{
tableView.endUpdates()
}
I am looking forward to hearing your thoughts and thanks in advance for the help!
EDIT:
My code for the CoinCategory class:
// This class is supposed to represent a category of coin objects in CoreData
import Foundation
import CoreData
public class CoinCategory: NSObject, NSCoding
{
//These are the various types of categories that a user can create out of their coin collection
enum CategoryTypes : NSString
{
case COUNTRY_VALUE_AND_CURRENCY = //...
case COUNTRY = //...
case YEAR = //...
case CURRENCY = //...
case NO_CATEGORY = //...
}
//this struct is used to encode data in Key-Value pairs per the NSCoding protocol
struct Keys
{
static let Current_Category_Type = "current_category_type"
static let Coins_In_Category = "coins_in_category"
}
//this is the collection of the coins in the category
var coinsInCategory: [Coin] = [] //initially we have no coins in the collection
var currentCategoryType : CategoryTypes.RawValue = ""
public var count : NSNumber
{
get
{
//..number of coins in category
}
}
public var array : [Coin]
{
get
{
//read-only copy of the Coins array..
}
}
public required init?(coder aDecoder: NSCoder)
{
//we decode this object's information
if let categoryTypeObject = aDecoder.decodeObject(forKey: Keys.Current_Category_Type) as? CategoryTypes.RawValue
{
self.currentCategoryType = categoryTypeObject
}
if let coinsInCategoryArrayObject = aDecoder.decodeObject(forKey: Keys.Coins_In_Category) as? [Coin]
{
self.coinsInCategory = coinsInCategoryArrayObject
}
}
public func encode(with aCoder: NSCoder)
{
//we encode this object's information
aCoder.encode(currentCategoryType, forKey: Keys.Current_Category_Type)
aCoder.encode(self.coinsInCategory, forKey: Keys.Coins_In_Category)
}
override init()
{
super.init()
self.currentCategoryType = CategoryTypes.COUNTRY_VALUE_AND_CURRENCY.rawValue
self.coinsInCategory = []
}
convenience init(coins: [Coin], categoryType: CategoryTypes.RawValue)
{
self.init()
self.coinsInCategory = coins
if isACategoryType(categoryType: categoryType) == true
{
self.currentCategoryType = categoryType
}
else
{
self.currentCategoryType = CategoryTypes.NO_CATEGORY.rawValue
}
}
func isACategoryType(categoryType: NSString) -> Bool
{
switch categoryType
{
case CategoryTypes.COUNTRY_VALUE_AND_CURRENCY.rawValue:
return true
case CategoryTypes.COUNTRY.rawValue:
return true
case CategoryTypes.YEAR.rawValue:
return true
case CategoryTypes.CURRENCY.rawValue:
return true
default:
return false
}
}
func addCoin(newCoin: Coin)
{
//we are adding a new Coin object to this category
//if it falls into the category's type
if self.coinFitsCategory(aCoin: newCoin) == true
{
self.coinsInCategory.append(newCoin)
}
}
func coinFitsCategory(aCoin: Coin) -> Bool
{
//this function tests if aCoin fits into the category type
//but that all varies depending on which category the coin is
if self.coinsInCategory.count == 0
{
//this category is currently empty, so any addition goes!
return true
}
//otherwise, this category is not empty... so we are now going to
//examine the situation more critically
let testCoin = self.coinsInCategory[0]
switch self.currentCategoryType
{
case CategoryTypes.COUNTRY_VALUE_AND_CURRENCY.rawValue:
return (testCoin.getCountry().lowercased == aCoin.getCountry().lowercased) && (testCoin.getValue() == aCoin.getValue()) && (testCoin.getDenomination().lowercased == aCoin.getDenomination().lowercased)
case CategoryTypes.COUNTRY.rawValue:
return testCoin.getCountry().lowercased == aCoin.getCountry().lowercased
case CategoryTypes.CURRENCY.rawValue:
return testCoin.getDenomination().lowercased == aCoin.getDenomination().lowercased
case CategoryTypes.YEAR.rawValue:
return testCoin.getYear() == aCoin.getYear()
default:
return false
}
}
func getIndexOfCoinInCollection(coin: Coin) -> Int
{
//we are going to return -1 if the coin does not exist in the collection
//and are going to return the index otherwise if yes
for i in 0..<self.coinsInCategory.count
{
if coinsInCategory[i] == coin
{
return i
}
}
//have not found anything
return -1
}
func removeCoin(at: Int)
{
//we remove the coin at the index if it is in a valid range of the coinInCategory array
if isValidArrayIndex(index: at)
{
self.coinsInCategory.remove(at: at)
}
}
func getCoin(at: Int) -> Coin?
{
//we return nil if there is an issue in accessing the coin
if isValidArrayIndex(index: at)
{
return self.coinsInCategory[at]
}
else
{
return nil
}
}
func assignCoin(at: Int,c: Coin)
{
if isValidArrayIndex(index: at)
{
self.coinsInCategory[at].assign(right: c)
}
}
func deleteAllCoins()
{
//we delete all the coin in this category
self.coinsInCategory.removeAll()
}
func removeCoin(c: Coin)
{
//we delete a coin from the category
for i in 0..<self.coinsInCategory.count
{
if self.coinsInCategory[i] == c
{
//the coin at index "i" is equal to the coin "c" that we want to delete from the category
self.coinsInCategory.remove(at: i)
return
}
}
}
func hasCoin(c: Coin) -> Bool
{
return getIndexOfCoinInCollection(coin: c) != -1
}
func swapValuesWithAnotherCategory(other: CoinCategory)
{
swap(&self.currentCategoryType, &other.currentCategoryType)
swap(&self.coinsInCategory,&other.coinsInCategory)
}
func swapCoins(indexOne: Int, indexTwo: Int)
{
if isValidArrayIndex(index: indexOne) && isValidArrayIndex(index: indexTwo)
{
swap(&self.coinsInCategory[indexOne],&self.coinsInCategory[indexTwo])
}
}
private func isValidArrayIndex(index: Int) -> Bool
{
return (0 <= index && index < coinsInCategory.count)
}
}
And then the coin category class:
// This provides the class definition of Class Coin for the project
import UIKit
import CoreData
enum TimePeriods: String
{
//this enumeration represents the different time periods that a
//coin was minted in, for the sake of this programn
case BCE = "BCE"
case CE = "CE"
}
public class Coin : NSObject, NSCoding
{
//this struct represents all the keys used in encoding and decoding this object
struct Keys
{
static let Country = "country"
static let Mint = "mint"
static let Year = "year"
static let Currency = "currency"
static let Value = "value"
static let Grade = "grade"
static let Comments = "comments"
static let NumInstances = "numberInstances"
static let Description = "description"
static let Obverse = "obverse"
static let Reverse = "reverse"
}
//this represents a coin in the table view
static let GRADING_LOWER_LIMIT: NSNumber = 1
static let GRADING_UPPER_LIMIT: NSNumber = 70
//this represents the default strings returned if a field does not have the needed information
static let DEFAULT_DESCRIPTIVE_NAME: NSString = "(Description?)"
static let DEFAULT_COMMENTS: NSString = "(Comments?)"
static let DEFAULT_DENOMINATION: NSString = "(Denomination?)"
static let DEFAULT_MINT: NSString = "(Mint?)"
static let DEFAULT_COUNTRY: NSString = "(Country?)"
static let DEFAULT_YEAR: NSString = "(Year?)"
static let DEFAULT_GRADE: NSString = "(Grade?)"
static let DEFAULT_VALUE_AND_DENOMINATION: NSString = "(Value?) (Currency?)"
static let OBVERSE_IMAGE_STRING : NSString = "Obverse"
static let REVERSE_IMAGE_STRING : NSString = "Reverse"
static private let BULLET = "➣ " //represents the kind of bullet to be used to build a complete summary of the coin
//declare members with setters and getters
private var country: NSString = "" //what country/empire/etc. used in?
private var mint: NSString = "" //where minted? EX: US Mint, St. Petersburg
private var year: NSNumber? = nil //what year minted? per gregorian calendar
//the year can be negative to represent the BCE period
//positive to represent the CE periods
private var typeCurrency: NSString = "" //what is the unit of value? EX: Cents, dollars, centavos, etc
private var theValue: NSNumber = 0 //how many? EX: how many dollars, cents, centavos, etc.?
//additional information about the coin
private var grade: NSNumber? //on the american grading scale for coins. 1-70
private var comments: NSString = "" //extra comments stored by the user for himself
private var numberOfInstances: NSNumber = 0 //number of coins exactly like this. EX: 1,2,3,4...etc? For each instance, it must be >= 1.
//This describes the type of the coin
//EX: Walking Liberty Quarter, Barber Quarter, Standing Liberty Quarter... etc
private var descriptiveName: NSString = ""
private var obverseImage: UIImage? = nil
private var reverseImage: UIImage? = nil
public var valueAndDenomination: NSString
{
get
{
//need to check four cases
//case 1: we have the right values for value and denomination
//case 2: we do not have a value but do have denomination
//case 3: we have a value but do not have denomination
//case 4: we do not have both
//
//the reason why we consider 0 to be an empty value is because a coin that was worth
//nothing would not have been minted in the first place!!!
if (self.theValue != 0 && self.typeCurrency != "")
{
//have value and denomination
return "\(self.theValue) \(self.typeCurrency)" as NSString //like "20 Cents"
}
else if (self.theValue == 0 && self.typeCurrency != "" )
{
//do not have value, but have denomination
return "(Value?) \(self.typeCurrency)" as NSString
}
else if (self.theValue != 0 && self.typeCurrency == "")
{
//we have value, but do not have denomination
return "\(self.theValue) (Currency?)" as NSString
}
else
{
//we do not have both
return Coin.DEFAULT_VALUE_AND_DENOMINATION as NSString
}
}
}
public required init?(coder aDecoder: NSCoder)
{
//we decode this object's information
if let countryObject = aDecoder.decodeObject(forKey: Keys.Country) as? NSString
{
self.country = countryObject
}
if let mintObject = aDecoder.decodeObject(forKey: Keys.Country) as? NSString
{
self.mint = mintObject
}
if let yearObject = aDecoder.decodeObject(forKey: Keys.Year) as? NSNumber
{
self.year = yearObject
}
if let currencyObject = aDecoder.decodeObject(forKey: Keys.Currency) as? NSString
{
self.typeCurrency = currencyObject
}
if let valueObject = aDecoder.decodeObject(forKey: Keys.Value) as? NSNumber
{
self.theValue = valueObject
}
if let gradeObject = aDecoder.decodeObject(forKey: Keys.Grade) as? NSNumber
{
self.grade = gradeObject
}
if let commentObject = aDecoder.decodeObject(forKey: Keys.Comments) as? NSString
{
self.comments = commentObject
}
if let numInstancesObject = aDecoder.decodeObject(forKey: Keys.NumInstances) as? NSNumber
{
self.numberOfInstances = numInstancesObject
}
if let descriptiveNameObject = aDecoder.decodeObject(forKey: Keys.Description) as? NSString
{
self.descriptiveName = descriptiveNameObject
}
if let obverseImageObject = aDecoder.decodeObject(forKey: Keys.Obverse) as? UIImage
{
self.obverseImage = obverseImageObject
}
if let reverseImageObject = aDecoder.decodeObject(forKey: Keys.Reverse) as? UIImage
{
self.reverseImage = reverseImageObject
}
}
override init()
{
//default initializer
super.init()
self.country = ""
self.mint = ""
self.year = nil
self.typeCurrency = ""
self.theValue = 0
self.comments = ""
self.numberOfInstances = 1
self.descriptiveName = ""
self.obverseImage = nil
self.reverseImage = nil
}
init(country: NSString,year: Int?,typeCurrency: NSString, theValue: NSNumber,mint: NSString,grade: Int?,numInstances: NSNumber = 1,description: NSString, comments: NSString)
{
super.init()
self.country = country
self.mint = mint
self.year = year! as NSNumber
self.typeCurrency = typeCurrency
self.theValue = theValue
self.comments = comments
self.numberOfInstances = numInstances
self.descriptiveName = description
self.obverseImage = nil
self.reverseImage = nil
}
init(country: NSString,year: NSNumber?,typeCurrency: NSString, theValue: NSNumber,mint: NSString,grade: NSNumber?,numInstances: NSNumber = 1,description: NSString, comments: NSString,obverseImage: UIImage, reverseImage: UIImage)
{
super.init()
self.country = country
self.mint = mint
self.year = year
self.typeCurrency = typeCurrency
self.theValue = theValue
self.comments = comments
self.numberOfInstances = numInstances
self.descriptiveName = description
}
public func encode(with aCoder: NSCoder)
{
//we encode the coin's information
aCoder.encode(self.country, forKey: Keys.Country)
aCoder.encode(self.mint, forKey: Keys.Mint)
aCoder.encode(self.year, forKey: Keys.Year)
aCoder.encode(self.typeCurrency, forKey: Keys.Currency)
aCoder.encode(self.theValue, forKey: Keys.Value)
aCoder.encode(self.grade, forKey: Keys.Grade)
aCoder.encode(self.comments, forKey: Keys.Comments)
aCoder.encode(self.numberOfInstances, forKey: Keys.NumInstances)
aCoder.encode(self.descriptiveName, forKey: Keys.Description)
aCoder.encode(self.obverseImage, forKey: Keys.Obverse)
aCoder.encode(self.reverseImage, forKey: Keys.Reverse)
}
//setter and getter functions for class members...
func getCompleteSummary() -> NSString
{
//returns a bulleted list that represents the coin
//and it describes every single detail...
}
func getIncompleteSummary() -> String
{
//returns a bulleted list string that represents the coin
//and it describes every single detail...
}
/////////////////////////////////////////////////////////////////////////////////
func ofSameType(rhs: Coin) -> Bool
{
return (self.getCountry().lowercased == rhs.getCountry().lowercased) && (self.getValue() == rhs.getValue()) && (self.getDenomination().lowercased == rhs.getDenomination().lowercased)
}
public static func==(lhs: Coin, rhs: Coin) -> Bool
{
//we compare two coin objects for equality in ALL Categories
return lhs.country.lowercased == rhs.country.lowercased &&
lhs.theValue == rhs.theValue &&
lhs.typeCurrency.lowercased == rhs.typeCurrency.lowercased &&
lhs.mint.lowercased == rhs.mint.lowercased &&
lhs.year == rhs.year &&
lhs.grade == rhs.grade &&
lhs.comments == rhs.comments &&
lhs.numberOfInstances == rhs.numberOfInstances &&
lhs.descriptiveName == rhs.descriptiveName &&
lhs.obverseImage == rhs.obverseImage &&
lhs.reverseImage == rhs.reverseImage
}
func assign(right: Coin)
{
//we implement this instead of overloading the assignment "=" operator
//as it is not possible to overload the "=" operator
//we assign the right-hand-coin's field values
//to the left-hand coin's side
self.country = right.country
self.theValue = right.theValue
self.typeCurrency = right.typeCurrency
self.mint = right.mint
self.year = right.year
self.grade = right.grade
self.comments = right.comments
self.numberOfInstances = right.numberOfInstances
self.descriptiveName = right.descriptiveName
self.obverseImage = right.obverseImage
self.reverseImage = right.reverseImage
}
}
It looks like the problem is that your CoinCategory class does not implement the compare: method:
[CoinCollection.CoinCategory compare:]: unrecognized selector sent to instance 0x608000236cc0
My guess is that the FRC is trying to compare the coinCategory attribute of the second CoinCategoryMO to the coinCategory attribute of the first CoinCategoryMO in order to get them in the correct order (since your FRC is sorted using the coinCategory attribute).
So the direct answer is that you need to implement a compare: method for your CoinCategory class. But I think this is just an indicator that you need to change your approach. Rather than having an Entity with attributes that are custom objects (or collections thereof), you should probably aim to have multiple Entities (eg. a CoinMO entity, a CoinCategoryMO entity, a CoinCategoryTypeMO entity, etc) with to-many relationships between them. If you show the code for your custom objects (Coin and CoinCategory) it will be easier to advise how best to model them in CoreData.

Get the indexPath of the relevant element in a loop

I'm working in a project in Swift 3.0 and I have a loop that would give me the state of an object (gives a boolean value in a an array sequence). My requirement is if the element state is "true" I wants to get the relevant index path of that, so using that indexPath I can get the whole object from my original array. So inside a loop how can i get that as well? My partially done code is below. In a comment I have stated where I wants to pass this indexPath.
func tapFunction(sender: UIButton) {
// This gives me the selected indexPath
print("ID :",sender.accessibilityIdentifier!)
if let rowIndexString = sender.accessibilityIdentifier, let rowIndex = Int(rowIndexString) {
self.sateOfNewSongArray[rowIndex] = !self.sateOfNewSongArray[rowIndex]
}
sender.isSelected = !sender.isSelected
for (element) in self.sateOfNewSongArray {
//This is where I wants to pass the rowIndex that became true
if element == true {
selectedSongList.addingObjects(from: [songList[rowIndex]])
}
}
}
Note: Make sure your array 'sateOfNewSongArray' has boolean types
of elements
Try following solutions:
Sample Code 1: According to your current code in your question.
func tapFunction(sender: UIButton) {
// This gives me the selected indexPath
print("ID :",sender.accessibilityIdentifier!)
if let rowIndexString = sender.accessibilityIdentifier, let rowIndex = Int(rowIndexString) {
self.sateOfNewSongArray[rowIndex] = !self.sateOfNewSongArray[rowIndex]
}
sender.isSelected = !sender.isSelected
for (index, element) in self.sateOfNewSongArray.enumerated() {
if element {
if let songName:String = songList[index] {
selectedSongList.append(songName)
}
}
}
}
Sample Code 2: More easier and concise.
func tapFunction(sender: UIButton) {
// This gives me the selected indexPath
print("ID :",sender.accessibilityIdentifier!)
if let rowIndexString = sender.accessibilityIdentifier, let rowIndex = Int(rowIndexString) {
if let songName:String = songList[rowIndex] {
selectedSongList.append(songName)
}
}
}
enumerated() is a function, returns sequence of pairs enumerating with index and element of array (by iterating elements of array)
Here is basic sample code, for your understanding. Copy and execute this code:
let songList =  [“cat”, “dog”, “elephant”]
let sateOfNewSongArray = [false, true, false]
let selectedSongList = [String]()
for (index, element) in self.sateOfNewSongArray.enumerated() {
if element {
if let animalName:String = songList[index] {
selectedSongList.append(animalName)
}
}
}
print(“selectedSongList - \(selectedSongList)”)

Firebase restoring initial values in textfields after changing text value

I am practicing iOS (Swift) with Firebase. the first viewcontroller retrieves all the records from firebase db and populate the tableView from an array. when the user selects an item from that tableview a new viewcontroller pops up segueing the object from the listView viewcontroller to the detail viewcontroller. data is populated to the fields successfully!
however when i try to update any of the textfield, the moment i switch to another textfield the initial value is restored in the edited textfield.
I have tried to removeAllObservers... but nothing worked. i even removed "import Firebase" and all associated objects and still it restores the initial value.
am i missing any concept here?
this is the code from the ListViewController:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated);
self.activityIndicator.startAnimating();
self.observeIngrdUpdates = ref.observeEventType(.Value, withBlock: { (snapshot) in
self.ingredients.removeAll();
for child in snapshot.children {
var nutrition:String = "";
var type:Int = 0;
var desc:String = "";
var img:String = "";
var name:String = "";
var price:Double = 0.0;
if let _name = child.value["IngredientName"] as? String {
name = _name;
}
if let _nutrition = child.value["NutritionFacts"] as? String {
nutrition = _nutrition;
}
if let _type = child.value["IngredientType"] as? Int {
type = _type;
}
if let _desc = child.value["UnitDescription"] as? String {
desc = _desc;
}
if let _img = child.value["IngredientImage"] as? String {
img = _img;
}
if let _price = child.value["IngredientPrice"] as? Double {
price = _price;
}
let ingredient = Ingredient(name: name, type: type, image: img, unitDesc: desc, nutritionFacts: nutrition, price: price);
ingredient.key = child.key;
self.ingredients.append(ingredient);
}
self.tableView.reloadData();
})
}
and the PrepareForSegue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destinationVC = segue.destinationViewController as! EditIngredientVC;
if segue.identifier?.compare(SEGUE_EDIT_INGREDIENTS) == .OrderedSame {
destinationVC.ingredient = ingredients[tableView.indexPathForSelectedRow!.row];
destinationVC.controllerTitle = "Edit";
} else if segue.identifier?.compare(SEGUE_ADD_INGREDIENT) == .OrderedSame {
destinationVC.controllerTitle = "New";
}
}
this is the code for populating the fields in DetailViewController:
override func viewDidLayoutSubviews() {
lblControllerTitle.text = controllerTitle;
if controllerTitle?.localizedCaseInsensitiveCompare("NEW") == .OrderedSame {
self.segVegFru.selectedSegmentIndex = 0;
} else {
if ingredient != nil {
self.txtIngredientName.text = ingredient!.name;
self.txtUnitDesc.text = ingredient!.unitDesc;
self.segVegFru.selectedSegmentIndex = ingredient!.typeInt;
self.txtNutritionFacts.text = ingredient!.nutritionFacts;
self.txtPrice.text = "\(ingredient!.price)";
}
}
}
Thank you all for your help.
It's probably because you are putting your textField populating code, in viewDidLayoutSubviews() method.
This method will be triggered every time the layout of a views changes in viewcontroller.
Move it to viewDidLoad() and it should be fine.
The reason that it's being reverted to the previous value is because. You are populating the textField.text from the "ingredient" object. The ingredient will have the same value retained that you passed from previous view controller. Until you mutate it at some point.
And by the way Firebase is very cool I like it too :)

Resources