how to set 'setter' of an computed property to private? - swift

I know how to set 'setter' of an stored property to private (e.g. public private(set) var name: String = "John") but how do we set 'setter' of an computed property to private? In this case the 'setter' for the variable 'age'. When I tried to put an keyword private in front of set(newAge){}, XCode display an error. So is it possible to set 'setter' of an computed property to private?
public class Person {
public private(set) var name: String = "John"
var age: Int{
get {
return 10
}
set(newAge){ // how to set this setter to private so to restrict modification
}
}
}

You do it the same way as for a stored property:
private(set) var age: Int{
get {
return 10
}
set(newAge) {
// setter code here
}
}

Related

SwitfUI Published on static property

I have a View Model defined as such:
class MyViewModel: ObservableObject {
#Published public var logs: String = ""
}
I would like to modify the logs property from the global scope of my Swift code without initializing MyViewModel like so:
MyViewModel.logs = "log message\n"
I tried making the logs property static:
#Published public static var logs: String = ""
However I get this error:
'wrappedValue' is unavailable: #Published is only available on properties of classes
How do I modify the published property without an instance of MyViewModel?
you could try using a singleton pattern, such as
class MyViewModel: ObservableObject {
#Published public var logs: String = ""
public static let shared = MyViewModel() // <--- here
}
then
MyViewModel.shared.logs = "log message\n"

RealmSwift: storing an element with a timestamp

I am trying to define a wrapper around objects I want to store in Realm. The wrapper should contain an additional date object so that I can filter old objects. So far I have this
public final class RealmDateTaggedRealmObject: ObjectFacade {
#objc public dynamic var date: Date?
#objc public dynamic var value: ObjectFacade?
#objc private dynamic var id: String = ""
public override class func primaryKey() -> String? {
return #keyPath(id)
}
public convenience init<T: RealmMappable>(from object: RealmDateTagged<T>) {
self.init()
date = object.date
value = object.value.asRealmObject
id = object.primaryKey
}
}
The RealmMappable protocol enables transforming implementing entities into ObjectFacade. The ObjectFacade class is an empty class that inherits from Object, because I got an exception if I declared
#objc public dynamic var value: Object?
So I tried to be smart and created ObjectFacade. Turns out I'm not so smart cause it always stores nil. Also, value can't be a generic type because it is not supported in Objective-C.
Any suggestions?
Thanks!

Why does accessing a String parameter of a subclassed NSManagedObject with a parent relationship crash?

I have generated classes for two core data entities. The first is called Address and is an abstract entity. The second is called Person, and it inherits from Address. I've added a few example managed attributes for the purpose of this test. And i've added a non-managed String property to the Person class. Accessing the string property of the Person class will crash. Why does this crash?
The Address and Person classes are automatically generated by Xcode, with the exception of the extra parameter: let foo = "Foo"
If i modify the code to make Person inherit from NSManagedObject directly instead of Address, then the code works and doesn't crash.
Automatically generated Address class:
#objc(Address)
public class Address: NSManagedObject {
}
extension Address {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Address> {
return NSFetchRequest<Address>(entityName: "Address")
}
#NSManaged public var street: String?
#NSManaged public var city: String?
}
Automatically generated person class with the exception of the "foo" parameter:
#objc(Person)
public class Person: Address {
public let foo = "Foo" //added this parameter
}
extension Person {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Person> {
return NSFetchRequest<Person>(entityName: "Person")
}
#NSManaged public var name: String?
}
problem code
let person = Person(context: context)
print(person.foo) //doesn't crash, but prints empty line instead of value
print("VALUE:\(person.foo):") //crashes with Thread 1: EXC_BAD_ACCESS (code=1, address=0x18)
UPDATE:
if foo is defined as
public let foo: String? = "Foo"
then the print statements don't crash, instead they interpret the value as 'nil' and print that.
So my question becomes: Why is this value which is assigned as a constant being reset to nil under the covers?
I have two hand-waving explanations why you are getting nil:
Managed objects don't function very well until they are inserted.
Your foo is a what I would call a constant stored property. I made up the name because, red flag, I cannot find any examples of it in the Swift book chapter on Properties
Put these two together and you get an edge case that doesn't work.
That being said, I'm kind of surprised that your foo setting does not work, because foo is not a managed property (that is, it is not in the data model). If I make such a constant stored property in a regular, non-managed object…
public class Animal {
public let foo: String! = "Foo"
}
it reads back later as expected.
So, if you can accept that this edge case just doesn't work in Core Data, you can move on to several more normal ways that do work.
One way is to declare foo as a var and assign a value to in awakeFromInsert() which is, as I alluded to earlier, after insertion. In Core Data, awakeFromInsert() is one of your friends…
#objc(Person)
public class Person: Address {
public var foo: String!
override public func awakeFromInsert() {
foo = "Foo"
}
}
Another way that works is as a computed property…
#objc(Person)
public class Person: Address {
public var foo : String { return "Foo" }
}
And, finally, the most logical way, since foo is constant for all instances, is to make it a type property…
#objc(Person)
public class Person: Address {
static var foo: String = "Foo"
}
but of course if you do this you must reference it as Person.foo instead of person.foo.

Core Data One to Many Relationship not saving correctly/as expected

I have a core data project that includes the following NSManagedObjects:
********************** FSDJump NSManaged Object
extension FSDJump {
#nonobjc public class func fetchRequest() -> NSFetchRequest<FSDJump> {
return NSFetchRequest<FSDJump>(entityName: "FSDJump")
}
#NSManaged public var starSystem: String
#NSManaged public var starPos: NSArray
#NSManaged public var bodyName: String
#NSManaged public var jumpDist: Float
#NSManaged public var fuelUsed: Float
#NSManaged public var fuelLevel: Float
#NSManaged public var boostUsed: Bool
#NSManaged public var systemFaction: String
#NSManaged public var systemAllegiance: String
#NSManaged public var systemEconomy: String
#NSManaged public var systemGoverment: String
#NSManaged public var systemSecurity: String
#NSManaged public var powers: NSArray
#NSManaged public var powerplayState: String
#NSManaged public var timeStamp: String
#NSManaged public var factionState: String
#NSManaged public var factions: NSSet
}
// MARK: Generated accessors for factions
extension FSDJump {
#objc(addFactionsObject:)
#NSManaged public func addToFactions(_ value: Factions)
#objc(removeFactionsObject:)
#NSManaged public func removeFromFactions(_ value: Factions)
#objc(addFactions:)
#NSManaged public func addToFactions(_ values: NSSet)
#objc(removeFactions:)
#NSManaged public func removeFromFactions(_ values: NSSet)
}
********************** Factions NSManaged Object
extension Factions {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Factions> {
return NSFetchRequest<Factions>(entityName: "Factions")
}
#NSManaged public var name: String
#NSManaged public var allegiance: String
#NSManaged public var factionState: String
#NSManaged public var government: String
#NSManaged public var influence: Float
#NSManaged public var fsdJump: FSDJump
}
with a one to many relationship defined between FSDJump and Factions (i.e. one FSDJump may have multiple factions (with the inverse relationship defined as 'fsdJump').
I use the following code to save multiple Factions (where they exist) within an 'if else' statement saves each fsdJumpEvent.
if fsdJumpEvent["Factions"].exists() {
// save all of the Faction array objects in the variable arrayOfFactions
let arrayOfFactions = fsdJumpEvent["Factions"]
// create a newFaction managedObject to save the faction details to
let newFaction = (Factions(context: contextTCCEDJ))
for faction in arrayOfFactions {
// the following is strictly not necesary but it makes the code easier to read
// first allocate the values from faction to a local variable then allocate that variable to the newFaction managedObject
// Note faction is a tuple (string, JSON) so the construct 'faction.1' accesses the second value in the tuple
// 'faction.0' would access the first value in the tuple which is the array index "0", "1", "2", etc
let newFactionName = faction.1["Name"].string!
let newFactionState = faction.1["FactionState"].string!
let newFactionGovernment = faction.1["Government"].string!
let newFactionAllegiance = faction.1["Allegiance"].string!
let newFactionInfluence = faction.1["Influence"].float!
newFaction.name = newFactionName
newFaction.allegiance = newFactionAllegiance
newFaction.government = newFactionGovernment
newFaction.influence = newFactionInfluence
newFaction.factionState = newFactionState
// Add the new object to the context allowing it to be saved.
fsdJump.addToFactions(newFaction)
print("Faction added \(newFaction)")
}
}
The code appears to work. It builds, it runs and the print("Faction added \(newFaction)") statement prints multiple Factions per FSDJump as expected when they exist and as per the source data file I am using (JSON).
I can fetch the results and display them in a NSTableView. I can load data without any problems for the FSDJump managed object and display that in a NSTableView.
The fetch code is:
// Function returns all of the Factions associated with the timeStamp related entry in FSDJumps using the fsdJump relationship
func eventFactionsFetchSavedDataFromPersistenStore (contextTCCEDJ: NSManagedObjectContext, timeStamp: String) -> [Factions] {
var result = Array<Factions>()
let localFetchRequest = Factions.fetchRequest() as NSFetchRequest
localFetchRequest.predicate = NSPredicate(format:"fsdJump.timeStamp == '\(timeStamp)'")
do {
result = try contextTCCEDJ.fetch(localFetchRequest)
} catch {
print("Error in returning Factions event saved data from the persistent store")
}
print(result.count)
for reSult in result {
print(reSult.fsdJump.timeStamp)
}
return result
}
However, it only seems to save the last 'faction' for each relationship - rather than the multiple factions that are shown in the for faction in arrayOfFactions loop. Because the fetch statement only returns one Faction per fetch. Even if I remove the predicate statement it returns all of the saved factions but again only one has been saved per fsdJumpEvent instead of the multiple factions that are identified by the print statement.
I have tried everything I can think of. I have not been able to find a previous question that relates to this specific issue.
Am I using the 'addToFactions' function incorrectly?
Any help would be gratefully received.
(And yes - for any Elite Dangerous fans out there I am writing a macOS app to parse my journal files as a companion app to my windows version of Elite Dangerous.)
You are creating a new, single instance of newFaction outside of your for faction in arrayOfFactions loop. So that single newFaction is used each time the loop runs and just assigns new values to it (overwriting previous values) resulting in it ending up with the last set of values. Hence you seeing a single faction with the 'last' set of values. Move the line:
Let newFaction = Factions(context: contectTCCEDJ)
Inside (at the beginning) of the for in loop.
Edit: you are adding your faction to a Set (which by definition requires unique entities) so all you're currently doing is re-adding the same faction each time rather than a new one. To-many relationships point to a Set.

How to change Swift property visibility in subclass

Is there a way to change Swift property visibility in subclass without introducing new property?
What I would like to do is (having property initialized to default value is not mandatory requirement):
public class MyBaseClass
{
private var abc: Int = 0
}
public class MyClass: MyBaseClass
{
public override var abc: Int = 0 // this cannot be compiled
}
Above code shows compiler error:
Cannot override with stored property 'abc'
The only way I can currently solve this is introducing another property but that is not what I prefer to do:
public class MyClass: MyBaseClass
{
public var abcd: Int
{
get
{
return abc
}
set
{
abc = newValue
}
}
}
Swift has two types of properties: stored properties and computed properties. You can override both of them but the overriden version cannot be a stored property, you have to override using computed properties.
Note that a stored property is just a chunk of memory but a computed property is a set of two methods - a getter and a setter. You cannot override a chunk of memory with another chunk of memory but you can override a method.
See Inheritance - Overriding, section Overriding Properties
You can override an inherited instance or type property to provide your own custom getter and setter for that property, or to add property observers to enable the overriding property to observe when the underlying property value changes.
You can provide a custom getter (and setter, if appropriate) to override any inherited property, regardless of whether the inherited property is implemented as a stored or computed property at source. The stored or computed nature of an inherited property is not known by a subclass—it only knows that the inherited property has a certain name and type. You must always state both the name and the type of the property you are overriding, to enable the compiler to check that your override matches a superclass property with the same name and type.
You can present an inherited read-only property as a read-write property by providing both a getter and a setter in your subclass property override. You cannot, however, present an inherited read-write property as a read-only property.
And a Note: under
If you provide a setter as part of a property override, you must also provide a getter for that override. If you don’t want to modify the inherited property’s value within the overriding getter, you can simply pass through the inherited value by returning super.someProperty from the getter, where someProperty is the name of the property you are overriding.
which tells us exactly what to do:
public class MyClass: MyBaseClass {
public override var abc: Int {
get {
return super.abc
}
set {
super.abc = newValue
}
}
}
Note that the following would also work. We just have to make sure we have a computed property:
public class MyClass: MyBaseClass {
public override var abc: Int {
didSet {
}
}
}
This looks better
public class MyClass: MyBaseClass {
override public var abc: Int {
get {
return super.abc
}
set {
super.abc = newValue
}
}
}
Some claims that this is a bug of the compiler, it doesn't synthesize correctly overridden properties.
Try the following:
public class MyClass: MyBaseClass {
private var abc: Int = 0
}
public class MyClass: MyBaseClass {
override init() {
super.init()
self.abc = 0
}
}