How to change properties of all set elements in Swift - swift

enum Posts {
case roxham
case satellite
case zone34
}
struct Member: Hashable, Identifiable {
var name: String
var permanent: Bool
var shift: Shifts
var post: Posts?
var canLedger = false
let id = UUID()
}
let allMembers: Set<Member> = [Member(name: "Bob Smith", permanent: false, shift: .fiveAM),
Member(name: "Dave Johnson", permanent: false, shift: .sevenAM)
// This set contains 15 more members ]
var tempSet = Set<Member>()
What I am trying to do is to assign .satellite to the post property of each element of the allMembers set and then put them into the tempSet Set (or put them in tempSet first then change the property, doesn’matter). I have tried mapping but the fact that I’m using a struct makes it impossible. I have also attempted some loops but with the same result. Of note, I have many more members in allMembers, I only showed 2 for simplification purposes. I obviously know I can just write it in manually in allMembers, however I will have to this a lot throughout the code and the sets will become much larger. Thanks

How about:
let tempSet = Set<Member>(allMembers.map {
var temp = $0
temp.post = .satellite
return temp
})

Related

Why is this showing 'Expected 'func' keyword in instance method declaration'

I'm new to coding and using SwiftUI on Xcode, and I don't see what's wrong with this code:
class NormalSpace {
var name = ""
var value = 0
var rent = 0
var owned = false
}
var newRoad = NormalSpace()
newRoad.name = "New Road"
newRoad.value = 600
newRoad.rent = 25
newRoad.owned = false
the error 'Expected 'func' keyword in instance method declaration' only shows on the newRoad.name line. The same line also has the error: Invalid redeclaration of 'newRoad'.
What have I done wrong?
In an normal project, this is not valid:
class NormalSpace {
var name = ""
var value = 0
var rent = 0
var owned = false
}
var newRoad = NormalSpace()
newRoad.name = "New Road"
newRoad.value = 600
newRoad.rent = 25
newRoad.owned = false
You can do that in a playground (where it just runs this code directly), but in an app, code (such as the setting of the properties) belongs in a function or initializer.
That initialization code needs to be placed within some context. Let us imagine that it is inside a struct. But this still is not valid:
class NormalSpace {
var name = ""
var value = 0
var rent = 0
var owned = false
}
struct Foo {
var newRoad = NormalSpace()
newRoad.name = "New Road"
newRoad.value = 600
newRoad.rent = 25
newRoad.owned = false
}
The property, newRoad is fine, but the values are not. You need to wrap it inside a func (hence the error) or an init. E.g., this initializes newRoad during the init of the struct:
struct Foo {
let newRoad: NormalSpace
init() {
newRoad = NormalSpace()
newRoad.name = "New Road"
newRoad.value = 600
newRoad.rent = 25
newRoad.owned = false
}
}
Or you might initialize it in a func:
struct Foo {
var newRoad: NormalSpace?
mutating func bar() {
let road = NormalSpace()
road.name = "New Road"
road.value = 600
road.rent = 25
road.owned = false
newRoad = road
}
}
Or, alternatively, you can initialize this property with a closure (note the extra () at the end):
struct Foo {
let newRoad: NormalSpace = {
let road = NormalSpace()
road.name = "New Road"
road.value = 600
road.rent = 25
road.owned = false
return road
}()
}
But the code where you initialize the properties must be placed within some context, so that the compiler knows when those lines of code should be run.
Note, we would generally give NormalSpace a “memberwise initializer”, e.g.:
class NormalSpace {
let name: String
let value: Int
let rent: Int
let owned: Bool
init(name: String, value: Int, rent: Int, owned: Bool) {
self.name = name
self.value = value
self.rent = rent
self.owned = owned
}
}
Or, if a struct (and we would generally prefer to make our model objects struct value-types rather than class reference-types), this memberwise initializer would be created for you:
struct NormalSpace {
let name: String
let value: Int
let rent: Int
let owned: Bool
}
Either way, you can then provide all the desired values during initialization, e.g.:
struct Foo {
let newRoad = NormalSpace(name: "New Road", value: 600, rent: 25, owned: false)
}
Note, that I've removed the “default” values because those really are not appropriate. If you wanted to say that they do not need to be provided, then you would make them “optionals”. But there is generally a big difference between, say, a rent of zero (i.e. it is my grandmother’s house and she's not charging me) and that no rent has been specified. In Swift, we generally avoid using “sentinel” values like "" or 0 for “no value provided”.
Also, now that we have a memberwise initializer, I have also made the properties immutable (let rather than var). If you need to make them mutable (e.g. to let someone change the rent later), fine, revert back to var. But only make properties mutable if you really need to change them later on.

Cannot convert value of type '<Int>' to expected element type <Any>

I'm trying to learn swift, but I have a problem where using <Object> in Java would fix my problem I think, and the Apple doc says I should use <Any> but I keep getting errors.
I'm trying to build a memorize card game, I have the following models:
Theme.swift <- In charge of modeling different kind of themes for the cards, the idea is that the cards could have numbers, images etc, thats why it has a generic type after the name
import Foundation
import UIKit
struct Theme<Type> {
internal init(name: String, emojis: [Type], numberOfPairs: Int, cardsColor: UIColor) {
self.name = name
self.emojis = emojis
if(numberOfPairs > emojis.count || numberOfPairs < emojis.count) {
fatalError("Index out of bounds")
}
self.numberOfPairs = numberOfPairs
self.cardsColor = cardsColor
}
var name: String
var emojis: [Type]
var numberOfPairs: Int
var cardsColor: UIColor
}
I also have a Game model in charge of the game logic and cards model, I still have to implement a lot of stuff, but here's the code
import Foundation
struct Game {
var themes: [Theme<Any>]
var cards: [Card<Any>]
var score = 0
var isGameOver = false
var choosenTheme: Theme<Any>
init(themes: [Theme<Any>]) {
self.themes = themes
self.choosenTheme = self.themes.randomElement()!
cards = []
for index in 0..\<choosenTheme.numberOfPairs {
cards.append(Card(id: index*2, content: choosenTheme.emojis[index]))
cards.append(Card(id: index*2+1, content: choosenTheme.emojis[index]))
}
}
mutating func endGame() {
isGameOver = true
}
mutating func penalizePoints() {
score -= 1
}
mutating func awardPoints () {
score += 2
}
struct Card<T>: Identifiable {
var id: Int
var isFaceUP: Bool = false
var content: T
var isMatchedUP: Bool = false
var isPreviouslySeen = false
}
}
As you can notice I've used the Any type for creating an array of Cards and themes, cuz they could have strings, numbers or images
In my ViewModel I have the following code, where I'm trying to fill the Array of themes with two themes, one of string type of content, and the other one of Int:
import Foundation
import SwiftUI
class GameViewModel {
static let halloweenTheme = Theme<Int>(name: "WeirdNumbers", emojis: [1, 2, 4, 9, 20, 30], numberOfPairs: 6, cardsColor: .darkGray)
static let emojisTheme = Theme<String>(name: "Faces", emojis: ["🥰", "😄", "😜", "🥳", "🤓", "😎", "😋", "🤩"], numberOfPairs: 5, cardsColor: .blue)
var gameController: Game = Game(themes: [halloweenTheme, emojisTheme])
}
But I keep getting this or a similar error:
Cannot convert value of type 'Theme<Int>' to expected element type
'Array<Theme<Any>>.ArrayLiteralElement' (aka 'Theme<Any>')
Cannot convert value of type 'Theme<String>' to expected element type
'Array<Theme<Any>>.ArrayLiteralElement' (aka 'Theme<Any>')
And my mind is going crazy, I thought that by using [Theme<Any>] I would be able to have an array like this: [Theme<String>, Theme<Int>, Theme<Image>, ...] but it looks like not
Does anybody have a clue of what is going on here?
You can use a wrapper struct, basic example below. Note: if you need conformance to Codable, you need to implement encode/decode on your own.
struct Values<A> {
let value: A
}
struct Wrapper {
let wrappedValue: Values<Any>
}
class MyClass {
var wrappedValues: [Wrapper] = [Wrapper(wrappedValue: Values(value: "hello")), Wrapper(wrappedValue: Values(value: 1))]
}

Swift - search entire struct for string

To search for a string included in a struct I use:
let results = myArray.filter( {$0.model.localizedCaseInsensitiveContains("bu")} )
But say the struct has several properties that I'd like to search - or maybe I'd even like to search all of them at one time. I can only filter primitive types so leaving 'model' out won't work.
Solution -------------------------
While I really liked the idea of using key paths as Matt suggested below, I ended up adding a function to my struct that made my view controller code much cleaner:
struct QuoteItem {
var itemIdentifier: UUID
var quoteNumber: String
var customerName: String
var address1: String
func quoteItemContains(_ searchString: String) -> Bool {
if self.address1.localizedCaseInsensitiveContains(searchString) ||
self.customerName.localizedCaseInsensitiveContains(searchString) ||
self.quoteNumber.localizedCaseInsensitiveContains(searchString)
{
return true
}
return false
}
Then, in my controller, quotes is an array of QuoteItem that I can search by simply writing:
searchQuoteArray = quotes.filter({ $0.quoteItemContains(searchString) })
This sounds like a job for Swift key paths. Just supply the key paths for the String properties you want to search.
struct MyStruct {
let manny = "Hi"
let moe = "Hey"
let jack = "Howdy"
}
let paths = [\MyStruct.manny, \MyStruct.moe, \MyStruct.jack]
let s = MyStruct()
let target = "y"
let results = paths.map { s[keyPath:$0].localizedCaseInsensitiveContains(target) }
// [false, true, true]
I hope i understood you correct. I think with this piece of code you can achieve what you want:
struct ExampleStruct {
let firstSearchString: String
let secondSearchString: String
}
let exampleOne = ExampleStruct(firstSearchString: "Hello", secondSearchString: "Dude")
let exampleTwo = ExampleStruct(firstSearchString: "Bye", secondSearchString: "Boy")
let exampleArray = [exampleOne, exampleTwo]
let searchString = "Hello"
let filteredArray = exampleArray.filter { (example) -> Bool in
// check here the properties you want to check
if (example.firstSearchString.localizedCaseInsensitiveContains(searchString) || example.secondSearchString.localizedCaseInsensitiveContains(searchString)) {
return true
}
return false
}
for example in filteredArray {
print(example)
}
This prints the following in Playgrounds:
ExampleStruct(firstSearchString: "Hello", secondSearchString: "Dude")
Let me know if it helps.

How to create a pointer in Swift?

I'm working with Swift 3.
I would like to have this C syntax :
int myVar;
int *pointer = &myVar;
So modifying pointer or myVar does the same exact same thing.
Also I don't know if it makes any difference, but in my case myVar is an array containing elements of a class and pointer is a pointer to one element of this array.
The & also exists in Swift but can only be used as part of a parameter list (e.g. init, func, closure).
var i = 5
let ptr = UnsafeMutablePointer(&i)
print(ptr.pointee) // 5
// or
let ptr = UnsafeMutablePointer<Int>.allocate(capacity: 1)
ptr.initialize(to: 5)
// or with a closure
let ptr: UnsafePointer = { $0 }(&i)
(Assuming I understand what you're asking for....)
Try the following code in a playground. It should print "99" three times.
class Row {
var rowNumber = 0
}
var rows = [Row]()
let testRow = Row()
testRow.rowNumber = 1
rows.append(testRow)
let selectedRow = rows[0]
selectedRow.rowNumber = 99
print(testRow.rowNumber)
print(selectedRow.rowNumber)
print(rows[0].rowNumber)
By default, there's no copying of objects as part of an assignment statement. If it were a struct, that would be different.
Adding a bit for completeness:
If you want a similar effect with scalar values instead of objects, Swift supplies various types of wrappers.
let intPointer = UnsafeMutablePointer<Int>.allocate(capacity: 8) // Should be 1, not 8 according to comment re: docs
let other = intPointer
other.pointee = 34
print(intPointer.pointee)
(Warning: I haven't used these wrappers for anything except experimenting in a playground. Don't trust it without doing some research.)
Same example as #Phillip. But I used struct. In this example rows[0] won't change:
struct Row {
var rowNumber = 0
}
var rows = [Row]()
var testRow = Row()
testRow.rowNumber = 1
rows.append(testRow)
var selectedRow = rows[0]
selectedRow.rowNumber = 99
print(testRow.rowNumber) // prints 1
print(selectedRow.rowNumber) // prints 99
print(rows[0].rowNumber) // prints 1
There are no C style pointers (Unsafe Pointer) as the question asks however objects are shared by reference and structures are by value:
Swift assign, pass and return a value by reference for reference type and by copy for Value Type
structures are always copied when they are passed around in your code, but classes are passed by reference.
For example
How to have pointers/ references to objects
class Song {
init(title: String, image: String, file: String, volume: Float, queuePlayer: AVQueuePlayer, playerLooper: AVPlayerLooper?) {
self.title = title
self.image = image
...
}
var title: String
var image: String
...
}
var aSong = Song(title: "", image: "", ...)
var arrOfSongReferences: [Song] = [Song]()
arrOfSongReferences.append(aSong)
var ptrToASong: Song = aSong
aSong = nil
// Due to Swift garbage collection ARC (Automatic Reference Counting), we still have references to the original aSong object so it won't be deleted
If data is struct you cannot do this
struct Song {
var title: String
var image: String
...
}
var aSong: Song = Song(title: "", image: "", ...)
var copyOfASong: Song = aSong
Method
You can also pass by reference into a function
// this would be inside a class, perhaps Player. It doesn't have to be a static btw
static func playSound(_ sound: inout Song, volume: Float = 0.0) {
if (sound.playerLooper == nil) {
...
}
}
// usage
Player.playSound(sound: &aSong)

Get and Set Array in Swift Class

I was build an IOS application, and somehow I have to do an aggregation in swift class, but every time I want to get the data, it always return an error. it seems like I the data always return a nil result.
I want to push the DoctorList member object (schedule) in the view controller class
I have create the object, and I also called the init() function. but, however, when I push the (or call) the init function for the DoctorList class and pass the array of ScheduleList, the content of the schedule member in doctorlist class will always be empty.
so when I try to get the schedule, it will return a nil result.
can every one tell me what I did wrong, because I already change the code but it still give a nil result.
I have two class like this
class DoctorList: NSObject {
var DoctorID: String?
var DoctorName: String?
var SpecialtyID: String?
var SpecialtyName: String?
var ImageURL: String?
var Schedule: [ScheduleList]?
init(_ DoctorID:String, _ DoctorName:String, _ SpecialtyID:String, _ SpecialtyName:String, _ ImageUrl:String, _ Schedule:[ScheduleList] ){
self.DoctorID = DoctorID
self.DoctorName = DoctorName
self.SpecialtyID = SpecialtyID
self.SpecialtyName = SpecialtyName
self.ImageURL = ImageUrl
for sc in Schedule {
self.Schedule?.append(ScheduleList(sc.DoctorID!, sc.DayName!, sc.FirstHour!, sc.LastHour!))
}
}
var getSchedule: [ScheduleList] {
get {
return self.Schedule!
}
}
and this one
class ScheduleList: NSObject {
var DoctorID: String?
var DayName: String?
var FirstHour: String?
var LastHour: String?
init(_ DoctorID:String, _ DayName:String, _ FirstHour:String, _ LastHour:String ){
self.DoctorID = DoctorID
self.DayName = DayName
self.FirstHour = FirstHour
self.LastHour = LastHour
}
the return value for the schedule was always empty
I'm sorry, could anyone give a suggestion how to make a global variable in swift?
You haven't initialized the Schedule array.
The append statement in the loop just never execute:
for sc in Schedule {
// self.Schedule is nil so anything come after the question mark does not run
self.Schedule?.append(ScheduleList(sc.DoctorID!, sc.DayName!, sc.FirstHour!, sc.LastHour!))
}
To fix it initialize your array before use:
self.Schedule = [ScheduleList]()
for sc in Schedule {
self.Schedule?.append(ScheduleList(sc.DoctorID!, sc.DayName!, sc.FirstHour!, sc.LastHour!))
}
Also, your code is a pain to read:
Optionals everywhere! You should decide what properties can and cannot be nil and get rid of the unnecessary ? and !
The convention in Swift is lowerCamelCase for variable names, and CamelCase for class names
No need to inherit from NSObject unless you want something from the ObjC world, whether working with ObjC or use KVO
Just noticed, recommendations from #CodeDifferent should be appropriate and helpful.
It is because you haven't initialised the Schedule in DoctorList
init(_ DoctorID:String, _ DoctorName:String, _ SpecialtyID:String, _ SpecialtyName:String, _ ImageUrl:String, _ Schedule:[ScheduleList] ){
self.DoctorID = DoctorID
self.DoctorName = DoctorName
self.SpecialtyID = SpecialtyID
self.SpecialtyName = SpecialtyName
self.ImageURL = ImageUrl
// Use this for init an empty array and append the content
self.Schedule = [ScheduleList]()
self.Schedule?.append(contentsOf: Schedule)
}
An example of the result:
let schedule = ScheduleList("scheduleId", "name", "1st", "last")
let doctorList = DoctorList("docId", "docName", "specId", "scpecName", "imgURL", [schedule])
if let list = doctorList.Schedule {
print("\(list[0].DoctorID)")
}