├── DisqusAuthenticateViewController.swift ├── DisqusComentsViewController.swift ├── DisqusManager.swift ├── DisqusSettings.swift └── README.md /DisqusAuthenticateViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DisqusAuthenticateViewController.swift 3 | // SwiftDisqusManager 4 | // 5 | // Created by TheFlow_ on 10/03/2015. 6 | // Copyright (c) 2015 TheFlow_. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DisqusAuthenticateViewController: UIViewController, UIWebViewDelegate, UIAlertViewDelegate { 12 | 13 | var webView: UIWebView! 14 | let disqusManager = DisqusManager() 15 | let authorizationURL = NSURL(string: "https://disqus.com/api/oauth/2.0/authorize/?scope=read,write&response_type=api_key&redirect_uri=\(DisqusManager().authRedirectURL)")! 16 | var onSuccess: (() -> ())! 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | 21 | // Do any additional setup after loading the view. 22 | self.navigationItem.title = "Connexion à Disqus" 23 | 24 | webView = UIWebView(frame: self.view.bounds) 25 | webView.delegate = self 26 | self.view.addSubview(webView) 27 | 28 | let cancelButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Cancel, target: self, action: "cancel") 29 | self.navigationItem.leftBarButtonItem = cancelButton 30 | 31 | webView.loadRequest(NSURLRequest(URL: authorizationURL)) 32 | } 33 | 34 | override func didReceiveMemoryWarning() { 35 | super.didReceiveMemoryWarning() 36 | // Dispose of any resources that can be recreated. 37 | } 38 | 39 | func paramsFromQueryString(queryString: String) -> [String: String] { 40 | var returnDict = [String: String]() 41 | for pair in queryString.componentsSeparatedByString("&") { 42 | let keyAndValue = pair.componentsSeparatedByString("=") 43 | if keyAndValue.count == 2 { 44 | let key = keyAndValue[0] as String 45 | let value = keyAndValue[1] as String 46 | 47 | returnDict[key] = value 48 | } 49 | } 50 | 51 | return returnDict 52 | } 53 | 54 | func cancel() { 55 | self.dismissViewControllerAnimated(true, completion: nil) 56 | } 57 | 58 | func complete() { 59 | self.dismissViewControllerAnimated(true, completion: onSuccess) 60 | } 61 | 62 | // MARK: - Web view delegate 63 | func webViewDidStartLoad(webView: UIWebView) { 64 | // Afficher l'icône de chargement dans la barre de status 65 | UIApplication.sharedApplication().networkActivityIndicatorVisible = true 66 | } 67 | 68 | func webViewDidFinishLoad(webView: UIWebView) { 69 | // Masquer l'icône de chargement dans la barre de status 70 | UIApplication.sharedApplication().networkActivityIndicatorVisible = false 71 | } 72 | 73 | func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool { 74 | if request.URL.host == NSURL(string: disqusManager.authRedirectURL)!.host { 75 | let params = self.paramsFromQueryString(request.URL.query!) 76 | if let code = params["code"] { 77 | disqusManager.authenticate(code, onSuccess: { 78 | self.complete() 79 | }, onFailure: { 80 | let errorAlert = UIAlertView(title: "Erreur", message: "Une erreur s'est produite lors de la connexion à Disqus. Merci de réessayer plus tard.", delegate: self, cancelButtonTitle: "OK") 81 | errorAlert.delegate = self 82 | dispatch_async(dispatch_get_main_queue(), { 83 | errorAlert.show() 84 | }) 85 | }) 86 | UIApplication.sharedApplication().networkActivityIndicatorVisible = false // Masquer l'icône de chargement dans la barre de status 87 | return false 88 | } 89 | } 90 | 91 | return true 92 | } 93 | 94 | // MARK: - Alert view delegate 95 | func alertView(alertView: UIAlertView, clickedButtonAtIndex buttonIndex: Int) { 96 | if buttonIndex == alertView.cancelButtonIndex { 97 | self.dismissViewControllerAnimated(true, completion: nil) 98 | } 99 | } 100 | 101 | /* 102 | // MARK: - Navigation 103 | 104 | // In a storyboard-based application, you will often want to do a little preparation before navigation 105 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 106 | // Get the new view controller using segue.destinationViewController. 107 | // Pass the selected object to the new view controller. 108 | } 109 | */ 110 | 111 | } 112 | -------------------------------------------------------------------------------- /DisqusComentsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DisqusComentsViewController.swift 3 | // SwiftDisqusManager 4 | // 5 | // Created by TheFlow_ on 10/03/2015. 6 | // Copyright (c) 2015 TheFlow_. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DisqusComentsViewController: UITableViewController { 12 | 13 | let disqusManager = DisqusManager() 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | 18 | } 19 | 20 | override func didReceiveMemoryWarning() { 21 | super.didReceiveMemoryWarning() 22 | // Dispose of any resources that can be recreated. 23 | } 24 | 25 | func showNewPostForm(onSuccess: () -> ()) { 26 | if disqusManager.isUserAuthenticated() { 27 | // Succeed 28 | onSuccess() 29 | } else { 30 | // Authenticate user 31 | let vc = DisqusAuthenticateViewController() 32 | vc.onSuccess = onSuccess 33 | let navigationController = UINavigationController(rootViewController: vc) 34 | navigationController.modalPresentationStyle = UIModalPresentationStyle.FormSheet 35 | self.presentViewController(navigationController, animated: true, completion: nil) 36 | } 37 | } 38 | 39 | /* 40 | // MARK: - Navigation 41 | 42 | // In a storyboard-based application, you will often want to do a little preparation before navigation 43 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 44 | // Get the new view controller using [segue destinationViewController]. 45 | // Pass the selected object to the new view controller. 46 | } 47 | */ 48 | 49 | } 50 | 51 | protocol DisqusComments { 52 | func presentNewPostForm() 53 | } 54 | -------------------------------------------------------------------------------- /DisqusManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DisqusManager.swift 3 | // SwiftDisqusManager 4 | // 5 | // Created by TheFlow_ on 08/03/2015. 6 | // 7 | 8 | import Foundation 9 | 10 | class DisqusManager { 11 | let publicAPIKey = DisqusSettings.publicAPIKey 12 | let secretAPIKey = DisqusSettings.secretAPIKey 13 | let forum = DisqusSettings.forum 14 | let authRedirectURL = DisqusSettings.authRedirectURL 15 | 16 | let appLabel = DisqusSettings.appLabel 17 | let appOrganization = DisqusSettings.appOrganization 18 | let appDescription = DisqusSettings.appDescription 19 | let appWebsite = DisqusSettings.appWebsite 20 | let appTermsURL = DisqusSettings.appTermsURL 21 | let appCallbackURL = DisqusSettings.appCallbackURL 22 | 23 | private var isAuthenticated: Bool? { 24 | get { 25 | return NSUserDefaults.standardUserDefaults().objectForKey("SwiftDisqusIsAuthenticated") as? Bool 26 | } 27 | set { 28 | NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: "SwiftDisqusIsAuthenticated") 29 | } 30 | } 31 | private var userID: Int? { 32 | get { 33 | return NSUserDefaults.standardUserDefaults().objectForKey("SwiftDisqusUserID") as? Int 34 | } 35 | set { 36 | NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: "SwiftDisqusUserID") 37 | } 38 | } 39 | private var userSecretAPIKey: String? { 40 | get { 41 | return NSUserDefaults.standardUserDefaults().objectForKey("SwiftDisqusUserSecretAPIKey") as? String 42 | } 43 | set { 44 | NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: "SwiftDisqusUserSecretAPIKey") 45 | } 46 | } 47 | private var userAccessToken: String? { 48 | get { 49 | return NSUserDefaults.standardUserDefaults().objectForKey("SwiftDisqusUserAccessToken") as? String 50 | } 51 | set { 52 | NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: "SwiftDisqusUserAccessToken") 53 | } 54 | } 55 | 56 | private struct Constants { 57 | static let sharedManager = DisqusManager() 58 | } 59 | 60 | class func sharedManager() -> DisqusManager { 61 | return Constants.sharedManager 62 | } 63 | 64 | init() { 65 | if isAuthenticated == nil { 66 | isAuthenticated = false 67 | } 68 | } 69 | 70 | func listPostsInThread(threadID: String, completionHandler: (comments: [Comment]!, error: NSError!) -> ()) { 71 | var url = NSURL(string: "http://disqus.com/api/3.0/threads/listPosts.json?api_key=\(publicAPIKey)&thread=\(threadID)")! // URL du JSON 72 | var request = NSURLRequest(URL: url) // Création de la requête HTTP 73 | var queue = NSOperationQueue() // Création de NSOperationQueue à laquelle le bloc du gestionnaire est distribué lorsque la demande complète ou échoué 74 | 75 | var comments = [Comment]() 76 | 77 | // Envoi de la requête asynchrone en utilisant NSURLConnection 78 | NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{(response: NSURLResponse!, data: NSData!, error: NSError!) ->Void in 79 | // Gestion des erreurs de connexion 80 | if error == nil { 81 | // Récupération du JSON 82 | let json = JSON(data: data) 83 | 84 | for comment in json["response"].arrayValue { 85 | var dateFormat = NSDateFormatter() 86 | dateFormat.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" 87 | 88 | let id = comment["id"].intValue 89 | let parent = comment["parent"].int 90 | let likes = comment["likes"].intValue 91 | let dislikes = comment["dislikes"].intValue 92 | let isApproved = comment["isApproved"].boolValue 93 | let author = comment["author"]["name"].stringValue 94 | let avatar = "http:" + comment["author"]["avatar"]["cache"].stringValue 95 | let message = comment["raw_message"].stringValue 96 | let date = dateFormat.dateFromString(comment["createdAt"].stringValue)! 97 | 98 | comments.append(Comment(id: id, parent: parent, likes: likes, dislikes: dislikes, isApproved: isApproved, author: author, avatar: avatar, message: message, date: date)) 99 | } 100 | } 101 | 102 | completionHandler(comments: comments, error: error) 103 | }) 104 | } 105 | 106 | func listPostsInThread(link threadLink: String, completionHandler: (comments: [Comment]!, error: NSError!) -> ()) { 107 | var url = NSURL(string: "http://disqus.com/api/3.0/threads/listPosts.json?api_key=\(publicAPIKey)&forum=\(forum)&thread:link=\(threadLink)")! // URL du JSON 108 | var request = NSURLRequest(URL: url) // Création de la requête HTTP 109 | var queue = NSOperationQueue() // Création de NSOperationQueue à laquelle le bloc du gestionnaire est distribué lorsque la demande complète ou échoué 110 | 111 | var comments = [Comment]() 112 | 113 | // Envoi de la requête asynchrone en utilisant NSURLConnection 114 | NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{(response: NSURLResponse!, data: NSData!, error: NSError!) ->Void in 115 | // Gestion des erreurs de connexion 116 | if error == nil { 117 | // Récupération du JSON 118 | let json = JSON(data: data) 119 | 120 | for comment in json["response"].arrayValue { 121 | var dateFormat = NSDateFormatter() 122 | dateFormat.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" 123 | 124 | let id = comment["id"].intValue 125 | let parent = comment["parent"].int 126 | let likes = comment["likes"].intValue 127 | let dislikes = comment["dislikes"].intValue 128 | let isApproved = comment["isApproved"].boolValue 129 | let author = comment["author"]["name"].stringValue 130 | let avatar = "http:" + comment["author"]["avatar"]["cache"].stringValue 131 | let message = comment["raw_message"].stringValue 132 | let date = dateFormat.dateFromString(comment["createdAt"].stringValue)! 133 | 134 | comments.append(Comment(id: id, parent: parent, likes: likes, dislikes: dislikes, isApproved: isApproved, author: author, avatar: avatar, message: message, date: date)) 135 | } 136 | } 137 | 138 | completionHandler(comments: comments, error: error) 139 | }) 140 | } 141 | 142 | func listPostsInThread(ident threadIdent: String, completionHandler: (comments: [Comment]!, error: NSError!) -> ()) { 143 | var urlString = "http://disqus.com/api/3.0/threads/listPosts.json?api_key=\(publicAPIKey)&forum=\(forum)&thread:ident=\(threadIdent)" 144 | 145 | var url = NSURL(string: urlString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!)! // URL du JSON 146 | var request = NSURLRequest(URL: url) // Création de la requête HTTP 147 | var queue = NSOperationQueue() // Création de NSOperationQueue à laquelle le bloc du gestionnaire est distribué lorsque la demande complète ou échoué 148 | 149 | var comments = [Comment]() 150 | 151 | // Envoi de la requête asynchrone en utilisant NSURLConnection 152 | NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{(response: NSURLResponse!, data: NSData!, error: NSError!) ->Void in 153 | // Gestion des erreurs de connexion 154 | if error == nil { 155 | // Récupération du JSON 156 | let json = JSON(data: data) 157 | 158 | for comment in json["response"].arrayValue { 159 | var dateFormat = NSDateFormatter() 160 | dateFormat.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" 161 | 162 | let id = comment["id"].intValue 163 | let parent = comment["parent"].int 164 | let likes = comment["likes"].intValue 165 | let dislikes = comment["dislikes"].intValue 166 | let isApproved = comment["isApproved"].boolValue 167 | let author = comment["author"]["name"].stringValue 168 | let avatar = "http:" + comment["author"]["avatar"]["cache"].stringValue 169 | let message = comment["raw_message"].stringValue 170 | let date = dateFormat.dateFromString(comment["createdAt"].stringValue)! 171 | 172 | comments.append(Comment(id: id, parent: parent, likes: likes, dislikes: dislikes, isApproved: isApproved, author: author, avatar: avatar, message: message, date: date)) 173 | } 174 | } 175 | 176 | completionHandler(comments: comments, error: error) 177 | }) 178 | } 179 | 180 | func threadDetails(link threadLink: String, completionHandler: (thread: Thread!, error: NSError!) -> ()) { 181 | var url = NSURL(string: "https://disqus.com/api/3.0/threads/details.json?api_key=\(publicAPIKey)&forum=\(forum)&thread:link=\(threadLink)")! // URL du JSON 182 | var request = NSURLRequest(URL: url) // Création de la requête HTTP 183 | var queue = NSOperationQueue() // Création de NSOperationQueue à laquelle le bloc du gestionnaire est distribué lorsque la demande complète ou échoué 184 | 185 | // Envoi de la requête asynchrone en utilisant NSURLConnection 186 | NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{(response: NSURLResponse!, data: NSData!, error: NSError!) ->Void in 187 | var thread: Thread? 188 | // Gestion des erreurs de connexion 189 | if error == nil { 190 | // Récupération du JSON 191 | let json = JSON(data: data) 192 | 193 | if json["code"].int == 0 { 194 | let id = json["response"]["id"].intValue 195 | let title = json["response"]["title"].stringValue 196 | 197 | thread = Thread(id: id, title: title) 198 | } 199 | } 200 | 201 | completionHandler(thread: thread, error: error) 202 | }) 203 | } 204 | 205 | func threadDetails(ident threadIdent: String, completionHandler: (thread: Thread!, error: NSError!) -> ()) { 206 | var urlString = "https://disqus.com/api/3.0/threads/details.json?api_key=\(publicAPIKey)&forum=\(forum)&thread:ident=\(threadIdent)" 207 | 208 | var url = NSURL(string: urlString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!)! // URL du JSON 209 | var request = NSURLRequest(URL: url) // Création de la requête HTTP 210 | var queue = NSOperationQueue() // Création de NSOperationQueue à laquelle le bloc du gestionnaire est distribué lorsque la demande complète ou échoué 211 | 212 | // Envoi de la requête asynchrone en utilisant NSURLConnection 213 | NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{(response: NSURLResponse!, data: NSData!, error: NSError!) ->Void in 214 | var thread: Thread? 215 | // Gestion des erreurs de connexion 216 | if error == nil { 217 | // Récupération du JSON 218 | let json = JSON(data: data) 219 | 220 | if json["code"].int == 0 { 221 | let id = json["response"]["id"].intValue 222 | let title = json["response"]["title"].stringValue 223 | 224 | thread = Thread(id: id, title: title) 225 | } 226 | } 227 | 228 | completionHandler(thread: thread, error: error) 229 | }) 230 | } 231 | 232 | func authenticate(code: String, onSuccess: () -> (), onFailure: () -> ()) { 233 | let url = NSURL(string: "https://disqus.com/api/oauth/2.0/api_key/")! 234 | let request = NSMutableURLRequest(URL: url) 235 | let queue = NSOperationQueue() 236 | let params = "grant_type=api_key&redirect_uri=\(authRedirectURL)&code=\(code)&application[label]=\(appLabel)&application[description]=\(appDescription)&application[website]=\(appWebsite)&application[organization]=\(appOrganization)&application[terms_url]=\(appTermsURL)&application[callback_url]=\(appCallbackURL)" 237 | request.HTTPBody = params.dataUsingEncoding(NSUTF8StringEncoding) 238 | request.HTTPMethod = "POST" 239 | 240 | // Envoi de la requête asynchrone en utilisant NSURLConnection 241 | NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{(response: NSURLResponse!, data: NSData!, error: NSError!) ->Void in 242 | // Gestion des erreurs de connexion 243 | if error == nil { 244 | // Récupération du JSON 245 | let json = JSON(data: data) 246 | 247 | if json["error"].string == nil { 248 | // Connexion réussie ! 249 | self.userID = json["user_id"].intValue 250 | self.userSecretAPIKey = json["api_secret"].stringValue 251 | self.userAccessToken = json["access_token"].stringValue 252 | 253 | self.isAuthenticated = true 254 | onSuccess() 255 | } 256 | else { 257 | let errorDesc = json["error_description"].stringValue 258 | println("\(errorDesc)") 259 | onFailure() 260 | } 261 | 262 | println("\(json)") 263 | } 264 | else { 265 | println("\(error.localizedDescription)") 266 | onFailure() 267 | } 268 | }) 269 | } 270 | 271 | func isUserAuthenticated() -> Bool { 272 | return isAuthenticated! 273 | } 274 | 275 | func createPost(message: String, inThread threadID: String?, withParent parent: String? = nil, onSuccess: () -> (), onFailure: () -> ()) { 276 | if isUserAuthenticated() { 277 | let url = NSURL(string: "https://disqus.com/api/3.0/posts/create.json")! 278 | let request = NSMutableURLRequest(URL: url) 279 | let queue = NSOperationQueue() 280 | let message = message.stringByReplacingOccurrencesOfString(" ", withString: "+", options: nil, range: nil).stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)! 281 | var params = "api_secret=\(userSecretAPIKey!)&message=\(message)" 282 | if let threadID = threadID { 283 | params += "&thread=\(threadID)" 284 | } 285 | if let parent = parent { 286 | params += "&parent=\(parent)" 287 | } 288 | params += "&access_token=\(userAccessToken!)" 289 | request.HTTPBody = params.dataUsingEncoding(NSUTF8StringEncoding) 290 | request.HTTPMethod = "POST" 291 | 292 | // Envoi de la requête asynchrone en utilisant NSURLConnection 293 | NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{(response: NSURLResponse!, data: NSData!, error: NSError!) ->Void in 294 | // Gestion des erreurs de connexion 295 | if error == nil { 296 | // Récupération du JSON 297 | let json = JSON(data: data) 298 | 299 | if json["code"].int == 0 { 300 | // Envoyé 301 | 302 | onSuccess() 303 | } 304 | else { 305 | let response = json["response"].stringValue 306 | println("\(response)") 307 | onFailure() 308 | } 309 | } 310 | else { 311 | println("\(error.localizedDescription)") 312 | onFailure() 313 | } 314 | }) 315 | } 316 | else { 317 | onFailure() 318 | } 319 | } 320 | } 321 | 322 | class Comment { 323 | var id: Int 324 | var parent: Int? 325 | var likes: Int 326 | var dislikes: Int 327 | var isApproved: Bool 328 | var author: String 329 | var avatar: String 330 | var message: String 331 | var date: NSDate 332 | 333 | init(id: Int, parent: Int?, likes: Int, dislikes: Int, isApproved: Bool, author: String, avatar: String, message: String, date: NSDate) { 334 | self.id = id 335 | self.parent = parent 336 | self.likes = likes 337 | self.dislikes = dislikes 338 | self.isApproved = isApproved 339 | self.author = author 340 | self.avatar = avatar 341 | self.message = message 342 | self.date = date 343 | } 344 | } 345 | 346 | class Thread { 347 | var id: Int 348 | var title: String 349 | 350 | init(id: Int, title: String) { 351 | self.id = id 352 | self.title = title 353 | } 354 | } 355 | -------------------------------------------------------------------------------- /DisqusSettings.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DisqusSettings.swift 3 | // SwiftDisqusManager 4 | // 5 | // Created by TheFlow_ on 23/05/2015. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | struct DisqusSettings { 12 | let publicAPIKey = "" // Your Disqus public API key 13 | let secretAPIKey = "" // Your Disqus secret API key 14 | let forum = "" // Your forum shortname 15 | let authRedirectURL = "" // Your redirect url 16 | 17 | /* The following settings are used to log in with Disqus */ 18 | let appLabel = "" // The name of your application 19 | let appOrganization = "" // The name of your organization 20 | let appDescription = "" // A little description of your app 21 | let appWebsite = "" // The website of your app 22 | let appTermsURL = "" // Your terms URL 23 | let appCallbackURL = "" // Your callback URL 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftDisqusManager 2 | Disqus intergration into your apps in swift 3 | ## Installation 4 | Just drag all the files into your project (you might want to check "Copy items if needed") 5 | ## Usage 6 | For now, only 3 functions are available but I will add more soon. 7 | 8 | To know how to use SwiftDisqusManager, please check the [wiki](https://github.com/TheFlow95/SwiftDisqusManager/wiki). 9 | ## To do 10 | * Update thread class 11 | * listThreadsInForum function 12 | * Sample app 13 | 14 | If you have a special request, please contact me. 15 | --------------------------------------------------------------------------------