How to append Bool value of a struct in Swift - swift

So, I'm coding in Swift and I've made a struct called User. It has four categories of characteristics- name, email, number of followers, and activity status. I am able to append values of the variables of the structs I create without issue, so long as they are Strings or Ints, Dicts etc. It won't let me append the value of the Bool. I initially set it to 'true', but I'd like to append it to being 'false' after initializing it. I'm getting the error message: "Value of type 'Bool' has no member 'append'". I get that I'm not allowed to append it the same way as I can with say User.name.append("Tim"), but what I don't get is how I actually can append it. Google was unhelpful,searching in SO also yielded nothing of note, and Apple documentation on Structs shows them setting similar values in their explanation at the beginning using Strings and Bools and appending the Strings but not appending the Bools. I can't imagine it not being possible as things change all the time from true to false status, depending on the situation. If I remove the line below, it compiles fine.
userTwo.isAcctive.append(false) is where my error is, for clarity.
Any help would be appreciated!
struct User {
let name: String
let email: String?
var followers: Int
var isActive: Bool
init(name: String, email: String, followers: Int, isActive: Bool){
self.name = name
self.email = email
self.followers = followers
self.isActive = isActive
}
func logStatus(){
if isActive == true{
print("\(name) is working hard.")
} else {
print("\(name) has left the earth.")
}
}
}
var userOne = User(name: "Richard", email:("") , followers: 0, isActive: false)
var userTwo = User(name: "Elon", email:("elon#tesla.com") , followers: 2100, isActive: true)
print(userTwo.isActive)
userTwo.isActive.append(false)
userOne.logStatus()
userTwo.logStatus()

Appending is to add a new element to an array. I think you want to change isActive property. You can do this by:
var userTwo = User(name: "Elon", email:("elon#tesla.com") , followers: 2100, isActive: true)
userTwo.isActive = false

struct User {
var name :String
var email :String?
var followers :Int
var isActive :Bool
init(name: String, email: String?, followers: Int, isActive: Bool){
self.name = name
self.email = email!
self.followers = followers
self.isActive = isActive
}
func logStatus() {
if isActive == true {
print("\(name) is working hard")
}
else {
print("\(name) has left earth")
}
}
}
let userOne = User(name: "Richard", email:("richard#virgin"), followers: 0, isActive: false)
userOne.logStatus()
print("\nDiagnostic code (i.e., Challenge Hint):")
var musk = User(name: "Elon", email: "elon#tesla.com", followers: 2001, isActive: true)
musk.logStatus()
print("Contacting \(musk.name) on \(musk.email!) ...")
print("\(musk.name) has \(musk.followers) followers")
musk.isActive = false
musk.logStatus()

Related

Argument passed to call that takes no arguments when mapping firebase fetch call

I get this error when mapping the results of a firebase query into my model.
"Argument passed to call that takes no arguments" exactly in the return conversations point. why??????
I sincerely don't know why, probably something simple, hopefully Peter Friese will see this post...
#Published var chats = [Conversations]()
private let db = Firestore.firestore()
private let user = Auth.auth().currentUser
func getFilteredConversations(query: String) {
if (user != nil) {
db.collection("conversations").whereField("users", arrayContains: user!.displayName).addSnapshotListener { (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("no conversations found")
return
}
//mapping
self.chats = documents.map{(queryDocumentSnapshot) -> Conversations in
let data = queryDocumentSnapshot.data()
let docId = queryDocumentSnapshot.documentID
let users = data["users"] as? [String] ?? [""]
let msgs = data["messages"] as? [Message] ?? []
let unreadmsg = data["hasUnreadMessage"] as? Bool ?? false
//ERROR HERE!!!!! Argument passed to call that takes no arguments
return Conversations(id: docId, users: users, messages: msgs, hasUnreadMessage: unreadmsg)
}
}
}
}
class Conversations: Decodable, Identifiable, ObservableObject {
//var id: UUID { person.id }
var id: String = ""
var users: [String] = [""]
var messages: [Message] = []
var hasUnreadMessage : Bool = false
}
//ERROR here as well saying it's not decodable!
struct Message: Decodable, Identifiable {
enum MessageType {
case Sent, Received
}
let id = UUID()
let date: Date
let text: String
let type: MessageType?
init(_ text: String, type: MessageType, date: Date) {
self.date = date
self.text = text
self.type = type
}
init(_ text: String, type: MessageType) {
self.init(text, type: type, date: Date())
}
}
The problem is that Conversations is defined as a class with default values and no initializer. So, the only way to initialize it is to use Conversations() (thus the no arguments error). Instead, Conversations should almost certainly be a struct and not be an ObservableObject (it doesn't have any #Published properties anyway and is already getting broadcasted by chats which is #Published. Nesting ObservableObjects is fraught with other issues anyway, so it's best to avoid it):
struct Conversations: Decodable, Identifiable {
var id: String = ""
var users: [String] = [""]
var messages: [Message] = []
var hasUnreadMessage : Bool = false
}
To fix your Codable issue, mark MessageType as Codable then use CodingKeys to tell Swift that there won't be an id to decode:
struct Message: Decodable, Identifiable {
enum CodingKeys : CodingKey {
case date, text, type
}
enum MessageType : Codable {
case Sent, Received
}
let id = UUID()
let date: Date
let text: String
let type: MessageType?
init(_ text: String, type: MessageType, date: Date) {
self.date = date
self.text = text
self.type = type
}
init(_ text: String, type: MessageType) {
self.init(text, type: type, date: Date())
}
}
(It's unclear to me whether there will always be a date to decode -- if you run into issues with this, it's probably best to start a new question about it since it's tangential from the original issue anyway)

Loop through an Array in Vapor and handle future correctly

I have an array of customers which follow the following model:
struct GuestListPeople: Codable {
var fName: String
var lName: String
var dob: Date
var waiver: Bool
}
I want to loop through the array each time checking a MongoDB collection, using Mongokitten, to see if the waiver is signed then update the bool to true or false as needed. I have created this function to check the waiver status.
func checkGuestListWaiver(req: Request, col: Collection, firstName: String, lastName: String, dob: Date, orgId: String)throws->Future<Bool>{
col.findOne(["firstName":firstName, "lastName":lastName, "dob": dob ,"orgId":orgId, "waiver":true]).flatMap{ custDoc in
if let cd = custDoc {
//found one so waiver must be true
return req.future().map{
return true
}
}else {
//not found return false.
return req.future().map{
return false
}
}
}
}
My issue is the function returns a Future Bool, and I am not sure how I should handle the future in the for loop.
for var c in guestList {
let w = try CustomerController().checkGuestListWaiver(req: req, col: customerCol, firstName: c.fName, lastName: c.lName, dob: c.dob, orgId: b.orgId)
c.waiver = w
}
I get an error can't assign EventLoopFurture to type Bool. I have tried a few options, but always come back to the same issue of getting a Future back from the database.
You could iterate using flatten
func processGuestList(_ guestList: [Guest], on req: Request) throws -> Future<[Guest]> {
var newList: [Guest] = []
return try guestList.map { guest in
return try CustomerController().checkGuestListWaiver(req: req, col: customerCol, firstName: c.fName, lastName: c.lName, dob: c.dob, orgId: b.orgId).map { w in
var newGuest = guest
newGuest.waiver = w
newList.append(newGuest)
}
}.flatten(on: req).transform(to: newList)
}

Simplify Swift data struct

Any suggestion on how to simplify this data struct? The data will be saved as a dictionary on the user's drive and when I read the data from the drive I have to convert them back to Member for easy accessing the properties.
I would like to have it typesafe.
struct Member {
var id: Int
var firstname: String
var lastname: String
var address: String?
var zipCode: Int?
var city: String?
enum Value: String {
case id = "id"
case firstname = "firstname"
case lastname = "lastname"
case address = "address"
case zipCode = "zipCode"
case city = "city"
}
var member: [String:Any] {
return [
Value.id.rawValue:Int(),
Value.firstname.rawValue:firstname,
Value.lastname.rawValue:lastname,
Value.address.rawValue:address ?? String(),
Value.zipCode.rawValue:zipCode ?? Int(),
Value.city.rawValue:city ?? String()
]
}
}
func memberToDic(member: Member) -> [String:Any] {
return [
Member.Value.firstname.rawValue:member.firstname,
Member.Value.lastname.rawValue:member.lastname,
Member.Value.address.rawValue:member.address ?? String(),
Member.Value.zipCode.rawValue:member.zipCode ?? Int(),
Member.Value.city.rawValue:member.city ?? String()
]
}
func dicToMember(dic: [String:Any]) -> Member {
return Member(
id: dic[Member.Value.id.rawValue] as! Int,
firstname: dic[Member.Value.firstname.rawValue] as! String,
lastname: dic[Member.Value.lastname.rawValue] as! String,
address: dic[Member.Value.address.rawValue] as? String,
zipCode: dic[Member.Value.zipCode.rawValue] as? Int,
city: dic[Member.Value.city.rawValue] as? String
)
}
Almost certainly, this is the correct implementation:
struct Member: Codable {
var id: Int
var firstName: String // "first name" is two words, so capitalize "name"
var lastName: String
var address: String // "No address" should be empty, not nil
var zipCode: String // ZIP codes are not integers
var city: String // "No city" should be empty, not nil
}
In order to save this as a plist, use PropertyListEncoder:
let data = try PropertyListEncoder().encode(member)
To read it, use PropertyListDecoder.
Codable automatically creates key mappings for your properties, so there's no need for Value.
You should strongly avoid creating or consuming [String: Any] dictionaries. These exist mostly due to Objective-C interfaces that could not generate strong types.
If address, zipCode, and city all should be set together, or not set together, then you should collect them into a single struct:
struct Address: Codable {
var streetAddress: String
var zipCode: String
var city: String
}
struct Member: Codable {
var id: Int
var firstName: String // "first name" is two words, so capitalize "name"
var lastName: String
var address: Address?
}
In this case, and Optional makes sense because "empty" is not the same thing as "missing."

How to make an empty object in swift

How do I initialize an empty object in swift? This is what I have but it wants the parameters too
var userInfo:User = User()
init(email: String, isVerified: String, profileImageURL: String, reputation: String, twitterHandle: String, userName: String) {
self._email = email
self._isVerified = isVerified
self._profileImageURL = profileImageURL
self._reputation = reputation
self._twitterHandle = twitterHandle
self._userName = userName
}
Create the class/struct with optional properties like this
struct Employee {//struct or class
var name: String?
var number: String?
var position: String?
}
Then you can create an object without any value, with some value
let emp = Employee()
let emp = Employee(name: nil, number: nil, position: nil)
let emp = Employee(name: "abc", number: nil, position: "xyz")
By creating a init method with default values parameter can be ignored while creating an object
struct Employee {
var name: String?
var number: String?
var position: String?
init(name: String? = nil, number: String? = nil, position: String? = nil) {
self.name = name
self.number = number
self.position = position
}
}
let emp = Employee()
let emp = Employee(name: "abc", number: "124", position: "xyz")
let emp = Employee(name: "abc", position: "xyz")
let emp = Employee(number: "124")
let emp = Employee(name: "abc", number: "124")
I assume you are creating empty User objects so that the information can be filled in later on. There are two problems I can see with this: one, object properties will all have to be variables and second, it's easy to make mistakes and pass the wrong information since the object does not always correspond to a real entity.
A better approach would be to define a set of parameters that are mandatory for a User object to be defined, and let every other parameters either be optional parameters, or give them default values. For example, if we choose username and email to be mandatory, and let profile image be optional, and isVerified to have a default value of false:
class User {
var userName: String
var email: String
var isVerified: Bool
var profileImageURL: String?
init(userName: String, email: String, isVerified: Bool = false) {
self.userName = userName
self.email = email
self.isVerified = isVerified
}
}

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
}