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

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.

Related

iOS Core Data - how to save Tuple property

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.

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.

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!)

Swift. Refer to instance variables by string name

I've got a number of user properties in a user viewcontroller class ie
//user vars
var email: String?
var givenName: String?
var familyName:String?
var phone: String?
var dob: NSDate?
In a method within that class i retrieve user data from coredata and set the user text fields with that data in a loop
for i in 0 ..< userTextFields.count {
let field = userTextFields[i]
let fieldName = userTextFieldKeyNames[i]
let fieldText = currentUser.valueForKey(fieldName) as? String
field.text = fieldText
}
the fieldName variable in the loop matches the class's ivars above. Is there a way i can reference the ivars within the loop by matching it with the fieldName string so I can set the values of the ivars with the fetched coredata values ie in a literal sense saying something like the following ...
if self.property.name.text == fieldName {
self.property.value == fieldText
}
ie somehow resolving the various property names withing the class ... or is this bad design? .... if so any suggestions on achieving the same result in a better way
Not Swift-ish, as it's bypassing compile time type checking. Best option is to keep them all in a dictionary, and use a protocol to define allowed data types in the dictionary, but even that is rather poor

Modal creating class object and setting values

Hello this problem is more concerned with the programming problem rather then the specific language syntax. I am getting a nil user_id value after setting a value in class object. So I thing I am doing something wrong here
I have two classes. User and Trip
Trip.swift
class Trip: NSObject {
var tripID: Int?
var tripTitle: String?
var user:User?
var params: [String:NSObject] {
return [
"user_id": self.user!.userID!,
"trip_title": self.tripTitle!,
]
}
When I send these params to server I get a nil value user_id
var trip: Trip?
trip?.user?.userID = 9
I am not writing a whole code. I think I am doing something wrong in modal class or not setting the userID correctly. Please let me know if you can't figure out the problem from this info so I'll post full code here
class User: NSObject {
var userID: Int?
var email: String?
var password: String?
var firstName: String?
var lastName: String?
var params: [String:NSObject] {
return [
"email": self.email!,
"password": self.password!,
"first_name": self.firstName!,
"last_name": self.lastName!,
]
}
If all the data in both classes is always mandatory in real life, then
you should not designate those properties as optional.
This will force you to write proper initialisers and then
you will be forced to populate each object with correct values, which in turn
will lead to abandoning the need for force-unwrapping just before you send to server.
Following these steps will fix your problem.
Bonus advice: You don't have to inherit from NSObject. Do not inherit from anything. Everything here is possible to do with swift standard library. The dictionary then will use AnyObject as value.

Resources