How to create an variable property based on the other property in the class - swift

class someClass
{
// This is a list of restaurant names
var restaurantNames = ["Cafe Deadend", "Homei", "Teakha", "Cafe Loisl", "Petite Oyster", "For Kee Restaurant", "Po's Atelier", "Bourke Street Bakery", "Haigh's Chocolate", "Palomino Espresso", "Upstate", "Traif", "Graham Avenue Meats", "Waffle & Wolf", "Five Leaves", "Cafe Lore", "Confessional", "Barrafina", "Donostia", "Royal Oak", "Thai Cafe"]
// This is an array to record the restaurants I have visited
var restaurantIsVisited:[Bool] = [Bool](count: 21, repeatedValue: false)
// This is a function to set the restaurants I have visited
func setTheVisitedRestaurants(somenumber:Int)
{
self.restaurantIsVisited[somenumber] = true
}
}
let thisVisit = someClass()
thisVisit.setTheVisitedRestaurants(1)
let somearray = thisVisit.restaurantIsVisited
Above code has no error in playground. But what if I don't know the number of the total restaurants is 21 (It could be hundreds and I don't want to count). I tried the following code to create an immutable computed property. But it won't work because later on the property will be changed by function setTheVisitedRestaurants and it will return an error.
// var restaurantIsVisited:[Bool]{
// return [Bool](count: restaurantNames.count, repeatedValue: false)
// }
In a word, the question is like how to create an variable property based on the other property in the class. I am a beginner and I really tried. Please help!

Declare restaurantIsVisited with the lazy keyword. This will insure that it isn't created until it is accessed the first time, and by that time you will be able to ask restaurantNames for its count:
class someClass
{
// This is a list of restaurant names
var restaurantNames = ["Cafe Deadend", "Homei", "Teakha", "Cafe Loisl", "Petite Oyster", "For Kee Restaurant", "Po's Atelier", "Bourke Street Bakery", "Haigh's Chocolate", "Palomino Espresso", "Upstate", "Traif", "Graham Avenue Meats", "Waffle & Wolf", "Five Leaves", "Cafe Lore", "Confessional", "Barrafina", "Donostia", "Royal Oak", "Thai Cafe"]
// This is an array to record the restaurants I have visited
lazy var restaurantIsVisited:[Bool] = [Bool](count: self.restaurantNames.count, repeatedValue: false)
// This is a function to set the restaurants I have visited
func setTheVisitedRestaurants(somenumber:Int)
{
self.restaurantIsVisited[somenumber] = true
}
}
let thisVisit = someClass()
thisVisit.setTheVisitedRestaurants(1)
let somearray = thisVisit.restaurantIsVisited
println(somearray.count) // "21"

Why not use a struct to represent a restaurant, for example:
struct Restaurant {
let name: String
var visited: Bool
init(name: String, visited: Bool = false) {
self.name = name
self.visited = visited
}
}
Now it's much easier to keep track of which restaurants have been visited and which ones haven't because you haven't got to worry about information on each restaurant being separated into two arrays.
struct SomeStruct {
var restaurants = [Restaurant(name: "Cafe Deadend"),
Restaurant(name: "Homei"),
Restaurant(name: "Cafe Loisl")]
}
Usage:
var thisVisit = SomeStruct()
thisVisit.restaurants[0].visited = true

Related

How can I refer to properties from a struct within a struct/

I'm trying to get the hang of how to make my code the most efficient using Structs / Classes, and I'm trying to understand it via the following example.
I'd be really grateful if someone could correct me or guide me about the same:
Just as an example, I'll use Harry Potter. There are four houses, and each house has certain characteristics.
So now I have a struct for 2 of them:
struct Gryffindor {
let name = "Gryffindor"
let characteristic = "Brave"
let image = Image("Lion")
}
struct Slytherin {
let name = "Slytherin"
let characteristic = "Cunning"
let image = Image("Snake")
}
Now if I wish to have a wizard struct as follows, but I don't know how to include a House property within, such that when I try to create an instance of a wizard, I can call the properties from their respective houses.
struct Wizard {
let name: String
var house: ?
}
let harryPotter = Wizard(name: "Harry", house: Gryffindor)
Basically, I wish to be able to refer to harry's house using the harryPotter instance, as such:
print(harryPotter.characteristic) //should print "Brave"
Is what I'm trying to achieve even possible?
First of all you are mixing types with objects so you should have a type House
struct House {
let name: String
let characteristic: String
let image: Image
}
And then use that in the Wizard struct
struct Wizard {
let name: String
var house: House
}
And now you create first a House object for the Wizard and then the Wizard object
let gryffindor = House(name: "Gryffindor", characteristic: "Brave", image: Image("Lion"))
let harryPotter = Wizard(name: "Harry", house: gryffindor)
or all in one call
let harryPotter = Wizard(name: "Harry",
house: House(name: "Gryffindor", characteristic: "Brave", image: Image("Lion")))
Use protocol & generics, like below. Tested with Xcode 11.4.
protocol House {
var name: String { get }
var characteristic: String { get }
var image: Image { get }
}
struct Gryffindor: House {
let name = "Gryffindor"
let characteristic = "Brave"
let image = Image("Lion")
}
struct Wizard<H: House> {
let name: String
var house: H
}
let harryPotter = Wizard(name: "Harry", house: Gryffindor())

Swift iOS -How come I can loop through an array of class objects and make property changes but not structs [duplicate]

This question already has answers here:
Is Swift Pass By Value or Pass By Reference
(10 answers)
Swift can change struct declared with let if using an index but not if using a loop
(3 answers)
Closed 3 years ago.
If I loop through an array of Class objects I can make changes to a property on it
class Country {
var name: String?
var region: String?
init(name: String?, region: String?) {
self.name = name
self.region = region
}
}
let canada = Country(name: "Canada", region: "North America")
let mexico = Country(name: "Mexico", region: "North Ameria")
let france = Country(name: "France", region: "Europe")
let korea = Country(name: "Korea", region: "Asia")
var countryArr = [canada, mexico, france, korea]
// this works fine
let transformed = countryArr.map { $0.name = "Random" }
But if I try this with Struct objects I get
Cannot assign to property: '$0' is immutable
struct Country {
var name: String?
var region: String?
}
var canada = Country(name: "Canada", region: "North America")
var mexico = Country(name: "Mexico", region: "North Ameria")
var france = Country(name: "France", region: "Europe")
var korea = Country(name: "Korea", region: "Asia")
var countryArr = [canada, mexico, france, korea]
// this gets an error
let transformed = countryArr.map { $0.name = "Random" }
The issue is caused by the fact that structs are value types, so mutating any properties of the struct mutates the struct instance itself as well and the closure input arguments in map are immutable. So when you try to mutate a property $0 in the closure of map, you are trying to mutate $0 itself in case map is called on a collection of value types.
On the other hand, classes are reference types, so mutating a property of a class instance doesn't mutate the instance itself.
A solution for your problem is to create a mutable copy of the struct instance in the map, mutate its name property and return that. There are two solutions, if you have a small number of properties on your type, calling its memberwise initialiser is easier, but if you have a lot of properties and only want to mutate a few, copying the struct and then modifying the necessary properties is the better choice.
let transformed = countryArr.map { Country(name: "Random", region: $0.region) }
let transformed2 = countryArr.map { country->Country in
var copy = country
copy.name = "Random"
return copy
}

Realm query problem with sorted localized data

Consider the following Realm models:
class Fruit: Object {
#objc dynamic var name = ""
let localizations = List<Localization>()
/**
Returns the localized name of the fruit matching the preferred language of the app
or self.name if the fruit does not have a localization matching the user preferred language codes.
*/
var localizedName: String? {
guard !Locale.isPreferredLanguageDefaultAppLanguage else { return self.name }
let preferredLanguagesCodes = Locale.preferredLanguagesCodes
let localizations = preferredLanguagesCodes.compactMap({ languageCode in Array(self.localizations).filter({ $0.languageCode == languageCode }).first })
return localizations.first?.localizedName ?? self.name
}
}
class Localization: Object {
#objc dynamic var localizedName: String = ""
#objc dynamic var languageCode: String = ""
}
Let's say I have 2 fruits in my database (represented in JSON format for the sake of simplicity):
[
{
"name": "Apple",
"localizations": [
{
"localizedName": "Pomme",
"languageCode": "fr"
}
]
},
{
"name": "Banana",
"localizations": [
{
"localizedName": "Banane",
"languageCode": "fr"
}
]
}
]
Now I want to get all the fruits in my database, and sort them alphabetically by their localizedName.
var localizedNameSortingBlock: ((Fruit, Fruit) -> Bool) = {
guard let localizedNameA = $0.localizedName, let localizedNameB = $1.localizedName else { return false }
return localizedNameA.diacriticInsensitive < localizedNameB.diacriticInsensitive
}
let sortedFruits = Array(Realm().objects(Fruit.self)).sorted(by: localizedNameSortingBlock)
If the first preferred language of my device is "English", I get this:
Apple
Banana
If it's set to "French":
Banane
Pomme
It's quite simple, but this solution has a major inconvenient:
By casting the Results<Fruit> collection into an array, I'm loosing the ability to get live updates via Realm's notification token system.
The problem is, I can't sort using NSPredicate directly on the Results collection, because the localizedName property is a computed property, thus is ignored by Realm.
I thought about writing the localizedName value directly into the name property of the Fruit object, but doing so requires to loop through all fruits and change their name whenever the user change of preferred language. There must be a better way.
So my question is:
Is there a way to retrieve all the fruits in my database, get them sorted by their localizedName, without loosing the ability to receive batch updates from Realm?

Retrieving data from dictionary Swift

I am trying to retrieve data from a dictionary and assign the value to "city". I would like to use information[1]["City"], but that generates an error, as well. Also, not sure if I need to unwrap an optional here.
let locals = ["Jerry":["City": "Seattle", "Preference": "Adventurer"],"Emily":["City": "Boston", "Preference": "History Buff"]
func matchTravellerWithLocal(location: String, type: String) -> String {
var message = " "
let locals = LocalsInformation().locals
for (name, information) in locals{
let city = information["City"]!
print(city)
let preference = information["Preference"]
if city == location{
message = "Meet your local: \(name), a local of \(city)"
}else{
message = "Apologies, there aren't in \(city) on LocalRetreat. Try again soon!"
break
}
if preference == type{
message += "who is also a \(preference)"
}
}
return message
}
Your locals should be defined like this:
let locals: [[String: Dictionary<String, String>]] = [["Jerry":["City": "Seattle", "Preference": "Adventurer"]],["Emily":["City": "Boston", "Preference": "History Buff"]]]
Then you can access it like that:
let jerry = locals[0]["Jerry"]

Delcare and access multidimensional array in swift

I would like to declare an array which should look like this:
myArray
->Car (Array)
- Ford
- Audi
- ...
->Color (Array)
- Red
- Green
- ...
->Wheels (Simple String, eg "4")
->Roof (Simple String, eg, "NO")
Im stuck in space to delare and access this type of array. Thank you for any help!
I think creating custom objects (with classes or structs) would be better suited for you, like this for example:
class Car {
var brand: String?
var color: String?
var wheels: Int?
init(brand: String, color: String, wheels: Int = 4) {
self.brand = brand
self.color = color
self.wheels = wheels
}
}
let myAudi = Car(brand: "Audi", color: "red")
let myFord = Car(brand: "Ford", color: "green", wheels: 3)
var myCars = [Car]()
myCars.append(myAudi)
myCars.append(myFord)
for aCar in myCars {
println("My \(aCar.brand!) is \(aCar.color!) and has \(aCar.wheels!) wheels.")
}
Result:
My Audi is red and has 4 wheels.
My Ford is green and has 3 wheels.
Note: have a look at this, it will help.
you can create an array with each elements as an array. For example
var tempArr : [[String]] = [["Ford","Audi"],["Red","Green"],["4"],["NO"]]
based on your question i guess you need a dictionary and not an array
var tempDict : [String : [String]] = ["Car" : ["Ford","Audi"],.....]