Ive already talked about the basics of a Firebase social network project, and adding posts in two other articles. Here I want to explain a method for loading those posts from a Firebase array, streaming them in chunks to a UITableView in an 'infinite' way.
To explain more, the scenario is : you have an array of items in your Firebase database - perhaps hundreds or thousands. You want to display the latest bunch of items, but you don't want to go and load the lot of course, you just want to load enough to populate the visible part of your table. But as the user then scrolls down the table, you want the next set of items to be loaded in as required, on and on until the oldest item in the database is displayed, and the table stops scrolling. This is quite easily achievable without too much code - heres how I do it.
In this example the items I am loading are posts from a social network. From now on I refer to the downloaded items as posts, and my local type to hold them is called GZPost. In terms of class properties on my UIViewController, theres three I need. This snippet has the three properties plus some setup items for my UITableView. Comments explain what the properties are for.
My 'reloadPost' func is mentioned in the refreshOptions above. Heres the code for that :
This is fairly self explanatory - empty the posts array, set the loadingPosts count to zero (more on that later), and reload the UITableView to empty it of previous cells, before we start loading again.
The main work here is done by my 'loadSomeMorePosts' func, the code for which is here.
This method is called every time the UITableView scrolls and we need more data to fill the cells with posts.
First its important to picture this : the data in our Firebase database is ordered in terms of newest post last - so in date order from old to new. In our UITableView, we want to see this order reversed, because we want the newest post at the top of the table.
For our query then, to fill the top of the table first, we must go to the very end of our posts array in the database, and get the last posts. This ensures that the newest posts (those at the end of the array) are the ones we see at the top of the table. Last = newest.
Then as we fill further down the table, we are effectively going up that Firebase array from the bottom to the top. So that we know where we are up to as we go, we have a class property called 'idOfFinalPostInNextQuery'. When we first query the database, we get the last six posts from the array - the six most recent posts. But we dont display all 6 : we display 5, and the oldest of the bunch, we save the key of into that property. Then when we next need to load some posts, we go from that key backwards up the array, until we finally get to the top of the array, which is the oldest post.
At the top of the code snippet above, you first see the creation of the DatabaseQuery object, which is a Firebase type. 'QueryOrderedByKey' also ensures they are ordered by date, as the automatically created keys do this by default. You can see what I have described above in the next line, where the 'queryEnding' property on that query is set to 'idOfFinalPostInNextQuery' - this ensures that second time and beyond that, we dont go from the end of the array, but from that last point backwards.
Now a word on 'loadingPostCount' (line 10 in the above code snippet). The value of this at any time, is equal to the number of posts we already have loaded and in cells in the table, plus the number of posts that we are currently querying (and possibly waiting) for. It effectively shows the total number of posts. Later on theres a tableview:willDisplayCell: method, that allows us to trigger a further round of loading of data. We compare this loadingPostCount, to the row number currently being displayed. If we have reached the bottom, we call to load more posts. We cannot simply use self.posts.count for this : because we might not have loaded all the posts we are currently querying for.
Ok so the main chunk above, line 12 : queryLimited(toLast .. . This is the call to the Firebase database. We load one more than the number of posts we have asked to load - because we save one of these as the marker point for where we next will need to load more data. You see the code for that on lines 15-17 - we set the idOfFinalPostInNextQuery property, then remove it from the array so it isnt displayed this time.
Then we reverse the posts because we want newest first, and our downloaded data is ordered as newest last. Line 20 onwards : those reversed snapshots are iterated through. We check to see if we already have the given post id in our posts array. If we do, we update the post, and otherwise we load it.
This is fairly self explanatory - take the snapshot and turn it into our local Post type, GZPost. Create an index path, add or replace the post in our local posts array, then either replace or add the new rows to our UITableView.
Finally, heres the UITableView datasource / delegate methods. The 'numberOfRowsInSection' is simply the count of our local Posts array. The cellForRow gets a customized cell type I made, and calls a func I created on it - configureCell, which simply puts the data into the cell. Then at the bottom you see the bit that makes the table load new data as you go. the willDisplayCell: method, which checks the indexPath of the cell that is about to be displayed, and compares it to the loading post count. If we are about to run out of data, off we go to load more.
Thats everything. Please do comment if you would like further explanation of anything.
Thursday, April 19, 2018
Friday, April 13, 2018
Closures in function definitions : Creating a 'completionHandler' in Swift
Heres an overview of using a closure in a function definition, that gives you your own 'completionHandler' for functions in Swift 4. The end result I am talking about is something like this :
A completion handler is just a closure, and it is given the name 'completionHandler' in the function definition. A closure is a chunk of code that can be passed into a function, just like a property can, to be executed later in the course of that function.
Looking at the code snippet above, just after the 'completionHandler:' part of the call, I have a closure - thats the part in curly braces. The closure itself is passed into the function like a property in a way, along with all the other properties I might send into the function.
So how is that function defined, so that I can pass that closure in, call it a 'completionHandler', and get it to execute and a given time during that function call?
First, I need to know the kinds of properties I want to have returned from the call, for my closure code to use (the 'result, ref in' part). In my case, I wanted a true or false as to whether my network call had succeeded. Should it have worked, I wanted to know the database reference that the network call returned.
So I'm looking to get back a Bool property, and a Firebase type called DatabaseReference. As I say, this was just for my particular task - obviously you can return any amount of properties for your closure to use.
So heres the function definition itself to make that work :
Thats the whole thing. Breaking it down, the part that allows the closure to be passed in is this:
In a nutshell, I am later able to begin my closure with '{ result, ref in', because I have defined those types in the line of code above, as (Bool, DatabaseReference). The 'result' property is the Bool, and the 'ref' property is the DatabaseReference.
Just after that, I have '-> Void'. This just completes the definition of the type of closure I can send it. In this case, the closure is not expected to return anything to the method that calls it, such as 'return true'. Nothing is returned at all, ie - 'Void', so we dont have to 'return' in the closure statement.
Ignore the line of code with Database.database().. etc. Thats just the main task of my particular function in this case, which updated an online Firebase database. The important bit comes just after that, where we call the completionHandler closure to execute it.
To do that we simply call the 'name' of that closure as defined in the function - we gave it the name 'completionHandler' in the function definition. So we execute the closure by calling that named property, and we set the 'result' and 'ref' properties just after we call it. Like this :
And thats when the closure gets executed, with those properties that we wanted set. And thats it. Theres more to closures of course, but this achieves most of what you need most of the time.
A completion handler is just a closure, and it is given the name 'completionHandler' in the function definition. A closure is a chunk of code that can be passed into a function, just like a property can, to be executed later in the course of that function.
Looking at the code snippet above, just after the 'completionHandler:' part of the call, I have a closure - thats the part in curly braces. The closure itself is passed into the function like a property in a way, along with all the other properties I might send into the function.
So how is that function defined, so that I can pass that closure in, call it a 'completionHandler', and get it to execute and a given time during that function call?
First, I need to know the kinds of properties I want to have returned from the call, for my closure code to use (the 'result, ref in' part). In my case, I wanted a true or false as to whether my network call had succeeded. Should it have worked, I wanted to know the database reference that the network call returned.
So I'm looking to get back a Bool property, and a Firebase type called DatabaseReference. As I say, this was just for my particular task - obviously you can return any amount of properties for your closure to use.
So heres the function definition itself to make that work :
Thats the whole thing. Breaking it down, the part that allows the closure to be passed in is this:
In a nutshell, I am later able to begin my closure with '{ result, ref in', because I have defined those types in the line of code above, as (Bool, DatabaseReference). The 'result' property is the Bool, and the 'ref' property is the DatabaseReference.
Just after that, I have '-> Void'. This just completes the definition of the type of closure I can send it. In this case, the closure is not expected to return anything to the method that calls it, such as 'return true'. Nothing is returned at all, ie - 'Void', so we dont have to 'return' in the closure statement.
Ignore the line of code with Database.database().. etc. Thats just the main task of my particular function in this case, which updated an online Firebase database. The important bit comes just after that, where we call the completionHandler closure to execute it.
To do that we simply call the 'name' of that closure as defined in the function - we gave it the name 'completionHandler' in the function definition. So we execute the closure by calling that named property, and we set the 'result' and 'ref' properties just after we call it. Like this :
And thats when the closure gets executed, with those properties that we wanted set. And thats it. Theres more to closures of course, but this achieves most of what you need most of the time.
Tuesday, March 20, 2018
UITableView using custom .xib files for table and cells.
Occasionally I find myself needing a UITableView that has been setup from a custom .xib (interface builder) file, rather than from within a Storyboard. The process for setting up a UITableView within an xib is different to that for setting one within a Storyboard, mainly because you cannot use prototype cells and design your cells within the UITableView itself - the custom cell requires its own .xib file too. Ive documented the process for setting up a UITableView in this way below.
First up, I assume you have a UIViewController that is within an .xib file. If not, theres a guide to setting up a UIViewController within an xib here. You can also use the below method for a UITableView within a storyboard - you just need to delete the UITableViewCell from within the UITableView first.
First up, I assume you have a UIViewController that is within an .xib file. If not, theres a guide to setting up a UIViewController within an xib here. You can also use the below method for a UITableView within a storyboard - you just need to delete the UITableViewCell from within the UITableView first.
- Drag in a new UITableView object into your UIViewController
- Drag datasource and delegate outlets onto Files Owner
- Drag an outlet connection for the UITableView into your UIViewController
- Create a new .xib file to hold the custom cell
- Delete any view in that file and add a new UITableViewCell object.
- Add a new swift file to contain your custom cell UITableViewCell subclass, and define the basic class
- Back in the custom cell .xib file, set the Custom Class of the UITableViewCell object to the sub-class you just defined.
- Then in the same xib file, but this time on the Attributes Inspector rather than the Identity Inspector, set the Re-use Identifier to something memorable - I usually just use that class name again.
- Now back in the main UIViewController swift that contains the UITableView, add a line of code to register the custom nib you've just set up. Somewhere on initialization is good, such as viewDidLoad. Re-use identifier should match the 'RestorationId' property set in the cells custom xib file.
- Now add the basic stub functions for UITableViewDataSource. You can see how the custom cell is dequeued in the code below. The identifier mentioned there is the one we set in the custom cell as 'RestorationId'.
- Thats all the basic setup done. From here you can add any UI elements you wish into the custom cell xib file, and drag the properties over to the swift file for your custom UITableViewCell class. Then within the 'cellForRowAtIndexPath' you can set and populate those properties.
Tuesday, March 13, 2018
Social Network with Firebase 2 - Making a Post
Ive already outlined my database structure for my Firebase based social network. Ive done the auth for new users, and Ive added each new user to an array at the root of my JSON database called Users. Now for the first stage of allowing the user to create data : making posts.
As the database structure shows, my posts will also live in a 'posts' array at the root of the database structure. Within that array, each post will have a unique key, which by the way consists mainly of a timestamp, which allows them to be easily sorted by date. Within each key will live the post data, which consists most importantly of : id of the user that made the post, the date + time it was made, the text body of the post, longitude / latitude at which the post was made, and other things. The gist of this blog post here is to simply describe how Firebase makes it easy for my app to add to the database.
Ive created a UIViewController for entering the post and an accompanying .xib file to contain the layout of it. I didnt do the layout for this within a storyboard. For reasons why and to see how to set up a UIViewController with an .xib, see here.
So assume that you have an interface of some kind that allows the user to enter text for the post body, which in my case includes a post title and post body text. My social network is location based, so I'm using iOS location services to get the current GPS position of the users device so I can save lat and long data to this post too. Incidentally, for simplicity - my JSON upload consists almost entirely of text elements.
So back to the point - what I basically want to do is to add this post to my posts array in the database. First thing I need is a unique id for the post, which will be its key in the JSON array in the database. To do this, i use a method called 'childByAutoId'.
So the ref property I made simply refers to the root of the database. My posts live in an array with key "posts" which is why its on the second line there, and following that I get my unique key for a new element in that array. Now I simply need to upload some JSON to that ref, containing the properties I want to store.
To do this, first Ive got my own local class which will represent the post data. In my case its called GZPost. It has properties like this :
I initialize it with a custom initializer that takes the unique key I have just made, which will be the posts 'id' locally (and its key in the JSON structure in Firebase).
Within that class I have a function called 'updateDictionary'. All this does is convert my properties into a Dictionary of type [String : Any], which will be uploaded to the Database. The func looks like this :
So as you can see, it is simply taking its own properties and turning them into a Dictionary of the type that I can upload to Firebase. One of the beauties of Firebase is that - that dictionary can contain any properties I like, and I never need to go into Firebase itself and change anything at the backend. Whatever I send is stored automatically, which frees up so much time for the developer. At any point I can just add an array or a property wherever I choose in the structure of the Dictionary that I send.
Point to note here also - you can see it has an 'Author' property. This contains the id of the user who made the post, and in the case where its me making the post, thats obviously my id. I have a local class called GZUser which contains my id so I can get that from there, or I can also query Firebase for the current users' Id, like this :
The final method I have on this GZPost class is called 'referencePathString'. All this does is return the path to this item in Firebase. It takes the local 'postId' property and adds it to 'posts' which is the key for the posts array in the database. It looks like this :
So thats all the parts of the puzzle - now I just put them all together to upload my new post to Firebase. Using those methods specified above, the upload code looks like this :
To test this out, you can go to your Firebase Console, click on Database on the left, and just watch the new post data appear in your posts Array online.
Next I'll be outlining how to observe an array reference such as the posts one there and displaying the data in a UITableView.
As the database structure shows, my posts will also live in a 'posts' array at the root of the database structure. Within that array, each post will have a unique key, which by the way consists mainly of a timestamp, which allows them to be easily sorted by date. Within each key will live the post data, which consists most importantly of : id of the user that made the post, the date + time it was made, the text body of the post, longitude / latitude at which the post was made, and other things. The gist of this blog post here is to simply describe how Firebase makes it easy for my app to add to the database.
Ive created a UIViewController for entering the post and an accompanying .xib file to contain the layout of it. I didnt do the layout for this within a storyboard. For reasons why and to see how to set up a UIViewController with an .xib, see here.
So assume that you have an interface of some kind that allows the user to enter text for the post body, which in my case includes a post title and post body text. My social network is location based, so I'm using iOS location services to get the current GPS position of the users device so I can save lat and long data to this post too. Incidentally, for simplicity - my JSON upload consists almost entirely of text elements.
So back to the point - what I basically want to do is to add this post to my posts array in the database. First thing I need is a unique id for the post, which will be its key in the JSON array in the database. To do this, i use a method called 'childByAutoId'.
So the ref property I made simply refers to the root of the database. My posts live in an array with key "posts" which is why its on the second line there, and following that I get my unique key for a new element in that array. Now I simply need to upload some JSON to that ref, containing the properties I want to store.
To do this, first Ive got my own local class which will represent the post data. In my case its called GZPost. It has properties like this :
I initialize it with a custom initializer that takes the unique key I have just made, which will be the posts 'id' locally (and its key in the JSON structure in Firebase).
Within that class I have a function called 'updateDictionary'. All this does is convert my properties into a Dictionary of type [String : Any], which will be uploaded to the Database. The func looks like this :
So as you can see, it is simply taking its own properties and turning them into a Dictionary of the type that I can upload to Firebase. One of the beauties of Firebase is that - that dictionary can contain any properties I like, and I never need to go into Firebase itself and change anything at the backend. Whatever I send is stored automatically, which frees up so much time for the developer. At any point I can just add an array or a property wherever I choose in the structure of the Dictionary that I send.
Point to note here also - you can see it has an 'Author' property. This contains the id of the user who made the post, and in the case where its me making the post, thats obviously my id. I have a local class called GZUser which contains my id so I can get that from there, or I can also query Firebase for the current users' Id, like this :
The final method I have on this GZPost class is called 'referencePathString'. All this does is return the path to this item in Firebase. It takes the local 'postId' property and adds it to 'posts' which is the key for the posts array in the database. It looks like this :
So thats all the parts of the puzzle - now I just put them all together to upload my new post to Firebase. Using those methods specified above, the upload code looks like this :
To test this out, you can go to your Firebase Console, click on Database on the left, and just watch the new post data appear in your posts Array online.
Next I'll be outlining how to observe an array reference such as the posts one there and displaying the data in a UITableView.
Wednesday, March 7, 2018
Social Network with Firebase 1 - Signing In, Creating User
Im currently tasked with building a social network using Firebase as a back-end. Im pleasantly surprised by how usable and generally liberating Firebase is, allowing a solitary developer like me to create both front and back-ends without too much hassle. I dont want to create a full tutorial here on creating a social network with Firebase. But I can create an overview of the process with some code snippets here for anyone else looking to undertake something like this.
First of all Ive sketched out (literally) my database structure for the Firebase back-end. Ive copied what they did with their FriendlyPix app, which is an excellent example, but lacking some more thorough documentation. Ive already written an explanation of that database structure in an article here. Thats very close to the structure Ive got with a few additions for properties they dont have.
Ive also outlined how they do Auth in that article. For my app Ive set up Firebase via Cocoapods, and Ive done the basic set up on the Firebase Console.
For authorization, I have a RegisterViewController with UITextField's for username, password (plus re-type), and also first name and surname fields. When my user clicks to register, this happens :
Four fairly simple steps there. First call to create the user using FirebaseAuth. If that works, we get a User (Firebase type) object back, which contains amongst other things, our 'uid', which is the unique identifier for that user.
Second, we do a change request to add the displayName property of that user into Firebase Auth. Third, we go to our Firebase database and we populate a whole new entity into the People database. The key for this entity, is the unique uid that we got back from registering the user. The values are a URL to the photo (more in future on this), the full name (made from whatever the user entered in the first name / surname fields), and a search index with a reversed version of the name for quicker searching.
Lastly, that user is signed in, which will give the app access to the data it needs to see. This signing in is being observed in other locations in the app, like this :
So the signing in that happened at the end of the registration code, once complete, causes this listener to fire, as it notice the sign-in success. The Firebase User object is passed in here so again we can use the 'uid' property. At this point I set up an observer to go and retrieve that data that I just wrote to the database in the People array.
So in the method above, I firstly remove any previous observers that I may have created, in an array of type [DatabaseQuery]. This ensures Im not making Firebase do more work than it needs to, and slowing the app down in the process. Then I create an observer to have a look at the database and let me know first what is in the database at the specified location (which is - in the people array, in a key which is my users uid), and to then update me should whatever in there change.
The result of that call returns 'snapshot', which is another Firebase type. Within that object is a property called 'value', which can contain an NSDictionary, NSArray, NSNumber or NSString. To handle the contents there, I have an initialiser for my local user object, which is called GZUser.
So quite simply, I have just taken back the data that I wrote during the registration process, that Firebase stored in its JSON format. I have taken that back as a dictionary and I have initialised my local GZUser object with that dictionary (that was packaged in a Firebase snapshot).
Finally that data is used to populate the displayName field in my UI. But that observer will carry on watching the space for changes, until I remove that observer. Next up I have a look at uploading data to the Firebase Database, in this post here.
First of all Ive sketched out (literally) my database structure for the Firebase back-end. Ive copied what they did with their FriendlyPix app, which is an excellent example, but lacking some more thorough documentation. Ive already written an explanation of that database structure in an article here. Thats very close to the structure Ive got with a few additions for properties they dont have.
Ive also outlined how they do Auth in that article. For my app Ive set up Firebase via Cocoapods, and Ive done the basic set up on the Firebase Console.
For authorization, I have a RegisterViewController with UITextField's for username, password (plus re-type), and also first name and surname fields. When my user clicks to register, this happens :
Four fairly simple steps there. First call to create the user using FirebaseAuth. If that works, we get a User (Firebase type) object back, which contains amongst other things, our 'uid', which is the unique identifier for that user.
Second, we do a change request to add the displayName property of that user into Firebase Auth. Third, we go to our Firebase database and we populate a whole new entity into the People database. The key for this entity, is the unique uid that we got back from registering the user. The values are a URL to the photo (more in future on this), the full name (made from whatever the user entered in the first name / surname fields), and a search index with a reversed version of the name for quicker searching.
Lastly, that user is signed in, which will give the app access to the data it needs to see. This signing in is being observed in other locations in the app, like this :
So the signing in that happened at the end of the registration code, once complete, causes this listener to fire, as it notice the sign-in success. The Firebase User object is passed in here so again we can use the 'uid' property. At this point I set up an observer to go and retrieve that data that I just wrote to the database in the People array.
So in the method above, I firstly remove any previous observers that I may have created, in an array of type [DatabaseQuery]. This ensures Im not making Firebase do more work than it needs to, and slowing the app down in the process. Then I create an observer to have a look at the database and let me know first what is in the database at the specified location (which is - in the people array, in a key which is my users uid), and to then update me should whatever in there change.
The result of that call returns 'snapshot', which is another Firebase type. Within that object is a property called 'value', which can contain an NSDictionary, NSArray, NSNumber or NSString. To handle the contents there, I have an initialiser for my local user object, which is called GZUser.
So quite simply, I have just taken back the data that I wrote during the registration process, that Firebase stored in its JSON format. I have taken that back as a dictionary and I have initialised my local GZUser object with that dictionary (that was packaged in a Firebase snapshot).
Finally that data is used to populate the displayName field in my UI. But that observer will carry on watching the space for changes, until I remove that observer. Next up I have a look at uploading data to the Firebase Database, in this post here.
Wednesday, February 21, 2018
Firebase 'Friendly Pix' : Data structure, and Auth
Googles Firebase team have created a demo social media app called FriendlyPix, to give some examples of best practise when creating a social network with a Firebase back-end. Its like a simple Instagram, allowing users to upload photos, comment, etc.
You can get the original iOS version from here. At the time of writing, theres a design document, which gives a general overview of data structures and how things work in a superficial way, which is available here. I haven't found a more detailed discussion of the iOS version though, so thats what I'm aiming to do with some articles here.
Firebase structures data in the database as JSON. You can examine it at any time on the Firebase console and have a look at what is where, which makes it nice and easy to use. So at the top level the data is sorted into these 6 arrays : People, Posts, Comments, Feed, Followers and Likes. Below that, People and Posts then have a unique key to identify each user or post, and within each lie the details. Comments, Feed, Followers and Likes are structured first with the id of the user that each relate to - so for e.g. the first set of keys in the comments array is an array of author_id, which is a copy of the user_id of the user that created the comment, followed by the post_id that the user commented on, and then the comment details. Similarly the Likes array first has an array of keys of the user that created the like, then the post_id that was liked.
Within a single user_id element, there are full_name and reversed_full_name properties, which is done to help speed up searches for users.
Firebase has a whole pod dedicated to 'Auth', and a section on the Firebase console. In general everything within Auth is dedicated to helping us recognise who a user is, and give them access to data, or get them 'logged-in'.
For a simple start in getting users authorised for your app, you can enable email/password authorisation in the Auth area of the Firebase console. Then the following code within an iOS application would create a new user, and if successful, the user will be logged in (via the code in the completion closure).
The default setting on the Firebase database is that without this authorisation, a user cannot access data. So on a simple level, this code has also unlocked the database for the app to query from now on (unless you change settings and open it up for unauthorised users).
Having authorised the user, the app goes on to ensure that the user details have been added into the People array in the database. The following code will add a new user into that People array in the main database, if it doesn't already exist, or update it if its there already.
As I mentioned above, the People array within the database has user_id's as keys at the top level, which is why the database reference above looks like : .reference(withPath: "people/\(user.uid)"). Beyond that you can see the dictionary structure that will populate that user in the database - profile picture URL, full name, etc.
At no point do you need to go into the Firebase console and add properties to data models or anything like that. The code snippet above has directly created the JSON in the database, or it has updated the entry if that key (the user_id) existed already. This is one of the reasons why Firebase is so nice and easy to use - you just send dictionary structures to the database with key values that you have decided upon, and the data is created and saved away for easy extraction later.
I'll be writing a few more articles on this app shortly. I'll be looking at how the Feed page organizes batched downloading of posts next. All feedback or questions welcome of course.
You can get the original iOS version from here. At the time of writing, theres a design document, which gives a general overview of data structures and how things work in a superficial way, which is available here. I haven't found a more detailed discussion of the iOS version though, so thats what I'm aiming to do with some articles here.
Data Structure
Its handy to have a look at how they have structured the data, considering their social network (like most) has users, posts, comments, likes, followers, and a personal 'news' feed. Ive sometimes wondered on the various merits of alternatives ways of structuring that data. The solution for FriendlyPix is described in the design doc, but heres an overview:- (root)
- people
- user_id
- searchIndex (full_name, reversed_full_name)
- posts (ids..)
- etc
- posts
- post_id
- author (full_name, pic, uid)
- full_storage_uri
- etc.
- comments
- author_id
- post_id
- author :
- text :
- timestamp : etc.
- feed
- user_id
- post_id : true
- followers
- followee user_id
- follower user_id
- likes
- user_id
- post_id : timestamp
Firebase structures data in the database as JSON. You can examine it at any time on the Firebase console and have a look at what is where, which makes it nice and easy to use. So at the top level the data is sorted into these 6 arrays : People, Posts, Comments, Feed, Followers and Likes. Below that, People and Posts then have a unique key to identify each user or post, and within each lie the details. Comments, Feed, Followers and Likes are structured first with the id of the user that each relate to - so for e.g. the first set of keys in the comments array is an array of author_id, which is a copy of the user_id of the user that created the comment, followed by the post_id that the user commented on, and then the comment details. Similarly the Likes array first has an array of keys of the user that created the like, then the post_id that was liked.
Within a single user_id element, there are full_name and reversed_full_name properties, which is done to help speed up searches for users.
Firebase Auth
For a simple start in getting users authorised for your app, you can enable email/password authorisation in the Auth area of the Firebase console. Then the following code within an iOS application would create a new user, and if successful, the user will be logged in (via the code in the completion closure).
The default setting on the Firebase database is that without this authorisation, a user cannot access data. So on a simple level, this code has also unlocked the database for the app to query from now on (unless you change settings and open it up for unauthorised users).
Having authorised the user, the app goes on to ensure that the user details have been added into the People array in the database. The following code will add a new user into that People array in the main database, if it doesn't already exist, or update it if its there already.
As I mentioned above, the People array within the database has user_id's as keys at the top level, which is why the database reference above looks like : .reference(withPath: "people/\(user.uid)"). Beyond that you can see the dictionary structure that will populate that user in the database - profile picture URL, full name, etc.
At no point do you need to go into the Firebase console and add properties to data models or anything like that. The code snippet above has directly created the JSON in the database, or it has updated the entry if that key (the user_id) existed already. This is one of the reasons why Firebase is so nice and easy to use - you just send dictionary structures to the database with key values that you have decided upon, and the data is created and saved away for easy extraction later.
I'll be writing a few more articles on this app shortly. I'll be looking at how the Feed page organizes batched downloading of posts next. All feedback or questions welcome of course.
Tuesday, February 20, 2018
UIView broken .. by a print statement?
Is your print statement causing your app to crash? Well thats what happened to me this morning. Totally unexpected but undoubtedly true. Heres what happened.
Ive have a large UIView in the app Im working on right now, with loads of IBOutlets. There are two instances of charts, various labels, textfields, etc. All are linked to my UIView via explicitly unwrapped optionals, as Apple advise, like this :
Now at the same time as this view is being loaded, I have downloads happening in the background. Large downloads, of XML, as it happens. When each download finishes, I have a parser that is taking that XML apart and turning it into useful local objects. Eg :
You can see the start of the parser class there. Those commented print statements are what is breaking my UIView. How you ask? Well Im attempting to print out what are large chunks of XML. My UIView loading work must happen on the main thread, and that main thread is being held up by the printing process, which takes several seconds.
Meanwhile, my UIView is being asked to render the chart. My IBOutlets are explicitly unwrapped optionals, and Swift fans will know that if you try and use an implicitly unwrapped optional when it is nil, the result is in instant crash. And that is what happened in my case. As Curtis Herbert points out on his blog, doing as Apple suggests and always using implicitly unwrapped optionals for UIView IBOutlets is not always a good idea.
My immediate solution? Removing the print statements got rid of the crash, as the main thread had plenty of time to render the view before using my unwrapped properties. But in general this doesnt feel very safe - so the real fix here would be to use optionals, and unwrap as you go. Its more work, but its safer.
Ive have a large UIView in the app Im working on right now, with loads of IBOutlets. There are two instances of charts, various labels, textfields, etc. All are linked to my UIView via explicitly unwrapped optionals, as Apple advise, like this :
@IBOutlet weak var detailBox: UIView!
Now at the same time as this view is being loaded, I have downloads happening in the background. Large downloads, of XML, as it happens. When each download finishes, I have a parser that is taking that XML apart and turning it into useful local objects. Eg :
convenience required init(xml: AEXMLDocument, patient: Patient?, externalContext: NSManagedObjectContext?) {
self.init()
//print("------- get EOBS info by location -------")
//print("XML : \(xml.xml)")
self.isValid = true
You can see the start of the parser class there. Those commented print statements are what is breaking my UIView. How you ask? Well Im attempting to print out what are large chunks of XML. My UIView loading work must happen on the main thread, and that main thread is being held up by the printing process, which takes several seconds.
Meanwhile, my UIView is being asked to render the chart. My IBOutlets are explicitly unwrapped optionals, and Swift fans will know that if you try and use an implicitly unwrapped optional when it is nil, the result is in instant crash. And that is what happened in my case. As Curtis Herbert points out on his blog, doing as Apple suggests and always using implicitly unwrapped optionals for UIView IBOutlets is not always a good idea.
My immediate solution? Removing the print statements got rid of the crash, as the main thread had plenty of time to render the view before using my unwrapped properties. But in general this doesnt feel very safe - so the real fix here would be to use optionals, and unwrap as you go. Its more work, but its safer.
Wednesday, February 14, 2018
Loading custom UIViewController from .xib file
Occasionally I like to create UIViewController files within their own .xib files, rather than within a storyboard. One example of when I favour this approach is for when I have a UIViewController that can appear above multiple different VC's within the app, such as a login screen for example. In this case its not really part of the 'storyboard' flow of an app, linked by a segue. A big advantage too is that I can create and develop my xib and be sure of no conflicts with other developers on a team, which is not the case with storyboards. You can of course help to simplify storyboards and avoid conflicts by having multiple storyboards dedicated to different areas of an app. But in the case of a login screen, theres no flow there necessarily - just a single screen I need to show anywhere. So a single .xib UIViewController is a nice easy way to go. Heres how its set up.
First, make a new UserInterface file, of type View.
Then make a new .swift file to contain the UIViewController class you are making, and add the basic methods :
Now return to interface builder to edit the new .xib file you made. First, go to Files Owner and set the class as your new view controller class (in my case, TestViewController). Tip - if you start typing the class and it autocompletes, you know you have it ready to go. Its possible to enter the class name here even if the class doesnt exist, and stuff wont work later.
Now, very important bit : set the view property on Files Owner to be the view in your xib. You do this by Ctrl-dragging from Files Owner onto the view then selecting view from the little menu that appears :
From here, you have properly set up the class, and the xib. Now you are able to do things like drag properties from your view into the UIViewController class swift file.
To instantiate this in code :
Important to note there : your new UIViewController subclass name (in my case TestViewController) is used both in the name of the call, and as a string in the nib name field. In that code I present simply by presenting on top of the current view controller. You can of course do many different things there, for example, pushing on to the current UINavigationController.
One final note - if you try and call to instantiate a UIViewController like this from within the viewDidLoad method of another view controller, it wont work. It is unable to present the view at this stage. You need to go beyond the viewDidLoad phase into viewWillAppear for eg, at which point and beyond, you can do it. All comments welcome of course.
First, make a new UserInterface file, of type View.
Then make a new .swift file to contain the UIViewController class you are making, and add the basic methods :
Now return to interface builder to edit the new .xib file you made. First, go to Files Owner and set the class as your new view controller class (in my case, TestViewController). Tip - if you start typing the class and it autocompletes, you know you have it ready to go. Its possible to enter the class name here even if the class doesnt exist, and stuff wont work later.
Now, very important bit : set the view property on Files Owner to be the view in your xib. You do this by Ctrl-dragging from Files Owner onto the view then selecting view from the little menu that appears :
From here, you have properly set up the class, and the xib. Now you are able to do things like drag properties from your view into the UIViewController class swift file.
To instantiate this in code :
Important to note there : your new UIViewController subclass name (in my case TestViewController) is used both in the name of the call, and as a string in the nib name field. In that code I present simply by presenting on top of the current view controller. You can of course do many different things there, for example, pushing on to the current UINavigationController.
One final note - if you try and call to instantiate a UIViewController like this from within the viewDidLoad method of another view controller, it wont work. It is unable to present the view at this stage. You need to go beyond the viewDidLoad phase into viewWillAppear for eg, at which point and beyond, you can do it. All comments welcome of course.
Subscribe to:
Posts (Atom)
Social Network with Firebase 3 - Infinitely scrolling UITableView of Firebase Items
Ive already talked about the basics of a Firebase social network project , and adding posts in two other articles. Here I want to explain ...
-
Ive already outlined my database structure for my Firebase based social network. Ive done the auth for new users , and Ive added each new ...
-
Im doing an awful lot of work at the moment with AVMutableVideoCompositions, and AVAssetExportSessions. When exporting a video using the la...







