Tagged: xcode

Finally managed to unpack everything after moving flats so no more boxes, yey! Unfortunately somehow managed to lose the box with my socks and underwear tho. Oh well, you win some you lose some. Anyway, had some time today to work on this app of mine.

First of all I wanted to pretty it up a little bit. I have no sense of style at all (don’t think any coder does) but I do not like working with anything too messy. So here are a couple of things I did to make me feel more comfortable:

I’ve reordered my tabs in my TabBarController to make the “Play” item to be in the center. For much of my surprise the storyboard editor doesn’t let me select the default tab. I realized that I have to extend the TabBarController. So after much research I found a code snippet that did the trick

I’ve also made the tab bar’s color scheme inverse so the above code takes care of the font colors as well. I also wanted to display a heading on all pages with a title so dragged in a Navigation Controller and connected it to my Tab Bar Controller. It displayed the heading placeholder but I couldn’t find a way in the storyboard editor to set the text for each tab element. Clever people of stack overflow said that i can set the title from the ViewDidAppear method of each controller. Here is an example of how:

Swift

1

2

3

4

5

overridefuncviewDidAppear(animated:Bool){

super.viewDidAppear(animated)

self.tabBarController?.title="Play BillionQuestions"

}

Since I really don’t like the UIColor API (coming from web development background I think it’s understandable) I was looking for an easy to use alternative. Thats how i found UIColorExtension. Its a simple extension that allows me to use RGB(a) hex codes in my color definitions. So I can write stuff like this:

Swift

1

UITabBar.appearance().tintColor=UIColor(rgba:"#ffffff")

Winning (except for the 15 minutes it took me how to do # on a mac keyboard)

Also replaced my label that was used for the countdown with a Progress View. Progress View’s are really straight forward to use:

All right. I think I’m done with prettying stuff up. It is still ugly as fuck but bit easier on the eye:

I hooked it up with the question API I made in my previous post and it worked like a charm. Except one thing: Since the API was using random sometimes it gave me the same question twice. In order to avoid that I needed to log the user’s answers. So created a new table:

PgSQL

1

2

3

4

5

6

7

8

9

10

11

12

CREATETABLE`user_answers`(

`id`int(11)NOT NULLAUTO_INCREMENT,

`user_id`int(11)NOT NULL,

`question_id`int(11)NOT NULL,

`answer`tinyint(4)NOT NULL,

`correct`tinyint(4)NOT NULL,

PRIMARYKEY(`id`),

KEY`user_id`(`user_id`),

KEY`question_id`(`question_id`),

KEY`answer`(`answer`),

KEY`correct`(`correct`)

)DEFAULTCHARSET=utf8

And a new class:

PHP

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

classAnswerextendsModel{

public$db=null;

public$schema=array("id"=>array("type"=>"int",

"unique"=>true,

"notnull"=>true,

"nice_name"=>"Id"),

"question_id"=>array("type"=>"int",

"unique"=>false,

"required"=>true,

"notnull"=>true,

"nice_name"=>"QuestionId"),

"user_id"=>array("type"=>"int",

"required"=>true,

"notnull"=>true,

"nice_name"=>"UserId"),

"answer"=>array("type"=>"int",

"required"=>true,

"nice_name"=>"Answer"),

"correct"=>array("type"=>"int",

"required"=>true,

"nice_name"=>"Correct")

);

public$table="user_answers";

public$nice_name="Answer";

function__construct($db){

global$baseurl,$basepath;

$this->db=$db;

}

}

The PHP API has been modified a little as well to able to record answers + avoid sending the same question to the same user again:

PHP

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

publicfunctiongetUserQuestions($user_id){

$questions=array();

$res=$this->answer_api->get(array("user_id"=>$user_id),0);

foreach($resas$row_id=>$row){

$questions[]=$row['question_id'];

}

return$questions;

}

publicfunctionanswer(){

$errors=$this->answer_api->validate($this->params);

if(!empty($errors)){

$this->raiseError(json_encode($errors));

}

$this->answer_api->add($this->params);

}

publicfunctionquestion(){

if(!isset($this->params['user_id'])||!$this->params['user_id']){

$this->raiseError("missing param: user_id");

}

$question_ids=$this->getUserQuestions($this->params['user_id']);

if(empty($question_ids)){

$query="SELECT * FROM questions ORDER BY rand() LIMIT 1";

}else{

$query="SELECT * FROM questions WHERE id NOT IN (".implode(",",$question_ids).") ORDER BY rand() LIMIT 1";

}

$res=$this->db->query($query)ordie($this->db->error." on line #".__LINE__);

if($res->num_rows){

$row=$res->fetch_assoc();

$ret=array();

$ret['id']=(int)$row['id'];

$ret['question']=$row['question'];

$ret['category']=$row['category'];

$ret['correct']=(int)$row['correct_answer'];

$ret['current_post']=100000000;// TODO

$ret['answers']=array(

$row['answer1'],

$row['answer2'],

$row['answer3'],

$row['answer4']

);

print(json_encode($ret));

}

}

Great, now the only thing left to do is to make the app send home the user’s answer for logging. In my PlayViewController I modified the logic that happens when the user clicks one of the answer buttons:

When the user hits the button it just checks against self.currentCorrectAnswer and depending on the result it sends home the questionId, userId and 0 or 1 for logging.

Finally I decided to make this project fully open source. Please feel free to fork / do a pull request / whatever, but keep in mind it is very much a work in progress and I have literally no idea what am I doing in Swift

The game is actually playable now and it is moderately entertaining. Will need to find a new source of questions because the current ones are way too British.

What is next? The gameplay is pretty much ready now. It needs some refactoring and some security on the API but its ok for now. Next time I’m going to work on the leaderboard view. Deal with some TableViews in xcode and probably implement Facebook friend list somehow.

Ok, I’m cheating a little bit here. I did not work on the app during the past two weeks or so but I have very good reasons. First of all I moved to a new flat (which I absolutely love btw) and as you know moving is hard. Secondly the Macbook I borrowed from work pretty much died under me. It kept presenting the so called “black screen of death” 9 out of 10 times I tried to boot. Tried to do all the PRAM resets and mental keyboard combos I could find online but no joy. Anyway, bought the new generation Macbook Pro now (force touch trackpad rulez btw) so I’m back in business.

That being said yesterday I only worked on the back-end of the app. In order to go forward with the actual iOS development I needed some questions and answers in my database so I decided to look around to find a site which I can scrape. I’m really surprised that there are no good quiz sites around with 4 answers for each question and the correct answer included. Found some databases for sale that would fit my needs but the sales page were either crappy or the database was ridiculously overpriced. The only site I could find that met all my requirements is PubQuizArea, which ticks all the boxes, however it is really British. Oh well, it should do for now.

First of all I need a table structure:

PgSQL

1

2

3

4

5

6

7

8

9

10

11

CREATETABLE`questions`(

`id`int(11)NOT NULLAUTO_INCREMENT,

`question`varchar(500)COLLATEutf8_binNOT NULL,

`answer1`varchar(200)COLLATEutf8_binNOT NULL,

`answer2`varchar(200)COLLATEutf8_binNOT NULL,

`answer3`varchar(200)COLLATEutf8_binNOT NULL,

`answer4`varchar(200)COLLATEutf8_binNOT NULL,

`correct_answer`tinyint(4)NOT NULL,

`category`varchar(200)COLLATEutf8_binDEFAULTNULL,

PRIMARYKEY(`id`)

)ENGINE=InnoDBDEFAULTCHARSET=utf8COLLATE=utf8_bin

The model for this is written with my No-BS Framework. It does not need to modify the base model at all, just need to define the table fields and some validation rules (question needs to be unique:

PHP

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

<?php

classQuestionextendsModel{

public$db=null;

public$schema=array("id"=>array("type"=>"int",

"unique"=>true,

"notnull"=>true,

"nice_name"=>"Id"),

"question"=>array("type"=>"string",

"unique"=>true,

"required"=>true,

"notnull"=>true,

"nice_name"=>"Question"),

"answer1"=>array("type"=>"string",

"required"=>true,

"notnull"=>true,

"nice_name"=>"Answer1"),

"answer2"=>array("type"=>"string",

"required"=>true,

"notnull"=>true,

"nice_name"=>"Answer2"),

"answer3"=>array("type"=>"string",

"required"=>true,

"notnull"=>true,

"nice_name"=>"Answer3"),

"answer4"=>array("type"=>"string",

"required"=>true,

"notnull"=>true,

"nice_name"=>"Answer4"),

"correct_answer"=>array("type"=>"int",

"required"=>true,

"notnull"=>true,

"nice_name"=>"Correct answer"),

"category"=>array("type"=>"string",

"required"=>true,

"notnull"=>false,

"nice_name"=>"Category"),

);

public$table="questions";

public$nice_name="Question";

function__construct($db){

global$baseurl,$basepath;

$this->db=$db;

}

}

The main part of the job is writing a simple scraper. Luckily the site’s structure is pretty well thought out so it makes it easy to scrape:

I know I know. Heard it million times: my scraping style is really old school. Guess what? I tried all those hipster libraries for scraping which promise you bells and whistles, none of those worked for me. Every once in a while they stop working and it is really hard to find the actual issue under the hood. As always I’m using my trusty curl class to fetch the data.

The script is just looping through the subpages of the category url from the command line argument and get the question / answer data from them. Luckily the correct answer is highlighted with a separate class (“blue”) so it is easy to recognize. $question->validate($db_data); takes care of removing dupes too.

That site gave me about ~1,800 questions. Obviously not enough, but will do for testing.

Last thing was to add an endpoint to my api. My __construct became:

PHP

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

function__construct($db,$params){

$this->db=$db;

$this->user=newUser($this->db);

$this->question_api=newQuestion($this->db);

$this->params=$params;

if(!isset($params['method'])){

$this->raiseError("No method provided");

}

if($this->params['method']=="register"){

$this->register();

}elseif($this->params['method']=="login"){

$this->login();

}elseif($this->params['method']=="question"){

$this->question();

}elseif($this->params['method']=="facebookLogin"){

$this->facebookLogin();

}elseif($this->params['method']=="stub"){

$this->stub();

}else{

$this->raiseError("unknown method: {$this->params['method']}");

}

}

And the implementation is:

PHP

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

publicfunctionquestion(){

$res=$this->db->query("SELECT * FROM questions ORDER BY rand() LIMIT 1")ordie($this->db->error." on line #".__LINE__);

if($res->num_rows){

$row=$res->fetch_assoc();

$ret=array();

$ret['id']=$row['id'];

$ret['question']=$row['question'];

$ret['category']=$row['category'];

$ret['correct']=$row['correct_answer'];

$ret['current_post']=100000000;// TODO

$ret['answers']=array(

$row['answer1'],

$row['answer2'],

$row['answer3'],

$row['answer4']

);

print(json_encode($ret));

}

}

Obviously this logic will change as I hook it up with the app (rand() doesn’t exactly prevents the user from getting the same question over and over again) but again, its ok for testing. If you fancy trying it out, click here

With the general framework of my front end’s register / login interface being done it’s time to create the API for handling user signup and login. I can’t stress it enough how great is it to work a little bit with the language I’m most familiar with after spending 3 days with Swift.

I will keep the database very simple for now:

PgSQL

1

2

3

4

5

6

7

8

CREATETABLE`users`(

`id`int(11)NOT NULLAUTO_INCREMENT,

`username`varchar(30)NOT NULL,

`password`varchar(200)NOT NULL,

`email`varchar(200)NOT NULL,

`facebook_id`varchar(50)NOT NULL,

PRIMARYKEY(`id`)

)DEFAULTCHARSET=utf8

For the API itself I’m going to use my No Bullshit Framework. The framework is very much in alpha stage but will speed up my development. I only need the model module from it so I’m gonna go ahead and copy the “includes” directory and the example config from the framework. I’m also going to create a user.class.php inside includes which will implement my interaction with my users table. Here is the pretty straight-forward code:

This implements everything I need from this API right now. Added a new DNS entry billion.lepunk.co.uk and pointed it to my server where the code is deployed. This way I can test the app with actual networking.

Back to the app, back to Xcode, back to OSX. In my register view I’ve added a couple of new things:

A hidden input field to store the user’s Facebook ID once they connected

A hidden label for explaining some stuff to Facebook users

I should not touch the original validation code in my doRegister function but need to update what happens once the form is validated:

Basically doing an async request to the API’s register method. If the request comes back with some errors display them otherwise grab the user id and chuck it in the App’s NSUserDefaults.standardUserDefaults storage (I will probably need to update this to be CoreData in the future) and redirect the user to the play area.

When the user decides to sign up with Facebook instead of the form there are two cases:

The user already registered (probably on a different device), in which case we need to redirect them to the play area

The user is not yet registered, so we need to ask them to set up a password

So we have a valid Facebook session and we can do a /me Graph API call to get the user’s details (including their email, since we asked for that permission). We do an async request to our API’s facebookLogin method to check if the user is already registered with us. If not populate the register form with the user’s data and add the FB id to the hidden input field. If the user already registered chuck the user id into the storage and redirect to the play view.

The code for the login view is pretty much the same so I’m not gonna share it here. The only difference really is when the user signs in with Facebook but they are not yet registered instead of redirecting them to the play area I’m redirecting them to the register view.

But of course it would be really annoying if the user needed to log in each time they used the app so some modification is needed for my Home view. I’ve added two new methods to my HomeViewController:

So when viewDidAppear is called I’m checking the standardUserDefaults storage for the user id. If that is present I’m immediately redirecting the user to the Play view. One of the gotchas about this code is you can not use viewDidLoad for this. According to stackoverflow it is because if you do a redirect in viewDidLoad you can end up with two active views which is a big no-no. Every day I learn something.

For testing purposes I also added a button to my Me view to let the user log out. Here is the associated action:

If the user hits the button I’m removing the user id from the local storage and redirect them to the Home view. Important to note that calling defaults.synchronize() is a must here. Apparently iOS does the synchronisation of this storage periodically so you could end up in a weird state.

So this is the full login / register flow. Wasn’t too hard but I’m glad I’m over with it since it wasn’t the most exciting task to do. One thing I still need to figure out is how to kill the Facebook connection with the app when the user hits the log out button.

Tomorrow I’m gonna work on the API for the gameplay. Minor setback is that I need to give back the MacBook Pro I’m currently working on and start using my Mac Mini which is slow as hell.

I have to say so far I’m liking Swift a lot. It is a nice, straight forward language with some awesome feature like conditionals.

Ok so what I did today? (well yesterday, but whatever)

First of all I added some new views to my interface. I realized that i will need to record the user’s activity on the server so I will need some sort of identifier to them. Of course I could just use the device’s ID but its better to make them create an account and use their login name / user id as the identifier. Anyway, I needed to add a login view as the first responder of my app. For now I will just add a simple button to redirect the flow to the “play” view, will deal with the actual registration later. Also added a new view to my tab controller for editing the user profile / view activity. Here is how my storyboard looks like at the moment:

The code for my LoginViewController is really just a placeholder now

Swift

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

importUIKit

classLoginViewController: UIViewController{

overridefuncviewDidLoad(){

super.viewDidLoad()

// Do any additional setup after loading the view, typically from a nib.

One thing I’ve learned about this and it is very important: never ever (EVER) try to update UI elements directly from the async request’s callback because it won’t take effect. It took me like half an hour to recognize that my code in fact works, but the UI elements are not updated. Clever people on stackoverflow told me that I should use dispatch_async to with dispatch_get_main_queue() to tell the main execution thread to update the UI.

I want to allow the user 30 seconds to answer each question. I really hoped that the SDK has some sort of built in timer object, otherwise I should have implemented my own recursive async thing which I really didn’t want to do. Fortunately the SDK in fact has an NSTimer class so implementing the countdown was easy:

Simples. If the clicked button’s tag equals to the one got from the (fake) API display a big green message, otherwise a red one. In the future the app will report the result to the server for logging purposes and of course to move the player on the global board but that is for the future.

Tomorrow I’m gonna start building the server side a little bit and try to make the registration / login view and hopefully add Facebook login too