Swift Tutorial: Build a Facebook Album Browser

Welcome to our third Swift tutorial. I hope that, by now, you have a good grasp at using String, Collection and other basic operations in Swift. Today, we’ll take it one step further and learn to develop our first iOS app (Facebook Album Browser) using Swift. What we hope to have learnt by the end of this particular tutorial is basically how to develop an application as shown below. In order to develop this application, we need to know a few things. For example: How to work in Facebook SDK for iOS How to work with Objective C Framework/Library in Swift […]

Welcome to our third Swift tutorial. I hope that, by now, you have a good grasp at using String, Collection and other basic operations in Swift. Today, we’ll take it one step further and learn to develop our first iOS app (Facebook Album Browser) using Swift.

What we hope to have learnt by the end of this particular tutorial is basically how to develop an application as shown below.

In order to develop this application, we need to know a few things. For example:

  • How to work in Facebook SDK for iOS
  • How to work with Objective C Framework/Library in Swift
  • How to work with TableView in Swift (please refer to my first article on TableView in Swift)
  • Notification API in iOS (How to use Notification system in Swift. The notification system is like a “Publish/Subscribe” messaging system.)
  • Some basic ideas about Storyboard and MVC.

That is more or less about it. Clearly, the Facebook Album Browser is not the most fancy or complex stuff you can make with Swift. The reason why we’ll go through this tutorial is that it should provide a strong foundation that will eventually help us to write our own Facebook related application in iOS.

So without further ado, let’s jump directly into our main focus- the app development. We’ll go slow and easy, step by step.

Step 1:

Our first priority is to create a “Single View Application” and select Swift as the desired language. So now we should have a default Storyboard and a “ViewController” with a single view. Now, let’s open our Storyboard. Let’s keep it simple and go for a design like this one:

I’ll admit it- this is not a very good UI since I’m not a great designer, but this should do for now. Here we have two buttons; one is the “Login to Facebook” button and the other being the “Fetch Albums” button.

So let’s break down the functionality of the page we just designed. First, one has to login into their respective accounts, after which the option of fetching albums from your profile becomes open. Doing this will load the album underneath the “TableView”. Very straightforward so far.

When a user clicks the “Login to Facebook” button, it will use the Facebook SDK and will make a login to facebook on behalf of the user. After a successful login, the user’s profile picture will be loaded and the “Login to Facebook” button will be replaced by a “Logout” label. But before anything else, let’s see how we can use “Facebook SDK” inside iOS.

Let us first download Facebook SDK from facebook developer site, which has been developed to work with “Objective C”. However, please do realize that Swift and Objective C are different languages. Hence, we have to follow certain procedures in order to use the framework.

After having downloaded the framework, drag it onto your project as shown below:

app_structure

Since this was built for working with Objective C, there is a simple little trick that we need to do to get around it-  right click on the FBApp folder and select New File; after this, proceed to select an Objective C File. This should prompt a notification as to whether we want to create a Bridge Header, upon which we must select yes.

So why is XCode doing this? Since this project is built to be used on Swift, any Objective C file can also be used in it; given you just add a “Bridge Header”.

So far so good? Now we simply delete the newly created Objective C file, keeping the “Bridge Header” file as it is (the image above may help). We will see a “FBApp-Bridge-Header.h”. Now let’s open this file and write down the code below:

#import <FacebookSDK/FacebookSDK.h>

This should automatically expose all the Facebook related class to your Swift.

Step 2:

To use the Facebook SDK, we also have to put some information in the “application plist” file. Please visit the Facebook developer site and see how to use this SDK. It is very simple and shouldn’t take more than 15 minutes of your time to understand how it works.

Step 3:

As we will use the Facebook SDK, it’s a good practice to keep all the facebook SDK related functionalities under one class. This is a kind of domain specific encapsulation. I prefer to create a helper class for this called “FBHelper”. This helper class is responsible for doing all the facebook related activities inside your app such as “Login”, “Logout”, “Share”, “Post”, etc. See the code below:

//<br />
//  FBHelper.swift<br />
//  FBApp<br />
//<br />
//  Created by Md. Arifuzzaman Arif on 7/4/14.<br />
//  Copyright (c) 2014 Md. Arifuzzaman Arif. All rights reserved.<br />
//<br />
import Foundation</p>
<p>class FBHelper{<br />
var fbSession:FBSession?;<br />
init(){<br />
self.fbSession = nil;<br />
}</p>
<p>func fbAlbumRequestHandler(connection:FBRequestConnection!, result:AnyObject!, error:NSError!){</p>
<p>if let gotError = error{<br />
println(gotError.description);<br />
}<br />
else{<br />
let graphData = result.valueForKey("data") as Array;<br />
var albums:AlbumModel[] =  AlbumModel[]();<br />
for obj:FBGraphObject in graphData{<br />
let desc = obj.description;<br />
println(desc);<br />
let name = obj.valueForKey("name") as String;<br />
println(name);<br />
if(name == "ETC"){<br />
let test="";<br />
}<br />
let id = obj.valueForKey("id") as String;<br />
var cover = "";<br />
if let existsCoverPhoto : AnyObject = obj.valueForKey("cover_photo"){<br />
let coverLink = existsCoverPhoto  as String;<br />
cover = "/(coverLink)/photos";<br />
}</p>
<p>//println(coverLink);<br />
let link = "/(id)/photos";</p>
<p>let model = AlbumModel(name: name, link: link, cover:cover);<br />
albums.append(model);</p>
<p>}<br />
NSNotificationCenter.defaultCenter()?.postNotificationName("albumNotification", object: nil, userInfo: ["data":albums]);<br />
}<br />
}</p>
<p>func fetchPhoto(link:String){<br />
let fbRequest = FBRequest.requestForMe();<br />
fbRequest.graphPath = link;<br />
fbRequest.startWithCompletionHandler(fetchPhotosHandler);<br />
}</p>
<p>func fetchPhotosHandler(connection:FBRequestConnection!, result:AnyObject!, error:NSError!){<br />
if let gotError = error{</p>
<p>}<br />
else{<br />
var pictures:UIImage[] = UIImage[]();<br />
let graphData = result.valueForKey("data") as Array;<br />
var albums:AlbumModel[] =  AlbumModel[]();<br />
for obj:FBGraphObject in graphData{<br />
println(obj.description);<br />
let pictureURL = obj.valueForKey("picture") as String;<br />
let url = NSURL(string: pictureURL);<br />
let picData = NSData(contentsOfURL: url);<br />
let img = UIImage(data: picData);<br />
pictures.append(img);<br />
}</p>
<p>NSNotificationCenter.defaultCenter().postNotificationName("photoNotification", object: nil, userInfo: ["photos":pictures]);<br />
}<br />
}</p>
<p>func fetchAlbum(){</p>
<p>let request =  FBRequest.requestForMe();<br />
request.graphPath = "me/albums";</p>
<p>request.startWithCompletionHandler(fbAlbumRequestHandler);<br />
}</p>
<p>func logout(){<br />
self.fbSession?.closeAndClearTokenInformation();<br />
self.fbSession?.close();<br />
}</p>
<p>func login(){<br />
let activeSession = FBSession.activeSession();<br />
let fbsessionState = activeSession.state;<br />
if(fbsessionState.value != FBSessionStateOpen.value &amp;&amp; fbsessionState.value != FBSessionStateOpenTokenExtended.value){</p>
<p>let permission = ["basic_info", "email","user_photos","friends_photos"];</p>
<p>FBSession.openActiveSessionWithPublishPermissions(permission, defaultAudience: FBSessionDefaultAudienceFriends, allowLoginUI: true, completionHandler: self.fbHandler);</p>
<p>}<br />
}</p>
<p>func fbHandler(session:FBSession!, state:FBSessionState, error:NSError!){<br />
if let gotError = error{<br />
//got error<br />
}<br />
else{</p>
<p>self.fbSession = session;</p>
<p>FBRequest.requestForMe()?.startWithCompletionHandler(self.fbRequestCompletionHandler);<br />
}<br />
}</p>
<p>func fbRequestCompletionHandler(connection:FBRequestConnection!, result:AnyObject!, error:NSError!){<br />
if let gotError = error{<br />
//got error<br />
}<br />
else{<br />
//let resultDict = result as Dictionary;<br />
//let email = result["email"];<br />
//let firstName = result["first_name"];</p>
<p>let email : AnyObject = result.valueForKey("email");<br />
let firstName:AnyObject = result.valueForKey("first_name");<br />
let userFBID:AnyObject = result.valueForKey("id");<br />
let userImageURL = "https://graph.facebook.com/(userFBID)/picture?type=small";</p>
<p>let url = NSURL.URLWithString(userImageURL);</p>
<p>let imageData = NSData(contentsOfURL: url);</p>
<p>let image = UIImage(data: imageData);</p>
<p>println("userFBID: (userFBID) Email (email) n firstName:(firstName) n image: (image)");</p>
<p>var userModel = User(email: email, name: firstName, image: image);</p>
<p>NSNotificationCenter.defaultCenter().postNotificationName("PostData", object: userModel, userInfo: nil);</p>
<p>}<br />
}<br />
}

We also need couples of Model class to hold the facebook information:

  1. FBUserModel.swift
  2. AlbumModel.swift

See the code below:

User

//<br />
//  FBUserModel.swift<br />
//  FBApp<br />
//<br />
//  Created by Md. Arifuzzaman Arif on 7/4/14.<br />
//  Copyright (c) 2014 Md. Arifuzzaman Arif. All rights reserved.<br />
//<br />
import Foundation<br />
class User{<br />
let email:AnyObject = "";<br />
let name:AnyObject = "";<br />
let image:UIImage;</p>
<p>init(email:AnyObject, name:AnyObject, image:UIImage){<br />
self.image = image<br />
self.name = name;<br />
self.email=email;<br />
}<br />
}

AlbumModel

//<br />
//  AlbumModel.swift<br />
//  FBApp<br />
//<br />
//  Created by Md. Arifuzzaman Arif on 7/5/14.<br />
//  Copyright (c) 2014 Md. Arifuzzaman Arif. All rights reserved.<br />
//<br />
import Foundation<br />
class AlbumModel{<br />
let name = "";<br />
let link = "";<br />
let cover = "";<br />
init(name:String, link:String, cover:String){<br />
self.name = name;<br />
self.link = link;<br />
self.cover = cover;<br />
}<br />
}

All we need now is to call the FBHelper class and extract the information. Please see the “ViewController.swift”

//<br />
//  ViewController.swift<br />
//  FBApp<br />
//<br />
//  Created by Md. Arifuzzaman Arif on 7/4/14.<br />
//  Copyright (c) 2014 Md. Arifuzzaman Arif. All rights reserved.<br />
//<br />
import UIKit</p>
<p>class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {</p>
<p>let fbHelper = FBHelper();<br />
var sources:AlbumModel[] = AlbumModel[]();<br />
var currentAlbumModel = AlbumModel(name: "", link: "", cover:"");<br />
var destController:AlbumViewController?;</p>
<p>@IBOutlet var albumTable : UITableView<br />
@IBOutlet var imgProfile : UIImageView</p>
<p>@IBAction func fetchDataAction(sender : AnyObject) {<br />
fbHelper.fetchAlbum();<br />
}<br />
@IBOutlet var btnLoginLogout : UIButton</p>
<p>@IBAction func facebookLogoutAction(sender : AnyObject) {<br />
self.fbHelper.logout();<br />
self.btnLoginLogout.titleLabel.text = "Login to Facebook";<br />
}<br />
@IBAction func facebookLoginAction(sender : AnyObject) {</p>
<p>if(self.btnLoginLogout.titleLabel.text == "Login to Facebook"){<br />
fbHelper.login();<br />
}<br />
else{<br />
fbHelper.logout();<br />
}</p>
<p>}</p>
<p>func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!)<br />
{<br />
self.currentAlbumModel = self.sources[indexPath.row];<br />
if(self.destController){<br />
self.destController!.albumModel = self.currentAlbumModel;<br />
self.destController!.fbHelper = self.fbHelper;<br />
self.destController!.executePhoto();<br />
}</p>
<p>}</p>
<p>func selectRowAtIndexPath(indexPath: NSIndexPath!, animated: Bool, scrollPosition: UITableViewScrollPosition){</p>
<p>}</p>
<p>func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -&gt; UITableViewCell!{<br />
var cell:UITableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell1") as UITableViewCell;<br />
let data = self.sources[indexPath.row];<br />
cell.textLabel.text = data.name;<br />
cell.detailTextLabel.text = data.link;<br />
if(data.cover != ""){<br />
let coverPhotoURL = NSURL(string: data.cover);<br />
let coverPhotoData = NSData(contentsOfURL: coverPhotoURL);</p>
<p>cell.imageView.image = UIImage(data: coverPhotoData);</p>
<p>}<br />
return cell;<br />
}</p>
<p>func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -&gt; Int{<br />
return self.sources.count;<br />
}</p>
<p>func executeAlbum(notification:NSNotification){<br />
let data = notification.userInfo.objectForKey("data") as AlbumModel[];<br />
self.sources = data;<br />
self.albumTable.reloadData();<br />
}</p>
<p>func executeHandle(notification:NSNotification){<br />
let userData = notification.object as User;</p>
<p>let name = userData.name as String;<br />
let email = userData.email as String;<br />
//lblName.text = name;<br />
//lblEmail.text = email;<br />
imgProfile.image = userData.image;<br />
self.btnLoginLogout.titleLabel.text = "Logout";</p>
<p>}</p>
<p>override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {<br />
if(segue.identifier == "photoSegue"){<br />
let destinitionController = segue.destinationViewController as AlbumViewController;<br />
destinitionController.albumModel = self.currentAlbumModel;<br />
self.destController = destinitionController;<br />
}<br />
}</p>
<p>override func viewDidDisappear(animated: Bool) {<br />
NSNotificationCenter.defaultCenter().removeObserver(self, name: "PostData", object: nil);<br />
NSNotificationCenter.defaultCenter().removeObserver(self, name: "albumNotification", object: nil);<br />
}</p>
<p>override func viewDidLoad() {</p>
<p>NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("executeHandle:"), name: "PostData", object: nil);<br />
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("executeAlbum:"), name: "albumNotification", object: nil);</p>
<p>super.viewDidLoad()<br />
// Do any additional setup after loading the view, typically from a nib.<br />
}</p>
<p>override func didReceiveMemoryWarning() {<br />
super.didReceiveMemoryWarning()<br />
// Dispose of any resources that can be recreated.<br />
}</p>
<p>}

This will load the album and other things on the main screen.

Step 4:

To show photos of each album, we would need another screen with its own controller. In this case, I have used “AlbumViewController” as a sub class of “UITableViewController”.

Just looking at this view, you’d know that its ready made. You just need to drag a “UITableViewController” in the storyboard to achieve this. Please refer back to my first tutorial if you want to know more details about table view.

View controller code of this view is given below:

//<br />
//  AlbumViewController.swift<br />
//  FBApp<br />
//<br />
//  Created by Md. Arifuzzaman Arif on 7/5/14.<br />
//  Copyright (c) 2014 Md. Arifuzzaman Arif. All rights reserved.<br />
//</p>
<p>import Foundation<br />
class AlbumViewController:UITableViewController{</p>
<p>var albumModel:AlbumModel = AlbumModel(name: "", link: "", cover:"");<br />
var fbHelper:FBHelper?<br />
var sources:UIImage[] = UIImage[]();<br />
var singlePhotoViewController:SinglePhotoViewController?;</p>
<p>func photoExecuted(notification:NSNotification){<br />
let photos = notification.userInfo.valueForKey("photos") as UIImage[];<br />
self.sources = photos;<br />
self.tableView.reloadData();<br />
}</p>
<p>override func tableView(tableView: UITableView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -&gt; CGFloat {<br />
let img = self.sources[indexPath.row];<br />
return img.size.height;<br />
}<br />
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -&gt; UITableViewCell! {</p>
<p>var cell = tableView.dequeueReusableCellWithIdentifier("Cell1") as UITableViewCell;</p>
<p>//cell.textLabel.text = self.sources[indexPath.row] as String;<br />
//let imageView = UIImageView(frame: CGRectMake(2, 2, 100, 100));<br />
//imageView.image = self.sources[indexPath.row];<br />
//cell.contentView.addSubview(imageView);<br />
cell.imageView.image = self.sources[indexPath.row];<br />
return cell;<br />
}</p>
<p>override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -&gt; Int {<br />
return self.sources.count;<br />
}</p>
<p>override func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {<br />
let photo = self.sources[indexPath.row];<br />
if(self.singlePhotoViewController){<br />
self.singlePhotoViewController!.photo = photo;<br />
//self.singlePhotoViewController!.loadPhoto();<br />
}</p>
<p>}</p>
<p>func executePhoto(){<br />
self.fbHelper!.fetchPhoto(self.albumModel.link);<br />
}</p>
<p>func coverPhotoExecuted(notification:NSNotification){<br />
let photos = notification.userInfo.valueForKey("photos") as UIImage[];<br />
let backgroundImage = UIImageView(image: photos[0]);</p>
<p>self.tableView.backgroundView.addSubview(backgroundImage);<br />
}</p>
<p>override func viewDidLoad() {<br />
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("photoExecuted:"), name: "photoNotification", object: nil);<br />
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("coverPhotoExecuted:"), name: "coverPhotoNotification", object: nil);</p>
<p>self.navigationItem.title = self.albumModel.name;</p>
<p>super.viewDidLoad();<br />
}</p>
<p>override func viewDidDisappear(animated: Bool) {<br />
NSNotificationCenter.defaultCenter().removeObserver(self, name: "coverPhotoNotification", object: nil);<br />
NSNotificationCenter.defaultCenter().removeObserver(self, name: "photoNotification", object: nil);<br />
}</p>
<p>override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {<br />
if(segue.identifier == "ShowEachSegue"){<br />
self.singlePhotoViewController = segue.destinationViewController as? SinglePhotoViewController;<br />
}<br />
}</p>
<p>}

 

Assuming we’ve done everything properly so far, we should now have a main view where we can login and browse all the albums. Selecting each album should also show its constituent photos.

I plan to shed some light on “Functions and Closure” soon, as well as discussing “Nullable” type in Swift. Till then, keep your eyes peeled for the next tutorial!


Md. Arifuzzaman Md. Arifuzzaman is a Senior Software Engineer at SELISE rockin’ software. He has extensive experience in diverse facets of C#, .NET, BI development, and mobile app development (Objective-C, Android).

15 Comments

  1. Lorenzo says:

    This is a really nice tutorial for swift i must say, one thing that would be awesome is to have the xcode project.

    Thank you

  2. rahul h kambad says:

    i want the source code of this tutorial . i currently work on it and i face some difficulties so please send on my email id.. its really nice wonderful tutorial

  3. Akhil V Nair says:

    Hi sir,
    i would be greatful if you send me the source code for this app as i am eager to learn swift.Please send me the source code for this
    mail:akhilvn143@gmail.com
    skype:akhilvnair1

  4. Bhoomi says:

    Hello sir,
    This is very helpful tutorial to learn more in swift..
    I am very thankful to you if you can send me code for this tutorial on my mail id..

    Thank you..

  5. zoma says:

    hi , i wanna to learn swift language and i don’t have plan from where can be start.can you pls help me?

  6. Paula says:

    What’s up all, here every person is sharing such experience, so it’s nice
    to read this blog, and I used to pay a visit this blog all the time.

  7. jOnomato says:

    Thanks for this great Swift tutorial mr Arifuzzaman,
    I followed your instructions however got stuck with errors. I will be much obliged if you could send me the xcode.proj. Best regards.

  8. Vegiecat says:

    Thanks for this great Swift tutorial Dear Mr. Arifuzzaman,

    I too followed your instructions, however got stuck with many errors and problems. It would be absolutely wonderful if you can share the xcode.proj. with me and the rest of the commenters. Thank you so Much~!

  9. Jay says:

    Great tutorial!

    For people having error, make sure you go to “Project General Setting” > got to Tab “Build Setting”

    Search for Swift
    Look for “Objective-C Bridging Header” and enter the location of your bridge file “FBApp/Bridge.h”

    This should take care of most of your errors.

    If not feel free to contact me.

    Cheers,
    Jay

  10. silver says:

    Hello,

    Where do you paste the FBHelper, the User, and the AlbumModel code in Step 3? I’ve tried putting the code snippets in .swift files and I get 13 errors. The first error being in the let graphData… line

    I have the bridge.h header file and it’s linked in the Build Settings already.

    Thanks!

  11. Michael says:

    Really good tutorial, the only thing I can’t seem to get to work is the Album covers don’t load. I think the request link is wrong? Does anybody know the correct one?

    Thanks

  12. saray says:

    Awesome tutorials. I currently do on it, get photo picture from Facebook. I follow your tutorials it cause some errors, but after I fix it, it work perfectly, but when I use Facebook SDK v4.x it appears many errors, so could you please update it for the last SDK.

  13. sarayak says:

    Nice tutorial, but it cause a lots of errors when I use FBSDK v 4.x, so could you update this post to the last version of FBSDK. Thanks in advance

  14. Arifuzzaman says:

    Guys,

    I have just updated the sources with latest swift. Please get the source from below:
    https://drive.google.com/file/d/0B8b5lGWblJjCVDNVenpRaHpyU0k/view?usp=sharing

    • Arifuzzaman says:

      Just one thing, change your facebook app name and id from your plist file. I have kept that empty for you.

Leave a Reply

Your email address will not be published. Required fields are marked *

+1
Share
Share
Tweet