Swift - A function with no parameters with return value - swift

I am learning Swift and am writing a basic card game init function where I want to use a function to setup some decks.
Swift keeps complaining that I'm missing an argument in parameter #1, but there aren't any parameters, nor am I wanting any.
Game class is as follows
class Game
{
// MARK: ** Private vars **
private var gameState: GameState?
private var playerOnTurn: Player?
private var seedCash:Int?
// MARK: ** Public vars **
lazy var players = [Player]()
var chequesDeck:Deck = Deck()
var propertiesDeck:Deck = Deck()
init()
{
self.gameState = .Initialize
self.playerOnTurn = nil // No player on turn when game is initialized
self.seedCash = kInitialSeedCash
}
func setup(numberOfPlayers:Int)
{
// Create decks of properties and cheques
self.propertiesDeck = Deck.createProperties()
self.chequesDeck = Deck.createCheques()
}
}
Deck class is as follows
// Deck of cards
// Two deck types in the game - (1) Properties & (2) Cheques
class Deck
{
private var cards:[Card] = [] // Empty Array
// #return: An array of cards
func createProperties() -> [Card]
{
var propertyDeck:[Card] = []
// TODO: - Needs Local JSON reader
let prop1 = Card.init(name:"Cardboard box", value:1)
propertyDeck.append(prop1)
let prop2 = Card.init(name:"Outhouse", value:2)
propertyDeck.append(prop2)
let prop3 = Card.init(name:"Outhouse", value:3)
propertyDeck.append(prop3)
return propertyDeck
}
// #return: An array of cards
func createCheques() -> [Card]
{
var chequeDeck:[Card] = []
// create 2 copies of each card, but skip 1s
for var i:Int = 0; i<=15; i++
{
if (i != 1)
{
let chequeCard = Card.init(name: "Cheque", value: i * 1000)
chequeDeck.append(chequeCard)
}
}
return chequeDeck
}
func addCard()
{
}
func shuffle()
{
}
}
Deck() is a class
func setup() {
var propertiesDeck:Deck = Deck()
// Create property deck
self.propertiesDeck = Deck.createProperties()
}
// Deck.createProperties file
// #return: An array of cards
func createProperties() -> [Card]
{
var propertyDeck:[Card] = []
let prop1 = Card.init(name:"Penthouse", value:1)
propertyDeck.append(prop1)
return propertyDeck
}
But Swift keeps complaining that;
Missing argument for parameter #1 in call
But there aren't any arguments or parameters.
Perhaps I'm doing something wrong/silly?

This error would generally say that Deck is expecting some constructor parameters. Could you please post your Deck class so I can see if there are any?
Also some more suggestions. You seem to be creating the Deck variable propertiesDeck, but then statically accessing createProperties by stating Deck.createProperties(). Should you not be calling propertiesDeck.createProperties()? Also createProperties is returning an Array of the Card object, but propertiesDeck is a Deck class.

Since you're accessing your function like this:
Deck.createProperties()
you probably want a static method instead:
static func createProperties() -> [Card] {
...
}
Which shouldn't give you an error anymore.
Another way to make it work is by calling createProperties() on your already defined Deck like this (not recommended):
self.propertiesDeck = propertiesDeck.createProperties()
The reason for the missing parameter comes from the fact that methods take the class instance as their first parameter, so you could actually call it like Deck.createProperties(propertiesDeck)().
I believe you've got some other flaws in your code, I will try to make a better example for you:
struct Card {
let name : String
let value : Int
}
class Deck {
var cards : [Card]
init() {
cards = [
Card(name: "Penthouse", value: 1)
]
}
}

Try This,
func createProperties() -> [Card]
{
var propertyDeck:[Card] = []
let prop1 = Card(name:"Penthouse", value:1)
propertyDeck.append(prop1)
return propertyDeck
}
If this does not works then show the code for the class Deck and Card.

We can't answer your question without the complete code. We need the code of your Deck class.
But you can try class funcs...
public class Cards {
class func createProperties() -> [Card]
{
var propertyDeck:[Card] = []
let prop1 = Card.init(name:"Penthouse", value:1)
propertyDeck.append(prop1)
return propertyDeck
}
} 
You can call the function with:
Cards.createProperties()

Related

Observing a #Published var from another Object

I am trying to get one object to listen to changes in the property of another object. I have it working as shown below, but I would prefer the observing object knew nothing of the Model, just the property.
class Model : ObservableObject{
#Published var items: [Int] = []
}
class ObjectUsingItems{
var itemObserver: AnyCancellable?
var items: [Int] = []
func observeItems(model: Model){
itemObserver = model.$items
.sink{ newItems in
self.items = newItems
print("New Items")
}
}
}
At the moment I begin observing the model.items as follows - which works:
let model = Model()
let itemUser = ObjectUsingItems()
itemUser.observeItems(model: model)
model.items.append(1) // itemUser sees changes
Unfortunately I can’t seem to figure out just what is required as the parameter to the observeItems method so that it works without knowing anything about the Model - like this:
class ObjectUsingItems{
var itemObserver: AnyCancellable?
var items: [Int] = []
func observeItems(propertyToObserve: WhatGoesHere?){
itemObserver = propertyToObserve
.sink{ newItems in
// etc.
}
}
}
And then call it like so:
itemUser.observeItems(XXX: model.$items)
Can anyone explain what I need to do? Thanks!
You can just accept a publisher as a parameter, if you don't care where the value comes from.
In your very specific case, it could be:
func observeItems(propertyToObserve: Published<[Int]>.Publisher) {
itemObserver = propertyToObserve
.sink { self.items = $0 }
}
But this might be too restrictive - why only this specific publisher? In principle, you shouldn't care what the publisher is - all you care about is the output value and error type. You can make it generic to any publisher, so long as its Output is [Int] and Failure is Never (like that of the #Published one):
func observeItems<P: Publisher>(propertyToObserve: P)
where P.Output == [Int], P.Failure == Never {
itemObserver = propertyToObserve
.sink { self.items = $0 }
}
The usage would be something like this:
let model = Model()
let itemUser = ObjectUsingItems()
itemUser.observeItems(propertyToObserve: model.$items)

`#Published var name: ClassType` doesn't work _outside_ of SwiftUI / manual trigger?

I found a lot of SwiftUI-related topics about this which didn't help (eg Why an ObservedObject array is not updated in my SwiftUI application?)
This doesn't work with Combine in Swift (specifically not using SwiftUI):
class SomeTask {
#Published var progress = Progress(totalUnitCount: 5) // Progress is a Class
[...]
}
var task = SomeTask()
let cancellable = task.$progress.sink { print($0.fractionCompleted) }
task.progress.completedUnitCount = 2
This is not SwiftUI-related so no ObservableObject inheritance to get objectWillChange, but even if I try to use ObservableObject and task.objectWillChange.send() it doesn't do anything, also trying to add extension Progress: ObservableObject {} doesn't help.
Since the publisher emits values through the var's willSet and since Progress is itself class-type nothing happens.
Looks like there is no real decent way to manually trigger it?
Only solution I found is to just re-assign itself which is quite awkward:
let pr = progress
progress = pr
(writing progress = progress is a compile-time error).
Only other way which might be working is probably by using Key-value-observing/KVO and/or writing a new #PublishedClassType property wrapper?
I was able to implement this using KVO, wrapped by a #propertyWrapper, with a CurrentValueSubject as the publisher:
#propertyWrapper
class PublishedClass<T : NSObject> {
private let subject: CurrentValueSubject<T, Never>
private var observation: NSKeyValueObservation? = nil
init<U>(wrappedValue: T, keyPath: ReferenceWritableKeyPath<T, U>) {
self.wrappedValue = wrappedValue
subject = CurrentValueSubject(wrappedValue)
observation = wrappedValue.observe(keyPath, options: [.new]) { (wrapped, change) in
self.subject.send(wrapped)
}
}
var wrappedValue: T
var projectedValue: CurrentValueSubject<T, Never> {
subject
}
deinit {
observation.invalidate()
}
}
Usage:
class Bar : NSObject {
#objc dynamic var a: Int
init(a: Int) {
self.a = a
}
}
class Foo {
#PublishedClass(keyPath: \.a)
var bar = Bar(a: 0)
}
let f = Foo()
let c = f.$bar.sink(receiveValue: { x in print(x.a) })
f.bar.a = 2
f.bar.a = 3
f.bar.a = 4
Output:
0
2
3
4
The disadvantage of using KVO is, of course, that the key path you pass in must be #objc dynamic and the root of the keypath must be an NSObject subclass. :(
I haven't tried, but it should be possible to extend this to observe on multiple key paths if you want.
You can try using CurrentValueSubject<Progress, Never>:
class SomeTask: ObservableObject {
var progress = CurrentValueSubject<Progress, Never>(Progress(totalUnitCount: 5))
func setProgress(_ value: Int) {
progress.value.completedUnitCount = value
progress.send(progress.value)
}
}
var task = SomeTask()
let cancellable = task.progress.sink { print($0.fractionCompleted) }
task.setProgress(3)
task.setProgress(1)
This way your Progress can still be a class.
Based on the ideas I did implement a #PublishedKVO property wrapper and put it up on github as a small swift package, supporting multiple key paths.
https://github.com/matis-schotte/PublishedKVO
Usable as:
class Example {
#PublishedKVO(\.completedUnitCount)
var progress = Progress(totalUnitCount: 2)
#Published
var textualRepresentation = "text"
}
let ex = Example()
// Set up the publishers
let c1 = ex.$progress.sink { print("\($0.fractionCompleted) completed") }
let c1 = ex.$textualRepresentation.sink { print("\($0)") }
// Interact with the class as usual
ex.progress.completedUnitCount += 1
// outputs "0.5 completed"
// And compare with Combines #Published (almost°) same behaviour
ex.textualRepresentation = "string"
// outputs "string"
ex.$progress.emit() // Re-emits the current value
ex.$progress.send(ex.progress) // Emits given value

Executiong closure on array modification

I have the following code:
class Note: NSObject {
}
struct Global {
static var notes: Array<Note> = [] {
didSet {
print("hi")
}
}
}
This prints "hi" if I add or remove an item from the array or if I do
Global.notes = []
Is there a way to print("hi") every time when one of the Note objects in the array is modified?
Thanks for your answers
Without changing the class to a struct, I have two basic ways to handle this.
This is the object you asked about
class Note: NSObject {
}
struct Global {
static var notes: Array<Note> = [] {
didSet {
print("hi")
}
}
}
Wrap Notes in a wrapper that is a struct to get the struct behavior.
extension Note {
struct Wrapper { let note: Note }
}
extension Global {
static var wrappedNotes = [Note.Wrapper]() {
didSet {
print("hi")
}
}
}
Global.wrappedNotes.append(Note.Wrapper(note: Note()))
Global.wrappedNotes[0] = Note.Wrapper(note: Note())
Global.wrappedNotes.remove(at: 0)
The other way is to create a note manager to wrap access to the array.
class NoteManager {
subscript(index: Int) -> Note {
get {
return values[index]
}
set {
defer { onUpdate() }
values[index] = newValue
}
}
func append(_ newNote: Note) {
defer { onUpdate() }
values.append(newNote)
}
func remove(at index: Int) -> Note {
defer { onUpdate() }
return values.remove(at: index)
}
private func onUpdate() {
print("hi")
}
private var values = [Note]()
}
extension Global {
static var managedNotes = NoteManager()
}
Global.managedNotes.append(Note())
Global.managedNotes[0] = Note()
Global.managedNotes.remove(at: 0)
As per #staticVoidMan comment , If you make your model , a struct, rather than a class, then the property observer didSet will work for your Note model's own properties as well.
import Foundation
struct Note {
var name: String
}
struct Global {
static var notes: Array<Note> = [] {
didSet {
print("hi")
}
}
}
Global.notes.append(Note(name: "Shubham"))
Global.notes.append(Note(name: "Bakshi"))
Global.notes[0].name = "Boxy"
This will print the following on the console :
hi
hi
hi
Swift Array is a struct, and structs are value-type which means they change completely when elements are added/removed/replaced. Hence when you add/remove/replace a Note, the didSet property observer gets called as the array has been set again.
However, as per you question:
Is there a way to print("hi") every time when one of the Note objects in the array is modified?
By this I am assuming that you want to do something when an element within this array is accessed and an internal property is modified.
This would have been fine if you were dealing with only value-type objects, i.e. had your Note object also been a struct, then changing anything inside one Note would have caused the array to change as well.
But your Note object is a class, i.e. reference-type, and stays as the same object even if it's internal elements change. Hence your array doesn't need to update and didSet does not get called.
Read: Value and Reference Types
KVO Solution:
Now... Since your Note is subclassing NSObject, you can use the KVO concept
As per the following working example, we observe only one property of the Note class.
If you want to observe more properties then you will need to observe those many more keypaths.
Example:
class Note: NSObject {
#objc dynamic var content = ""
init(_ content: String) {
self.content = content
}
}
class NoteList {
var notes: [Note] = [] {
didSet {
print("note list updated")
//register & save observers for each note
self.noteMessageKVOs = notes.map { (note) -> NSKeyValueObservation in
return note.observe(\Note.content, options: [.new, .old]) { (note, value) in
print("note updated: \(value.oldValue) changed to \(value.newValue)")
}
}
}
}
//array of observers
var noteMessageKVOs = [NSKeyValueObservation]()
}
let list = NoteList()
list.notes.append(Note("A")) //note list updated
list.notes.append(Note("B")) //note list updated
list.notes[0].content = "X" //note updated: A changed to X
list.notes[1].content = "Y" //note updated: B changed to Y
Notes:
NSObject is required for KVO
#objc dynamic is required to make a property observable
\Note.message is a keypath
noteMessageKVOs are required to keep the observers alive

Swift Get value from function

I have two class and i would like to get a variable with the value (the variable is in a function) to my second class :
public class StreamPlayer {
class var sharedInstance : StreamPlayer{
struct Static {
static let instance : StreamPlayer = StreamPlayer()
}
return Static.instance
}
public var intermediate = NSString()
func metaDataUpdated(metaData : NSString){
var result : String = ""
var listItems = metaData.componentsSeparatedByString(";") as [String]
if (listItems.count > 0){
var containerName = listItems[0]
result = "StreamTitle=\'([^\"]*)\'".matchesForRegexIn(containerName, atRangeIndex: 1)
self.intermediate = result
}
}
}
and the second class
class RadioViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
println(test + StreamPlayer.sharedInstance.intermediate)
}
}
The problem is that the var intermediate doesn't change and don't get the value of result (in my first class)
I've copied your StreamPlayer class code into a playground. I've just commented out the matchesForRegexIn method as it seems it's your String extension so my code looks like this:
public class StreamPlayer {
class var sharedInstance : StreamPlayer{
struct Static {
static let instance : StreamPlayer = StreamPlayer()
}
return Static.instance
}
public var intermediate = String()
func metaDataUpdated(metaData : NSString){
var result : String = ""
let listItems = metaData.componentsSeparatedByString(";") as [String]
if (listItems.count > 0){
// var containerName = listItems[0]
result = "StreamTitle=\'([^\"]*)\'" //.matchesForRegexIn(containerName, atRangeIndex: 1)
intermediate = result
}
}
}
// calling the method to make sure intermediate gets updated
StreamPlayer.sharedInstance.metaDataUpdated("asd")
// check if it got updated
print(StreamPlayer.sharedInstance.intermediate)
The last line prints StreamTitle=\'([^\"])\'* so all is good. Just make sure to call StreamPlayer.sharedInstance.metaDataUpdated before checking intermediate
PS. I'm really not sure what you're trying to achieve by sharing intermediate results from a function to the outside world but it feels off. Think about splitting metaDataUpdated method into two methods maybe?
PPS. metaDataUpdated is a really bad name for a function
PPPS. If I were you I'd declare intermediate as String?
self.intermediate is a NSString while result is a String
Try
self.intermediate = result as NSString

Change the value that is being set in variable's willSet block

I'm trying to sort the array that is being set before setting it but the argument of willSet is immutable and sort mutates the value. How can I overcome this limit?
var files:[File]! = [File]() {
willSet(newFiles) {
newFiles.sort { (a:File, b:File) -> Bool in
return a.created_at > b.created_at
}
}
}
To put this question out of my own project context, I made this gist:
class Person {
var name:String!
var age:Int!
init(name:String, age:Int) {
self.name = name
self.age = age
}
}
let scott = Person(name: "Scott", age: 28)
let will = Person(name: "Will", age: 27)
let john = Person(name: "John", age: 32)
let noah = Person(name: "Noah", age: 15)
var sample = [scott,will,john,noah]
var people:[Person] = [Person]() {
willSet(newPeople) {
newPeople.sort({ (a:Person, b:Person) -> Bool in
return a.age > b.age
})
}
}
people = sample
people[0]
I get the error stating that newPeople is not mutable and sort is trying to mutate it.
It's not possible to mutate the value inside willSet. If you implement a willSet observer, it is passed the new property value as a constant parameter.
What about modifying it to use didSet?
var people:[Person] = [Person]()
{
didSet
{
people.sort({ (a:Person, b:Person) -> Bool in
return a.age > b.age
})
}
}
willSet is called just before the value is stored.
didSet is called immediately after the new value is stored.
You can read more about property observers here
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html
You can also write a custom getter and setter like below. But didSet seems more convenient.
var _people = [Person]()
var people: [Person] {
get {
return _people
}
set(newPeople) {
_people = newPeople.sorted({ (a:Person, b:Person) -> Bool in
return a.age > b.age
})
}
}
It is not possible to change value types (including arrays) before they are set inside of willSet. You will need to instead use a computed property and backing storage like so:
var _people = [Person]()
var people: [Person] {
get {
return _people
}
set(newPeople) {
_people = newPeople.sorted { $0.age > $1.age }
}
}
Another solution for people who like abstracting away behavior like this (especially those who are used to features like C#'s custom attributes) is to use a Property Wrapper, available since Swift 5.1 (Xcode 11.0).
First, create a new property wrapper struct that can sort Comparable elements:
#propertyWrapper
public struct Sorting<V : MutableCollection & RandomAccessCollection>
where V.Element : Comparable
{
var value: V
public init(wrappedValue: V) {
value = wrappedValue
value.sort()
}
public var wrappedValue: V {
get { value }
set {
value = newValue
value.sort()
}
}
}
and then assuming you implement Comparable-conformance for Person:
extension Person : Comparable {
static func < (lhs: Person, rhs: Person) -> Bool {
lhs.age < lhs.age
}
static func == (lhs: Person, rhs: Person) -> Bool {
lhs.age == lhs.age
}
}
you can declare your property like this and it will be auto-sorted on init or set:
struct SomeStructOrClass
{
#Sorting var people: [Person]
}
// … (given `someStructOrClass` is an instance of `SomeStructOrClass`)
someStructOrClass.people = sample
let oldestPerson = someStructOrClass.people.last
Caveat: Property wrappers are not allowed (as of time of writing, Swift 5.7.1) in top-level code— they need to be applied to a property var in a struct, class, or enum.
To more literally follow your sample code, you could easily also create a ReverseSorting property wrapper:
#propertyWrapper
public struct ReverseSorting<V : MutableCollection & RandomAccessCollection & BidirectionalCollection>
where V.Element : Comparable
{
// Implementation is almost the same, except you'll want to also call `value.reverse()`:
// value = …
// value.sort()
// value.reverse()
}
and then the oldest person will be at the first element:
// …
#Sorting var people: [Person]
// …
someStructOrClass.people = sample
let oldestPerson = someStructOrClass.people[0]
And even more directly, if your use-case demands using a comparison closure via sort(by:…) instead of implementing Comparable conformance, you can do that to:
#propertyWrapper
public struct SortingBy<V : MutableCollection & RandomAccessCollection>
{
var value: V
private var _areInIncreasingOrder: (V.Element, V.Element) -> Bool
public init(wrappedValue: V, by areInIncreasingOrder: #escaping (V.Element, V.Element) -> Bool) {
_areInIncreasingOrder = areInIncreasingOrder
value = wrappedValue
value.sort(by: _areInIncreasingOrder)
}
public var wrappedValue: V {
get { value }
set {
value = newValue
value.sort(by: _areInIncreasingOrder)
}
}
}
// …
#SortingBy(by: { a, b in a.age > b.age }) var people: [Person] = []
// …
someStructOrClass.people = sample
let oldestPerson = someStructOrClass.people[0]
Caveat: The way SortingBy's init currently works, you'll need to specify an initial value ([]). You can remove this requirement with an additional init (see Swift docs), but that approach is much less complicated when your property wrapper works on a concrete type (e.g. if you wrote a non-generic PersonArraySortingBy property wrapper), as opposed to a generic-on-protocols property wrapper.