Closed bryceac closed 2 years ago
Are you able to provide some more details? E.g., what does your modified code look like, and what is the exact error message you are getting (i.e. what is the line in your code that is causing the issue)?
The code from when I tried looks like this, though the code looks more like your code on the github page.
let TEST_FILE = URL(fileURLWithPath: "~/transactions.bcheck").standardizedFileURL if let STORED_RECORDS = try? Record.load(from: TEST_FILE) { for record in STORED_RECORDS { Records.shared.add(record) } } let status = Application.run(startupHandler: nil) { app in let window = ApplicationWindowRef(application: app) window.title = "Hello, World!" window.setDefaultSize(width: 320, height: 240) /* let label = LabelRef(str: "Hello, World!") window.add(widget: label) window.showAll() */ let iterator = TreeIter() let store = ListStore(.string, .string, .boolean, .string, .string, .string, .string, .string) var listView = ListView(model: store) let columns = [ ("Date", "text", CellRendererText()), ("Check #", "text", CellRendererText()), ("Reconciled", "active", CellRendererToggle()), ("Vendor", "text", CellRendererText()), ("Memo", "text", CellRendererText()), ("Deposit", "text", CellRendererText()), ("Withdrawal", "text", CellRendererText()), ("Balance", "text", CellRendererText()) ].enumerated().map {(i: Int, c:(title: String, kind: PropertyName, renderer: CellRenderer)) in TreeViewColumn(i, title: c.title, renderer: c.renderer, attribute: c.kind) } listView.append(columns) window.add(widget: listView) for record in Records.shared.sortedRecords { switch record.event.type { case .deposit: if let checkNumber = record.event.checkNumber { store.append(asNextRow: iterator, "\(Event.DF.string(from: record.event.date))", "\(checkNumber)", record.event.isReconciled ? true : false, "\(record.event.vendor)", "\(record.event.memo)", "\(Event.CURRENCY_FORMAT.string(from: NSNumber(value: record.event.amount))!)", "N/A", "\(Event.CURRENCY_FORMAT.string(from: NSNumber(value: record.balance))!)") } else { store.append(asNextRow: iterator, "\(Event.DF.string(from: record.event.date))", "N/A", record.event.isReconciled ? true : false, "\(record.event.vendor)", "\(record.event.memo)", "\(Event.CURRENCY_FORMAT.string(from: NSNumber(value: record.event.amount))!)", "N/A", "\(Event.CURRENCY_FORMAT.string(from: NSNumber(value: record.balance))!)") } case .withdrawal: if let checkNumber = record.event.checkNumber { store.append(asNextRow: iterator, "\(Event.DF.string(from: record.event.date))", "\(checkNumber)", record.event.isReconciled ? true : false, "\(record.event.vendor)", "\(record.event.memo)", "N/A", "\(Event.CURRENCY_FORMAT.string(from: NSNumber(value: record.event.amount))!)", "\(Event.CURRENCY_FORMAT.string(from: NSNumber(value: record.balance))!)") } else { store.append(asNextRow: iterator, "\(Event.DF.string(from: record.event.date))", "N/A", record.event.isReconciled ? true : false, "\(record.event.vendor)", "\(record.event.memo)", "N/A", "\(Event.CURRENCY_FORMAT.string(from: NSNumber(value: record.event.amount))!)", "\(Event.CURRENCY_FORMAT.string(from: NSNumber(value: record.balance))!)") } } } window.showAll() } guard let status = status else { fatalError("Could not create Application") } guard status == 0 else { fatalError("Application exited with status \(status)") }
As can be seen here, I tried resorting to string interpolation, which I should not need to do for things that are already String or booleans values, like the following:
DF and CURRENCY_FORMAT are used to convert values to strings, yet those also receive complaints, even when using string interpolation.
As for the models, they are made up of the following:
EventTypeError:
enum EventTypeError: LocalizedError { case invalidType var errorDescription: String? { var error: String? = nil switch self { case .invalidType: error = "Specified type is not valid." } return error } }
EventType:
enum EventType: String, CaseIterable { case deposit, withdrawal } extension EventType: Codable { init(from decoder: Decoder) throws { let CONTAINER = try decoder.singleValueContainer() let TYPE_STRING = try CONTAINER.decode(String.self) guard let TYPE = EventType.allCases.first(where: { $0.rawValue.caseInsensitiveCompare(TYPE_STRING) == .orderedSame }) else { throw EventTypeError.invalidType } self = TYPE } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(self.rawValue) } }
Event:
struct Event { var date: Date = Date() var checkNumber: Int? = nil var vendor: String = "" var memo: String = "" var amount: Double = 0 var type: EventType = .withdrawal var isReconciled: Bool = false static let DF: DateFormatter = { let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd" return formatter }() static let CURRENCY_FORMAT: NumberFormatter = { let formatter = NumberFormatter() formatter.numberStyle = .currency return formatter }() } extension Event: Codable { private enum CodingKeys: String, CodingKey { case date, checkNumber = "check_number", vendor, memo, amount, type, isReconciled = "is_reconciled" } init(from decoder: Decoder) throws { let CONTAINER = try decoder.container(keyedBy: CodingKeys.self) if CONTAINER.contains(.date) { let DATE_STRING = try CONTAINER.decode(String.self, forKey: .date) if let TRANSACTION_DATE = Event.DF.date(from: DATE_STRING) { date = TRANSACTION_DATE } } if CONTAINER.contains(.checkNumber) { checkNumber = try CONTAINER.decode(Int.self, forKey: .checkNumber) } vendor = try CONTAINER.decode(String.self, forKey: .vendor) if CONTAINER.contains(.memo) { memo = try CONTAINER.decode(String.self, forKey: .memo) } amount = try CONTAINER.decode(Double.self, forKey: .amount) type = try CONTAINER.decode(EventType.self, forKey: .type) if CONTAINER.contains(.isReconciled) { isReconciled = try CONTAINER.decode(Bool.self, forKey: .isReconciled) } } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(Event.DF.string(from: date), forKey: .date) if let checkNumber = checkNumber { try container.encode(checkNumber, forKey: .checkNumber) } try container.encode(vendor, forKey: .vendor) if !memo.isEmpty { try container.encode(memo, forKey: .memo) } try container.encode(amount, forKey: .amount) try container.encode(type, forKey: .type) if isReconciled { try container.encode(isReconciled, forKey: .isReconciled) } } } extension Event: Comparable { static func ==(lhs: Event, rhs: Event) -> Bool { return lhs.date == rhs.date && lhs.checkNumber == rhs.checkNumber && lhs.vendor == rhs.vendor && lhs.memo == rhs.memo && lhs.amount == rhs.amount && lhs.type == rhs.type && lhs.isReconciled == rhs.isReconciled } static func < (lhs: Event, rhs: Event) -> Bool { var isLessThanOther = false if let firstNumber = lhs.checkNumber, let secondNumber = rhs.checkNumber { isLessThanOther = lhs.date < rhs.date || firstNumber < secondNumber || lhs.vendor < rhs.vendor || lhs.amount < rhs.amount } else { isLessThanOther = lhs.date < rhs.date || lhs.vendor < rhs.vendor || lhs.amount < rhs.amount } return isLessThanOther } } extension Event: Hashable {}
Record:
class Record: Identifiable, Codable { let id: String var event: Event var previousRecord: Record? = nil var balance: Double { var value = previousRecord?.balance ?? 0 switch event.type { case .deposit: value += event.amount case .withdrawal: value -= event.amount } return value } private enum CodingKeys: String, CodingKey { case id, event = "transaction" } init(withID id: String = UUID().uuidString, transaction: Event = Event(), previousRecord: Record? = nil) { (self.id, self.event, self.previousRecord) = (id, transaction, previousRecord) } required convenience init(from decoder: Decoder) throws { let CONTAINER = try decoder.container(keyedBy: CodingKeys.self) let ID = CONTAINER.contains(.id) ? try CONTAINER.decode(String.self, forKey: .id) : UUID().uuidString let TRANSACTION = try CONTAINER.decode(Event.self, forKey: .event) self.init(withID: ID, transaction: TRANSACTION) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: .id) try container.encode(event, forKey: .event) } class func load(from path: URL) throws -> [Record] { let JSON_DECODER = JSONDecoder() let RECORD_DATA = try Data(contentsOf: path) let DECODED_RECORDS = try JSON_DECODER.decode([Record].self, from: RECORD_DATA) return DECODED_RECORDS } } extension Record: Comparable { static func ==(lhs: Record, rhs: Record) -> Bool { return lhs.id == rhs.id } static func < (lhs: Record, rhs: Record) -> Bool { return lhs.id < rhs.id || lhs.event < rhs.event } } extension Record: Hashable { func hash(into hasher: inout Hasher) { hasher.combine(id) hasher.combine(event) } } extension Array where Element == Record { func save(to path: URL) throws { let JSON_ENCODER = JSONEncoder() JSON_ENCODER.outputFormatting = .prettyPrinted let ENCODED_RECORDS = try JSON_ENCODER.encode(self) #if os(iOS) try ENCODED_RECORDS.write(to: path, options: .noFileProtection) #else try ENCODED_RECORDS.write(to: path, options: .atomic) #endif } func element(before record: Record) -> Record? { guard self.first! != record else { return nil } guard let INDEX = self.firstIndex(of: record) else { return nil } let PREVIOUS_INDEX = self.index(before: INDEX) return self[PREVIOUS_INDEX] } func element(after record: Record) -> Record? { guard self.last! != record else { return nil } guard let INDEX = self.firstIndex(of: record) else { return nil } let NEXT_INDEX = self.index(after: INDEX) return self[NEXT_INDEX] } }
Records:
class Records { static let shared = Records() var items: [Record] { didSet { sortedRecords.forEach { record in record.previousRecord = items.element(before: record) } } } var sortedRecords: [Record] { return items.sorted { firstRecord, secondRecord in firstRecord.event.date < secondRecord.event.date } } private init(withRecords records: [Record] = []) { items = records guard !records.isEmpty && records.count > 1 else { return } for index in records.indices where index != records.startIndex { let PREVIOUS_INDEX = records.index(before: index) records[index].previousRecord = records[PREVIOUS_INDEX] } } func add(_ record: Record) { items.append(record) } func remove(at index: Int) { items.remove(at: index) } func clear() { items.removeAll() } func element(matching record: Record) -> Record? { guard items.contains(record) else { return nil } return items.first(where: { $0 == record }) } }
Records was one I had to make for Mac/iOS and since I could only compile the GTK stuff in Debian 10 with Swift 5.4, I had to turn it into a singleton from a ObservableObject, though I was considering getting rid of it entirely.
The project itself, since I made it in Debian 10, I did what the usage instructions in the README told me to, rather than cloning one of the examples.
As for the error message, it is pretty much the same as the title, as T is just a stand in.
On String likes the code retrieving the date and getting dollar amounts, it says, "cannot convert value of String to expected argument type of Value," which is also the same complaint that crop up when vendor and memo are retrieved outside of interpolation.
On the code for retrieving isReconciled without the ternaries, which should not be needed, it says, "cannot convert value of Bool to expected argument type of Value."
The JSON file, with the extension seen in the above code, itself contains the following:
[ { "id" : "FF04C3DC-F0FE-472E-8737-0F4034C049F0", "transaction" : { "amount" : 500, "vendor" : "Sam Hill Credit Union", "memo" : "Open Account", "check_number" : 1260, "type" : "deposit", "date" : "2021-07-08" } }, { "id" : "1422CBC6-7B0B-4584-B7AB-35167CC5647B", "transaction" : { "amount" : 200, "vendor" : "Fake Street Electronics", "memo" : "Head set", "type" : "withdrawal", "date" : "2021-07-08" } }, { "id" : "BB22187E-0BD3-41E8-B3D8-8136BD700865", "transaction" : { "amount" : 50000, "vendor" : "Velociraptor Entertainment", "memo" : "Pay Day", "type" : "deposit", "date" : "2021-07-08" } } ]
I hope this provides you with all the details you need.
if you look at the documentation for the append(asNextRow:...)
ListStore method, you can see that the parameters are Values, which are ExpressibleByStringLiteral, so any initialisation with a double-quoted string (such as the String interpolation in your example) just works.
If you want to initialise a value from a string variable (without going through a String literal), you can just pass the string to the corresponding initialiser, i.e. use Value(myString). So something like Value(Event.DF.string(from: record.event.date)
should work.
I'll give that a try thanks.
On Wed, Jul 28, 2021, 1:02 AM Rene Hexel @.***> wrote:
if you look at the documentation for the append(asNextRow:...) https://rhx.github.io/SwiftGtk3Doc/Classes/ListStore.html#/s:3Gtk9ListStoreC6append9asNextRow11startColumn_yx_Si10GLibObject5ValueCdtAA16TreeIterProtocolRzlF ListStore method, you can see that the parameters are Value https://rhx.github.io/SwiftGObject/Classes/Value.htmls, which are ExpressibleByStringLiteral https://developer.apple.com/documentation/swift/expressiblebystringliteral, so any initialisation with a double-quoted string (such as the String interpolation in your example) just works.
If you want to initialise a value from a string variable (without going through a String literal), you can just pass the string to the corresponding initialiser, i.e. use Value(myString) https://rhx.github.io/SwiftGObject/Classes/Value.html#/s:10GLibObject5ValueCyACxSgclufc. So something like Value(Event.DF.string(from: record.event.date) should work.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/rhx/SwiftGtk/issues/53#issuecomment-888101452, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAHKMOW3F6TRFONYX2GSSVLTZ62QPANCNFSM5BCY2YSA .
Thanks for the links. I fired up Debian today and wrapping them up in Value() worked, like how you suggested.
The date string was definitely complaining even when I used string interpolation, so that fixed that, though the check number started complaining to, so I did the same thing and it looks like the error is gone.
Still kind of annoying to have to do it this way, but that helps explain things not presented by my text Editor, which is VSCodium in the Linux environment, thanks to finding out somebody is distributing a prebuilt variant of the VS Code plugin.
Thanks again for the help.
Hello, I am currently playing around with SwiftGtk, trying to port an application over to Linux, and I am hitting a real snag here.
I am trying to make a ListView, so I am following the code as seen here, modifying as needed, which is currently loading objects from a JSON file, and when I get to the point of hooking up the rows with the data, I get the complaint that values of variables and/or functions cannot be converted, even though they are returning the right types, yet literals work just fine.
Can you either fix this or note down how exactly I'm supposed to deal with this?