Get a Swift class's property name as a String [duplicate] - swift

This question already has answers here:
Get a Swift Variable's Actual Name as String
(7 answers)
Closed 5 years ago.
How do I get the property name as a string?
class Person {
var firstName: String
var lastName: String
var downloading: Bool
func excludePropertiesFromCloud() -> [String] {
return //here I want to return ["downloading"]
}
}
I would like to avoid having to type return ["downloading"] to prevent errors if the name of the variable changes later on.
To get the class name you can use String(describing: Person.self), so I am looking at something similar for properties.
Note:
Although the title of the question is very similar to Get a Swift Variable's Actual Name as String, it is clear that the accepted answer in the original question returns the value of the property and does not answer this straightforward question. The original question is the first to come up on any google search with "Swift get property name", and the answer answers something else, as pointed to by #MarqueIV. This is why I created this question

If you are ok with making your properties #objc you can get the property name like so:
class Person {
#objc var firstName: String
var lastName: String
var downloading: Bool
func excludePropertiesFromCloud() -> [String] {
return [#keyPath(firstName)]
}
}

Related

How can I access private property outside that class in Swift? [duplicate]

This question already has answers here:
Read-Only properties
(3 answers)
Closed 9 months ago.
Here I have a class and make its properties private to prevent from modifications by accident.
class Article {
private var lineIndex: [Int] = []
private var text: [String] = []
....
}
I know I can write a function like func text(_ index: Int) -> String to get its value at index. But when I call it, article1.text(2) would be weird. Because it's less clear to indicate 2 is an index than what an array does like article1.text[2]. So can I use getter or something else instead, while keeping the clear syntax like text[2]. It couldn't be better if you can offer some examples.
You can use one of these ways:
with private(set), which allows editable inside the class:
class Article {
private(set) var text: [String] = []
...
}
with get only computed property (this is the same like your get function)
class Article {
var _text: [String] {
return text
}
}

Swift protocol covariant properties [duplicate]

This question already has answers here:
Why can't a get-only property requirement in a protocol be satisfied by a property which conforms?
(3 answers)
Closed 5 years ago.
Can anyone explain why this doesn't compile?
// In MyViewKit.framework
protocol UserRenderable {
var name : String { get }
}
protocol PostRenderable {
var title: String { get }
var author: UserRenderable { get }
}
// In MyDataKit.framework
struct User {
let id: String
let name : String
}
struct Post {
let id: String
let title: String
let author: User
}
// In MyApp
extension User : UserRenderable {}
extension Post: PostRenderable {}
But this (below) does? Apparently, read-only property requirements in a protocol can't be satisfied by a property which conforms to them (according to the answers to this question.)
What is typealias doing here?
// In MyViewKit.framework
protocol UserRenderable {
var name : String { get }
}
protocol PostRenderable {
var title: String { get }
var author: UserRenderable { get }
}
// In MyDataKit.framework
struct User {
let id: String
let name : String
}
struct Post {
let id: String
let title: String
let author: User
}
// In MyApp
extension User : UserRenderable {}
extension Post: PostRenderable {
// what is type alias doing here? Note: there are no associated types.
typealias User = UserRenderable
}
UPDATE Just clarifying why IMO this isn't a duplicate to Why can't a get-only property requirement in a protocol be satisfied by a property which conforms?. I understand the limitation in the Swift language: I'm trying to also understanding whether or not this is a valid workaround.
A typealias has nothing to do with associated types in this particular instance. Typealias is just a way to let the compiler know what is the associated type in a protocol if it cannot be inferred, but a typealias can be used in different contexts as well.
Your second example compiles, because you change User from a concrete type to a typealias for UserRenderable in your Post extension. If you check the type of author in Xcode, you'll see that its type is actually UserRenderable after the extension rather than User. Due to the typealias, inside Post, User won't refer to the User struct, but rather it will be UserRenderable due to the local overwrite you did with the typealias.

How to mute the warning of "never mutated" in Swift? [duplicate]

This question already has answers here:
Why constant constraints the property from a structure instance but not the class instance?
(2 answers)
Swift constant UIView never mutated warning
(1 answer)
UITableViewCell var "table view cell" was never mutated
(1 answer)
Closed 5 years ago.
Regarding duplicate flag: This question is different from the flagged question as I am asking about how to mute the warnings as I was not aware of the concept of Swift. The provided below answer helps me understand the very basic nature of Swift. Thus this question should not flagged as duplicate.
I have a class name Person having following variables.
private var _id:String = ""
var id:String {
get {
return _id
}
set (newId) {
_id = newId
}
}
private var _name:String = ""
var name:String {
get {
return _name
}
set (newName) {
_name = newName
}
}
private var _signedDate:Date? = nil
var signedDate:Date {
get {
return _signedDate!
}
set(newDate) {
_signedDate = newDate
}
}
These private var's are going to update with a setter.
So while creating an object for the Person class, I am writing this code.
var p1 = Person()
p1.id = "1"
p1.name = "Hemang"
array.append(p1)
Maybe later, I will update the value of signedDate with a setter.
So I should not create this object with let.
However, it's showing me this warning:
Variable 'p1' was never mutated; consider changing to 'let' constant.
How to mute this warning?
Please let me know if you need more information on this.
Because actually you don't change the Person object,
With let you can change the properties of the object. But you can't change the object it self.
So change your code to what the warning lead you.
And of course you can try before asking this question.

How to reflect not managed properties of NSManagedObject

I am searching how to get not managed property names and types of a NSManagedObject subclass.
here is few sample code to help me to ask my question :
#objc(Operation)
public class Operation : NSManagedObject {
#NSManaged var name: String
#NSManaged var amount: NSNumber
}
#objc(Account)
public class Account: NSManagedObject {
#NSManaged var bic: String
#NSManaged var number: String
#NSManaged var operations: Set<Operation>
#NSManaged var servicesSubscriptions: Set<ServiceSubcription>
// and more.
}
extension Account
{
public var lastOperation : Operation {
get
{
return self.operations.last
}
set(value)
{
self.operations.insert(value)
}
}
}
I have found many ways to get property names using reflect() function. reflect() do not work with NSManagedObject at all. (like this simple one)
edit
I have found examples with class_copyPropertyList function, that retrieve correctly property names, but don't found yet how to get types. Thank to Tom Harrington comment. (see that sample)
I have found many ways to get Attributes (or relations) of managed objects using NSEntityDescription. (like this one). Which work and get back bic and number, but not lastOperation.
edited
updated code sample to match better to reality
So my question is :
How to get back my lastOperation property, and its type, dynamically at run time ?
edit, what i am trying to do
I am parsing json, dnamically using reflection.
I need the type (or type name) of a property knowing only its name (i have "lastOperation", and need to get back Operation, or "Operation"). Once i get the type i can instanciate an object, then populate its own properties, using same mechanism (recursively).
Thank you for any help
When you get the list of properties using class_copyPropertyList, you can iterate through the list to look at each property in turn:
var propertyCount : UInt32 = 0
let properties = class_copyPropertyList(Account.self, &propertyCount)
for var i=0; i<Int(propertyCount); i++ {
let property = properties[i]
let propertyName = String(UTF8String: property_getName(property))
....
}
The type of each property is contained in one of the property attributes, as a string:
let propertyType = property_copyAttributeValue(property, "T")
let propertyTypeString = String(UTF8String: propertyType)
For your lastOperation property the string will look something like #\"Operation\". You'll have to clean up that string a little to get Operation.
I wrote a blog post a while ago describing something similar to what you're trying to do. The code is in Objective-C but all the functions, methods, etc are the same.
There is no way that I know of when it comes to a NSManagedObject. However, I would suggest creating title as a transient property inside of your model and then it will show up as part of the entity description.

missing argument for parameter in call in tuple [duplicate]

This question already has answers here:
How to append a tuple to an array object in Swift code? [duplicate]
(3 answers)
Closed 7 years ago.
So I am working on a menu-type class and for some reason I am getting the error: "Missing argument for parameter 'dishes' in call"
Here is the code:
class Meal {
var nameOfMeal: String
var menu : [(sectionName: String, dishes: [Dish])]
var hours: String
init(nameOfMeal: String, menuIDs: [(sectionName: String, dishIDs: [String])], hours: String) {
self.nameOfMeal = nameOfMeal
setMenuFromIDs(menuIDs)
self.hours = hours
}
func setMenuFromIDs(menuIDs: [(sectionName: String, dishIDs: [String])]){
menu = []
for menuSection in menuIDs {
var loadedDishes = CoreDataUtility.loadArrayOfDishesFromIDs(menuSection.dishIDs)
menu.append((sectionName: menuSection.sectionName, dishes: loadedDishes))
}
}
}
The error is on the menu.append((sectionName: menuSection.sectionName....))
I have checked the type of menu, loadedDishes, I've separated the argument in menu.append and checked it's type and they're all appearing as they should. None are optionals. I've removed the sectionName and dishes label from the tuple but the error still mysteriously appears.
Can anyone please help me figure out why?
Thanks!
As Nate Cook said here:
… don't do this. Tuples are designed for temporary use and will miss out on a lot of Swift's strengths (compiler optimizations, etc.) …
Based on that answer, I rewrote your code as:
typealias MyTuple = (sectionName: String, dishes:[Dish])
class Meal {
var nameOfMeal: String
var menu : [MyTuple]
var hours: String
init(nameOfMeal: String, menuIDs: [(sectionName: String, dishIDs: [String])], hours: String) {
self.nameOfMeal = nameOfMeal
self.hours = hours
self.menu = [("", [Dish]())]
setMenuFromIDs(menuIDs)
}
func setMenuFromIDs(menuIDs: [(sectionName: String, dishIDs: [String])]){
menu = []
for menuSection in menuIDs {
var loadedDishes = CoreDataUtility.loadArrayOfDishesFromIDs(menuSection.dishIDs)
menu.append((menuSection.sectionName, loadedDishes))
}
}
}
… and it now compiles!
Note that I also moved the line setMenuFromIDs(menuIDs) to the bottom of the init function and initialised your menu property (to avoid another compiler warning, of using self before its initialized).