Integrating a commenting module in your App – Part 1

When trying to build a community for your app, you have to go with every possibility to help your users interact easily with each other. Implementing your own, full-fledged commenting / voting system might be a tedious work and it might be worth it to integrate existing solutions into your app. This is what we decided to do with our latest product, Fontify. We have used Disqus on the Fontify website where users can read / post comments for every sketch they see and they are also able to share the pictures via social websites, all through the services provided by Disqus.

In today’s iDevBlogADay article I would like to describe how we integrated Disqus into our native iOS App, using their latest REST API.

When looking around the web for existing tutorials on how to use Disqus within an iOS app, I did not find many useful articles. There was one blog entry which proved to be a good starting point until I realized it was based on a much earlier API version and it was not usable anymore with the current 3.0 API release. The official Disqus documentation is pretty tight-lipped and brief, I had to do some guesswork to make things happen the way I wanted, however there is a Console for building some test messages, speeds up the development a lot!

In Part 1, I am going to show you how I implemented querying and displaying comments for a given sketch.

1. Register an account on the Disqus website
2. Register your application in the admin area. Each application is going to be a separate forum. A Disqus forum, as any other forum contains threads, and each of these threads can contain comments. In our case, every sketch a user had created was a different thread.
3. Integrate your app with Disqus:

3.1 You’ll need ASIHTTPRequest and JSON for the sample codes to compile, and import them in your class which communicates with Disqus.

#import "ASIFormDataRequest.h"
#import "JSONKit.h"

#define FORUM_KEY @"123456789"

Your forum key should be your public API key assigned to your Disqus application.

3.2. Build a request where you query all comments for a given thread:

- (void) getCommentsForThread:(NSString*)threadURL {

  NSString *urlString = [[NSString alloc] initWithFormat:@"http://disqus.com/api/3.0/threads/listPosts.json"];
  urlString = [urlString stringByAppendingFormat:@"?forum=%@&api_key=%@&thread:ident=%@",forumName, FORUM_KEY, threadURL];
  NSURL *url = [[NSURL alloc] initWithString:urlString];

  ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
  [request setDelegate:self];

  [request setDidFinishSelector:@selector(requestFinishedForGetComments:)];
  [request setDidFailSelector:@selector(requestFailedForGetComments:)];

  [request startAsynchronous];
}

The request we build for this query is going to be a HTTP GET message, passing in arguments within the URL. You need to give your forum’s shortname (you can get it by logging in to your Disqus dashboard), your public forum API key (also you can find it in your admin area on the Disqus website), and the thread ID. The thread ID can be specified in more than one ways. Either you identify the thread with your own URL-part, or you use the thread ID which was assigned by Disqus when the thread was created. If you use your own identifier, you have to use thread:ident= as in the example above. If you use the id provided by Disqus, you have to use thread= to pass on the ID.

You should also specify two delegate methods for the ASIHTTPRequest, one for successful and one for failed communication.

When you receive the response from Disqus, you will receive the data as a JSON object. We need to parse it:

- (void)requestFinishedForGetComments:(ASIHTTPRequest *)request {

	NSDictionary *dict = [[request responseData] objectFromJSONData];

	// check for possible errors

	if ([((NSNumber*)[dict objectForKey:@"code"]) intValue] != 0) {

		NSLog(@"++++++========+++++++ Received DISQUS error: %@", [request error]);

   // we should tell our delegate that the comment query has failed, this is something you can implement by creating a protocol and making your delegate class conform to it
		if ([delegate respondsToSelector:@selector(failedCommentQuery)]) {

			[delegate failedCommentQuery];
		}

		return;
	}

	// get thread comments by url results
	NSArray *rawCommentsArray = (NSArray*)[dict objectForKey: @"response"];

	if (rawCommentsArray == nil && [delegate respondsToSelector:@selector(failedCommentQuery)]) {

		[delegate failedCommentQuery];

		return;
	}

	//we will pass on the comments array to the delegate
	NSMutableArray *comments = [[NSMutableArray alloc] init];

	NSMutableDictionary *commentDictionary;

	NSString *threadid; 

	if ([rawCommentsArray count] != 0 ) {

		threadid = [[rawCommentsArray objectAtIndex: 0] objectForKey: @"thread"];
	}
	else {

		threadid = nil;
	}

	for (int i=0; i< [rawCommentsArray count]; i++) {

		commentDictionary = [[NSMutableDictionary alloc] init];

		NSString *comment = (NSString*) [(NSDictionary*) [rawCommentsArray objectAtIndex: i] objectForKey:@"raw_message"];
		NSString *author = (NSString*) [(NSDictionary*) [[rawCommentsArray objectAtIndex: i] objectForKey:@"author"] objectForKey:@"username"];

		if (author == nil) {

			author = (NSString*) [(NSDictionary*) [[rawCommentsArray objectAtIndex: i] objectForKey:@"author"] objectForKey:@"name"];
		}

		NSString *date = (NSString*) [(NSDictionary*) [rawCommentsArray objectAtIndex: i] objectForKey:@"createdAt"];

		[commentDictionary setObject:comment forKey:@"comment"];
		[commentDictionary setObject:author forKey:@"username"];
		[commentDictionary setObject:date forKey:@"date"];

		[comments addObject: commentDictionary];

		[commentDictionary release];
	}

	//here you should pass on the comments array befor the release to your delegate object
        //then your delegate can display all the comments

	[comments release];
}

There are several things to note in this method:

1. If you receive a code number different than 0, it means an error has occurred and has to be handled accordingly.
2. If you used your own threadID to identify the thread, you can store the disqus-generated threadID because you receive that back in the response. In the method above we store the threadid for later use. It will be required later on because posting a comment can only be done by referencing a thread with its Disqus-generated ID, not your own. Funny thing is, when you query the comments for a given thread, you will only receive th disqus-ID of the thread when there are already comments for the given thread.
3. There’s a lot of data related to the comment, in the example above we retrieve the message, the author’s name and the comment date, but there are many more to play with.
4. You can see that the author’s name can be retrieved in two different names: either by reading a username property value or if it null then the name property value. The explanation is: If the author of the comment used his Disqus username to post, the ‘username’ field will contain this data. If the author did not sign in with its Disqus username to post the comment, the name will appear in the ‘name’ field instead of ‘username’

Right now, we retrieved all the comments we wanted for a given thread and have them stored in an NSArray. We can pass this array on to our own delegate object to display and handle this data any way we like.

Hope you found this tutorial useful, I am planning on continuing with Part 2 where we delve into the topic of how to post a comment to a given thread.

This entry was posted in idevblogaday. Bookmark the permalink.

5 comments on “Integrating a commenting module in your App – Part 1

  1. This is a nicer way. Just wanted to know, is it possible to load the comment box using UIWebView ?

    • Yeah, it is absolutely possible! Check out the second part of the article which show you exactly that :)

  2. Pingback: DISQUS Comments « Under The Bridge

  3. Instant bookmark! Thanks so much for taking the time to write this tutorial. I absolutely love Disqus and never thought of taking advantage on iOS but it is a brilliant idea.