Overlapping accesses to 'but modification requires exclusive access; consider copying to a local variable' - swift

an employee can own more than one animal, but an animal must have only one employee.
First, I add an employee. Then when adding an animal, I select the employee.
After selecting the employee, I press the save button.
I want to transfer the animal I just added to that employee.
I want to take the employee's id and add it to the employee attribute of the animal I added.
Animal Model:
struct Animal {
let id: String?
var name: String?
var sound: String?
var age: Double?
var waterConsumption: Double?
var employee: Employee?
var animalType: String?
}
Employee Model:
struct Employee {
let id: String?
var name: String?
var lastName: String?
var age: Int?
var gender: String?
var salary: Double?
var experienceYear: String?
var animals: [Animal]?
}
ZooManager:
Error is on this line: newAnimal.employee?.animals?.append(newAnimal)
Overlapping accesses to 'newAnimal.employee', but modification requires exclusive access; consider copying to a local variable
protocol ZooManagerProtocol {
var animals: [Animal] { get set }
var employees: [Employee] { get set }
func addNewAnimal(_ model: Animal)
}
class ZooManager: ZooManagerProtocol {
static var shared = ZooManager()
var animals: [Animal] = []
var employees: [Employee] = []
private init() { }
func addNewAnimal(_ model: Animal) {
var newAnimal = model
guard var employee = employees.filter({ $0.id == model.employee?.id }).first else { return }
newAnimal.employee = employee
newAnimal.employee?.animals?.append(newAnimal)
dump(newAnimal)
}
func addNewEmployee(_ model: Employee) {
employees.append(model)
}
}

The solution recommended by the compiler works
func addNewAnimal(_ model: Animal) {
var newAnimal = model
guard var employee = employees.filter({ $0.id == model.employee?.id }).first else { return }
employee.animals?.append(newAnimal)
newAnimal.employee = employee
}
I've never seen this error before, hopefully someone can add a good answer explaining the why and not just the how!
EDIT:
Here's the why https://github.com/apple/swift-evolution/blob/main/proposals/0176-enforce-exclusive-access-to-memory.md

Related

Environmentobject keep track of arrays variables

I'm pretty new to xCode so this could have an obvious answer.
I've just learned about environment objects and created the following one:
import SwiftUI
class Data: ObservableObject {
#Published var types = [Type]()
#Published var customers = [Customer]()
#Published var templates = [SubscriptionTemplate]()
#Published var subscriptions = [Subscription]()
#Published var giftCodes = [Giftcode]()
}
As you can see the object contains an array of objects. One of these is a customer array. The customer object looks like this:
import SwiftUI
class Customer: Identifiable, Codable{
var id: Int
var firstname: String
var lastname: String
var address: String
var plz: Int
var place: String
var credit: Int
init(id: Int, firstname: String, lastname: String, address: String, plz: Int, place: String, credit: Int) {
self.id = id
self.firstname = firstname
self.lastname = lastname
self.address = address
self.plz = plz
self.place = place
self.credit = credit
}
}
extension Customer: Equatable {
static func == (lhs: Customer, rhs: Customer) -> Bool {
return lhs.id == rhs.id
}
}
In my project, I implemented an API call to update the customer. This works like a charm, but after updating, I also want to fetch the customer objects with the following method:
API().fetchCustomers { (customers) in
self.data.customers = customers
}
After updating an object this doesn't work. The environment object doesn't update, but after creating a new object or fetching the data initial, it works.
What is the difference between the update and the create / fetch?
Make Customer a value type (ie. struct):
struct Customer: Identifiable, Codable{
var id: Int
// ... other code

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.

Create an object array with few object properties of another object array using map

Suppose we have an Employee class like this
class Employee {
var id: String?
var name: String?
var role: String?
var age: Int?
var salary: Int?
}
var employeeList: [Employee]
I have to create a new object array with just few properties(eg: id, name, age) from employeeList.
Is there any other way(like map) other than using a for-loop to iterate the employeeList?
If you wanted to use map, you could do something like the following:
class Employee {
var id: String?
var name: String?
var role: String?
var age: Int?
var salary: Int?
}
var employeeList: [Employee]
var employeeModifiedList = employeeList.map {
(id: $0.id, name: $0.name, age: $0.age)
}
Something like this?
let myData: [(String?, String?, Int?)] = employeeList.map { ($0.id, $0.name, $0.age) }
Although not sure why your id is String type... usually ID's are Int

Copy one struct object

i have to quite similar struct objects. but one includes more values than the other. As the initial is required for KituraKuery methods i can not modify it but require more information for future processing.
my problem is now, that these struct objects look like this:
struct provider: Codable {
var firstName: String?
var lastName: String?
var email:String?
}
extension provider: Model{
class Persistence {}
}
struct provider2: Codable {
var firstName: String?
var lastName: String?
var email:String?
var providerCategories: [categories]?
}
extension provider: Model{
class Persistence {}
}
what i need is basically a smarter way to copy information from provider to provider2.
what i did as of now is i provided an init to provider2 taking provider as input and adding all values to it.
struct provider2: Codable {
var firstName: String?
var lastName: String?
var email:String?
var providerCategories: [categories]?
init(provider: provider?) {
if let provider = provider{
firstName = provider.firstName
lastName = provider.lastName
email = provider.lastName
}
}
extension provider: Model{
class Persistence {}
}
i however believe this is probably the worst way and there are much better and more lean approaches to it.
I tried myself on protocols but could that not really get to work.
Any input would be great :)
In your approach both Provider and Provider2 struct are tightly coupled to each other. So lets say in future if you want to change Provider struct or you want to initialise Provider2 struct with another struct, then you need to change lot of things.
We can solve the problem easily by decoupling both Provider and Provider2 struct
protocol BasicInfo {
var firstName: String? { get set }
var lastName: String? { get set }
var email:String? { get set }
}
protocol Address {
var address: String? {get set}
}
struct Provider: BasicInfo {
var firstName: String?
var lastName: String?
var email: String?
}
struct Provider2: BasicInfo, Address {
var firstName: String?
var lastName: String?
var email:String?
var address: String?
init(basic: BasicInfo, add: String) {
firstName = basic.firstName
lastName = basic.lastName
email = basic.email
address = add
}
}
//Below are instances of respective struct
let provider1 = Provider(firstName: "Test1", lastName: "TestLast1", email: "test1#gmail.com")
var provider2 = Provider2(basic: provider1, add: "Germany")
In above code i have two different Struct Provider and Provider2. Provider2 contains more variable than Provider (i have just added a single var to demonstrate). Now lets say in future we don't require Provider to fill Provider2, we have a new struct Provider3 which will fill Provider2 values.
struct Provider3: BasicInfo {
var firstName: String?
var lastName: String?
var email: String?
var middleName: String? //new property added
}
//Below are instances of respective struct
let provider3 = Provider3(firstName: "Test1", lastName: "TestLast1", email: "test1#gmail.com")
var provider2 = Provider2(basic: provider3, add: "Germany")
As you see there is no changes in struct Provider2, we have just introduce a new struct, create instance of new struct and passed that instance to Provider2 init method.
You could use extensions for this:
extension provider {
func asProvider2() -> provider2 {
return provider2(firstName: firstName,
lastName: lastName,
email: email,
providerCategories: nil)
}
}
// Then you can use it like this:
let bar = provider(firstName: "foo", lastName: "bar", email: "baz")
let bar2 = bar.asProvider2()

Keep value semantics on structs (containing an array of reference types)

Let's say (just as an example) I have a class Person and a struct House:
class Person {
var name: String
var lastName: String
init(name: String, lastName: String) {
self.name = name
self.lastName = lastName
}
}
struct House {
var address: String
var squareMeters: Float
var owner: Person
init(withAddress address: String, squareMeters: Float, andOwner owner: Person) {
self.address = address
self.squareMeters = squareMeters
self.owner = owner
}
}
To keep the value semantics on House is pretty easy in this case, if I'm not mistaken I can just do something like:
struct House {
var address: String
var squareMeters: Float
private var owner: Person //owner is now private
init(withAddress address: String, squareMeters: Float, andOwner owner: Person) {
self.address = address
self.squareMeters = squareMeters
//init now creates a copy of the owner passed
self.owner = Person(name: owner.name, lastName: owner.lastName)
}
//every time the owner gets modified from the outside we make sure to create a copy if needed
private var ownerToWrite: Person {
mutating get {
if !isKnownUniquelyReferenced(&owner) {
owner = Person(name: owner.name, lastName: owner.lastName)
}
return owner
}
}
var ownerName: String {
get {
return owner.name
}
mutating set {
ownerToWrite.name = newValue
}
}
var ownerLastName: String {
get {
return owner.name
}
mutating set {
return ownerToWrite.lastName = newValue
}
}
}
Ok, this should work. But what if House contained an array of Person? For instance:
struct House {
var address: String
var squareMeters: Float
var tenants: [Person]
init(withAddress address: String, squareMeters: Float, andTenants tenants: [Person]) {
self.address = address
self.squareMeters = squareMeters
self.tenants = tenants
}
}
How could I maintain the value semantics on the new House struct? Thank you in advance.