How to approach a task: Inquiry with Choices and Responses (Engine) - ios

ANSWERED! I posted an answer with code that I modified from Arbitur. Thanks to Phillip as well for the key advice that lead me to redirect my searches.
TLDR:
is there a way to run a function from a string? Like RunFunction("myfunction_" + number + "()"), or, is a way to hardcode like myfunction_\&number() ?
I'm Justin and this is my first post. I've worked hard the past 2 months to not ask a question, but I'm finally breaking bad. I'll try to be brief but I'm terribly wordy (and this long intro doesn't help). Appreciation in advance for your patience.
Basically, I'm making an engine that transforms simple words and phrases into Swift code; the goal of this is to have something to quickly make Text games, educational apps, and anything involving a non-linear set of questions / answers that change based on how you respond.
Now, I'm working on what I thought would be an easy part: storing the actual text for the Labels and Buttons (I'm working in Xcode / iOS atm). I decided to model it like film-makers / novelists do: a movie is composed of Scenes which are composed of Clips. You can also think of this as Chapters / Paragraphs
So (not actual code):
App = (Scene 1 -> Scene 2 -> Scene 3, etc)
Scene 1 = (Clip 1 -> Clip 2 -> Clip 3, etc)
Scene 2 = Clip 2 -> Clip 5 -> Clip 1, based on responses
....And so on.
The way I think (and program), I tend to avoid Classes as much as possible, outside of being strictly data or strictly function structures.
So, my first solution was to create a struct (or class) with nested functions:
struct SceneList {
//fScene calls, and passes the clip# from the button click
func fPlayClip(clipNUM: Int)
{
//Do logic stuff, then play the correct clip
}
//Called from button clicks on main screen; passes in clip#
func fScene1(play_clip clipNumber: Int){
//sub functions(S1)
func fClip1(){ print("I'm scene 1, clip 1")}
func fClip2(){ print ("I'm scene 1, clip 2")}
fPlayClip(clipNumber)
}
func fScene2(play_clip clipNumber: Int){
//sub functions(S2)
func fClip1(){ print("Yo, scene 2, clip 1")}
func fClip2(){ print ("Yo, scene 2, clip 2")}
fPlayClip(clipNumber)
}
}
Unfortunately, this design failed because there is no way for me to call sub-functions from fPlayClip(), so I took another approach:
//////////////////////////
/////SceneList.swift//////
//////////////////////////
struct Scene1{
func fClip1(){ print("I'm scene 1, clip 1")}
func fClip2(){ print ("I'm scene 1, clip 2")}}
struct Scene2{
func fClip1(){ print("Yo, scene 2, clip 1")}
func fClip2(){ print ("Yo, scene 2, clip 2")}}
//////////////////////////
////////Main.swift////////
//////////////////////////
// Implemention of the structs / clips.
// (playScene is called in button click method.)
func playScene(/*button prams go here*/){
// Switch(){ case desired: // Solve for X,
var oScenePlayer = SceneX() // where X is the desired scene
// Switch(){ case desired: // Solve for Z,
oScenePlayer.fClipZ() // where Z is the desired clip
}
Again, this failed because I couldn't use just one object, [oScenePlayer], because each struct was a different type.
I then went around for a while trying to figure out a way to destroy the object, then recreate it, but I couldn't do that. Then, I looked around for a way to reassign one object to another class type, but couldn't. Then, I peered into the extensions, protocols, generic types, etc, trying to figure out a way to extend / inherent to get this to work, but failed.
So now, my final two /working/ solutions, are to just create a new object on scene changes, and wait for ARC or whatever to destroy the old one; or, for me to go back to the first example, and simply embed [fPlayScene] into every function:
//SOLUTION 1:
struct SceneList {
func fScene1(play_clip clipNumber: Int){
//sub functions(S1)
func fClip1(){ print("I'm scene 1, clip 1")}
func fClip2(){ print ("I'm scene 1, clip 2")}
//Do logic stuff below, solving for Y
case desired:
fClipY()
}
}
//SOLUTION 2:
//////////////////////////
/////SceneList.swift//////
//////////////////////////
struct Scene1{
func fClip1(){ print("I'm scene 1, clip 1")}
func fClip2(){ print ("I'm scene 1, clip 2")}}
}
//And so on...
//////////////////////////
////////Main.swift////////
//////////////////////////
//////////////////////////
// Inside the globalish area:
let oScene1: Scene1,
oScene2: Scene2
//And so on...
var global_next_scene = 1
var global_next_clip = 1
/////////////////////////////////
// Inside the button call method:
func playScene(next_scene: Int, next_clip: Int){
switch(next_scene){ //Find the scene
case 1:
oScene1 = Scene1()
switch(next_clip){ //Play the clip
case 1:
oScene1.fClip1()
}
}
Basically, I feel that I'm using WAY too many switch statements, and in way too many places, (there could be hundreds of scenes and thousands of clips), when something as simple as a RunFunction("fClip" + next_clip + "()") would work, but I don't know of any 'execute a swift command from a string' functions :[, or something where I could hardcode like fClip\&next_clip\() which I think was possible in c++ 15 years ago when I last programmed anything
I came up with a few other crazy ideas to implement this, I even thought about OOP (shudder) embedded classes and having member variables hold instances of the subclasses, but with my current knowledge / resources I can't find a simpler way to do this than the last 2 snippets.
All of the structs, member functions, switch statements, etc, are going to be auto-generated via my engine--so it's not that I'm looking for a faster way to do this, it just seems like an inefficient / taxing way to do it.
Any guidance would be appreciated. Thanks much in advance, and I'm surprised it's taken me this long to need to ask a question here ^-^()
Peace and blessings

It seems to me that you're mixing what is naturally data into your code. That is, you're creating functions that know which element they're responsible for...and that's what leads to all the duplication you're wondering about.
Perhaps there are parts of the problem I don't understand, but why not have a structure such as:
class Clip {
func play() {
print("Playing \(self)")
}
}
class Scene {
var clips = [Clip]()
func play() {
print("Playing \(self)")
for aClip in clips {
aClip.play()
}
}
func playClip(number: Int) {
if number >= 0 && number < clips.count {
clips[number].play()
}
}
}
class SceneList {
var scenes = [Scene]()
func play() {
print("Playing \(self)")
for aScene in scenes {
aScene.play()
}
}
func playScene(number: Int) {
if number >= 0 && number < scenes.count {
scenes[number].play()
}
}
}
var list = SceneList()
for _ in 0..<2 {
var scene = Scene()
list.scenes.append(scene)
for _ in 0..<3 {
scene.clips.append(Clip())
}
}
list.play()
Add whatever other properties and logic you like but the main point is to turn those numbered functions into generic functions with data references.

Phillip's answer inspired me to look more into arrays and separating data from functions, and indeed what I was looking for was available closures--I didn't know that you could call a closure simply from stating (unwrapping) a variable :) What a neat language!
This works perfectly, and now I don't need any classes or structs ^-^() (all the better!!)
//initializer
var clips_for_scene = [
1: { print("initialized") }
]
//SceneList
var scenes = [
1: { clips_for_scene = [
1: { print("scene 1, clip 1") },
2: { print("s1, c2") }]},
2: { clips_for_scene = [
1: { print("scene 2, clip 1") },
2: { print("s2, c2") }]}]
func playClip(which_scene: Int, _ which_clip:Int){
scenes[which_scene]?()
clips_for_scene[which_clip]?()
}
playClip(1, 1)
playClip(1, 2)
playClip(2, 1)
playClip(2, 2)
//output:
//scene 1, clip 1
//s1, c2
//scene 2, clip 1
//s2, c2
Thanks to Arbitur as well, as their answer on a separate question:
https://stackoverflow.com/a/30286375/6593818

Related

Good Practices for Swift SpriteKit Class Structure

I am developing an iOS card game with Swift and have a question about good practice when it comes to designing the framework for games with SpriteKit and iOS. My basic structure is as follows:
class Card: SpriteKitNode {
cardValue: Int
func action() {}
}
struct Player {
playerName = "Joe"
playerPile = [Card]()
playerStack = [Card]()
}
struct Game {
// Create players and deals out the cards to each player pile.
}
Each player has a few piles of cards and they all come together in the game struct. Most of the cards in my game are independent cards. So, if player 1 plays a card, it has no impact on other players. However, some of the cards in my games have actions which are designed to impact other players decks depending on which card is played.
My question is, a card "action" sounds like it should be defined within the card class. It is a feature of the card itself that it has this ability. However, when I think about how I would implement this, I am not sure how it could impact at the game level to access other player piles. So what is the best practice when it comes to trying to define actions that have upstream impacts when the "Card" does not know how many players there are, which player it belongs to, and how to access another player's pile.
Example of an action I would like to implement: Player 1 can move the top card in any player stack to the top card in any other player's stack. So, player 1 could move the top card from player 2 to 1, 2 to 3, or any other combination based on the number of players on the board. I thought I could be able to do this by passing a lot of parameters to the action function action(moveFrom: Player1, moveTo: Player3), but thought I would come here to find out best practices.
There are also a few other actions which may have different inputs based on the card played. Should these be separate functions, or all built into one "Card Action" function?
// Possibly how this function might look.
func action(moveFrom: Player, moveTo: Player) {
let cardMoved = moveFrom.playerPile[0]
moveTo.playerPile.append(cardMoved)
}
Edit - Follow Up Question
After changing to a POP, I have a few further questions that are stumping me on how to implement.
My action function does not allow me to mutate the player's piles that have been selected. Error = "Cannot use mutating member on immutable value: 'fromPlayer' is a 'let' constant". Does this mean I would have to destroy and create and return new piles for each player each time it was called rather than just modifying the existing ones? This seems pretty inefficient is there a better way to do this?
I get another error when trying to call my action function. I have already checked that my card is indeed a "Slice Card", but I get the error "Value of type 'Card' has no member 'action'".
protocol ActionCard {
func action(fromPlayer: Player, toPlayer: Player)
}
class Card {
}
class SliceCard: Card, ActionCard {
func action(fromPlayer: Player, toPlayer: Player) {
let cardTaken = fromPlayer.stack.removeLast()
toPlayer.stack.append(cardTaken)
}
}
struct Player {
var stack = [Card]()
func playCard(card: Card, fromPlayer: Player, toPlayer: Player) {
if card is SliceCard {
card.action(fromPlayer: fromPlayer, toPlayer: toPlayer)
}
}
}
let player1 = Player()
let player2 = Player()
let cardSelected = SliceCard()
player1.playCard(card: cardSelected, fromPlayer: player1, toPlayer: player2)
Interesting question. I would suggest you a POP (Protocol Oriented Programming) approach.
The Player type
First of all I would suggest using a class for the Player type because you want to pass the same instance to other methods/actions and want these method to be able to mutate the original instance.
You could still use a struct + inout parameters but using a class feels more correct.
class Player {
let name: String
var pile: [Card] = []
var stack: [Card] = []
init(name: String) {
self.name = name
}
}
The ActionError enum
Just create an enum and add a case for every possible error an action can throw
enum ActionError: Error {
case playerHasNoCards
// add more errors here
}
The BaseCard class
You put here all the stuff common to any Card.
class BaseCard: SKSpriteNode {
let cardValue: Int
init(cardValue: Int) {
self.cardValue = cardValue
let texture = SKTexture(imageNamed: "card_image")
super.init(texture: texture, color: .clear, size: texture.size())
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The Card protocol
Here you required that in order to conform to Card a type must
be a class that inherits from BaseCard
and must have an action(...) method
Here's the code
protocol Card: BaseCard {
func action(currentPlayer: Player, destinatonPlayer: Player, allPlayers: [Player]) throws
}
Please note that the action method should receive all the parameters needed for any action you want to implement.
Your first card
Finally you can implement your first card
class CardToStoleACardFromAnotherPlayer: BaseCard, Card {
func action(currentPlayer: Player, destinatonPlayer: Player, allPlayers: [Player]) throws {
guard destinatonPlayer.pile.isEmpty == false else { throw ActionError.playerHasNoCards }
let card = destinatonPlayer.pile.removeFirst()
currentPlayer.pile.append(card)
}
}
Create as many classes as you want, you will write a different logic into each class.
Examples
class CardToStoleAllCardsFromAllPlayers: BaseCard, Card {
func action(currentPlayer: Player, destinatonPlayer: Player, allPlayers: [Player]) throws {
// ...
}
}
class CardToGiftACardToAnotherPlayer: BaseCard, Card {
func action(currentPlayer: Player, destinatonPlayer: Player, allPlayers: [Player]) throws {
// ...
}
}
Considerations
Now you when you pick a Card and you want to execute its action just call the action method passing all the parameters.
Depending on the type of the instance (CardToStoleACardFromAnotherPlayer, CardToStoleAllCardsFromAllPlayers, CardToGiftACardToAnotherPlayer, ...) contained into that variable a different logic will be executed.

How to specify two-sided material for RealityKit / ARView?

I'm trying to load a model and texture in RealityKit (set up in an ARView instance), but I can't seem to figure out how to specify the material should be two-sided.
I have the model loaded up as a ModelEntity, the texture loaded up as a TextureResource. The model and texture are loading up, but are rending one-sided. Since the model is open (i.e., back faces are visible), there are gaps on how it is rendered.
So far, I have,
let entity: ModelEntity = try .loadModel(named: "model.obj")
var material = SimpleMaterial()
material.baseColor = try .texture(.load(named: "texture.png"))
entity.model?.materials = [material]
I was hoping to find a property such as
material.twoSided = true
but so far, I have not found the equivalent thing in RealityKit.
Anyone know how to set two-sided materials in RealityKit?
There doesn't seem to be any way to do this programmatically at the moment via the RealityKit APIs.
Can you change your model definition so it doesn't do back face culling? For example in a USDZ file I am importing it defines one part of the mesh as:
def Mesh "Plane_1"
{
uniform bool doubleSided = 1
You might be able to convert your obj file to a used file using usdzconvert first (https://developer.apple.com/download/more/?=USDPython) then edit the file manually, then import that in to your scene.
It might also be depending on how the model is setup that you can pass in more than one material to the materials array that are applied to different parts of the model, you can see how many materials the model expects:
entity.model?.mesh.expectedMaterialCount
As others already answered you can't do it programmatically.
You can however do it manually for each model via the inspection panel.
See the image below. Near the bottom, you have "Double Sided" checkbox.
I don't think there is a way to do this. RealityKit is still in early days. Material support in RealityKit is very limited right now. I think there are plans to change this in iOS 14 or beyond. There are comments in the documentation that describe features that don't yet exist such as Material protocol says "Describes a material (colors, shaders, textures, etc.) that define the look of a mesh part." There currently is no way to define custom shaders. If you look at the RealityKit framework bundle, there are shader graph definitions and new material features that are not yet exposed in the public API. I suspect there will be a shader graph editor, support for custom shaders, and double-sided materials coming.
It can be done, but it takes a change of perspective: instead of making a double sided material, you create a double sided mesh. This is accomplished by taking every part in every model and creating a double with the normals inverted (and the triangles reversed). Using the code below, the solution to the stated question becomes:
do {
let entity: ModelEntity = try .loadModel(named: "model.obj")
if let model = entity.model {
try model.mesh.addInvertedNormals()
// or alternatively, since this model isn't onscreen yet:
// model.mesh = try model.mesh.addingInvertedNormals()
var material = SimpleMaterial()
material.baseColor = try .texture(.load(named: "texture.png"))
model.materials = [material]
entity.model = model
}
} catch {}
And the entity will now display the material on both sides. Here is the code to do it:
import Foundation
import RealityKit
public extension MeshResource {
// call this to create a 2-sided mesh that will then be displayed
func addingInvertedNormals() throws -> MeshResource {
return try MeshResource.generate(from: contents.addingInvertedNormals())
}
// call this on a mesh that is already displayed to make it 2 sided
func addInvertedNormals() throws {
try replace(with: contents.addingInvertedNormals())
}
static func generateTwoSidedPlane(width: Float, depth: Float, cornerRadius: Float = 0) -> MeshResource {
let plane = generatePlane(width: width, depth: depth, cornerRadius: cornerRadius)
let twoSided = try? plane.addingInvertedNormals()
return twoSided ?? plane
}
}
public extension MeshResource.Contents {
func addingInvertedNormals() -> MeshResource.Contents {
var newContents = self
newContents.models = .init(models.map { $0.addingInvertedNormals() })
return newContents
}
}
public extension MeshResource.Model {
func partsWithNormalsInverted() -> [MeshResource.Part] {
return parts.map { $0.normalsInverted() }.compactMap { $0 }
}
func addingParts(additionalParts: [MeshResource.Part]) -> MeshResource.Model {
let newParts = parts.map { $0 } + additionalParts
var newModel = self
newModel.parts = .init(newParts)
return newModel
}
func addingInvertedNormals() -> MeshResource.Model {
return addingParts(additionalParts: partsWithNormalsInverted())
}
}
public extension MeshResource.Part {
func normalsInverted() -> MeshResource.Part? {
if let normals, let triangleIndices {
let newNormals = normals.map { $0 * -1.0 }
var newPart = self
newPart.normals = .init(newNormals)
// ordering of points in the triangles must be reversed,
// or the inversion of the normal has no effect
newPart.triangleIndices = .init(triangleIndices.reversed())
// id must be unique, or others with that id will be discarded
newPart.id = id + " with inverted normals"
return newPart
} else {
print("No normals to invert, returning nil")
return nil
}
}
}
So, calling addingInvertedNormals() creates a mesh that will show the same material on both sides. I have used this to create a 2-sided grid plane.
With a little extra work (left as an exercise!), you could give the created parts different material indexes, and show different materials on each side.
What you describe is called culling. Check MTLCullMode for example. From there you can jump to various points where you can set culling mode (you are interested in no culling).

If variable is not set, wait for variable to be set

I have 2 delegate methods that are being called by notifications from a 3rd party library.
Method 1:
mediaContentWasUpdated()
Method 2:
adMediaDidBeginPlaying()
In Method 1, a key variable (adDuration) is set from a parameter that is passed in with the notification. As far as I can see this is the only place to get this information.
In Method 2, we check the adDuration and if it is greater than 0 then we update the UI to reflect that we are in fact play an ad.
A bug has appeared where sometimes these two methods are called in the wrong order. Meaning the adDuration is not set and Method 2 thinks there is no ad media to be played and does not update the UI accordingly.
My current attempt at a solution is to make adDuration optional and use an NSCondition to cause Method 2 to wait for Method 1 to set adDuration and then proceed.
var adDuration : Double?
let condition = NSCondition()
func mediaContentWasUpdated(notification: NSNotificiation) {
condition.lock()
if(notificationHasAdDurationInfo(notification)) {
self.adDuration = getAdDuration(notification)
condition.signal()
}
condition.unlock()
}
func adMediaDidBeginPlaying(notification: NSNotification) {
condition.lock()
while adDuration == nil {
condition.wait()
}
if adDuration! > Double(0) {
updateUIForAd()
}
condition.unlock()
}
This is my first time trying something like this and I worry I am doing something wrong. I also have some concerns about locking and unlocking threads needlessly (which would happen in a well timed run, or if there were no ad content to be played).
Outside factors are hindering my ability to test and I wanted to get some input to see if I am heading in the right direction while I wait for those issues to be resolved.
Your discussion of NSCondition got me on the same track with you, and I built two or three solutions using DispatchGroup (which is the better tool for this), but they always had little corner cases that could behave badly, and didn't really capture the intent.
(If you're interested in the DispatchGroup solutions, they're of the form: call .enter() in init, call .leave() when the duration comes in, call notify() when the playing starts. It works fine, but it introduces corner cases that can crash, just like NSCondition.)
Getting back to the real intent:
Update the UI when the duration is known and the ad has started playing.
There's no concurrency going on here. So pulling out GCD is not just overkill; it actually makes things worse because it introduces lots of complicated corner cases.
So I thought about how I'd have solved this back before GCD. And the answer is obvious: just check if you have the data you want, and then do the thing. (Reading through the comments, I see Paulw11 pointed this out as well.)
Personally I like to pull this kind of thing into its own type to make things more self-contained. I hate some of the names here, but the idea should be clear:
class AdPlayer {
private var readyToPlay = false
private var duration: Double = 0.0
private let completion: (Double) -> Void
func setDuration(from notification: Notification) {
if(notificationHasAdDurationInfo(notification)) {
duration = getAdDuration(notification)
}
playIfReady()
}
func play() {
readyToPlay = true
playIfReady()
}
private func playIfReady() {
if duration > 0 && readyToPlay {
completion(duration)
}
}
init(completion: #escaping (Double) -> Void) {
self.completion = completion
}
}
When you set each thing, see if you're ready to update, and if so, update. I've gotten rid of the optional as well, since I believe the intent is "0 duration is always wrong." But you could use an Optional so you could detect actually receiving a 0 from the notification.
With that, you just set up a player property:
player = AdPlayer(completion: updateUIForAd)
(Note that the above might be creating a retain loop, depending on what updateUIForAd is; you may need a [weak self] closure or the like here.)
And then update it as needed:
func mediaContentWasUpdated(notification: NSNotificiation) {
player.setDuration(from: notification)
}
func adMediaDidBeginPlaying(notification: NSNotification) {
player.play()
}
A big advantage of creating the AdPlayer type is that it's easy to reset the system when the ad is done (or if something goes wrong). Just throw away the whole object and create another one.

Swift App Architecture

I'm building out an audio tour component of an app, and I've hit a rut on organizing and calling data. I'm totally green to iOS development; coming from a Ruby/Rails background.
Class names in bold: The goal is to trigger specific information at each tour Stop. Each Stop, will be an AudioClip and play a soundbite, or it launches a Trail to guide the user to the next Stop, or it will flash a Video modal with video, etc. And at each stop, there need to be callbacks once reached. For example, if I arrive at the art museum, I would hear a clip about the Mona Lisa and then an image of the portrait would display on the phone. But all stops are not created equal, so even though the art meseum stop has an audio file to play and an image, the stop at the park may just display a walking trail with nothing else.
I have the app set up like this:
class Stop: NSObject {
func execStop() {
// do stuff
}
}
class AudioClip: Stop { }
class Trail: Stop { }
class StopImage: Stop { }
All of these tours need to be triggered in consequential order, and the next tour stop shouldn't be shown if the current stop hasn't been reached. The best way I could come up with is something like this:
// Each stop defined as its own class
class Museum: AudioClip {
override func execStop() {
playAudio(name: "AudioFile.m4a")
showImage(name: "MonaLisa.jpg")
}
}
class Park: Trail {
override func execStop() {
addRoute([Array, of, coordinates])
}
}
class Tour: NSObject {
var stops: [Int: AnyObject] = [:]
func resume() -> Void {
let active = nextAvailableStopLookup()
let currentStop = self.stops[(active as Int)] as! Stop
return currentStop.execStop()
}
}
// Individual tours, i.e. Museum, Resturant, Hike
class MuseumTour: Tour {
var TourPoints = [
1: Museum(...)
2: Park(...)
]
}
And then in the ViewController, the current Tour would be activated and based on the last stop completed, the next one would be queued.
override func viewDidLoad() {
let queueStop: Void = MuseumTour().resume()
}
The problem
This seems like a terrible way to set up an app with a lot of overhead. Each stop is a new class, and there could be upwards of 60 stops, which is 60 separate classes. The callback is the kicker - inherited-class functions (like func playAudio on AudioClip) are unavailable in the MuseumTour class. And I'm such a novice, I don't know how exactly to trigger dynamic closures.
Is there a better way of organizing and calling this type of data?
What you are doing certainly seems very silly in any language.
As far as I can tell, and to the extent that I was able to stay awake through your description, a "stop" is just a sequence of things to do (along with a pointer to another "stop"? I can't quite tell from your description). So it seems to me that a Stop is an instance of the Stop class (or, in Swift, more likely a struct) and that's all it is; there is no need for all these classes.
Your "I don't know how exactly to trigger dynamic closures" is quite telling, since an array of functions is exactly, it seems to me, what you want a "stop" to have. Maybe your first step should be to "stop" (sorry) and learn Swift, where functions are first-class citizens and so an array of them is perfectly normal.
In my very annoying app "99 Bottles", which sings "99 Bottles of Beer on the Wall", at each line of the song, we perform one or more actions, such as taking down a bottle, or removing the bottle from the screen, and we have to know what line of the song to sing. In my app, therefore, a Phrase struct consists of what to sing along with a list of actions to perform along with it:
struct Phrase {
let sound : String // name of sound file
let actions : [() -> ()] // what else to do
}
The song as a whole is thus nothing but an array of Phrase instances. We pop the first Phrase off the array, sing the sound, and call the actions functions in sequence:
let instruction = self.stages.removeAtIndex(0) // a Phrase
self.player.play(instruction.sound) // sing
for action in instruction.actions {
action() // perform the action
}
This sounds quite similar to what you are after.
(Of course, you say you are from a Ruby background; everything I just said would be equally true in Ruby. The notion of a function as a first-class citizen should come as no surprise to you.)

Swift: permanent variable change through global files

I am working in between three files: Menu.swift, Main.swift and Game.swift.
In my Main.swift, I define the variable swipeNumber:
class Main {
var swipeNumber: Int = 0 {
didSet{
println("The new swipe number is \(swipeNumber)")
}
}
}
N.B. It is in a class so that I can reference the variable from other files, and the didSet property observer will function.
As you can see, its initial value (I think) is 0.
Then, in my Menu.swift, I retrieve the information from the Main class in Main.swift.
let main = Main()
I then have three buttons, which will, on touch, change the swipeNumber variable, based on which button was pressed.
class Menu: UIViewController {
#IBAction func pressedThreeSwipes(sender: AnyObject) {
main.swipeNumber = 3
}
#IBAction func pressedFiveSwipes(sender: AnyObject) {
main.swipeNumber = 5
}
#IBAction func pressedTenSwipes(sender: AnyObject) {
main.swipeNumber = 10
}
//...
}
When I run the program, my property observer appears to work, printing messages such as:
The new swipe number is 3
The new swipe number is 5
The new swipe number is 10
And in the Game class, (for troubleshooting purposes), I have another property observer, checking the integer of the variable swipeNumber when the button test is pressed:
class Game: UIView {
let main = Main()
func didMoveToView(view: UIView) {
/* Setup your scene here */
println("now")
println("\(main.swipeNumber)"
//Nothing happens here, suggesting that didMoveToView is failing
}
#IBAction func test(sender: AnyObject) {
println("\(main.swipeNumber)")
}
}
My func test prints a number, but sadly that number is not 3, 5, or 10. It's 0.
I think that the problem lies with my variable in Main.swift, however I am not sure.
Any advice or 'fixes', whether quick or lengthy, would be very greatly appreciated.
Thank you,
Will
You have different instances of your class Main, and they each carry a different value for the same properties.
You should try the Singleton pattern (see e.g. here or here).
When you call Main(), you are creating a new object...emphasis on NEW. It has no knowledge of what you've done to other objects of the same type. If you want to use the same object in different places, you need to make it a parameter and pass it into methods rather than creating a different object.

Resources