How to create a shared part between two object - swift

List and User are 2 classes. List contains title and a set of movies someone may likes. The class User has a AddList(_: List) method. It add new movie list to object of User type
class List {
var title : String = "General"
var movies = [String]()
init(title: String){
self.title = title
}
init(title: String, movies: [String]){
self.title = title
self.movies = movies
}
}
class User {
private let userID: Int
let userName: String
private var movieList = [String: [String]]() // the list is a Dictionaly, the value of the dictionalry is a string array
init(userID: Int, userName: String){
self.userID = userID
self.userName = userName
}
func AddList(newMovieList: List) -> Dictionary<String, [String]> {
self.movieList.updateValue(newMovieList.movies, forKey: newMovieList.title)
return self.movieList
}
}
3 movie list had beend inilizied as follow
var actionMovieJhon = List(title: "Action", movies: ["The Equalizer", "Mad Max: Fury Road", "Star Wars"])
var actionMovieAnna = List(title: "Action", movies: ["Batman v Superman: Dawn of Justice", "American Sniper", "The Martia"])
var shareMovie = List(title: "ShareAction", movies: ["MovieA", "MovieB", "MovieC"])
And then 2 User objdects Jhon and Anna with ther own movie list was created
var Jhon = User(userID: 1001, userName: "Jhon")
var Anna = User(userID: 1002, userName: "Anna")
Jhon.AddList(actionMovieJhon)
Anna.AddList(actionMovieAnna)
Question: How can I add the shared movie list (sharMovie) into the dictionalry of Jhon and Anna, so that anything changed in the shared list of Jhon will get updated in the side of Anna and vice versa.
In anther word, the "dictionary" of Jhon and Anna has their own part and shared part. But the shared part is referenced to the same list object
More Question: I had feelings that the method self.movieList.updateValue is not update by reference
Thanks a lot for your help

The code is based on the sorece code provided by "Swift Apprentice, Beginning programming with Swift 2.2", chapter 14: Classes
import Foundation
class List {
let name: String
var movies: [String] = []
init(name: String){
self.name = name
}
func printList() {
print("Movie List: \(name)")
for movie in movies{
print(movie)
}
print("\n")
}
}
class User {
var lists: [String: List] = [:]//****
func addList(list: List){
lists[list.name] = list
}
func listForName(name: String)-> List? {
print(lists[name]?.movies)
return lists[name]
}
}
//create user
let jane = User()
let john = User()
let people = User()
//create list
var johnActionMovie = List(name: "ActionJhon")
var janeActionMovie = List(name: "ActionJane")
var sharedMovie = List(name: "SharedMovie")
var publicIntrest = sharedMovie
//call addList(_:) method on user object
jane.addList(janeActionMovie)
john.addList(johnActionMovie)
jane.addList(sharedMovie)
john.addList(sharedMovie)
people.addList(publicIntrest)
//private part appending
jane.lists["ActionJane"]?.movies.append("janeA")
jane.lists["ActionJane"]?.movies.append("janeB")
jane.lists["ActionJane"]?.movies.append("janeC")
john.lists["ActionJhon"]?.movies.append("johnA")
john.lists["ActionJhon"]?.movies.append("johnB")
john.lists["ActionJhon"]?.movies.append("johnB")
//Shared part appending
john.lists["SharedMovie"]?.movies.append("shareA")
jane.lists["SharedMovie"]?.movies.append("shareB")
people.lists["SharedMovie"]?.movies.append("shareC")
//print out the result
john.listForName("ActionJhon")
john.listForName("SharedMovie")
jane.listForName("ActionJane")
jane.listForName("SharedMovie")
people.listForName("SharedMovie")
//further check
print(jane.lists["ShareMovie"] === people.lists["ShareMovie"])
print(jane.lists["ActionJane"] === john.lists["ActionList"])

Related

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

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

How do you pass data dynamically is a Swift array?

Im creating a tinder like swipe app and I need a new CardData(name: "", age: "") to be created depending on how many profiles I pass through from my database. The number of cards will change. I need the number of cards created to match the the value of the results default. I have looked for the solution for a while and can't find it anywhere.
import UIKit
var nameArrayDefault = UserDefaults.standard.string(forKey: "nameArray")!
var ageArrayDefault = UserDefaults.standard.string(forKey: "ageArray")!
var nameArray = nameArrayDefault.components(separatedBy: ",")
var ageArray = ageArrayDefault.components(separatedBy: ",")
var results = UserDefaults.standard.string(forKey: "results")!
struct CardData: Identifiable {
let id = UUID()
let name: String
let age: String
static var data: [CardData] {[
CardData(name: “\(nameArray[0])”, age: “\(ageArray[0])”),
CardData(name: “\(nameArray[1])”, age: “\(ageArray[1])"),
CardData(name: “\(nameArray[2])”, age: “\(ageArray[2])”)
]}
}
You should initiate the array of CardData objects only the first time and update it after that. You can do the following
var _data = [CardData]()
var data: [CardData] {
if _data.isEmpty() {
self.initiateData()
}
return _data
}
// Initiate data for the first time only
func initiateData() -> [CardData] {
// Check that nameArray has the same size as ageArray
// If the size is different then the data are not valid.
guard nameArray.count == ageArray.count else {
return []
}
// For each of names inside nameArray create the corresponding CardData object
self.nameArray.forEach({ (index, item)
self._data.append(CardData(name: item, age: ageArray[index]))
})
}

Need acces from document to collection Firestore

I'm trying to do an iOS app and i've binded it with firebase, so I'm trying to get some posts ad fetch them, and this works fine, however this posts got 2 collections (likes and replies) and i'm trying to fetch likes, the thing is that I can't get the likes because for some reasons I can't a class for document forEach neither I can't access it, someone got any idea?
Code:
import Foundation
import Firebase
struct Post : Hashable {
var id : String
var dateAdded : String
var posterEmail : String
var posterUsername : String
var posterIcon : String
var postTitle : String
var postBody : String
var likes : [String]
var userLikedPost : Bool
}
struct Like {
var likeId : String
var likerEmail : String
}
class Likes {
var likes : [Like] = []
func fetchLikes() {
//Firestore.firestore()
}
}
class Posts : ObservableObject {
#Published var posts : [Post] = []
func fetchPosts() {
Firestore.firestore().collection("posts").getDocuments(completion: { (docPosts, error) in
if (error != nil) {
print("error fetching posts")
} else {
docPosts?.documents.forEach { (post) in
let id = post.documentID
let email = post.get("posterEmail") as! String
let username = post.get("posterUsername") as! String
let icon = post.get("posterIcon") as! String
let title = post.get("title") as! String
let body = post.get("body") as! String
// Here i want to insert the code that gets the likes class and access the likes variable
self.posts.append(Post(id: id, dateAdded:String(id.split(separator: "_").joined(separator: "/").prefix(10)) ,posterEmail: email, posterUsername: username, posterIcon: icon, postTitle: title, postBody: body,
likes: [],userLikedPost: false))
}
}
})
}
}
The Firestore structure was not included in the question so I will present one for use
user_wines
uid_0
name: "Jay"
favorite_wines:
0: "Insignia"
1: "Scavino Bricco Barolo"
2: "Lynch Bages"
uid_1
name: "Cindy"
favorite_wines
0: "Palermo"
1: "Mercury Head"
2: "Scarecrow"
And then the code to read all of the user documents, get the name, the wine list (as an array as Strings) and output it to console
func readArrayOfStrings() {
let usersCollection = self.db.collection("user_wines")
usersCollection.getDocuments(completion: { snapshot, error in
guard let allDocs = snapshot?.documents else { return }
for doc in allDocs {
let name = doc.get("name") as? String ?? "No Name"
let wines = doc.get("favorite_wines") as? [String] ?? []
wines.forEach { print(" ", $0) }
}
})
}
and the output
Jay
Insignia
Scavino Bricco Barolo
Lynch Bages
Cindy
Palermo
Mercury Head
Scarecrow
EDIT
Here's the same code using Codable
class UserWineClass: Codable {
#DocumentID var id: String?
var name: String
var favorite_wines: [String]
}
and the code to read data into the class
for doc in allDocs {
do {
let userWine = try doc.data(as: UserWineClass.self)
print(userWine.name)
userWine.favorite_wines.forEach { print(" ", $0) }
} catch {
print(error)
}
}

Filter not working in model object - swift

Basically my model object contains another model object and i want search/filter from model which is inside the main model object, please check below for more clarifications.
struct User {
var name: String!
var age: Int!
var hasPet: Bool!
var pets: [Pets]!
}
struct Pets {
var id: Int!
var petName: String!
var colour: String!
}
Here when my final model object is created in that model i want filter which should filter main model based on what pet name user enter.
For example if one user in model have 2 pets one with black colour and second with white colour.
Then second user in model have 2 pets one with grey colour and one with brown colour.
Now when user search for Users who have white colour pet can come up in list. As in this case list will show first user. like that.
I have tried below code but it's not working ::
let petsFiltered = users.filter { (user) -> Bool in
return (user.pets.)!
}
Thanks in advance!
Your structs:
struct User {
var name: String
var age: Int
var hasPet: Bool
var pets: [Pet]
}
struct Pet {
var id: Int
var petName: String
var colour: String
}
User and pets objects:
let pet1 = Pet(id: 1, petName: "Lucy", colour: "black, white")
let pet2 = Pet(id: 2, petName: "Nemo", colour: "white")
let pet3 = Pet(id: 3, petName: "Scooby-Doo", colour: "white")
let pet4 = Pet(id: 4, petName: "Garfield", colour: "brown, whiteerktjdfg")
let user1 = User(name: "Joe", age: 20, hasPet: true, pets: [pet1, pet2])
let user2 = User(name: "Sophia", age: 30, hasPet: true, pets: [pet3, pet4])
let users = [user1, user2]
You can filter user who have white pets with below code:
let whitePets = users.compactMap{user -> (User?) in
var userWithWhitePets = user
let pets = userWithWhitePets.pets.filter{$0.colour.contains("white")}
userWithWhitePets.pets = pets
return userWithWhitePets.pets.count > 0 ? userWithWhitePets : nil
}
Hope this will help you :)
Based on the comments. You need to use "pets" like array instead dictionary and check colour. Something like that:
let usersFiltered = users.filter { (user) -> Bool in
return user.pets.contains(where: { $0.colour == "white" })
}
I think will be better for you also use a computed property for hasPet.
If not, each time that you add a first pet to the array or remove all elements from it, you will need to also update var hasPet: Bool.
var hasPet: Bool{
return pets.count > 0
}

How call method to filter by year

I'm making easy example.
I'm trying to filter movies by year using func: filterByYear.
I have an error in one line on the bottom:
'var filterYear = MovieArchive.filterByYear(1980)' Compiler info is: 'Type 'MovieArchive' has no member filterByYear’
import UIKit
class Movie {
let title: String
let director: String
let releaseYear: Int
init(title: String, director: String, releaseYear: Int){
self.title = title
self.director = director
self.releaseYear = releaseYear
}
}
class MovieArchive{
var movies : [Movie]
init(movies:[Movie]){
self.movies = movies
func filterByYear(year:Int) -> [Movie]{
var filteredArray = [Movie]()
for movie in movies{
if movie.releaseYear == year {
filteredArray.append(movie)
}
}
return filteredArray
}
}
}
var newMovie1 = Movie(title: "IT", director: "S.S", releaseYear: 1980)
var newMovie2 = Movie(title: "PP", director: "O.N", releaseYear: 2003)
var moviesArray = [newMovie1, newMovie2]
var myArchive = MovieArchive(movies: moviesArray)
var filterYear = MovieArchive.filterByYear(1980)
var firstMovie = filterYear[0]
print(firstMovie)
You are calling the method on the class itself, not an instance of it. You would have to create an instance of the class like this:
let myMovieArchive = MovieArchive(movies: [Some Array])
and then call the method on the instance.
print(myMovieArchive.filterByYear)
So in your case, call it on myArchive, not MovieArchive.
Even though the question has been answered, you can simplify your filterByYear method to make it a bit more Swifty:
func filterByYear(year:Int) -> [Movie]
{
return movies.filter({ $0.releaseYear == year })
}