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:

//
//  FBHelper.swift
//  FBApp
//
//  Created by Md. Arifuzzaman Arif on 7/4/14.
//  Copyright (c) 2014 Md. Arifuzzaman Arif. All rights reserved.
//
import Foundation

class FBHelper{
var fbSession:FBSession?;
init(){
self.fbSession = nil;
}

func fbAlbumRequestHandler(connection:FBRequestConnection!, result:AnyObject!, error:NSError!){

if let gotError = error{
println(gotError.description);
}
else{
let graphData = result.valueForKey("data") as Array;
var albums:AlbumModel[] =  AlbumModel[]();
for obj:FBGraphObject in graphData{
let desc = obj.description;
println(desc);
let name = obj.valueForKey("name") as String;
println(name);
if(name == "ETC"){
let test="";
}
let id = obj.valueForKey("id") as String;
var cover = "";
if let existsCoverPhoto : AnyObject = obj.valueForKey("cover_photo"){
let coverLink = existsCoverPhoto  as String;
cover = "/(coverLink)/photos";
}

//println(coverLink);
let link = "/(id)/photos";

let model = AlbumModel(name: name, link: link, cover:cover);
albums.append(model);

}
NSNotificationCenter.defaultCenter()?.postNotificationName("albumNotification", object: nil, userInfo: ["data":albums]);
}
}

func fetchPhoto(link:String){
let fbRequest = FBRequest.requestForMe();
fbRequest.graphPath = link;
fbRequest.startWithCompletionHandler(fetchPhotosHandler);
}

func fetchPhotosHandler(connection:FBRequestConnection!, result:AnyObject!, error:NSError!){
if let gotError = error{

}
else{
var pictures:UIImage[] = UIImage[]();
let graphData = result.valueForKey("data") as Array;
var albums:AlbumModel[] =  AlbumModel[]();
for obj:FBGraphObject in graphData{
println(obj.description);
let pictureURL = obj.valueForKey("picture") as String;
let url = NSURL(string: pictureURL);
let picData = NSData(contentsOfURL: url);
let img = UIImage(data: picData);
pictures.append(img);
}

NSNotificationCenter.defaultCenter().postNotificationName("photoNotification", object: nil, userInfo: ["photos":pictures]);
}
}

func fetchAlbum(){

let request =  FBRequest.requestForMe();
request.graphPath = "me/albums";

request.startWithCompletionHandler(fbAlbumRequestHandler);
}

func logout(){
self.fbSession?.closeAndClearTokenInformation();
self.fbSession?.close();
}

func login(){
let activeSession = FBSession.activeSession();
let fbsessionState = activeSession.state;
if(fbsessionState.value != FBSessionStateOpen.value && fbsessionState.value != FBSessionStateOpenTokenExtended.value){

let permission = ["basic_info", "email","user_photos","friends_photos"];

FBSession.openActiveSessionWithPublishPermissions(permission, defaultAudience: FBSessionDefaultAudienceFriends, allowLoginUI: true, completionHandler: self.fbHandler);

}
}

func fbHandler(session:FBSession!, state:FBSessionState, error:NSError!){
if let gotError = error{
//got error
}
else{

self.fbSession = session;

FBRequest.requestForMe()?.startWithCompletionHandler(self.fbRequestCompletionHandler);
}
}

func fbRequestCompletionHandler(connection:FBRequestConnection!, result:AnyObject!, error:NSError!){
if let gotError = error{
//got error
}
else{
//let resultDict = result as Dictionary;
//let email = result["email"];
//let firstName = result["first_name"];

let email : AnyObject = result.valueForKey("email");
let firstName:AnyObject = result.valueForKey("first_name");
let userFBID:AnyObject = result.valueForKey("id");
let userImageURL = "https://graph.facebook.com/(userFBID)/picture?type=small";

let url = NSURL.URLWithString(userImageURL);

let imageData = NSData(contentsOfURL: url);

let image = UIImage(data: imageData);

println("userFBID: (userFBID) Email (email) n firstName:(firstName) n image: (image)");

var userModel = User(email: email, name: firstName, image: image);

NSNotificationCenter.defaultCenter().postNotificationName("PostData", object: userModel, userInfo: nil);

}
}
}

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

  1. FBUserModel.swift
  2. AlbumModel.swift

See the code below:

User

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

init(email:AnyObject, name:AnyObject, image:UIImage){
self.image = image
self.name = name;
self.email=email;
}
}

AlbumModel

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

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

//
//  ViewController.swift
//  FBApp
//
//  Created by Md. Arifuzzaman Arif on 7/4/14.
//  Copyright (c) 2014 Md. Arifuzzaman Arif. All rights reserved.
//
import UIKit

class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {

let fbHelper = FBHelper();
var sources:AlbumModel[] = AlbumModel[]();
var currentAlbumModel = AlbumModel(name: "", link: "", cover:"");
var destController:AlbumViewController?;

@IBOutlet var albumTable : UITableView
@IBOutlet var imgProfile : UIImageView

@IBAction func fetchDataAction(sender : AnyObject) {
fbHelper.fetchAlbum();
}
@IBOutlet var btnLoginLogout : UIButton

@IBAction func facebookLogoutAction(sender : AnyObject) {
self.fbHelper.logout();
self.btnLoginLogout.titleLabel.text = "Login to Facebook";
}
@IBAction func facebookLoginAction(sender : AnyObject) {

if(self.btnLoginLogout.titleLabel.text == "Login to Facebook"){
fbHelper.login();
}
else{
fbHelper.logout();
}

}

func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!)
{
self.currentAlbumModel = self.sources[indexPath.row];
if(self.destController){
self.destController!.albumModel = self.currentAlbumModel;
self.destController!.fbHelper = self.fbHelper;
self.destController!.executePhoto();
}

}

func selectRowAtIndexPath(indexPath: NSIndexPath!, animated: Bool, scrollPosition: UITableViewScrollPosition){

}

func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!{
var cell:UITableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell1") as UITableViewCell;
let data = self.sources[indexPath.row];
cell.textLabel.text = data.name;
cell.detailTextLabel.text = data.link;
if(data.cover != ""){
let coverPhotoURL = NSURL(string: data.cover);
let coverPhotoData = NSData(contentsOfURL: coverPhotoURL);

cell.imageView.image = UIImage(data: coverPhotoData);

}
return cell;
}

func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int{
return self.sources.count;
}

func executeAlbum(notification:NSNotification){
let data = notification.userInfo.objectForKey("data") as AlbumModel[];
self.sources = data;
self.albumTable.reloadData();
}

func executeHandle(notification:NSNotification){
let userData = notification.object as User;

let name = userData.name as String;
let email = userData.email as String;
//lblName.text = name;
//lblEmail.text = email;
imgProfile.image = userData.image;
self.btnLoginLogout.titleLabel.text = "Logout";

}

override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if(segue.identifier == "photoSegue"){
let destinitionController = segue.destinationViewController as AlbumViewController;
destinitionController.albumModel = self.currentAlbumModel;
self.destController = destinitionController;
}
}

override func viewDidDisappear(animated: Bool) {
NSNotificationCenter.defaultCenter().removeObserver(self, name: "PostData", object: nil);
NSNotificationCenter.defaultCenter().removeObserver(self, name: "albumNotification", object: nil);
}

override func viewDidLoad() {

NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("executeHandle:"), name: "PostData", object: nil);
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("executeAlbum:"), name: "albumNotification", object: nil);

super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

}

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:

//
//  AlbumViewController.swift
//  FBApp
//
//  Created by Md. Arifuzzaman Arif on 7/5/14.
//  Copyright (c) 2014 Md. Arifuzzaman Arif. All rights reserved.
//

import Foundation
class AlbumViewController:UITableViewController{

var albumModel:AlbumModel = AlbumModel(name: "", link: "", cover:"");
var fbHelper:FBHelper?
var sources:UIImage[] = UIImage[]();
var singlePhotoViewController:SinglePhotoViewController?;

func photoExecuted(notification:NSNotification){
let photos = notification.userInfo.valueForKey("photos") as UIImage[];
self.sources = photos;
self.tableView.reloadData();
}

override func tableView(tableView: UITableView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
let img = self.sources[indexPath.row];
return img.size.height;
}
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {

var cell = tableView.dequeueReusableCellWithIdentifier("Cell1") as UITableViewCell;

//cell.textLabel.text = self.sources[indexPath.row] as String;
//let imageView = UIImageView(frame: CGRectMake(2, 2, 100, 100));
//imageView.image = self.sources[indexPath.row];
//cell.contentView.addSubview(imageView);
cell.imageView.image = self.sources[indexPath.row];
return cell;
}

override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
return self.sources.count;
}

override func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
let photo = self.sources[indexPath.row];
if(self.singlePhotoViewController){
self.singlePhotoViewController!.photo = photo;
//self.singlePhotoViewController!.loadPhoto();
}

}

func executePhoto(){
self.fbHelper!.fetchPhoto(self.albumModel.link);
}

func coverPhotoExecuted(notification:NSNotification){
let photos = notification.userInfo.valueForKey("photos") as UIImage[];
let backgroundImage = UIImageView(image: photos[0]);

self.tableView.backgroundView.addSubview(backgroundImage);
}

override func viewDidLoad() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("photoExecuted:"), name: "photoNotification", object: nil);
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("coverPhotoExecuted:"), name: "coverPhotoNotification", object: nil);

self.navigationItem.title = self.albumModel.name;

super.viewDidLoad();
}

override func viewDidDisappear(animated: Bool) {
NSNotificationCenter.defaultCenter().removeObserver(self, name: "coverPhotoNotification", object: nil);
NSNotificationCenter.defaultCenter().removeObserver(self, name: "photoNotification", object: nil);
}

override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if(segue.identifier == "ShowEachSegue"){
self.singlePhotoViewController = segue.destinationViewController as? SinglePhotoViewController;
}
}

}

 

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. ArifuzzamanMd. 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
Leave a Reply

Please Login to comment
avatar
  Subscribe  
newest oldest most voted
Notify of
Lorenzo
Guest
Lorenzo

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

rahul h kambad
Guest
rahul h kambad

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

Akhil V Nair
Guest
Akhil V Nair

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

Bhoomi
Guest
Bhoomi

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..

zoma
Guest

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

Paula
Guest
Paula

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.

jOnomato
Guest
jOnomato

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.

Vegiecat
Guest
Vegiecat

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~!

Jay
Guest

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

silver
Guest
silver

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!

Michael
Guest
Michael

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

saray
Guest
saray

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.

sarayak
Guest
sarayak

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

Arifuzzaman
Guest
Arifuzzaman

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
Guest
Arifuzzaman

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