Does Objective-C have anything like PHP’s include_once?

The short answer is “That’s not how you do things in iOS”.

You could mimic the code replacement functionality of PHP includes by using #defines. If you wanted to you could write code just like you do in PHP and pull repetitive portions into #defines. Put them into {ProjectName}-Prefix.pch and they will be automatically available to all of your classes.

What you want to do is restructure your code so that it uses the Model-View-Controller pattern. Pull model-related code out of the main view and put it into a series of classes. Same with view-related code. Sometimes that’s all you need to do and sometimes it gets more complicated.

For example, in my quiz titles I have a preference pane where users can choose which category or categories they want to be quizzed on and the degree of difficulty. When the game first initializes I need to set the default preferences in NSUserDefaults. I created a class for the code and invoke it using

PreferencesInitialization *prefs =[[PreferencesInitialization alloc] init];
[prefs initializePreferences];

The code snippet above acts just like a PHP include and runs the code when it is called.

That works fine if the code in question has no interaction with the current view or data. In the quiz game I put scoring buttons at the bottom of the screen in a toolbar. I pulled all of the code out of the view and put it into a class. I call the class like I did above. Except that the class knows nothing about the environment it is running in. So I need to pass _everything_ it needs to know about to it.

self.scoringToolbar = [[ScoringToolbar alloc] initWithScoreKeeper:self.scoreKeeper parentView:self.view];

This is where it gets a bit tricky, it needs to know about the **current instance** of the scoreKeeper (the class that tallies the results) and the current view. In order to place the toolbar at the bottom of the screen scoringToolbar uses the self.view information to calculate positions. All of the self.view calculations are changed to self.parentView.
e.g.

CGSize windowSize = self.parentView.frame.size;

The scoringToolbar is completely self-contained so once it has its initial parameters it runs on its own. The view controller can get information from it by invoking its methods.

The third type of interaction is where the view controller tells the object to do something, say play a sound, and the target tells the view controller when it is done. The way they do that is with the use of delegates. I pulled the recording and playback portion of the game out of the main view and put it into a class. Just like the scoringToolbar it needs to know about the current view.

self.recordPlay = [[RecordPlay alloc] initWithParentView:self.view ];
[self.recordPlay setDelegate:self];

Tell the recordPlay class to play a sound with a playSound method.

[self.recordPlay playWord:targetSnd];

By setting up the view controller as a delegate of recordPlay it can be notified when the sound is finished playing and respond accordingly.

In recordPlay call

[self.delegate soundFinishedPlaying:self.soundToPlay];

This calls the soundFinishedPlaying method in the view controller and it does whatever is appropriate. Like telling the picture class to highlight a picture or the recordPlay class to play the next sound.

Delegates turn out to be key to the Model-View-Controller pattern. In my game, the controller asks the wordList class for a word using a method in wordList. If there aren’t any words, the wordList delegate lets the controller know and the controller launches the preferencesPane. When an picture is clicked on, the pictureView class tells the controller and the controller decides which sound to play. Then it tells the recordPlay class which sound to play.

Classes don’t communicate with each other—in fact they don’t even know about each other. The controller is an intermediary and it tells everyone else what to do.

I suppose this is obvious to someone with an object-oriented background, but it took me a year to figure out. I’ve rewritten my game to use these concepts and its much easier to manage. By swapping out classes I can reuse the basic structure in several similar games.

Hope his helps someone else.

More MySQL goodness

It can be tricky to update records in a MySQL database using regular expressions because the stock install doesn’t support replacement strings. Occasionally, you get lucky and your data can be manipulated with a combination of REGEXP and strings. This is one case.

My phonemes field is constructed of phonemes separated by spaces. e.g.


b ɛ,i n
bl æ,ə n tʃ
br æ,ə n t æ,ə

I want to update the Initial and Final sounds fields with the sounds IF they are consonants but not if they are vowels. My REGEXP selects all of the consonants—including sh (ʃ) and th (θ or ð) at the beginning ‘^’ or end ‘$’ of the phoneme. Then I split the phonemes field by the first or last space and place that value into the Initial or Final sound field. Along the way I convert it to lower case. Also note that I want initial the initial w to count as a consonant, but not if it occurs at the end—but my phoneme rules should have caught most of those.


UPDATE `words_for_slps` 
SET `I` = LOWER(SUBSTRING_INDEX(phonemes,' ', 1)) 
WHERE `phonemes` REGEXP '^[ʃθðbcdfghjklmnpqrstvwxz]+'


UPDATE `words_for_slps` 
SET `F` = LOWER(SUBSTRING_INDEX(phonemes,' ', -1)) 
WHERE `phonemes` REGEXP '[ŋʃθðbcdfghjklmnpqrstvxz]$'

REGEX and MySQL

I’m working on my words database using phpMyAdmin and thought others might benefit from some of the search expressions.

Find words that start with ‘Wr’ or ‘wr’ and the CVC category is not null.


SELECT *  FROM `words` WHERE `word` REGEXP '^[Ww]r[a-z]*' 
AND `CVC` IS NOT NULL

This gives me words like, Rhoda, Rhone, rhyme because I’m asking to start at the beginning of the word ‘^’ then look for either a capital or lowercase w ‘[Ww] followed by an ‘r’ and then any letter ‘[a-z]’ that occurs zero or more times ‘*’. I’m further restricting the search to words that have an entry in the CVC category. In phpMyAdmin you can paste this into the SQL editing field or just use the REGEX in the word field and set it to REGEXP.

Here’s one looking for all the words that end in the letters mb and the CVC category is not null.


SELECT *  FROM `words` WHERE `word` REGEXP 'mb$' 
AND `CVC` IS NOT NULL

The ‘$’ means the end of the word.

This search yields words like climb, plumb, thumb.

This search looks for words like gnome and gnat. If you want you can leave out the [a-z]* and you’ll get the same result.


SELECT *  FROM `words_for_slps` WHERE `word` REGEXP '^[Gg]n[a-z]*' 
AND `CVC` IS NOT NULL

This one’s a little more complicated, I’m looking for words like tongue and dengue but don’t know if there are words with gue in the middle—like tongues.


SELECT *  FROM `words_for_slps` WHERE `word` REGEXP '^[a-zA-z]*ngue[a-z]*' 
AND `CVC` IS NOT NULL

You can’t do OR queries in phpMyAdmin interface everything is an AND. But if you do a query, then edit the SQL it is easy to make the query into an OR.

This is the original query to find words where the grade and level are NULL.


SELECT *  FROM `words_for_slps` WHERE `level` IS NULL AND `grade` IS NULL 
AND `CVC` IS NOT NULL

And here is the same code where all I changed is the AND to OR and just to be sure I’m getting the right results, I grouped the OR with parentheses. (I can never the default rules for the different languages so I always use parentheses so I’m sure I get what I want.)


SELECT *  FROM `words_for_slps` WHERE (`level` IS NULL OR `grade` IS NULL) 
AND `CVC` IS NOT NULL

I like this result but want to look deal with the shorter words first and then work my way up to the harder ones. I also want to skip the words that do not follow the rules (DNFR). Use <> for not equal.


SELECT *  FROM `words_for_slps` WHERE (`level` IS NULL OR `grade` IS NULL) 
AND `CVC` IS NOT NULL AND `CVC` <> 'DNFR' ORDER BY LENGTH(word)

Here’s the code I use to find all of the conosonants


([bcdfghjklmnpqrstvwxz]|ch|sh|zh|th|wh|ng|qu|wh|tch|ph)

And this finds all the vowels.


(a|e|i|o|u|aw|ow|ie|ae|oe|oy|oo|uu|y)

And this finds all of the CVCCV words. Note the {2} that finds exactly two occurrences of the previous search string.


SELECT *  FROM `words_for_slps` WHERE `word`
^([bcdfghjklmnpqrstvwxz]|ch|sh|zh|th|wh|ng|qu|wh|tch|ph)
(a|e|i|o|u|aw|ow|ie|ae|oe|oy|oo|uu|y)
([bcdfghjklmnpqrstvwxz]|ch|sh|zh|th|wh|ng|qu|wh|tch|ph){2}
(a|e|i|o|u|aw|ow|ie|ae|oe|oy|oo|uu|y)$

Roxio Toast – Crashes

We’re redoing all of our software so that it works on OSX Lion so that means lots of Toasting. A couple of times I went home without quitting Toast and the next morning it was frozen. Force quitting got out of the app but it wouldn’t launch. I tried removing the preferences files. Still no luck. On two occasions, I reinstalled the app and it would still not open. The only solution I have found is very un-Mac like—reboot the computer. It’s worked the last two times so I think that’s the solution to freezes.

Hybrid Disc Problems with Toast and Director

We’ve been using Toast to burn Hybrid CDs since 1994. When I first purchased the new version I was a bit upset that they took out the ability to burn old-style hybrid CDs. However, they didn’t remove the functionality, they just turned it off. Go into the preferences and enable “Show legacy formats and settings” and it will behave just like it did before. Well, almost. Now it has some really annoying animation, but otherwise it works the same. One other difference was a bit harder to track down and fix.

All of our software current software titles were made with Macromedia Director and with the introduction of OSX Lion they no longer work. The transition to Adobe Director 11.5 has been relatively painless except for some old Flash code in Actionscript 1.0 that hasn’t updated nicely to ActionScript 2.0.

However, when we gave the discs to some people in the office to test they got two errors. When they tried to run program from the disc, an application alert came up telling them “This application requires Adobe Shockwave 11, which can not be found. Click to download it.” If they try to copy it to the hard drive, they get a different error. “The alias “Gamename” cannot be copied to the destination, perhaps because the destination does not support this type of alias.” Our development machines don’t give any errors—even the alias one.

It turns out that the problem is in the way Toast handles (or rather doesn’t handle) aliases in the app bundle.

The app bundle looks like this

Hybrid CD Error

If you follow the aliases you’ll find that
DPLib ==> /Contents/Frameworks/DPLib.framework/Versions/A/DPLib
Resources ==> /Contents/Frameworks/DPLib.framework/Versions/A/Resources

Replace the aliases with the source files, and do the same thing in IMLLib.framework and ProjLib.framework. As near as we can tell the Current alias isn’t necessary and can be deleted from all three locations.

Once you have all your aliases replaces and successfully burned a test CD, make a copy of the Frameworks folder. You can replace the Frameworks folder in new apps with the one that you have working.

The solution was tested on OSX 10.3.9, 10.4.11, 10.5.8, and 10.6.8. We haven’t had access to a Lion machine yet.