Tagged: ios

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.

Yesterday I was quite busy boxing up my apartment for next week’s move (fuck i hate moving) so relatively small progress on the app, but the login / registration screen is getting there

Created two new views, one for login and one for registration obviously and linked them to LoginViewController and RegisterViewController. Also renamed my home screen’s controller to be HomeViewController (very imaginative naming, I know)

The bottom buttons are there to switch views between register and login + I still have my “let me in” button for entering directly to the Play view for testing purposes (really need to add a TODO to remove that from the final product at some point). The code for switching the views looks like this

As for the login and registration code: Why is that that in any language the most boring and tedious thing to do is form validation? Wish someone would come up with a sure way of doing it. Anyway, the code for my register form looks like this. Fokin’ boring

Swift

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

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

funcisValidEmail(testStr:String)->Bool{

// println("validate calendar: \(testStr)")

letemailRegEx="[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,8}"

letemailTest=NSPredicate(format:"SELF MATCHES %@",emailRegEx)

returnemailTest.evaluateWithObject(testStr)

}

funcisValidUsername(testStr:String)->Bool{

// println("validate calendar: \(testStr)")

letuserRegEx="^[a-zA-Z_0-9]{5,}$"

letuserTest=NSPredicate(format:"SELF MATCHES %@",userRegEx)

returnuserTest.evaluateWithObject(testStr)

}

@IBAction funcdoRegister(sender:AnyObject){

varemail=self.emailInput.text

varusername=self.usernameInput.text

varpasswd=self.passwordInput.text

varhasError=false

if(email==""){

emailErrorLabel.text="Please enter your email address"

emailErrorLabel.hidden=false

hasError=true

}elseif(!self.isValidEmail(email)){

emailErrorLabel.text="Invalid email address"

emailErrorLabel.hidden=false

hasError=true

}else{

emailErrorLabel.hidden=true

}

if(username==""){

usernameErrorLabel.text="Please pick a username"

usernameErrorLabel.hidden=false

hasError=true

}elseif(count(username)30){

usernameErrorLabel.text="Username must be between 5 and 30 characters"

usernameErrorLabel.hidden=false

hasError=true

}elseif(!self.isValidUsername(username)){

usernameErrorLabel.text="Username can only contain alphanumeric characters"

Couldn’t find any built in methods to validate emails so had to use regex (and not a very good one) to validate it on the front end. Obviously will need to do the same on the server side as well. The code I think is pretty self-explanatory. Checking for certain field values and displaying error messages depending on what rules they break. If no error detected do an async request to the server and check it’s response. Since the API is not yet ready I’m currently only checking for valid status.

I also implemented (partially) Facebook connect which was surprisingly easy. Since I couldn’t find a straight forward step by step guide to implement it in Swift here it is for you:

The guide tells you to set up a new Facebook App. The setup requires a valid App Store ID which you might not have. To trick it I just used a random app’s ID. To get the id you just copy any app’s url and get the relatively long number from the end of the link.

Add the SDK to your building targets. Click on the top element in your file manager in Xcode and open the Build Phases tab. Then in the “Link Binary with Libraries” section add the following three frameworks: FBSDKLoginKit, Bolts, FBSDKCoreKit

The SDK is written in Objective-C so you need to create a “bridging header” to use it in Swift. To do so create a “Bridging-Header.h” file in your project’s main folder with the following content:

C

1

2

#import <FBSDKCoreKit/FBSDKCoreKit.h>

#import <FBSDKLoginKit/FBSDKLoginKit.h>

Click on the top element in your file browser and select “Build settings”. Make sure “All” is selected in the top menu. Scroll down to “Swift compiler – Code generation” and add “YourProjectname/Bridging-Header.h” as the “Objective-C bridging header”

Try building the project to make sure everything is linked correctly.

Using the Facebook SDK to implement FB Login is straight forward. First of all you need to modify your view class definition a bit.

This code basically adds a new subview to the current view, which contains the login button. At this point I have literally no idea how to style the button itself. It just shows up right in the middle of the screen, which is currently fine with my crappy interface but I can see it being a pain in the future. This is how it looks atm:

So thats it for today. Tomorrow I’m gonna try and create the server side API and hook up those forms / FB login button. Happy bank holiday Monday everyone

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

So I decided to step a little bit out of my comfort zone professionally and try myself outside the scope of web development. I took a beginner course on Udemy about developing iPhone apps using Swift, but up until today I didn’t write a single line of Swift code. I hereby challenge myself to write my first app in 14 days. Now the app itself most probably won’t be ready for the app store straight away since I’m mostly focusing on functionality and not usability / design but it will be a good learning experience for me.

The app: The app will be a very simple game. Imagine a massive, 1 billion step board game with one single player. Everyone who is using the app will move the same player by answering trivia questions. If a given user answers the question correctly the player moves forward 1 step, otherwise it moves backwards one. I know, not the most original idea but it really is just for learning.

So here is what I did today (keep in mind I have literally no idea what I’m doing so probably I’m doing it all wrong)

I started off building a basic interface. What really surprised me, coming from web development, is how un-intuitive xcode’s interface (storyboard) builder is when it comes to “responsive” design. It took me ages to figure out how to make a single button stretch when I switch the emulator from iPhone 5s mode to iPhone 6. Basically you have to define the relative distance between the elements and the main view and the buttons will stretch to fill the space. I mean it kinda makes sense but how hard would it be for to allow defining sizes in %? Anyway my lame interface looks like this at the moment:

I know, it looks fugly but it renders properly in the simulator (still fugly but at least symmetric)

I can’t believe I spent like 3 hours on this, but at least I learnt something. Besides the interface I defined my outlets and set up a base controller which will handle my interaction with the (so far non-existent API) and update the interface elements as needed.

It really is a frustrating yet very fulfilling experience to code for iOS. I mean it is one thing to switch from PHP to Python but it is completely different when you are switching from web dev to desktop / mobile dev. I think the last time I had to deal with pointers and complex types was when I was like 14 coding in Borland Pascal.

Tomorrow I’m gonna hook up my class with a stub API and do some actual work on the logic. Will keep you posted (not like anyone is reading this)