Avoid multiplying of values while appending in object - swift

I would like to ask how to corret my issue. I just simply append some "portals" to a depending country. EACH "portal" which comes more than once, I dont want to append.
I have following class definitions:
class cls_main{
var countries:[cls_country]!
init() {
countries = [cls_country]()
}
// "add Country"
func addCountry(iCountry:cls_country) {
countries.append(iCountry)
}
}
class cls_country{
var countryName:String!
var portals:[cls_portal]!
init() {
portals = [cls_portal]()
}
// "add Portal"
func addPortal(portName:String) {
var tmpPortal = cls_portal()
tmpPortal.portalName = portName
println("-->Input Portal: \(tmpPortal.portalName)")
if portals.count == 0 {
portals.append(tmpPortal)
} else {
for port in portals {
if port.portalName == portName {
println("SAME INPUT, DONT SAVE")
} else {
portals.append(tmpPortal)
}
}
}
}
func arrayCount(){
println("Portals : \(portals.count)")
}
}
class cls_portal{
var portalName:String!
}
And so I will call it:
var MAIN = cls_main()
var country = cls_country()
country.countryName = "USA"
country.addPortal("Dance")
country.addPortal("Dance") // Should not be appended...
country.addPortal("Hike")
country.addPortal("Swim")
country.addPortal("Play")
MAIN.addCountry(country)
country = cls_country()
After adding the values Im looping over the values and print them. The result would be like this:
Loop:
for country in MAIN.countries {
println("COUNTRY: \(country.countryName)")
if country.countryName == "USA" {
for portal in country.portals {
println(" -> PORTAL : \(portal.portalName)")
}
country.arrayCount()
}
}
Result:
-->Input Portal: Dance
-->Input Portal: Dance
SAME INPUT, DONT SAVE
-->Input Portal: Hike
-->Input Portal: Swim
-->Input Portal: Play
COUNTRY: USA
-> PORTAL : Dance
-> PORTAL : Hike
-> PORTAL : Swim
-> PORTAL : Swim
-> PORTAL : Play
-> PORTAL : Play
-> PORTAL : Play
-> PORTAL : Play
Portals : 8
So why every and each portal will be multiplying at the end? Thank you very much.

In your search loop, you are deciding after looking at each element whether tmpPortal is in your portals or not. You need to potentially look at all of the items before you can make that decision. Add a variable found to note that it has been found. You can break out of the loop once you've found the item.
if portals.count == 0 {
portals.append(tmpPortal)
} else {
var found = false
for port in portals {
if port.portalName == portName {
println("SAME INPUT, DONT SAVE")
found = true
// found it, stop searching
break
}
}
if !found {
portals.append(tmpPortal)
}
}

If you are using Swift 1.2 a better solution to this would by using Set so you wont need addPortal method at all. Set offers almost the same functionality as array but it simply does not store same values. Be aware that in order to make it work with set your cls_portal class must adopt hashable and equitable protocols
A set stores distinct values of the same type in a collection with no defined ordering. You can use sets as an alternative to arrays when the order of items is not important, or when you need to ensure that an item only appears once.

You’re looping multiple times over the array, checking it. But for every element that isn’t a match, you are inserting the portal. So when there are 3 other non-matching elements, you insert a portal 3 times.
Try replacing your inner loop (everything from if portals.count == all the way down to the end of the else) with this:
if !contains(portals, { $0.portalName == portName }) {
portals.append(tmpPortal)
}
contains is a function that checks if a collection (like your array of portals) contains an entry that matches a certain criteria. The criteria is determined by a closure, in this case one that checks if the element’s portal name matches the one you’re checking for. Try reading this link if you’re not familiar with closures – they can be very helpful in Swift.
P.S. there are a few other things in your code you might want to reconsider. For example, it’s best to avoid using implicitly-unwrapped optionals (i.e. types with a ! after them) like this. Implicit optionals are in Swift for some very specific purposes, which it doesn’t look like apply in your code. Especially not with arrays - better to just make the array empty on initialization. But also, if it makes no sense to have a portal without a country name, you make it part of the initializer.
So instead of this:
class cls_country{
var countryName:String!
var portals:[cls_portal]!
init() {
portals = [cls_portal]()
}
}
// and in use:
var tmpPortal = cls_portal()
tmpPortal.portalName = portName
you could write this:
class cls_country {
var portals = [cls_country]()
// consider let not var if a country will never
// need to change its name:
let countryName: String
init(countryName: String) {
self.countryName = countryName
//no need to initialize portals in init
}
}
// then later, in use:
var tmpPortal = cls_portal(countryName: portName)

Related

Swift: How to handle optional from Array.randomElement() CS193p 2021

(Non student) attempting 2021 programming assignment 2. I've got the app working with .randomElement, but obviously this allows multiple cards of the same emoji to be generated. I tried removing that element from my areay of emojis, but I can't handle the optional. any extra hints?
Be able to generate cards without duplicates
The following code is what I have so far. Part of it should look familiar from the lectures. my attempt to unwrap doesn't actually compile as shown here. I'm pretty sure I'm close with this..
static func createMemoryGame( chosenTheme : inout EmojiMemoryGame.gameTheme ) -> MemoryGame<String> {
MemoryGame<String>(numberOfPairsOfCards: chosenTheme.themeNoOfPairs) { _ in
var x = chosenTheme.emojis.randomElement()
if let safex = x {
if chosenTheme.emojis.count > 0 {
if let index = chosenTheme.emojis.firstIndex(of: safex ) {
chosenTheme.emojis.remove(at: index)
}//chosenTheme.emojis[pairIndex]
}
}
return safex
}
}

NSPredicate Filtering With Realm Swift

I'm having some trouble figuring out how to turn ordinary swift filtering code into an NSPredicate query with Realm. I have included a simplified example of my data structure. My goal is to figure out which of the User's contacts from the device are already registered for my app.
class User: Object {
let contacts = List<Contact>()
}
class Contact: Object {
let numbers = List<String>()
}
Each User object contains a list of 'Contacts' which are all the contacts currently stored on the Users device. And each 'Contact' Object has a list of 'numbers' because each contact can have multiple phone numbers.
In regular swift code, figuring out which of the users contacts are already registered for my app looks like this:
func alreadyRegistertedUserContacts(_ contacts: Results<Contact>,
allUsers: Results<User>) -> [Contact] {
return contacts.filter { (contact) -> Bool in
return contact.numbers.contains(where: { (number) -> Bool in
return allUsers.contains(where: { (user) -> Bool in
return user.phoneNumber == number
})
})
}
}
So the question is, for efficiencies sake, how would I change this function to use NSPredicates instead?
Thanks in advance for your help!
Realm does not currently support using properties whose types are lists of primitives in its predicates. Until that limitation is lifted, it's not possible to filter your objects using an NSPredicate alone.
If you were willing to change your model like so:
class StringObject: Object {
#objc dynamic var string = ""
}
class Contact: Object {
let numbers = List<StringObject>()
}
class User: Object {
let contacts = List<Contact>()
}
This should allow you to perform your filtering like so:
return contacts.filter("ANY numbers.value IN %#",
allUsers.flatMap { return $0.phoneNumber })

Using an instance in a function

I’m new to Swift so I’ve been using the Swift Playgrounds app. On level 2 “Two Experts”, I initialized two experts:
let expert1 = Expert()
let expert2 = Expert()
What I wanted to do was create a function and pass whichever instance into it, access it’s methods etc, something like:
func actions(who: Item, distance: Int, turn: String) {
for 0 to distance {
who.moveforward()
}
ff turn == “Left” {
who.turnleft()
} else if turn == “Right” {
who.turnright()
}
}
Where who is expert1 or expert2.
I couldn’t find a way of doing this so had to write the same actions twice:
Func actions(who: String, distance: Int, turn:String) {
if who == “expert1” {
for 0 to distance {
expert1.moveforward()
} Etc
if who == “expert2” {
for 0 to distance {
expert2.moveforward()
} Etc
Is there a way of passing an instance into a function then perform certain actions if it’s of a particular class?
Since your experts is of type Expert, then the who parameter in your actions method should be of type Expert, if I understand the code correctly. Then you don't need two functions for each of the Experts. Let me know if I understood you correctly, and if it worked out.
Update
#Alexander mentioned that you can also have these methods in an extension on Expert, like so:
extension Expert {
func actions(distance: Int, turn: String) {
// Add method code here
}
}
When adding the method in an extension, every Expert object can use the method. So you could write expert1.actions(1, "Left") or something like that. Here's a link to the official Swift Programming Language guide about extensions.

Get only one value from Observable

I am starting in RxSwift, coming from ReactiveCocoa. I have a conceptual question.
Let's say I have a value I want to observe over time, e.g. a temperatue. So there are many cases and places I subscribe this value to react on changes. No problem!
But there are also use cases when I just need the latest value e.g.:
if temperatue > 5 {
// do something
}
So i just want to do a decision/operation on that value or at least based on that value. That drives me close to using a shareReplay observable. But would I need to subscribe that value even when I just want to use it once?
Or is this approach wrong at all? How would I do that use case (value over time vs. accessing last value only once)? Would I need to sources, one hot one cold?
Use Variable:
class SomeClass {
let temperature = Variable<Int>(50)
func doSomething() {
if temperature.value > 50 {
print("something")
}
}
func subscribeToTemperature() {
temperature.asObservable.subscribeNext { t in
print("Temperature now is \(t)")
}.addDisposableTo(bag)
}
func setTemperature() {
temperature.value = 20
}
func observeTemperature(t: Observable<Int>) {
t.bindTo(temperature).addDisposableTo(bag)
}
}

Swift - access to Dictionary of a singleton causes EXC_BAD_ACCESS

I have an app managing a simple stocks portfolio. Amongst other things, it keeps a record of the required exchange rates in a dictionary, like so:
[ EURUSD=X : 1.267548 ]
This disctionary is a Dictionary property of a singleton called CurrencyRateStore.
When updating the stocks quotations, it checks for an updated exchange rate and updates the dictionary with the following code:
CurrencyRateStore.sharedStore()[symbol] = fetchedRate.doubleValue
That calls:
subscript(index: String) -> Double? {
get {
return dictionary[index]
}
set {
// FIXME: crashes when getting out of the app (Home button) and then relaunching it
dictionary[index] = newValue!
println("CurrencyRateStore - updated rate for \(index) : \(newValue!)")
}
}
The first time the app is started, it works fine.
But if I quit the app (with the Home button) and then relaunch it, the currency rates are updated again, but this time, I get a EXC_BAD_ACCESS at the line
dictionary[index] = newValue!
Here is a screenshot:
[EDIT] Here is the thread in the debug navigator:
I tried to update the dictionary without a subscript, like so:
CurrencyRateStore.sharedStore().dictionary[symbol] = fetchedRate.doubleValue
but without more success. Same if I use the function updateValue:forKey:
I didn't have the issue in Objective-C.
Thanks for your help !
[EDIT] Here is the whole class CurrencyRateStore:
class CurrencyRateStore {
// MARK: Singleton
class func sharedStore() -> CurrencyRateStore! {
struct Static {
static var instance: CurrencyRateStore?
static var token: dispatch_once_t = 0
}
dispatch_once(&Static.token) {
Static.instance = CurrencyRateStore()
}
return Static.instance!
}
// MARK: Properties
/** Dictionary of currency rates used by the portfolio, presented like [ EURUSD=X : 1.3624 ] */
var dictionary = [String : Double]()
/** Returns a sorted array of all the keys on the currency rates dictionary */
var allKeys: [String] {
var keysArray = Array(dictionary.keys)
keysArray.sort {$0 < $1}
return keysArray
}
init() {
if let currencyRateDictionary: AnyObject = NSKeyedUnarchiver.unarchiveObjectWithFile(currencyRateArchivePath) {
dictionary = currencyRateDictionary as [String : Double]
}
}
subscript(index: String) -> Double? {
get {
return dictionary[index]
}
set {
// FIXME: crashes when getting out of the app (Home button) and then relaunching it
// (ApplicationWillEnterForeground triggers updateStocks)
dictionary[index] = newValue!
println("CurrencyRateStore - updated rate for \(index) : \(newValue!)")
}
}
func deleteRateForKey(key: String) {
dictionary.removeValueForKey(key)
}
/** Removes all currency rates from the Currency rate store */
func deleteAllRates()
{
dictionary.removeAll()
}
// MARK: Archive items in CurrencyRateStore
var currencyRateArchivePath: String { // Archive path
var documentDirectories: Array = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
// Get the only document directory from that list
let documentDirectory: AnyObject = documentDirectories.first!
return documentDirectory.stringByAppendingPathComponent("currencyRates.archive")
}
func saveChanges()-> Bool
{
// return success or failure
return NSKeyedArchiver.archiveRootObject(dictionary, toFile: currencyRateArchivePath)
}
}
This looks to me like a concurrency issue. Swift dictionaries aren't thread safe, and using them from a singleton can lead to multiple reader/writer issues.
Edit: I am pretty sure this is the real answer, based on the given source/debugging dump. To correct what I wrote, specifically MUTABLE dictionaries and arrays (as well as NSMutableDictionary and NSMutableArray) aren't thread safe, and problems arise when using them within Singletons that are accessed from multiple threads, and that appears to be what the sample source code is doing, or enabling other parts of the code to do.
I don't have an Apple link discussing Swift collection class thread safety, but I"m pretty sure common knowledge. But the following tutorial on Grand Central Dispatch discusses the problem in depth and how to solve it using GCD.
http://www.raywenderlich.com/79149/grand-central-dispatch-tutorial-swift-part-1
The error, and the line itself:
dictionary[index] = newValue!
makes me think the problem is newValue being nil - and the error is caused by the forced unwrapping.
I would suggest to set a breakpoint and check its value, or otherwise print it before adding to the dict.
Moreover, it wouldn't be a bad idea to protect that statement with an optional binding:
if let value = newValue {
dictionary[index] = value
}
because if the value type is optional, it can be nil.
So in the end, I contacted Apple Technical Support.
They couldn't reproduce the issue.
I thought that maybe I don't need to save the currency rates, because during the quotes update, the function will check which currency rates it needs anyway, and repopulate the dictionary as needed.
So I deactivated the methods i created to save the CurrencyRateStore and to reload it again with NSKeyedUnarchiver.
Apparently, the crash is gone!