iOS Core Data - how to save Tuple property - ios

I'm in process of adding Core Data to my app, started out with a struct like this:
struct Expense: Equatable{
let id: String = UUID().uuidString
var name: String
var description: String
var category: String
var nextBill: Date
var billingCycle: (Int, String)
var currency: Currency?
var price: Decimal
}
Reading documentation look like I'll need to convert it to a class
#objc(Expense)
public class Expense: NSManagedObject {
let id: String = UUID().uuidString
var name: String
var description: String
var category: String
var nextBill: Date
var billingCycle: (Int, String)
var currency: Currency?
var price: Decimal
}
However this appears to have issue with the billingCycle property which is a tuple value ( example of value: 1 per week, 1 per month, 3 per year)
I'm thinking of splitting the billingCycle property into 2 new properties: billingCycleInt and billingCycleString. Would this be the best approach or there is other way?

That would be best. Core Data is— internally— still very much oriented toward Objective-C. As such it has no idea about Swiftisms like tuples. There are other approaches that could work, like trying to get NSCoding to work for this property, but they would all be significantly more complex.
If you’d like to keep the tuple, you might consider adding the two properties you mention and then converting the tuple into a computed property that looks up the values of the other two. Then you can use a Swift tuple where it’s convenient but still work with Core Data.

Related

Model Object Issue

I am trying to create a model object to store json data received from newsAPI. However one of the keys called description matched a reserved keyword in swift. How do I resolve this ? The error message read Error Message: Property 'description' with type 'String?' cannot override a property with type 'String'
class ArticlesData: NSObject {
var author: String?
var title: String?
var description: String?
var publishedAt: String?
var urlImage: String?
var urlWebsite : String?
}
author : Andrew Marino
title : Vergecast: this week’s Section 230 hearing and the season of weird gadgets
description : Nilay, Dieter, and Adi discuss the latest Section 230 congressional hearing featuring the CEOs of Big Tech. Ashley Carman stops by to talk about how gadget makers are turning to shopping channels to market their products.
url : https://www.theverge.com/2020/10/30/21541796/section-230-hearing-gadgets-qvc-razr-2020-review-vergecast-podcast-428
urlToImage : https://cdn.vox-cdn.com/thumbor/sgk9rqbUKhLVhNxKunmuX_Zm6TM=/0x146:2040x1214/fit-in/1200x630/cdn.vox-cdn.com/uploads/chorus_asset/file/19749210/vpavic_200214_3909_samsung_galaxy_Z_flip_0013.jpg
publishedAt : 2020-10-30T14:44:14Z
content : Photo by Vjeran Pavic / The Verge\n\n This week on The Verge’s flagship podcast, The Vergecast: Sen. Ted Cruz (R-TX) yells at Twitter CEO Jack Dorsey, gadget makers are going to QVC, and it is weird ph… [+3098 chars]
Simplest solution: Use a struct, neither a class or even a subclass of NSObject is necessary.
struct ArticlesData {
Otherwise just use a different name.
If you are going to decode the JSON with Decodable add CodingKeys to map the dictionary key to the struct member with a different name.
And don't declare all properties carelessly as optional. Declare only those as optional which can be nil.
I've run into the same 'issue' several times and it's quite annoying indeed. You should rename your property to something else.
How are you mapping your JSON into the object? If you use Codable, you can use custom CodingKeys such as:
class ArticlesData: NSObject, Codable
{
var author: String?
var title: String?
var myDescription: String?
var publishedAt: String?
var urlImage: String?
var urlWebsite: String?
private enum CodingKeys: String, CodingKey
{
case author, title, myDescription = "description", publishedAt, urlImage = "urlToImage", urlWebsite = "url"
}
}
EDIT: As vadian said, you can also use a struct. I actually prefer that solution over renaming the propery to another name than you want.

Subqueries/relationships in Realm Swift iOS

I've got two Objects:
Account: Object {
dynamic var id: String?
dynamic var name: String?
dynamic var valid: String?
}
Transaction: Object {
dynamic var id: String?
dynamic var desc: String?
dynamic var accountId: String?
}
What I'm trying to do is somehow link the two objects so when I do a query on the transactions "table", I can find out which transactions come from an account which is valid.
I've tried linking them with
let transactions = LinkingObjects(fromType: Transaction.self, property: "fromAccount")
in the Account object and doing the inverse in the Transaction object. The best result I get is either an empty list or a null record.
I suppose the next question is, is there a way to do what I'm trying? I've also looked into subqueries but haven't had any luck with them (the documentation is super slim it seems for subqueries).
What I've done in the past is whenever a new transaction is created, it goes and fetches the full account object (using accountId) and stores it in that record but I feel there must be a better way. Surely.
There are a number of solutions but your linkingObject is a good option. It's really going to come down to what kind of queries you want to run.
So, let's start with a super simple example that will allow you to query transactions for any that have a parent account where valid is 'true' - that addresses your question.
class Account: Object {
#objc dynamic var id: String?
#objc dynamic var name: String?
#objc dynamic var valid: String?
}
class Transaction: Object {
#objc dynamic var id: String?
#objc dynamic var desc: String?
#objc dynamic var accountId: String?
#objc dynamic var parent_account: Account?
}
then create a couple of objects
let a0 = Account()
a0.name = "account 0"
a0.valid = "true"
let a1 = Account()
a1.name = "account 1"
a1.valid = "false"
let t0 = Transaction()
t0.desc = "trans 0"
let t1 = Transaction()
t1.desc = "trans 1"
t0.parent_account = a0
t1.parent_account = a1
try! realm.write {
realm.add([t0, t1])
}
once that data is written, if you want transactions where the parent_account's valid property == 'true'
let results = realm.objects(Transaction.self).filter("parent_account.valid == 'true'")
for account in results {
print(account.desc)
}
and the output is
Optional("trans 0")
Keep in mind this is very very basic and probably not going to work for your use case long term. Investigating LinkingObjects and inverse relationships will probably be a better solution but this should get you going.
Oh... probably making the active property a bool is going to be better than string.

Is there a way to customise declarations for variable naming in swift?

I'm trying to initialise a value in dictionary as follow,
var og:image: String
But after og: it tries to assign the type considering og as the variable to site_name, which is obviously wrong.
Is there a way to assign og:image as variable to String type using
special or escape characters?
In reference to this, apple does not provide any meaningful explanations to variable naming conventions in such a situation.
Edit-1:
Here is code snippet to clarify dictionary usage is in JSON parsing data structure,
struct Metadata: Decodable{
var metatags : [enclosedTags]
}
struct enclosedTags: Decodable{
var image: String
var title: String
var description: String
var og:site_name: String
}
You cannot use : (colon). But if you really want:
var ogCOLONimage: String
Joking apart. You could use a Dictionary or something like that:
var images: [String: String] = ["og:image" : "your string"]
Now you can access your og:image data with images["og:image"].
Swift allow you to use almost any character when naming variables. You can even use Unicode characters.
However, there are a few restrictions:
Constant and variable names can’t contain whitespace characters, mathematical symbols, arrows, private-use (or invalid) Unicode code points, or line- and box-drawing characters. Nor can they begin with a number, although numbers may be included elsewhere within the name.
Said that, it is not possible to use : in the name of a variable. However, you can use a Unicode character similar to that symbol. Depending on what you want it for, this may be a valid solution.
Here you have a list of the Unicode characters similar to : that can be used in the name of a variable:
︓
﹕
:
(https://www.compart.com/en/unicode/based/U+003A)
Based on the example you provided it would be this:
struct Metadata: Decodable{
var metatags : [enclosedTags]
}
struct enclosedTags: Decodable{
var image: String
var title: String
var description: String
var og:site_name: String
}
Turns out swift has it's own feature in terms of naming specificity of variables in structs i.e. CodingKeys, so in terms for me the below naming convention worked,
struct Metadata: Decodable{
var metatags: [enclosedTags]
}
struct enclosedTags: Decodable{
let image: String
let title: String
let description: String
let siteName: String
private enum CodingKeys : String, CodingKey{
case image = "og:image", title = "og:title", description = "og:description", siteName = "og:site_name"
}
This was rightfully pointed out by #hamish in comments (Thanks mate!)

How to save array of tuples in UserDefaults

I am using a tuple of arrays in one of my files and want to start saving it in UserDefaults. Here is how I am storing my current data:
typealias savemytuple = (Name: String, NameID: String, BookID: String, Picture: NSData)
var savefun = [savemytuple]()
I have researched and found I may need to do something with the following for saving:
NSKeyedArchiver.archivedDataWithRootObject
And this for retrieving:
NSKeyedUnarchiver.unarchiveObjectWithData
Not sure if this way will work as my tuple has multiple data types. Any idea on how to do this?
The values in UserDefaults must be property lists, so you need to convert each tuple to a property list. A Data is a property list, and there are several ways to convert things to Data.
One way is to stop using a tuple and use a struct instead. If all of the struct's stored properties conform to Codable, then you can ask Swift to generate everything needed to make the struct itself conform to Codable just by declaring the conformance. Both String and Data conform to Codable.
Once the struct is Codable, you can convert one of them, or even an array of them, into a property list via JSONEncoder or PropertyListEncoder:
import Foundation
struct Bookie: Codable {
var name: String
var nameId: String
var bookId: String
var picture: Data
}
let bookies = [
Bookie(name: "mbro12", nameId: "id1", bookId: "b1", picture: Data()),
Bookie(name: "mayoff", nameId: "id2", bookId: "b2", picture: Data())
]
let bookiesData = try! PropertyListEncoder().encode(bookies)
UserDefaults.standard.set(bookiesData, forKey: "bookies")
let fetchedData = UserDefaults.standard.data(forKey: "bookies")!
let fetchedBookies = try! PropertyListDecoder().decode([Bookie].self, from: fetchedData)
print(fetchedBookies)
You have to convert the tuples into Dictionary to save it, not really a proper way, rather make your tuple into a custom object, then conform NSCoding protocol for a proper way to convert between data and object.
You don't really need NSKeyedArchiver.archivedDataWithRootObject, just simply convert the tuple into a Dictionary and have another function to convert back to the tuple type.

Swift: model structs, using optionals vs initialization of empty values

In Swift, say for example I have a struct for this model:
struct Message {
var message: String = ""
var timestamp: String = ""
var id: String = ""
}
And I would be instantiating multiple Messages using this struct from a database, and then populate a TableView with them.
Would it be best practice to using optionals instead of setting these variables with empty strings like such?
struct Message {
var message: String?
var timestamp: String?
var id: String?
}
Would it be more efficient to basically setting the variables to nil vs an empty string? Does nil take less memory vs empty string?
Before thinking about optimization, you have to ask yourself the good question: is there any chance that Message may contain optionals for one or several of its properties? If yes, use optionals, if no, don't use optionals.
Then, if you want to improve your code, you can use memberwise initializer for your struct:
struct Message {
var message: String
var timestamp: String?
var id: String
}
let message = Message(message: "Some message", timestamp: nil, id: "Id14")
Finally, I doubt any memory optimization on Struct (with optional or non optional properties) will give any significant improvement to your app/project.
Avoid optionals whenever it is possible. Stored properties are not as magical as they are in objective-c. Just give them default values if it is appropriate.

Resources