Function to return object's property - swift

I'd like to change an object's parameter, but I like to decide later about what's the parameter that should change. I'm thinking to make a function for that, but don't know how. Here's a sample:
class Car {
var color = "green"
var brand = "Zastava"
var mechanic = "Mark Gaia"
}
struct Change {
var property: String
var newValue: String
}
let car = Car()
let change = Change(property: "brand", newValue: "Fiat")
car.propertyFromChange(change) = change.newValue // ??? How to implement this line
How to make a function that returns what's the parameter of an object that needs to be changed?
Alternatively, how to choose which parameter to change?
(...I thought about switch statement. Is a good direction?)
I'm pretty sure that's something that has already been discussed on internet. I've read everything about functions and couldn't find the solution, so I guess I'm searching using wrong keywords.

You don't need to create a function you can use KVC like this after you inherit from NSObject, or you can make a new func that guards again bad property names.
class Car: NSObject {
var color = "green"
var brand = "Zastava"
var mechanic = "Mark Gaia"
}
struct Change {
var property: String
var newValue: String
}
let car = Car()
let change = Change(property: "brand", newValue: "Fiat")
car.setValue(change.newValue, forKey: change.property)
Keep in mind that this code will crash if you enter bad property names.
Hope this answers your question.

If Carinherits from NSObject you get key-value-coding for free and can change the value with setValue:forKey
class Car : NSObject {
var color = "green"
var brand = "Zastava"
var mechanic = "Mark Gaia"
}
struct Change {
var property: String
var newValue: String
}
let car = Car()
let change = Change(property: "brand", newValue: "Fiat")
car.setValue(change.newValue, forKey: change.property)

You can take advantage of KVC setValue(value: AnyObject?, forKey key: String) method by making Car a subclass of NSObject.
class Car: NSObject {
var color = "green"
var brand = "Zastava"
var mechanic = "Mark Gaia"
}
struct Change {
var property: String
var newValue: String
}
let car = Car()
let change = Change(property: "brand", newValue: "Fiat")
car.setValue(change.newValue, forKey: change.property)

You can simplify your approach a bit by doing this
If you store all your properties in the struct itself, you wouldnt need to worry about that at all.
:) Hope this helps
struct CarProperties {
var color : String = ""
var brand : String = ""
var mechanic : String = ""
}
class Car {
var carProperties = CarProperties()
}
let car = Car()
car.carProperties.color = "Green"
car.carProperties.brand = "yourFavBrand"
car.carProperties.mechanic = "whatever"
let change = "this"
// Suppose you want to change the brand
car.carProperties.brand = change
print(car.carProperties.brand)

Related

`#Published var name: ClassType` doesn't work _outside_ of SwiftUI / manual trigger?

I found a lot of SwiftUI-related topics about this which didn't help (eg Why an ObservedObject array is not updated in my SwiftUI application?)
This doesn't work with Combine in Swift (specifically not using SwiftUI):
class SomeTask {
#Published var progress = Progress(totalUnitCount: 5) // Progress is a Class
[...]
}
var task = SomeTask()
let cancellable = task.$progress.sink { print($0.fractionCompleted) }
task.progress.completedUnitCount = 2
This is not SwiftUI-related so no ObservableObject inheritance to get objectWillChange, but even if I try to use ObservableObject and task.objectWillChange.send() it doesn't do anything, also trying to add extension Progress: ObservableObject {} doesn't help.
Since the publisher emits values through the var's willSet and since Progress is itself class-type nothing happens.
Looks like there is no real decent way to manually trigger it?
Only solution I found is to just re-assign itself which is quite awkward:
let pr = progress
progress = pr
(writing progress = progress is a compile-time error).
Only other way which might be working is probably by using Key-value-observing/KVO and/or writing a new #PublishedClassType property wrapper?
I was able to implement this using KVO, wrapped by a #propertyWrapper, with a CurrentValueSubject as the publisher:
#propertyWrapper
class PublishedClass<T : NSObject> {
private let subject: CurrentValueSubject<T, Never>
private var observation: NSKeyValueObservation? = nil
init<U>(wrappedValue: T, keyPath: ReferenceWritableKeyPath<T, U>) {
self.wrappedValue = wrappedValue
subject = CurrentValueSubject(wrappedValue)
observation = wrappedValue.observe(keyPath, options: [.new]) { (wrapped, change) in
self.subject.send(wrapped)
}
}
var wrappedValue: T
var projectedValue: CurrentValueSubject<T, Never> {
subject
}
deinit {
observation.invalidate()
}
}
Usage:
class Bar : NSObject {
#objc dynamic var a: Int
init(a: Int) {
self.a = a
}
}
class Foo {
#PublishedClass(keyPath: \.a)
var bar = Bar(a: 0)
}
let f = Foo()
let c = f.$bar.sink(receiveValue: { x in print(x.a) })
f.bar.a = 2
f.bar.a = 3
f.bar.a = 4
Output:
0
2
3
4
The disadvantage of using KVO is, of course, that the key path you pass in must be #objc dynamic and the root of the keypath must be an NSObject subclass. :(
I haven't tried, but it should be possible to extend this to observe on multiple key paths if you want.
You can try using CurrentValueSubject<Progress, Never>:
class SomeTask: ObservableObject {
var progress = CurrentValueSubject<Progress, Never>(Progress(totalUnitCount: 5))
func setProgress(_ value: Int) {
progress.value.completedUnitCount = value
progress.send(progress.value)
}
}
var task = SomeTask()
let cancellable = task.progress.sink { print($0.fractionCompleted) }
task.setProgress(3)
task.setProgress(1)
This way your Progress can still be a class.
Based on the ideas I did implement a #PublishedKVO property wrapper and put it up on github as a small swift package, supporting multiple key paths.
https://github.com/matis-schotte/PublishedKVO
Usable as:
class Example {
#PublishedKVO(\.completedUnitCount)
var progress = Progress(totalUnitCount: 2)
#Published
var textualRepresentation = "text"
}
let ex = Example()
// Set up the publishers
let c1 = ex.$progress.sink { print("\($0.fractionCompleted) completed") }
let c1 = ex.$textualRepresentation.sink { print("\($0)") }
// Interact with the class as usual
ex.progress.completedUnitCount += 1
// outputs "0.5 completed"
// And compare with Combines #Published (almost°) same behaviour
ex.textualRepresentation = "string"
// outputs "string"
ex.$progress.emit() // Re-emits the current value
ex.$progress.send(ex.progress) // Emits given value

How to save and load GKGameModelPlayer from Realm in Swift?

I am attempting to implement a GKGameModel in my application. In it, it holds variables to a few things, but for the purposes of my question I'm interested in the following two variables:
import GameplayKit
final class GameModel: NSObject, GKGameModel {
var players: [GKGameModelPlayer]?
var activePlayer: GKGameModelPlayer?
}
I do something like this to initialise the game with 3 players (not exact)
let game = GameModel.init()
game.players = [Player(),Player(),Player()] // Create 3 players
guard let firstPlayer = game.players.first else {
return
}
game.activePlayer = firstPlayer
A player class is defined as:
class Player : NSObject, GKGameModelPlayer {
var playerId: Int // GKGameModelPlayer protocol variable
let name: String
var cash: Int = 0
}
In my project I have Realm Entities and the models seperated. So there will be a PlayerEntity and a Player class.
I'm wanting to use RealmSwift to save and load the GKGameModelPlayer data, and more specifically the ability to store/re-store the active player.
I think the key here is the playerId variable; but I am not sure.
But what I'm not sure about is retrieving this information and then re-mapping it into a valid GKGameModelPlayer format
My current idea/theory is that I need to map my model to an entity class and vice-versa.
Ie:
// [REALM] Player entity
class PlayerEntity: Object {
#objc dynamic var id = UUID().uuidString
#objc dynamic var playerId: Int = 0
#objc dynamic var name: String = ""
#objc dynamic var cash: Int = 0
override static func primaryKey() -> String {
return "id"
}
}
And then I extend this class to do some "mapping":
extension PlayerEntity {
// Map model -> entity
convenience init(model: Player) {
self.init()
self.playerId = model.playerId
self.name = model.name
self.cash = model.cash
}
}
extension Player {
// Map entity -> model
convenience init(entity: PlayerEntity) {
let playerId = entity.playerId
let name = entity.name
let cash = entity.cash
self.init(id: playerId, name: name, cash: cash)
}
}
Right now, the playerId is always zero (0) because I'm not really sure how to set it.
I can save a player to realm.
The issue comes from when I try to restore the player, and I want to restore the activePlayer variable in the GameModel
Therefore, my question is:
How would I go about saving and restoring the activePlayer variable so that it continues to comply to GKGameModelPlayer?
I appreciate any assistance on this.
With thanks
While you could use those extensions, sometimes simpler is better. Here's a rough example:
class PlayerEntity: Object {
#objc dynamic var playerId: Int = 0
#objc dynamic var name: String = ""
#objc dynamic var cash: Int = 0
convenience init(withPlayer: PlayerClass) {
self.init()
self.playerId = withPlayer.playerId
self.name = withPlayer.name
self.cash = withPlayer.cash
}
func getPlayer() -> Player {
let p = Player()
p.playerId = self.playerId
p.name = self.name
p.cash = self.cash
return p
}
override static func primaryKey() -> String {
return "playerId"
}
}
to load all the players into an array... this will do it
let playerResults = realm.objects(PlayerEntity.self)
for player in playerResults {
let aPlayer = player.getPlayer()
self.playerArray.append(aPlayer)
}
Notice the removal of
#objc dynamic var id = UUID().uuidString
because it's not really being used to identify the object as a primary key.
The primary key is really
var playerId: Int // GKGameModelPlayer protocol variable
which is fine to use as long as it's unique.

Listing all class attributes swift 3

I'm trying to print all the values from an object that inherits from a class, here is my example:
I create the class:
class Pokemon {
var name: String?
var type: String?
var level: Int?
var exp = 0.0
}
Create the object and assign some values:
var pikachu = Pokemon()
pikachu.name = "Pika Pika"
pikachu.level = 1
pikachu.type = "electricity"
pikachu.exp = 0
Now I would like to loop through all the pikachu object attributes and print the values. I'm thinking in a for each loop but I'm not sure how to implement it.
I know I can do something like this:
func printStats(pokemon: Pokemon) {
if pokemon.name != nil {
print(" name: \(pokemon.name!)\n level:\(pokemon.level!)\n type:\(pokemon.type!)\n exp: \(pokemon.exp!)")
}
}
printStats(pokemon: pikachu)
output:
name: Pika Pika
level:1
type:electricity
exp: 0.0
But I just want to loop through all values, instead of explicit writing every attribute in the function.
I found it the way of doing it:
let pokeMirror = Mirror(reflecting: pikachu)
let properties = pokeMirror.children
for property in properties {
print("\(property.label!) = \(property.value)")
}
output:
name = Optional("Pika Pika")
type = Optional("electricity")
level = Optional(1)
exp = Optional(0.0)
and if you want to remove the "Optional" just initialize the attributes.
Looks like a duplicate of Does Swift support reflection?
Alternatively, you can use a dictionary to store the attributes of Any? type.
e.g.
class Pokemon {
var attributes = [String:Any?]()
}
var pikachu = Pokemon()
pikachu.attributes["name"] = "Pika Pika"
pikachu.attributes["level"] = 1
pikachu.attributes["type"] = "electricity"
pikachu.attributes["exp"] = 0
func printStats(pokemon: Pokemon) {
pokemon.attributes.forEach { key, value in
if let value = value {
print("\(key): \(value)")
}
}
}
In Swift 5 you can create a new func in your class:
func debugLog() {
print(Mirror(reflecting: self).children.compactMap { "\($0.label ?? "Unknown Label"): \($0.value)" }.joined(separator: "\n"))
}
And then call it with MyObject().debugLog()
use Mirror API to get instance's properties
if you are developing iOS app, using NSObject, you may want to override description. Then can use print to print the instance.
A mirror describes the parts that make up a particular instance, such as the instance’s stored properties, collection or tuple elements, or its active enumeration case.
class YourClass: NSObject {
public override var description: String {
var des: String = "\(type(of: self)) :"
for child in Mirror(reflecting: self).children {
if let propName = child.label {
des += "\(propName): \(child.value) \n"
}
}
return des
}
}
let instance = YourClass()
print(instance)
see more in Reflection in Swift

Mapping in Swift Between Protocol Conforming Types

I want to map between any two objects which conform to the same protocol. It would be convenient to do so via a function with the signature:
func mapFrom<T>(objectA: T, to inout objectB: T)
Even better though (for immutable types) would be to have it in the form:
func map<T, U: T>(from source: T) -> U
where somehow it could initialize a U object from the values in T.
I would like to do this via Swift Reflection rather than using the Objective-C run-time, but I would settle for that if it was the only way. If somehow it could be done without reflection that would be amazing, but I don't see how.
The reason I want to do this is because I have mutable Realm classes which conform to their respective protocol, and I want to map them to the immutable struct types.
An example would be:
/**
The protocol.
*/
protocol Food {
var name: String { get }
var weight: Float { get }
var price: Float { get }
}
/**
The mutable Realm class representation.
*/
final class FoodEntity: Object, Food {
dynamic var name = ""
dynamic var weight = 0.0
dynamic var price = 0.0
}
/**
The final struct I want to map to from the Realm representation.
*/
struct FoodProduct: Food {
let name: String
let weight: Float
let price: Float
}
I would like to be able to have a generic function or method with which to map a FoodEntity to a FoodProduct without having to manually do something like:
FoodProduct(name: entity.name, weight: entity.weight, price: entity.price)
How can this be done, if it can be done at all?
I think you are looking for something like this.
func fetchAllFoodProducts() -> [FoodProduct]
{
var foodProducts : [FoodProduct] = []
// Fetch From Realm
let products = realm.objects(FoodEntity.self)
for product in products
{
foodProducts.append(FoodProduct(name: product.name, weight: product.weight, price: product.price))
}
return foodProducts
}
The thing is that there can't be a generic way to do this. Because you have to assign the values of name, weight & price somehow. This is the closest you can get, I think.
Or you can do something like this.
func fetchAllFoodProducts() -> [FoodProduct]
{
var foodProducts : [FoodProduct] = []
// Fetch From Realm
let products = realm.objects(FoodEntity.self)
for product in products
{
foodProducts.append(FoodProduct(entity: product))
}
return foodProducts
}
By altering your FoodEntity a little.
struct FoodProduct: Food {
let name: String
let weight: Float
let price: Float
init(entity : FoodEntity)
{
self.name = entity.name
self.weight = entity.weight
self.price = entity.price
}
}

Init a computed variable

I'm going to create a new class, and in this class there's a computed variable; so I'm looking for a way to init this variable:
import UIKit
class Squadra: NSCoder, NSCoding
{
var nomeSquadra: String
var numeroCoriSquadra: Int
var coloreSquadra: String
var immagineSquadra: String
var sottotitoloSquadra: String
{
get
{
return "I migliori cori: \(nomeSquadra)"
}
}
init(nome: String, numero: Int, colore: String, immagine: String, sottotitolo: String)
{
nomeSquadra = nome
coloreSquadra = colore
numeroCoriSquadra = numero
immagineSquadra = immagine
sottotitoloSquadra = sottotitolo
}
}
obviously with this line of code Xcode gives my a compile error (because the var is a get only property).
I think that i have to use a set to make the var writable, but I don't know how to operate because I don't know exactly how get and set work.
Either remove sottotitoloSquadra = sottotitolo or assign to a different variable. Even if the assignment worked, you never actually use the value that comes in as sottotitolo for anything.
I can't see useful behavior while you use independent property nomeSquadra and trying to have setter for sottotitoloSquadra at the same time. Maybe better to use hidden support property for computed variable in your case?
private var _sottotitoloSquadra: String
var sottotitoloSquadra: String
{
get
{
return "I migliori cori: \(_sottotitoloSquadra)"
}
set
{
_sottotitoloSquadra = newValue
}
}
init(nome: String, numero: Int, colore: String, immagine: String, sottotitolo: String)
{
//...
_sottotitoloSquadra = sottotitolo
}
If I understand you class correctly, you want to use the variable's default string when the init() did not provide a value (I'm just guessing though).
So if the team doesn't have a specific subTitle, you would make one up from the team's name.
I also understand that you don't want that property to be modifiable after the object is instantiated.
If that is the case, (I assume you would get an empty string for sottotitolo), you can define a private variable to hold the provided title and expose it using a computed variable. The default value (made up title) can be returned by that computed variable if a title was not supplied on the init().
class Squadra
{
var nomeSquadra: String
var numeroCoriSquadra: Int
var coloreSquadra: String
var immagineSquadra: String
private var _sottotitoloSquadra = ""
var sottotitoloSquadra: String
{
return _sottotitoloSquadra == ""
? "I migliori cori: \(nomeSquadra)"
: _sottotitoloSquadra
}
init(nome: String, numero: Int, colore: String, immagine: String, sottotitolo: String)
{
nomeSquadra = nome
coloreSquadra = colore
numeroCoriSquadra = numero
immagineSquadra = immagine
_sottotitoloSquadra = sottotitolo
}
}
Only your class, including its init() function, can modify the private variable holding the supplied sottotitolo. Outside of that source file, the private variable is not accessible at all.