├── Resources ├── Responses │ ├── authenticated │ │ ├── deletecomment.json │ │ ├── deleteimage.json │ │ ├── myalbumcount.json │ │ ├── mycommentcount.json │ │ ├── myimagecount.json │ │ ├── myimagedelete.json │ │ ├── conversationdelete.json │ │ ├── conversationpost.json │ │ ├── deletenotification.json │ │ ├── myalbumids.json │ │ ├── myimageIDs.json │ │ ├── mysettingschange.json │ │ ├── mycommentdeletewithid.json │ │ ├── mycommentids.json │ │ ├── postcomment.json │ │ ├── replytocomment.json │ │ ├── myaccount.json │ │ ├── invalidcode.json │ │ ├── invalidrefresh.json │ │ ├── conversations.json │ │ ├── invalidaccess.json │ │ ├── imagenotfound.json │ │ ├── conversationswithravener.json │ │ ├── refreshedtokens.json │ │ ├── mycomments.json │ │ ├── mycommentwithid.json │ │ ├── mysettings.json │ │ ├── allnotifications.json │ │ ├── freshnotification.json │ │ ├── postimage.json │ │ ├── myimages.json │ │ ├── myimagewithid.json │ │ ├── myalbums.json │ │ ├── conversationwithid.json │ │ ├── mysubmissions.json │ │ ├── myalbumwithid.json │ │ └── myfavs.json │ └── anonymous │ │ ├── badhotgalleryrequest-noauthenticiation.json │ │ ├── geoffsaccount.json │ │ ├── geoffscomments.json │ │ ├── image.json │ │ ├── galleryimage.json │ │ └── postimage.json ├── example.gif ├── image-example.jpg ├── ImgurSession Tests-Prefix.pch ├── Anonymous Test Config.plist ├── ImgurSession-Prefix.pch ├── Authenticated Test Config.plist └── ImgurSession-Info.plist ├── SampleApp ├── en.lproj │ └── InfoPlist.strings ├── main.m ├── GMMAppDelegate.h ├── Images.xcassets │ ├── LaunchImage.launchimage │ │ └── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── GMMViewController.h ├── SampleApp-Prefix.pch ├── SampleApp-Info.plist ├── GMMViewController.m ├── GMMAppDelegate.m └── Base.lproj │ └── Main.storyboard ├── ImgurSession ├── NSError+IMGError.m ├── NSDictionary+IMG.h ├── IMGResponseSerializer.h ├── Models │ ├── IMGAlbum.h │ ├── IMGMeme.h │ ├── IMGModel.h │ ├── IMGVote.h │ ├── IMGGalleryObject.h │ ├── IMGGalleryAlbum.h │ ├── IMGMessage.h │ ├── IMGModel.m │ ├── IMGConversation.h │ ├── IMGGalleryImage.h │ ├── IMGAccount.h │ ├── IMGNotification.h │ ├── IMGMeme.m │ ├── IMGVote.m │ ├── IMGObject.h │ ├── IMGGalleryProfile.h │ ├── IMGAlbum.m │ ├── IMGAccountSettings.h │ ├── IMGComment.h │ ├── IMGImage.h │ ├── IMGGalleryProfile.m │ ├── IMGBasicAlbum.h │ ├── IMGMessage.m │ ├── IMGAccount.m │ ├── IMGNotification.m │ ├── IMGGalleryImage.m │ ├── IMGConversation.m │ ├── IMGGalleryAlbum.m │ └── IMGAccountSettings.m ├── NSDictionary+IMG.m ├── Requests │ ├── IMGMemeGen.h │ ├── IMGEndpoint.h │ ├── IMGMemeGen.m │ ├── IMGNotificationRequest.h │ ├── IMGEndpoint.m │ ├── IMGConversationRequest.h │ ├── IMGAlbumRequest.h │ ├── IMGCommentRequest.h │ ├── IMGNotificationRequest.m │ ├── IMGImageRequest.h │ ├── IMGConversationRequest.m │ ├── IMGCommentRequest.m │ └── IMGAccountRequest.h ├── ImgurSession.h ├── NSError+IMGError.h └── IMGResponseSerializer.m ├── .gitignore ├── ImgurSession.podspec ├── Integrated Tests ├── IMGIntegratedTestCase.h ├── IMGCommentTests.m ├── IMGNotificationTests.m ├── IMGConversationTest.m └── IMGGalleryTests.m ├── Podfile ├── Stubbed Tests ├── IMGTestCase.h ├── IMGGalleryTests.m ├── IMGImageTests.m ├── IMGCommentTests.m ├── IMGNotificationTests.m ├── IMGConversationTest.m └── IMGTestCase.m ├── LICENSE └── README.md /Resources/Responses/authenticated/deletecomment.json: -------------------------------------------------------------------------------- 1 | {"data":true,"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/deleteimage.json: -------------------------------------------------------------------------------- 1 | {"data":true,"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/myalbumcount.json: -------------------------------------------------------------------------------- 1 | {"data":1,"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/mycommentcount.json: -------------------------------------------------------------------------------- 1 | {"data":1,"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/myimagecount.json: -------------------------------------------------------------------------------- 1 | {"data":1,"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/myimagedelete.json: -------------------------------------------------------------------------------- 1 | {"data":true,"success":true,"status":200} -------------------------------------------------------------------------------- /SampleApp/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Resources/Responses/authenticated/conversationdelete.json: -------------------------------------------------------------------------------- 1 | {"data":true,"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/conversationpost.json: -------------------------------------------------------------------------------- 1 | {"data":true,"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/deletenotification.json: -------------------------------------------------------------------------------- 1 | {"data":true,"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/myalbumids.json: -------------------------------------------------------------------------------- 1 | {"data":["b59dz"],"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/myimageIDs.json: -------------------------------------------------------------------------------- 1 | {"data":["xQhKdf1"],"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/mysettingschange.json: -------------------------------------------------------------------------------- 1 | {"data":true,"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/mycommentdeletewithid.json: -------------------------------------------------------------------------------- 1 | {"data":true,"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/mycommentids.json: -------------------------------------------------------------------------------- 1 | {"data":[196246384],"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoffmacd/ImgurSession/HEAD/Resources/example.gif -------------------------------------------------------------------------------- /Resources/Responses/authenticated/postcomment.json: -------------------------------------------------------------------------------- 1 | {"data":{"id":"196257706"},"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/replytocomment.json: -------------------------------------------------------------------------------- 1 | {"data":{"id":"196257709"},"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/image-example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geoffmacd/ImgurSession/HEAD/Resources/image-example.jpg -------------------------------------------------------------------------------- /Resources/Responses/anonymous/badhotgalleryrequest-noauthenticiation.json: -------------------------------------------------------------------------------- 1 | {"data":{"error":"Invalid client_id","request":"\/3\/gallery","method":"GET"},"success":false,"status":403} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/myaccount.json: -------------------------------------------------------------------------------- 1 | {"data":{"id":10677334,"url":"jobsjobs","bio":null,"reputation":0,"created":1395673404,"pro_expiration":false},"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/anonymous/geoffsaccount.json: -------------------------------------------------------------------------------- 1 | {"data":{"id":10680082,"url":"geoffygeoff","bio":null,"reputation":13,"created":1395684219,"pro_expiration":false},"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/invalidcode.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "error": "Invalid Pin", 4 | "request": "\/oauth2\/token", 5 | "method": "POST" 6 | }, 7 | "success": false, 8 | "status": 400 9 | } -------------------------------------------------------------------------------- /Resources/Responses/authenticated/invalidrefresh.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "error": "Invalid refresh token", 4 | "request": "\/oauth2\/token", 5 | "method": "POST" 6 | }, 7 | "success": false, 8 | "status": 400 9 | } -------------------------------------------------------------------------------- /Resources/Responses/authenticated/conversations.json: -------------------------------------------------------------------------------- 1 | {"data":[{"id":25773082,"with_account":"Ravener","with_account_id":2538546,"last_message_preview":"why are you deleted","message_count":1,"datetime":1395685845}],"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/invalidaccess.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "error": "The access token provided is invalid.", 4 | "request": "\/3\/account\/me", 5 | "method": "GET" 6 | }, 7 | "success": false, 8 | "status": 403 9 | } -------------------------------------------------------------------------------- /Resources/Responses/authenticated/imagenotfound.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "error": "Unable to find an image with the id, fdsfdsfdsa", 4 | "request": "\/3\/image\/fdsfdsfdsa", 5 | "method": "GET" 6 | }, 7 | "success": false, 8 | "status": 404 9 | } -------------------------------------------------------------------------------- /Resources/Responses/authenticated/conversationswithravener.json: -------------------------------------------------------------------------------- 1 | {"data":[{"id":25773082,"with_account":"Ravener","with_account_id":2538546,"last_message_preview":"you must be the designer of th...","message_count":2,"datetime":1395685900}],"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/refreshedtokens.json: -------------------------------------------------------------------------------- 1 | { 2 | "access_token": "3ec3add9e2a6cb8fe933802cda85dd7960468c7d", 3 | "expires_in": 3600, 4 | "token_type": "bearer", 5 | "scope": null, 6 | "refresh_token": "37a1e20c76b0ca7e3b3cb25785fb82838a6a02a4", 7 | "account_username": "arcadefire89" 8 | } -------------------------------------------------------------------------------- /Resources/Responses/authenticated/mycomments.json: -------------------------------------------------------------------------------- 1 | {"data":[{"id":196246384,"image_id":"OLvRkCm","comment":"hahaha","author":"jobsjobs","author_id":10677334,"on_album":false,"album_cover":null,"ups":1,"downs":0,"points":1,"datetime":1395684135,"parent_id":0,"deleted":false,"vote":null}],"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/mycommentwithid.json: -------------------------------------------------------------------------------- 1 | {"data":{"id":196246384,"image_id":"OLvRkCm","comment":"hahaha","author":"jobsjobs","author_id":10677334,"on_album":false,"album_cover":null,"ups":1,"downs":0,"points":1,"datetime":1395684135,"parent_id":0,"deleted":false,"vote":"up"},"success":true,"status":200} -------------------------------------------------------------------------------- /ImgurSession/NSError+IMGError.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSError+IMGError.m 3 | // ImgurSession 4 | // 5 | // Created by Xtreme Dev on 2014-04-22. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "NSError+IMGError.h" 10 | 11 | @implementation NSError (IMGError) 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Resources/Responses/anonymous/geoffscomments.json: -------------------------------------------------------------------------------- 1 | {"data":[{"id":196825225,"image_id":"mOqejNf","comment":"very ffunny","author":"geoffygeoff","author_id":10680082,"on_album":false,"album_cover":null,"ups":1,"downs":0,"points":1,"datetime":1395775641,"parent_id":0,"deleted":false,"vote":null}],"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/mysettings.json: -------------------------------------------------------------------------------- 1 | {"data":{"email":"geoffmacd@icloud.com","high_quality":false,"public_images":true,"album_privacy":"public","pro_expiration":false,"accepted_gallery_terms":true,"active_emails":["geoffmacd@icloud.com"],"messaging_enabled":true,"blocked_users":[]},"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/allnotifications.json: -------------------------------------------------------------------------------- 1 | {"data":{"replies":[],"messages":[{"id":"95572642","account_id":"10680082","viewed":"0","content":{"id":"25773145","account_id":"10680082","with_account":"10680364","spam":"0","message_num":"1","last_message":"fuck you","from":"cntower89","datetime":1395686319}}]},"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/freshnotification.json: -------------------------------------------------------------------------------- 1 | {"data":{"replies":[],"messages":[{"id":"95572642","account_id":"10680082","viewed":"0","content":{"id":"25773145","account_id":"10680082","with_account":"10680364","spam":"0","message_num":"1","last_message":"fuck you","from":"cntower89","datetime":1395686319}}]},"success":true,"status":200} -------------------------------------------------------------------------------- /ImgurSession/NSDictionary+IMG.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+IMG.h 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-04-10. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSDictionary (IMG) 12 | 13 | -(NSDictionary *)IMG_cleanNull; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Resources/Responses/authenticated/postimage.json: -------------------------------------------------------------------------------- 1 | {"data":{"id":"Nt8zLts","title":null,"description":null,"datetime":1395685630,"type":"image\/jpeg","animated":false,"width":500,"height":333,"size":75582,"views":0,"bandwidth":0,"favorite":false,"nsfw":null,"section":null,"deletehash":"xOGLEFZGif4YcaX","link":"http:\/\/i.imgur.com\/Nt8zLts.jpg"},"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/anonymous/image.json: -------------------------------------------------------------------------------- 1 | {"data":{"id":"mOqejNf","title":"The hard truth about getting out of user submitted","description":null,"datetime":1395775413,"type":"image\/gif","animated":true,"width":320,"height":240,"size":2816463,"views":0,"bandwidth":0,"favorite":false,"nsfw":null,"section":null,"link":"http:\/\/i.imgur.com\/mOqejNf.gif"},"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/myimages.json: -------------------------------------------------------------------------------- 1 | {"data":[{"id":"xQhKdf1","title":"kitties","description":null,"datetime":1395676562,"type":"image\/jpeg","animated":false,"width":500,"height":333,"size":75582,"views":1,"bandwidth":75582,"favorite":false,"nsfw":null,"section":null,"deletehash":"UNYqFMZLg96ee2s","link":"http:\/\/i.imgur.com\/xQhKdf1.jpg"}],"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/myimagewithid.json: -------------------------------------------------------------------------------- 1 | {"data":{"id":"xQhKdf1","title":"kitties","description":null,"datetime":1395676562,"type":"image\/jpeg","animated":false,"width":500,"height":333,"size":75582,"views":1,"bandwidth":75582,"favorite":false,"nsfw":null,"section":null,"deletehash":"UNYqFMZLg96ee2s","link":"http:\/\/i.imgur.com\/xQhKdf1.jpg"},"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/myalbums.json: -------------------------------------------------------------------------------- 1 | {"data":[{"id":"b59dz","title":"geoff","description":null,"datetime":1395685379,"cover":"hbrL16f","cover_width":500,"cover_height":333,"account_url":"geoffygeoff","privacy":"public","layout":"blog","views":0,"link":"http:\/\/imgur.com\/a\/b59dz","favorite":false,"nsfw":null,"section":null,"deletehash":"KT7mB7ai4zbBCFF","order":0}],"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/conversationwithid.json: -------------------------------------------------------------------------------- 1 | {"data":{"id":25773082,"with_account":"Ravener","with_account_id":2538546,"last_message_preview":"why are you deleted","message_count":1,"messages":[{"id":38037799,"from":"geoffygeoff","account_id":10680082,"sender_id":10680082,"body":"why are you deleted","conversation_id":25773082,"datetime":1395685845}],"datetime":1395685845},"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/Responses/authenticated/mysubmissions.json: -------------------------------------------------------------------------------- 1 | {"data":[{"id":"xQhKdf1","title":"kitties","description":null,"datetime":1395684586,"type":"image\/jpeg","animated":false,"width":500,"height":333,"size":75582,"views":1,"bandwidth":75582,"favorite":false,"nsfw":false,"section":null,"account_url":"jobsjobs","link":"http:\/\/i.imgur.com\/xQhKdf1.jpg","ups":5,"downs":1,"score":4,"is_album":false}],"success":true,"status":200} -------------------------------------------------------------------------------- /Resources/ImgurSession Tests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #ifdef __OBJC__ 8 | #import 9 | #import 10 | 11 | #if TARGET_OS_IPHONE 12 | #import 13 | #else 14 | #import 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /SampleApp/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SampleApp 4 | // 5 | // Created by Xtreme Dev on 2014-05-13. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "GMMAppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([GMMAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Resources/Responses/anonymous/galleryimage.json: -------------------------------------------------------------------------------- 1 | {"data":{"id":"mOqejNf","title":"The hard truth about getting out of user submitted","description":null,"datetime":1395775413,"type":"image\/gif","animated":true,"width":320,"height":240,"size":2816463,"views":0,"bandwidth":0,"vote":null,"favorite":false,"nsfw":false,"section":null,"account_url":"Salcano","link":"http:\/\/i.imgur.com\/mOqejNf.gif","ups":21,"downs":1,"score":20,"is_album":false},"success":true,"status":200} -------------------------------------------------------------------------------- /SampleApp/GMMAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // GMMAppDelegate.h 3 | // SampleApp 4 | // 5 | // Created by Xtreme Dev on 2014-05-13. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ImgurSession.h" 11 | 12 | @interface GMMAppDelegate : UIResponder 13 | 14 | @property (strong, nonatomic) UIWindow *window; 15 | @property (copy) void(^continueHandler)(); 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /ImgurSession/IMGResponseSerializer.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGResponseSerializer.h 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-07. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /* 12 | Subclass of AFJSONResponseSerializer to provide ImgurSession a method to track client API rate limits 13 | **/ 14 | 15 | @interface IMGResponseSerializer : AFJSONResponseSerializer 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | */build/* 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | profile 14 | *.moved-aside 15 | DerivedData 16 | .idea/ 17 | *.hmap 18 | *.xccheckout 19 | 20 | #CocoaPods 21 | Pods 22 | Podfile.lock 23 | 24 | Resources/ImgurKit_Tests-Info.plist 25 | ImgurSession.xcodeproj/project.xcworkspace/contents.xcworkspacedata 26 | 27 | ImgurSession.xcworkspace/contents.xcworkspacedata 28 | -------------------------------------------------------------------------------- /Resources/Responses/anonymous/postimage.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "id": "ttYl5r4", 4 | "title": null, 5 | "description": null, 6 | "datetime": 1400014032, 7 | "type": "image\/jpeg", 8 | "animated": false, 9 | "width": 500, 10 | "height": 333, 11 | "size": 69908, 12 | "views": 0, 13 | "bandwidth": 0, 14 | "favorite": false, 15 | "nsfw": null, 16 | "section": null, 17 | "deletehash": "w3IyTaG8W0vK5cC", 18 | "link": "http:\/\/i.imgur.com\/ttYl5r4.jpg" 19 | }, 20 | "success": true, 21 | "status": 200 22 | } -------------------------------------------------------------------------------- /ImgurSession/Models/IMGAlbum.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGAlbum.h 3 | // ImgurSession 4 | // 5 | // Created by Johann Pardanaud on 11/07/13. 6 | // Distributed under the MIT license. 7 | // 8 | 9 | #import "IMGBasicAlbum.h" 10 | 11 | 12 | /** 13 | Model object class to represent albums not posted to the gallery. https://api.imgur.com/models/album 14 | */ 15 | @interface IMGAlbum : IMGBasicAlbum 16 | 17 | /** 18 | Delete hash string 19 | */ 20 | @property (nonatomic, readwrite, copy) NSString *deletehash; 21 | 22 | 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /ImgurSession/NSDictionary+IMG.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+IMG.m 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-04-10. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "NSDictionary+IMG.h" 10 | 11 | @implementation NSDictionary (IMG) 12 | 13 | -(NSDictionary *)IMG_cleanNull { 14 | return [self dictionaryWithValuesForKeys:[[self keysOfEntriesPassingTest:^BOOL(id key, id obj, BOOL *stop) { 15 | return ![obj isEqual:[NSNull null]]; 16 | }] allObjects]]; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /ImgurSession/Requests/IMGMemeGen.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGMemeGen.h 3 | // ImgurSession 4 | // 5 | // Created by Xtreme Dev on 2014-05-14. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGEndpoint.h" 10 | 11 | @interface IMGMemeGen : IMGEndpoint 12 | 13 | /** 14 | Return list of meme IMGImage models. Use IMGMeme to create memes and upload them with regular file upload in IMGImageRequest 15 | */ 16 | +(void)defaultMemes:(void (^)(NSArray * memeImages))success failure:(void (^)(NSError * error))failure; 17 | @end 18 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGMeme.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGMeme.h 3 | // ImgurSession 4 | // 5 | // Created by Xtreme Dev on 2014-05-14. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGImage.h" 10 | 11 | @interface IMGMeme : IMGImage 12 | 13 | @property (readonly,nonatomic,copy) NSString * topText; 14 | @property (readonly,nonatomic,copy) NSString * bottomText; 15 | 16 | 17 | -(instancetype)initWithImage:(IMGImage*)image withTopText:(NSString*)top withBottomText:(NSString*)bottom withTitle:(NSString*)title withDescription:(NSString*)description; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /SampleApp/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Resources/Anonymous Test Config.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | imgurClientCredentials 6 | 7 | anonymous 8 | 9 | id 10 | 11 | 12 | imgurUnitTestParams 13 | 14 | title 15 | Just messing with the API, you can ignore this 16 | recipientId 17 | stevesteve89 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Resources/ImgurSession-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'ImgurSession' target in the 'ImgurSession' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #import 8 | #import 9 | 10 | #if TARGET_OS_IPHONE 11 | #import 12 | #import 13 | #import 14 | #else 15 | #import 16 | #import 17 | #endif 18 | #endif -------------------------------------------------------------------------------- /Resources/Responses/authenticated/myalbumwithid.json: -------------------------------------------------------------------------------- 1 | {"data":{"id":"b59dz","title":"geoff","description":null,"datetime":1395685379,"cover":"hbrL16f","cover_width":500,"cover_height":333,"account_url":"geoffygeoff","privacy":"public","layout":"blog","views":0,"link":"http:\/\/imgur.com\/a\/b59dz","favorite":false,"nsfw":null,"section":null,"deletehash":"KT7mB7ai4zbBCFF","images_count":1,"images":[{"id":"hbrL16f","title":null,"description":null,"datetime":1395685402,"type":"image\/jpeg","animated":false,"width":500,"height":333,"size":75582,"views":0,"bandwidth":0,"favorite":false,"nsfw":null,"section":null,"link":"http:\/\/i.imgur.com\/hbrL16f.jpg"}]},"success":true,"status":200} -------------------------------------------------------------------------------- /SampleApp/GMMViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // GMMViewController.h 3 | // SampleApp 4 | // 5 | // Created by Xtreme Dev on 2014-05-13. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "ImgurSession.h" 12 | 13 | @interface GMMViewController : UIViewController 14 | 15 | @property (weak, nonatomic) IBOutlet UILabel *titleLabel; 16 | @property (weak, nonatomic) IBOutlet UILabel *desriptionLabel; 17 | @property (weak, nonatomic) IBOutlet UIImageView *coverView; 18 | @property (weak, nonatomic) IBOutlet UIButton *loginButton; 19 | @property (weak, nonatomic) IBOutlet UILabel *stateLabel; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /SampleApp/SampleApp-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | 17 | #import 18 | #import 19 | #import 20 | #import 21 | #import 22 | #endif 23 | -------------------------------------------------------------------------------- /Resources/Authenticated Test Config.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | imgurClientCredentials 6 | 7 | anonymous 8 | 9 | id 10 | 11 | secret 12 | 13 | refreshToken 14 | 15 | 16 | imgurUnitTestParams 17 | 18 | title 19 | Just messing with the API, you can ignore this 20 | recipientId 21 | stevesteve89 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGModel.h 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-15. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "NSError+IMGError.h" 10 | 11 | @interface IMGModel : NSObject 12 | 13 | /** 14 | Common initializer for JSON HTTP response which processes the "data" JSON object into model object class 15 | @return initilialized instancetype object 16 | */ 17 | - (instancetype)initWithJSONObject:(NSDictionary *)jsonData error:(NSError *__autoreleasing *)error; 18 | 19 | /** 20 | Intercept class to allow notification/delegates 21 | @return initilialized instancetype object 22 | */ 23 | - (instancetype)trackModels; 24 | 25 | 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /SampleApp/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Resources/Responses/authenticated/myfavs.json: -------------------------------------------------------------------------------- 1 | {"data":[{"id":"ptgMh","title":"'What's the weirdest\/funniest\/most stupid thing you've ever done during sex?' Part II","description":null,"datetime":1395656483,"cover":"OS6wCGK","cover_width":0,"cover_height":0,"account_url":"mltex","privacy":"public","layout":"blog","views":19341,"link":"http:\/\/imgur.com\/a\/ptgMh","ups":0,"downs":0,"score":0,"is_album":true,"vote":null,"favorite":true,"nsfw":null,"section":null},{"id":"OLvRkCm","title":"I see your angry goldfish and raise you my evil Hitler goldfish","description":null,"datetime":1395659696,"type":"image\/jpeg","animated":false,"width":720,"height":960,"size":71058,"views":592738,"bandwidth":42118776804,"favorite":true,"nsfw":null,"section":null,"account_url":null,"link":"http:\/\/i.imgur.com\/OLvRkCm.jpg","is_album":false}],"success":true,"status":200} -------------------------------------------------------------------------------- /ImgurSession.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = 'ImgurSession' 3 | spec.version = '1.3' 4 | spec.license = {:type => 'MIT'} 5 | spec.homepage = 'https://github.com/geoffmacd/ImgurSession' 6 | spec.summary = 'Imgur API for Objective C' 7 | spec.source = {:git => 'https://github.com/geoffmacd/ImgurSession.git', :tag => '1.3'} 8 | spec.source_files = 'ImgurSession/**/*.{h,m}' 9 | spec.author = { "Geoff MacDonald" => "geoffmacd@gmail.com" } 10 | spec.framework = 'Foundation' 11 | spec.dependency 'AFNetworking', '2.6.3' 12 | spec.ios.deployment_target = '8.0' 13 | spec.osx.deployment_target = "10.9" 14 | spec.requires_arc = true 15 | 16 | spec.prefix_header_contents = <<-EOS 17 | #ifdef __OBJC__ 18 | #import 19 | #endif /* __OBJC__*/ 20 | EOS 21 | end 22 | -------------------------------------------------------------------------------- /Integrated Tests/IMGIntegratedTestCase.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGTestCase.h 3 | // ImgurSession 4 | // 5 | // Created by Xtreme Dev on 2014-03-18. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "ImgurSession.h" 12 | 13 | #define EXP_SHORTHAND YES 14 | #import "Expecta.h" 15 | 16 | @interface IMGIntegratedTestCase : XCTestCase { 17 | 18 | //various metadata to store 19 | NSDictionary *imgurUnitTestParams; 20 | NSURL * testfileURL; 21 | NSURL * testGifURL; 22 | __block void(^ failBlock)(NSError * error); 23 | BOOL anon; 24 | } 25 | 26 | -(void)postTestGalleryImage:(void(^)(IMGGalleryImage *,void(^)()))success; 27 | -(void)postTestImage:(void(^)(IMGImage *,void(^)()))success; 28 | -(void)postTestGalleryAlbumWithOneImage:(void(^)(IMGGalleryAlbum *,void(^)()))success; 29 | -(void)postTestAlbumWithOneImage:(void(^)(IMGAlbum *,void(^)()))success; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /Resources/ImgurSession-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | NSHumanReadableCopyright 26 | Distributed under the MIT license. 27 | NSPrincipalClass 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | 3 | 4 | target "ImgurSessionOSX", :exclusive => true do 5 | 6 | platform :osx, "10.9" 7 | pod 'AFNetworking', '~> 3.1.0' 8 | 9 | end 10 | 11 | target "OSXAuthenticatedTests", :exclusive => true do 12 | 13 | platform :osx, "10.9" 14 | pod 'Expecta' 15 | pod 'OCMock' 16 | pod 'OHHTTPStubs' 17 | 18 | end 19 | 20 | target "OSXAnonTests", :exclusive => true do 21 | 22 | platform :osx, "10.9" 23 | pod 'Expecta' 24 | pod 'OCMock' 25 | pod 'OHHTTPStubs' 26 | 27 | end 28 | 29 | target "OSXIntegratedAuthenticatedTests", :exclusive => true do 30 | 31 | platform :osx, "10.9" 32 | pod 'Expecta' 33 | 34 | end 35 | 36 | target "OSXIntegratedAnonTests", :exclusive => true do 37 | 38 | platform :osx, "10.9" 39 | pod 'Expecta' 40 | 41 | end 42 | 43 | target "ImgurSession", :exclusive => true do 44 | 45 | platform :ios, "8.0" 46 | pod 'AFNetworking', '~> 3.1.0' 47 | 48 | end 49 | 50 | target "iOSAuthenticatedTests", :exclusive => true do 51 | 52 | platform :ios, "8.0" 53 | pod 'Expecta' 54 | pod 'OCMock' 55 | pod 'OHHTTPStubs' 56 | 57 | end 58 | -------------------------------------------------------------------------------- /Stubbed Tests/IMGTestCase.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGTestCase.h 3 | // ImgurSession 4 | // 5 | // Created by Xtreme Dev on 2014-03-18. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "ImgurSession.h" 12 | 13 | #define EXP_SHORTHAND YES 14 | #import "Expecta.h" 15 | #import "OCMock.h" 16 | #import 17 | 18 | @interface IMGTestCase : XCTestCase { 19 | 20 | //various metadata to store 21 | NSDictionary *imgurUnitTestParams; 22 | __block void(^ failBlock)(NSError * error); 23 | 24 | NSURL * testfileURL; 25 | NSURL * testGifURL; 26 | 27 | } 28 | 29 | @property BOOL calledImgurView; 30 | 31 | /** 32 | Stub reponse for next request 33 | */ 34 | -(void)stubWithFile:(NSString * )filename; 35 | /** 36 | Stub reponse for next request with status code option 37 | */ 38 | -(void)stubWithFile:(NSString *)filename withStatusCode:(int)status; 39 | /** 40 | Stub reponse for next request with custom headers 41 | */ 42 | -(void)stubWithFile:(NSString * )filename withHeader:(NSDictionary*)headerDict; 43 | 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2014 "ImgurSession" contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /ImgurSession/Requests/IMGEndpoint.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGEndpoint.h 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-12. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "NSError+IMGError.h" 10 | 11 | /** 12 | Endpoint superclass for Imgur endpoints. Provides convenience methods common to all endpoints. 13 | */ 14 | @interface IMGEndpoint : NSObject 15 | 16 | /** 17 | @return path component common to this endpoint 18 | */ 19 | +(NSString*)pathComponent; 20 | 21 | /** 22 | @return full path to this endpoint 23 | */ 24 | +(NSString*)path; 25 | /** 26 | @return full path to this endpoint appended by /id1 27 | */ 28 | +(NSString*)pathWithID:(NSString*)id1; 29 | /** 30 | @return full path to this endpoint appended by /id1/option 31 | */ 32 | +(NSString*)pathWithID:(NSString*)id1 withOption:(NSString*)option; 33 | /** 34 | @return full path to this endpoint appended by /option/id2 35 | */ 36 | +(NSString*)pathWithOption:(NSString*)option withID2:(NSString*)id2; 37 | /** 38 | @return full path to this endpoint appended by /id1/option/id2 39 | */ 40 | +(NSString*)pathWithID:(NSString*)id1 withOption:(NSString*)option withID2:(NSString*)id2; 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGVote.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGVote.h 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-15. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGModel.h" 10 | 11 | typedef NS_ENUM(NSInteger, IMGVoteType) { 12 | IMGDownVote = -1, 13 | IMGNeutralVote = 0, 14 | IMGUpVote = 1 15 | }; 16 | 17 | 18 | /** 19 | Model object class to represent votes on images, albums, and comments. https://api.imgur.com/models/vote 20 | */ 21 | @interface IMGVote : IMGModel 22 | 23 | /** 24 | up votes 25 | */ 26 | @property (readonly,nonatomic) NSInteger ups; 27 | /** 28 | down votes 29 | */ 30 | @property (readonly,nonatomic) NSInteger downs; 31 | 32 | /** 33 | Return string for vote value 34 | @param vote enumerated value with IMGVoteType 35 | @return string description of vote as per https://api.imgur.com/endpoints/gallery#gallery-voting specifications 36 | */ 37 | +(NSString*)strForVote:(IMGVoteType)vote; 38 | /** 39 | Return NSInteger from enumerable representing input string 40 | @param voteStr string description of vote type 41 | @return integer representing this vote 42 | */ 43 | +(IMGVoteType)voteForStr:(NSString*)voteStr; 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /ImgurSession/Requests/IMGMemeGen.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGMemeGen.m 3 | // ImgurSession 4 | // 5 | // Created by Xtreme Dev on 2014-05-14. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGMemeGen.h" 10 | 11 | #import "IMGImage.h" 12 | #import "IMGSession.h" 13 | 14 | @implementation IMGMemeGen 15 | 16 | +(NSString*)pathComponent{ 17 | return @"memegen"; 18 | } 19 | 20 | +(void)defaultMemes:(void (^)(NSArray * memeImages))success failure:(void (^)(NSError * error))failure{ 21 | NSString *path = [self pathWithID:@"defaults"]; 22 | 23 | [[IMGSession sharedInstance] GET:path parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) { 24 | 25 | NSMutableArray * memeImages = [NSMutableArray new]; 26 | NSArray * jsonArray = responseObject; 27 | 28 | for(NSDictionary * json in jsonArray){ 29 | 30 | NSError *JSONError = nil; 31 | IMGImage * meme = [[IMGImage alloc] initWithJSONObject:json error:&JSONError]; 32 | if(!JSONError && meme) 33 | [memeImages addObject:meme]; 34 | } 35 | if(success) 36 | success([NSArray arrayWithArray:memeImages]); 37 | 38 | 39 | } failure:failure]; 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGGalleryObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGGalleryObject.h 3 | // ImgurSession 4 | // 5 | // Created by Xtreme Dev on 2014-04-14. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | #import "IMGVote.h" 9 | #import "IMGObject.h" 10 | 11 | #ifndef ImgurSession_IMGGalleryObject_h 12 | #define ImgurSession_IMGGalleryObject_h 13 | 14 | /** 15 | Protocol to represent both IMGGalleryImage and IMGGalleryAlbum which contain similar information. 16 | */ 17 | @protocol IMGGalleryObjectProtocol 18 | 19 | 20 | /** 21 | Has the user favorited the object, false if anon 22 | */ 23 | -(BOOL)isFavorite; 24 | /** 25 | Is it safe for work? 26 | */ 27 | -(BOOL)isNSFW; 28 | /** 29 | The user's vote for the object, if authenticated 30 | */ 31 | -(IMGVoteType)usersVote; 32 | /** 33 | ID for the gallery object 34 | */ 35 | -(NSString*)objectID; 36 | /** 37 | Score 38 | */ 39 | -(NSInteger)score; 40 | /** 41 | Ups 42 | */ 43 | -(NSInteger)ups; 44 | /** 45 | downs 46 | */ 47 | -(NSInteger)downs; 48 | /** 49 | Username who submitted this gallery image or album. 50 | */ 51 | -(NSString*)fromUsername; 52 | /** 53 | Sets users vote 54 | */ 55 | -(void)setUsersVote:(IMGVoteType)vote; 56 | /** 57 | Sets users fav 58 | */ 59 | -(void)setUsersFav:(BOOL)faved; 60 | 61 | 62 | @end 63 | 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGGalleryAlbum.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGGalleryAlbum.h 3 | // ImgurSession 4 | // 5 | // Created by Johann Pardanaud on 11/07/13. 6 | // Distributed under the MIT license. 7 | // 8 | 9 | #import "IMGAlbum.h" 10 | 11 | #import "IMGVote.h" 12 | #import "IMGGalleryImage.h" 13 | #import "IMGComment.h" 14 | 15 | 16 | /** 17 | Model object class to represent albums posted to the gallery. https://api.imgur.com/models/gallery_album 18 | */ 19 | @interface IMGGalleryAlbum : IMGBasicAlbum 20 | 21 | 22 | /** 23 | Users up or down vote on the image 24 | */ 25 | @property (nonatomic, readonly) IMGVoteType vote; 26 | /** 27 | Section description of album 28 | */ 29 | @property (nonatomic, readonly, copy) NSString *section; 30 | /** 31 | Global up votes 32 | */ 33 | @property (nonatomic, readonly) NSInteger ups; 34 | /** 35 | Global down votes 36 | */ 37 | @property (nonatomic, readonly) NSInteger downs; 38 | /** 39 | Up votes minus down vote. 40 | */ 41 | @property (nonatomic, readonly) NSInteger score; 42 | /** 43 | Has the user favorited? 44 | */ 45 | @property (nonatomic, readonly) BOOL favorite; 46 | /** 47 | Is it flagged NSFW> 48 | */ 49 | @property (nonatomic, readonly) BOOL nsfw; 50 | 51 | /** 52 | Custom init with comment 53 | */ 54 | -(instancetype)initWithComment:(IMGComment*)comment error:(NSError *__autoreleasing *)error; 55 | @end 56 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGMessage.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGMessage.h 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-15. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGModel.h" 10 | 11 | 12 | /** 13 | Model object class to represent messages. https://api.imgur.com/models/message 14 | */ 15 | @interface IMGMessage : IMGModel 16 | 17 | /** 18 | Message ID 19 | */ 20 | @property (readonly,nonatomic, copy) NSString * messageID; 21 | /** 22 | Username who sent the message 23 | */ 24 | @property (readonly,nonatomic, copy) NSString * fromUsername; 25 | /** 26 | Authors account id 27 | */ 28 | @property (readonly,nonatomic) NSInteger authorID; 29 | /** 30 | message subject 31 | */ 32 | @property (readonly,nonatomic, copy) NSString * subject; 33 | /** 34 | message body 35 | */ 36 | @property (readonly,nonatomic, copy) NSString * body; 37 | /** 38 | Readable string of time since now message was sent 39 | */ 40 | @property (readonly,nonatomic) NSDate * datetime; 41 | /** 42 | Parent convoId 43 | */ 44 | @property (readonly,nonatomic) NSInteger conversationID; 45 | 46 | /** 47 | Custom Init for use when usinbg model that hasn't been returned from server yet 48 | */ 49 | - (instancetype)initWithBody:(NSString*)body; 50 | 51 | //ALL MESSAGE ENDPOINTS ARE DEPRECATED, USE CONVERSATION INSTEAD 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGModel.m 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-15. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGModel.h" 10 | 11 | #import "IMGSession.h" 12 | 13 | //category for declaring non-public IMGSession method only exposed to this class 14 | @interface IMGSession() 15 | 16 | #pragma mark - Model Tracking 17 | /** 18 | Tracks new imgur Model objects being created to allow introspection by client 19 | @param model the model object that was created 20 | */ 21 | -(void)trackModelObjectsForDelegateHandling:(id)model; 22 | 23 | @end 24 | 25 | @implementation IMGModel 26 | 27 | - (instancetype)initWithJSONObject:(NSDictionary *)jsonData error:(NSError *__autoreleasing *)error{ 28 | NSAssert(NO, @"Should be overridden by subclass"); 29 | return nil; 30 | } 31 | 32 | -(instancetype)trackModels{ 33 | 34 | //track if object is not nil 35 | if(self) 36 | [[IMGSession sharedInstance] trackModelObjectsForDelegateHandling:self]; 37 | 38 | return self; 39 | } 40 | 41 | #pragma mark - NSCoding 42 | 43 | //all model objects conform to NSCoding 44 | 45 | - (id)initWithCoder:(NSCoder *)decoder { 46 | 47 | if(self = [super init]){ 48 | 49 | } 50 | return self; 51 | } 52 | 53 | -(void)encodeWithCoder:(NSCoder *)aCoder{ 54 | 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGConversation.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGConversation.h 3 | // ImgurSession 4 | // 5 | // Created by Xtreme Dev on 2014-03-20. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGModel.h" 10 | 11 | /** 12 | Model object class to represent conversation. Not listed on API docs 13 | */ 14 | @interface IMGConversation : IMGModel 15 | 16 | 17 | /** 18 | Message ID 19 | */ 20 | @property (readonly,nonatomic) NSInteger conversationID; 21 | /** 22 | Username who sent the message 23 | */ 24 | @property (readonly,nonatomic, copy) NSString * fromUsername; 25 | /** 26 | Authors account id 27 | */ 28 | @property (readonly,nonatomic) NSInteger authorID; 29 | /** 30 | message subject 31 | */ 32 | @property (readonly,nonatomic, copy) NSString * lastMessage; 33 | /** 34 | date last message was sent 35 | */ 36 | @property (readonly,nonatomic) NSDate * datetime; 37 | /** 38 | Number of messages sent back and fortg 39 | */ 40 | @property (readonly,nonatomic) NSInteger messageCount; 41 | /** 42 | Actual messages send with /{id} 43 | */ 44 | @property (readonly,nonatomic, copy) NSArray * messages; 45 | 46 | 47 | /** 48 | Special Init for notifications with different keys returned from /notification endpoints 49 | */ 50 | - (instancetype)initWithJSONObjectFromNotification:(NSDictionary *)jsonData error:(NSError *__autoreleasing *)error; 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGGalleryImage.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGGalleryImage.h 3 | // ImgurSession 4 | // 5 | // Created by Johann Pardanaud on 11/07/13. 6 | // Distributed under the MIT license. 7 | // 8 | 9 | #import "IMGImage.h" 10 | 11 | #import "IMGVote.h" 12 | #import "IMGGalleryObject.h" 13 | #import "IMGComment.h" 14 | 15 | /** 16 | Model object class to represent images that are posted to the Imgur Gallery. Can be a part of an album. https://api.imgur.com/models/gallery_image 17 | */ 18 | @interface IMGGalleryImage : IMGImage 19 | 20 | /** 21 | Users up or down vote on the image 22 | */ 23 | @property (nonatomic, readonly) IMGVoteType vote; 24 | /** 25 | Username of submitter if not anon 26 | */ 27 | @property (nonatomic, readonly, copy) NSString *accountURL; 28 | /** 29 | Global up votes 30 | */ 31 | @property (nonatomic, readonly) NSInteger ups; 32 | /** 33 | Global down votes 34 | */ 35 | @property (nonatomic, readonly) NSInteger downs; 36 | /** 37 | Up votes minus down vote. 38 | */ 39 | @property (nonatomic, readonly) NSInteger score; 40 | /** 41 | Has the user favorited? 42 | */ 43 | @property (nonatomic, readonly) BOOL favorite; 44 | /** 45 | Is it flagged NSFW> 46 | */ 47 | @property (nonatomic, readonly) BOOL nsfw; 48 | 49 | /** 50 | Custom init with comment 51 | */ 52 | -(instancetype)initWithComment:(IMGComment*)comment error:(NSError *__autoreleasing *)error; 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGAccount.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGAccount.h 3 | // ImgurSession 4 | // 5 | // Created by Johann Pardanaud on 10/07/13. 6 | // Distributed under the MIT license. 7 | // 8 | 9 | #import "IMGModel.h" 10 | 11 | /** 12 | Model object class to represent account settings. https://api.imgur.com/models/account 13 | */ 14 | @interface IMGAccount : IMGModel 15 | 16 | /** 17 | Account ID 18 | */ 19 | @property (nonatomic, readonly) NSInteger accountID; 20 | /** 21 | Username string 22 | */ 23 | @property (nonatomic, readonly, copy) NSString *username; 24 | /** 25 | Biography string displayed on right pane on account page 26 | */ 27 | @property (nonatomic, readonly, copy) NSString *bio; 28 | /** 29 | Reputation 30 | */ 31 | @property (nonatomic, readonly) NSInteger reputation; 32 | /** 33 | Creation date for account 34 | */ 35 | @property (nonatomic, readonly) NSDate *created; 36 | 37 | #pragma mark - Initializer 38 | /** 39 | @param jsonData response "data" json Object for account 40 | @param username name of account 41 | @param error address of error object to output to 42 | */ 43 | - (instancetype)initWithJSONObject:(NSDictionary *)jsonData withName:(NSString*)username error:(NSError * __autoreleasing *)error; 44 | 45 | 46 | 47 | #pragma mark - Convenience 48 | 49 | /** 50 | Returns string representing 'notoriety' as seen on Imgur account page based on reputation. Not localized. 51 | */ 52 | -(NSString*)notorietyString; 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /ImgurSession/Requests/IMGNotificationRequest.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGNotificationRequest.h 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-15. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGEndpoint.h" 10 | 11 | 12 | @class IMGNotification; 13 | 14 | /** 15 | User notification requests. https://api.imgur.com/endpoints/notification 16 | */ 17 | @interface IMGNotificationRequest : IMGEndpoint 18 | 19 | #pragma mark - Load 20 | /** 21 | Get all unread notifications for the user that's currently logged in. Must be logged in. 22 | */ 23 | + (void)unreadNotifications:(void (^)(NSArray * notifications))success failure:(void (^)(NSError * error))failure; 24 | 25 | /** 26 | Get all notifications for the user that's currently logged in. Must be logged in. 27 | */ 28 | + (void)allNotifications:(void (^)(NSArray * notifications))success failure:(void (^)(NSError * error))failure; 29 | /** 30 | Returns the data about a specific notification. Must be logged in. 31 | */ 32 | + (void)notificationWithID:(NSString*)notificationId success:(void (^)(IMGNotification * notification))success failure:(void (^)(NSError * error))failure; 33 | 34 | 35 | #pragma mark - Delete 36 | /** 37 | Marks a notification as viewed, this way it no longer shows up in the basic notification request. Must be logged in. 38 | */ 39 | + (void)notificationViewed:(NSString *)notificationId success:(void (^)())success failure:(void (^)(NSError * error))failure; 40 | 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /SampleApp/SampleApp-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | 32 | UIMainStoryboardFile 33 | Main 34 | UIRequiredDeviceCapabilities 35 | 36 | armv7 37 | 38 | UISupportedInterfaceOrientations 39 | 40 | UIInterfaceOrientationPortrait 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ImgurSession/ImgurSession.h: -------------------------------------------------------------------------------- 1 | // 2 | // ImgurSession.h 3 | // ImgurSession 4 | // 5 | // Created by Xtreme Dev on 2014-03-19. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | 10 | //iOS 7 or OS X 10.9 only 11 | #if ( ( defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) || \ 12 | ( defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 ) ) 13 | 14 | #ifndef __ImgurSession__ 15 | #define __ImgurSession__ 16 | 17 | #import "IMGSession.h" 18 | 19 | #import "IMGAccountRequest.h" 20 | #import "IMGAlbumRequest.h" 21 | #import "IMGCommentRequest.h" 22 | #import "IMGConversationRequest.h" 23 | #import "IMGGalleryRequest.h" 24 | #import "IMGImageRequest.h" 25 | #import "IMGNotificationRequest.h" 26 | #import "IMGMemeGen.h" 27 | 28 | #import "IMGObject.h" 29 | #import "IMGGalleryObject.h" 30 | 31 | #import "IMGAccount.h" 32 | #import "IMGAccountSettings.h" 33 | #import "IMGAlbum.h" 34 | #import "IMGBasicAlbum.h" 35 | #import "IMGComment.h" 36 | #import "IMGConversation.h" 37 | #import "IMGNotification.h" 38 | #import "IMGGalleryAlbum.h" 39 | #import "IMGGalleryImage.h" 40 | #import "IMGGalleryProfile.h" 41 | #import "IMGImage.h" 42 | #import "IMGMessage.h" 43 | #import "IMGMeme.h" 44 | #import "IMGVote.h" 45 | 46 | #endif 47 | 48 | #endif -------------------------------------------------------------------------------- /ImgurSession/Requests/IMGEndpoint.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGEndpoint.m 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-12. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGEndpoint.h" 10 | 11 | #import "IMGSession.h" 12 | 13 | 14 | @implementation IMGEndpoint 15 | 16 | +(NSString *)pathComponent{ 17 | NSAssert(NO, @"Should be overridden by subclass"); 18 | return nil; 19 | } 20 | 21 | +(NSString*)path{ 22 | return [NSString stringWithFormat:@"%@/%@", IMGAPIVersion, [self pathComponent]]; 23 | } 24 | 25 | +(NSString*)pathWithID:(NSString*)id1 { 26 | NSParameterAssert(id1); 27 | 28 | return [NSString stringWithFormat:@"%@/%@/%@", IMGAPIVersion, [self pathComponent], id1]; 29 | } 30 | 31 | +(NSString*)pathWithID:(NSString*)id1 withOption:(NSString*)option{ 32 | NSParameterAssert(id1); 33 | NSParameterAssert(option); 34 | 35 | return [NSString stringWithFormat:@"%@/%@/%@/%@", IMGAPIVersion, [self pathComponent], id1, option]; 36 | } 37 | 38 | +(NSString*)pathWithOption:(NSString*)option withID2:(NSString*)id2{ 39 | NSParameterAssert(id2); 40 | NSParameterAssert(option); 41 | 42 | return [NSString stringWithFormat:@"%@/%@/%@/%@", IMGAPIVersion, [self pathComponent], option, id2]; 43 | } 44 | 45 | +(NSString*)pathWithID:(NSString*)id1 withOption:(NSString*)option withID2:(NSString*)id2{ 46 | NSParameterAssert(id1); 47 | NSParameterAssert(option); 48 | NSParameterAssert(id2); 49 | 50 | return [NSString stringWithFormat:@"%@/%@/%@/%@/%@", IMGAPIVersion, [self pathComponent], id1, option, id2]; 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /Stubbed Tests/IMGGalleryTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGGalleryTests.m 3 | // ImgurSession 4 | // 5 | // Created by Xtreme Dev on 2014-03-19. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGTestCase.h" 10 | 11 | @interface IMGGalleryTests : IMGTestCase 12 | 13 | @end 14 | 15 | @implementation IMGGalleryTests 16 | 17 | - (void)testGalleryHot{ 18 | 19 | __block BOOL isSuccess; 20 | [self stubWithFile:@"hotgallery.json"]; 21 | 22 | [IMGGalleryRequest hotGalleryPage:0 success:^(NSArray * images) { 23 | 24 | expect(images).haveCountOf(185); 25 | 26 | isSuccess = YES; 27 | 28 | } failure:failBlock]; 29 | 30 | expect(isSuccess).will.beTruthy(); 31 | } 32 | 33 | - (void)testGalleryViral{ 34 | 35 | __block BOOL isSuccess; 36 | [self stubWithFile:@"topgallery.json"]; 37 | 38 | [IMGGalleryRequest topGalleryPage:0 withWindow:IMGTopGalleryWindowDay success:^(NSArray * images) { 39 | 40 | expect(images).haveCountOf(185); 41 | 42 | isSuccess = YES; 43 | 44 | } failure:failBlock]; 45 | 46 | expect(isSuccess).will.beTruthy(); 47 | } 48 | 49 | - (void)testGalleryUser{ 50 | 51 | __block BOOL isSuccess; 52 | [self stubWithFile:@"usersubmittedgallery.json"]; 53 | 54 | [IMGGalleryRequest userGalleryPage:0 withViralSort:YES showViral:YES success:^(NSArray * images) { 55 | 56 | expect(images).haveCountOf(185); 57 | 58 | isSuccess = YES; 59 | 60 | } failure:failBlock]; 61 | 62 | expect(isSuccess).will.beTruthy(); 63 | } 64 | 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /Stubbed Tests/IMGImageTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGImageTests.m 3 | // ImgurSession 4 | // 5 | // Created by Xtreme Dev on 2014-05-13. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGTestCase.h" 10 | 11 | @interface IMGImageTests : IMGTestCase 12 | 13 | @end 14 | 15 | 16 | @implementation IMGImageTests 17 | 18 | -(void)testPostImage{ 19 | 20 | __block BOOL isPosted; 21 | 22 | [self stubWithFile:@"postimage.json"]; 23 | 24 | [IMGImageRequest uploadImageWithURL:testfileURL success:^(IMGImage *image) { 25 | 26 | isPosted = YES; 27 | } failure:failBlock]; 28 | 29 | expect(isPosted).will.beTruthy(); 30 | } 31 | 32 | -(void)testImageNotFound{ 33 | 34 | __block BOOL isSuccess = NO; 35 | 36 | //should fail request with not found 37 | [self stubWithFile:@"imagenotfound.json" withStatusCode:404]; 38 | 39 | [IMGImageRequest imageWithID:@"fdsfdsfdsa" success:^(IMGImage *image) { 40 | 41 | //should not success 42 | failBlock([NSError errorWithDomain:IMGErrorDomain code:0 userInfo:nil]); 43 | 44 | } failure:^(NSError *error) { 45 | 46 | //imgur sometimes responds with previous account requests for some reasons saying it is a cache hit even though it is a different URL 47 | //in this case this test will fail with code 1 == IMGErrorResponseMissingParameters 48 | 49 | expect(error.code == 404).beTruthy(); 50 | expect([error.userInfo[IMGErrorServerMethod] isEqualToString:@"GET"]).beTruthy(); 51 | 52 | //should go here 53 | isSuccess = YES; 54 | }]; 55 | 56 | expect(isSuccess).will.beTruthy(); 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGNotification.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGNotification.h 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-15. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGModel.h" 10 | 11 | @class IMGComment, IMGMessage,IMGConversation; 12 | 13 | /** 14 | Model object class to represent user notifications. https://api.imgur.com/models/notifications 15 | */ 16 | @interface IMGNotification : IMGModel 17 | 18 | /** 19 | notification ID 20 | */ 21 | @property (readonly,nonatomic, copy) NSString * notificationID; 22 | /** 23 | Account ID notification is associated with 24 | */ 25 | @property (readonly,nonatomic) NSInteger accountID; 26 | /** 27 | Has notification been viewed yet? 28 | */ 29 | @property (readwrite,nonatomic) BOOL isViewed; 30 | /** 31 | Is the notification a IMGComment? Else it is IMGConversation 32 | */ 33 | @property (readonly,nonatomic) BOOL isReply; 34 | /** 35 | Message object if this notification was a reply to a user's post 36 | */ 37 | @property (readonly,nonatomic) IMGComment * reply; 38 | /** 39 | Conversation object if this notification was a conversation 40 | */ 41 | @property (readonly,nonatomic) IMGConversation * conversation; 42 | /** 43 | Datetime notification was triggered 44 | */ 45 | @property (readonly,nonatomic) NSDate * datetime; 46 | 47 | 48 | /** 49 | Special Init for notification with IMGComment object 50 | */ 51 | - (instancetype)initReplyNotificationWithJSONObject:(NSDictionary *)jsonData error:(NSError *__autoreleasing *)error; 52 | /** 53 | Special Init for notification with IMGConversation object 54 | */ 55 | - (instancetype)initConversationNotificationWithJSONObject:(NSDictionary *)jsonData error:(NSError *__autoreleasing *)error; 56 | 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /Integrated Tests/IMGCommentTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGCommentTests.m 3 | // ImgurSession 4 | // 5 | // Created by Xtreme Dev on 2014-03-20. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGIntegratedTestCase.h" 10 | 11 | @interface IMGCommentTests : IMGIntegratedTestCase 12 | 13 | @end 14 | 15 | 16 | @implementation IMGCommentTests 17 | 18 | /** 19 | Posts image to comment on, comments on it, replies to comment, then deletes everything 20 | */ 21 | #warning: may fail due to comment rate, which imgur thinks is spam 22 | - (void)testCommentReplyAndDelete{ 23 | 24 | __block BOOL deleteSuccess = NO; 25 | 26 | [self postTestImage:^(IMGImage * image, void(^success)()){ 27 | 28 | [IMGCommentRequest submitComment:@"test comment" withImageID:image.imageID success:^(NSInteger commentId) { 29 | 30 | [IMGCommentRequest replyToComment:@"test reply" withImageID:image.imageID withParentCommentID:commentId success:^(NSInteger replyId) { 31 | 32 | 33 | [IMGCommentRequest deleteCommentWithID:replyId success:^() { 34 | 35 | [IMGCommentRequest deleteCommentWithID:commentId success:^() { 36 | 37 | success(); 38 | deleteSuccess = YES; 39 | 40 | } failure:failBlock]; 41 | 42 | } failure:failBlock]; 43 | 44 | } failure:failBlock]; 45 | 46 | } failure:failBlock]; 47 | }]; 48 | 49 | expect(deleteSuccess).will.beTruthy(); 50 | } 51 | 52 | -(void)testReportComment{ 53 | 54 | //Not sure how to implement test of this without destroying account 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /Stubbed Tests/IMGCommentTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGCommentTests.m 3 | // ImgurSession 4 | // 5 | // Created by Xtreme Dev on 2014-03-20. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGTestCase.h" 10 | 11 | @interface IMGCommentTests : IMGTestCase 12 | 13 | @end 14 | 15 | 16 | @implementation IMGCommentTests 17 | 18 | /** 19 | Posts image to comment on, comments on it, replies to comment, then deletes everything 20 | */ 21 | - (void)testCommentSubmit{ 22 | 23 | __block BOOL isSuccess; 24 | [self stubWithFile:@"postcomment.json"]; 25 | 26 | [IMGCommentRequest submitComment:@"test comment" withImageID:@"grsgdf" success:^(NSInteger commentId) { 27 | 28 | expect(commentId).beGreaterThan(0); 29 | isSuccess = YES; 30 | 31 | } failure:failBlock]; 32 | 33 | expect(isSuccess).will.beTruthy(); 34 | } 35 | 36 | -(void)testCommentReply{ 37 | 38 | __block BOOL isSuccess; 39 | [self stubWithFile:@"replytocomment.json"]; 40 | 41 | [IMGCommentRequest replyToComment:@"test reply" withImageID:@"sdfsdf" withParentCommentID:4354363476 success:^(NSInteger replyId) { 42 | 43 | expect(replyId).beGreaterThan(0); 44 | isSuccess = YES; 45 | 46 | } failure:failBlock]; 47 | 48 | expect(isSuccess).will.beTruthy(); 49 | } 50 | 51 | -(void)testCommentDelete{ 52 | 53 | __block BOOL isSuccess; 54 | [self stubWithFile:@"deletecomment.json"]; 55 | 56 | [IMGCommentRequest deleteCommentWithID:25235 success:^() { 57 | 58 | isSuccess = YES; 59 | 60 | } failure:failBlock]; 61 | 62 | 63 | expect(isSuccess).will.beTruthy(); 64 | } 65 | 66 | -(void)testReportComment{ 67 | 68 | //Not sure how to implement test of this without destroying account 69 | } 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGMeme.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGMeme.m 3 | // ImgurSession 4 | // 5 | // Created by Xtreme Dev on 2014-05-14. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGMeme.h" 10 | 11 | @interface IMGImage () 12 | 13 | @property (readwrite,nonatomic) NSString *imageID; 14 | @property (readwrite,nonatomic) NSString *title; 15 | @property (readwrite,nonatomic) NSString * imageDescription; 16 | @property (readwrite,nonatomic) NSDate *datetime; 17 | @property (readwrite,nonatomic) NSString *type; 18 | @property (readwrite,nonatomic) BOOL animated; 19 | @property (readwrite,nonatomic) CGFloat width; 20 | @property (readwrite,nonatomic) CGFloat height; 21 | @property (readwrite,nonatomic) NSInteger size; 22 | @property (readwrite,nonatomic) NSInteger views; 23 | @property (readwrite,nonatomic) NSInteger bandwidth; 24 | @property (readwrite,nonatomic) NSString *deletehash; 25 | @property (readwrite,nonatomic) NSString *section; 26 | @property (readwrite,nonatomic) NSURL *url; 27 | 28 | @end 29 | 30 | @interface IMGMeme() 31 | 32 | @property (readwrite,nonatomic) NSString * topText; 33 | @property (readwrite,nonatomic) NSString * bottomText; 34 | 35 | @end 36 | 37 | //TODO: - implementation using Core Graphics on images 38 | 39 | @implementation IMGMeme 40 | 41 | -(instancetype)initWithImage:(IMGImage*)image withTopText:(NSString*)top withBottomText:(NSString*)bottom withTitle:(NSString*)title withDescription:(NSString*)description{ 42 | 43 | NSError * err; 44 | 45 | //init with bare bones IMGImage 46 | if((self = [super initWithGalleryID:image.imageID error:&err]) && !err){ 47 | 48 | self.imageDescription = description; 49 | self.title = title; 50 | 51 | _bottomText = bottom; 52 | _topText = top; 53 | 54 | 55 | 56 | } 57 | return self; 58 | } 59 | 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /ImgurSession/Requests/IMGConversationRequest.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGConversationRequest.h 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-15. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGEndpoint.h" 10 | 11 | @class IMGMessage,IMGConversation; 12 | 13 | /** 14 | Conversation requests. https://api.imgur.com/endpoints/conversation 15 | */ 16 | @interface IMGConversationRequest : IMGEndpoint 17 | 18 | 19 | #pragma mark - Load 20 | /** 21 | Get list of all conversations for the logged in user. Must be logged in. 22 | */ 23 | + (void)conversations:(void (^)(NSArray * conversations))success failure:(void (^)(NSError *error))failure; 24 | /** 25 | Get information about a specific conversation. Includes messages. Must be logged in. 26 | */ 27 | + (void)conversationWithMessageID:(NSInteger)messageID success:(void (^)(IMGConversation * conversation))success failure:(void (^)(NSError *error))failure; 28 | 29 | #pragma mark - Create 30 | /** 31 | Create a new message. Must be logged in. 32 | */ 33 | + (void)createMessageWithRecipient:(NSString*)recipient withBody:(NSString*)body success:(void (^)())success failure:(void (^)(NSError *error))failure; 34 | 35 | 36 | #pragma mark - Delete 37 | /** 38 | Delete a conversation. Must be logged in. 39 | @param commentId comment id to delete 40 | */ 41 | + (void)deleteConversation:(NSInteger)convoID success:(void (^)())success failure:(void (^)(NSError *error))failure; 42 | 43 | #pragma mark - Report 44 | /** 45 | Report a user for sending messages that are against the Terms of Service. Must be logged in. 46 | */ 47 | + (void)reportSender:(NSString*)username success:(void (^)())success failure:(void (^)(NSError *error))failure; 48 | 49 | #pragma mark - Block 50 | /** 51 | Report a user for sending messages that are against the Terms of Service. Must be logged in. 52 | */ 53 | + (void)blockSender:(NSString*)username success:(void (^)())success failure:(void (^)(NSError *error))failure; 54 | @end 55 | -------------------------------------------------------------------------------- /Stubbed Tests/IMGNotificationTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGNotificationTests.m 3 | // ImgurSession 4 | // 5 | // Created by Xtreme Dev on 2014-03-20. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | 10 | #import "IMGTestCase.h" 11 | 12 | @interface IMGNotificationTests : IMGTestCase 13 | 14 | @end 15 | 16 | 17 | @implementation IMGNotificationTests 18 | 19 | - (void)testLoadNotifications{ 20 | 21 | __block BOOL isSuccess; 22 | [self stubWithFile:@"freshnotification.json"]; 23 | 24 | [IMGNotificationRequest unreadNotifications:^(NSArray * notifications) { 25 | 26 | expect(notifications).haveCountOf(1); 27 | IMGNotification * first = [notifications firstObject]; 28 | expect(first).beInstanceOf([IMGNotification class]); 29 | expect(first.notificationID).beTruthy(); 30 | expect(first.accountID).beTruthy(); 31 | 32 | isSuccess = YES; 33 | 34 | } failure:failBlock]; 35 | 36 | expect(isSuccess).will.beTruthy(); 37 | } 38 | 39 | - (void)testViewedNotification{ 40 | 41 | __block BOOL isSuccess; 42 | [self stubWithFile:@"deletenotification.json"]; 43 | 44 | //mark first one as viewed 45 | [IMGNotificationRequest notificationViewed:@"dsgdsg" success:^{ 46 | 47 | isSuccess = YES; 48 | 49 | } failure:failBlock]; 50 | 51 | expect(isSuccess).will.beTruthy(); 52 | } 53 | 54 | - (void)testLoadStaleNotifications{ 55 | 56 | __block BOOL isSuccess; 57 | [self stubWithFile:@"allnotifications.json"]; 58 | 59 | [IMGNotificationRequest allNotifications:^(NSArray * notifications) { 60 | 61 | expect(notifications).haveCountOf(1); 62 | IMGNotification * first = [notifications firstObject]; 63 | expect(first).beInstanceOf([IMGNotification class]); 64 | expect(first.notificationID).beTruthy(); 65 | expect(first.accountID).beTruthy(); 66 | 67 | isSuccess = YES; 68 | 69 | } failure:failBlock]; 70 | 71 | expect(isSuccess).will.beTruthy(); 72 | } 73 | @end 74 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGVote.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGVote.m 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-15. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGVote.h" 10 | 11 | @implementation IMGVote 12 | 13 | #pragma mark - Init With Json 14 | 15 | - (instancetype)initWithJSONObject:(NSDictionary *)jsonData error:(NSError *__autoreleasing *)error{ 16 | 17 | if(self = [super init]) { 18 | 19 | if(![jsonData isKindOfClass:[NSDictionary class]]){ 20 | 21 | if(error) 22 | *error = [NSError errorWithDomain:IMGErrorDomain code:IMGErrorMalformedResponseFormat userInfo:@{@"ImgurClass":[self class]}]; 23 | return nil; 24 | } else if (!jsonData[@"ups"] || !jsonData[@"downs"]){ 25 | 26 | if(error) 27 | *error = [NSError errorWithDomain:IMGErrorDomain code:IMGErrorResponseMissingParameters userInfo:nil]; 28 | return nil; 29 | } 30 | _ups = [jsonData[@"ups"] integerValue]; 31 | _downs = [jsonData[@"downs"] integerValue]; 32 | } 33 | return [self trackModels]; 34 | } 35 | 36 | #pragma mark - Describe 37 | 38 | - (NSString *)description{ 39 | return [NSString stringWithFormat:@"%@; ups: \"%ld\"; downs: \"%ld\";", [super description], (long)self.ups, (long)self.downs]; 40 | } 41 | 42 | +(NSString*)strForVote:(IMGVoteType)vote{ 43 | NSString * str; 44 | switch (vote) { 45 | case IMGDownVote: 46 | str = @"down"; 47 | break; 48 | 49 | case IMGUpVote: 50 | str = @"up"; 51 | break; 52 | case IMGNeutralVote: 53 | str = @""; 54 | break; 55 | default: 56 | break; 57 | } 58 | return str; 59 | } 60 | 61 | +(IMGVoteType)voteForStr:(NSString*)voteStr{ 62 | 63 | if([voteStr isEqualToString:@"up"]) 64 | return IMGUpVote; 65 | else if([voteStr isEqualToString:@"down"]) 66 | return IMGDownVote; 67 | else 68 | return IMGNeutralVote; 69 | } 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGObject.h 3 | // ImgurSession 4 | // 5 | // Created by Xtreme Dev on 2014-04-14. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | @class IMGImage; 10 | 11 | /* 12 | There are 6 total thumbnails that an image can be resized to. Each one is accessable by appending a single character suffix to the end of the image id, and before the file extension. The thumbnails are: 13 | 14 | Thumbnail Suffix Thumbnail Name Thumbnail Size Keeps Image Proportions 15 | s Small Square 90x90 No 16 | b Big Square 160x160 No 17 | t Small Thumbnail 160x160 Yes 18 | m Medium Thumbnail 320x320 Yes 19 | l Large Thumbnail 640x640 Yes 20 | h Huge Thumbnail 1024x1024 Yes 21 | For example, the image located at http://i.imgur.com/12345.jpg has the Medium Thumbnail located at http://i.imgur.com/12345m.jpg 22 | */ 23 | typedef NS_ENUM(NSInteger, IMGSize) { 24 | IMGSmallSquareSize, 25 | IMGBigSquareSize, 26 | IMGSmallThumbnailSize, 27 | IMGMediumThumbnailSize, 28 | IMGLargeThumbnailSize, 29 | IMGHugeThumbnailSize 30 | }; 31 | 32 | #ifndef ImgurSession_IMGObject_h 33 | #define ImgurSession_IMGObject_h 34 | 35 | /** 36 | Protocol to represent both IMGGalleryImage and IMGGalleryAlbum which contain similar information. 37 | */ 38 | @protocol IMGObjectProtocol 39 | 40 | /** 41 | Is the object an an album 42 | */ 43 | -(BOOL)isAlbum; 44 | /** 45 | Get the cover image representation of object 46 | */ 47 | -(IMGImage*)coverImage; 48 | /** 49 | Set the cover image representation of object 50 | */ 51 | -(void)setCoverImage:(IMGImage*)coverImage; 52 | /** 53 | ID for the object 54 | */ 55 | -(NSString*)objectID; 56 | /** 57 | Title of object 58 | */ 59 | -(NSString*)title; 60 | /** 61 | ID of cover Image 62 | */ 63 | -(NSString*)coverID; 64 | /** 65 | Views 66 | */ 67 | -(NSInteger)views; 68 | /** 69 | description 70 | */ 71 | -(NSString*)galleryDescription; 72 | /** 73 | Get thumbnails for image/album 74 | */ 75 | - (NSURL *)URLWithSize:(IMGSize)size; 76 | /** 77 | Get date object was created 78 | */ 79 | - (NSDate *)datetime; 80 | /** 81 | Get imgur web page for object 82 | */ 83 | -(NSURL*)link; 84 | 85 | @end 86 | 87 | 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /ImgurSession/NSError+IMGError.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSError+IMGError.h 3 | // ImgurSession 4 | // 5 | // Created by Xtreme Dev on 2014-04-22. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //error keys 12 | #define IMGErrorDomain @"com.geoffmacdonald.imgursession" 13 | #define IMGErrorServerDescription NSLocalizedDescriptionKey //the 'error' key within the 'data' object delivered by failed requests 14 | #define IMGErrorServerMethod @"ImgurErrorMethod" //the 'method' key within the 'data' object delivered by failed requests 15 | #define IMGErrorServerPath @"ImgurErrorPath" //the 'request' key within the 'data' object delivered by failed requests 16 | #define IMGErrorDecoding @"ImgurOriginalDecodingError" //key for original error object when decoding 17 | #define IMGErrorAuthenticationError @"ImgurAuthenticationError" //key for original error object when decoding 18 | 19 | //error codes from model object creation 20 | #define IMGErrorMalformedResponseFormat 152 //Response data is in wrong format for model object to be created 21 | #define IMGErrorResponseMissingParameters 153 //some critical fields are not in response for model objects 22 | #define IMGErrorNeededVerificationAndSent 154 //request failed but email was sent to verify user email 23 | #define IMGErrorNeededVerificationCouldNotSend 155 //request failed due to email verification needed 24 | 25 | //status codes 26 | #define IMGErrorInvalidRefreshToken 400 //also used as non-specific incorrect parameters 27 | #define IMGErrorRequiresUserAuthentication 401 //valid tokens? 28 | #define IMGErrorForbidden 403 //valid tokens or rate limiting? 29 | #define IMGErrorUserRateLimitExceeded 429 //user rate limit hit 30 | 31 | //non-status code custom codes 32 | #define IMGErrorMissingClientAuthentication 150 //no authentication parameters for authorized session 33 | #define IMGErrorCouldNotAuthenticate 151 //refresh token did not succeed, possibly banned or rate limited? 34 | 35 | @interface NSError (IMGError) 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Integrated Tests/IMGNotificationTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGNotificationTests.m 3 | // ImgurSession 4 | // 5 | // Created by Xtreme Dev on 2014-03-20. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | 10 | #import "IMGIntegratedTestCase.h" 11 | 12 | @interface IMGNotificationTests : IMGIntegratedTestCase 13 | 14 | @end 15 | 16 | 17 | @implementation IMGNotificationTests 18 | 19 | - (void)testLoadNotifications{ 20 | 21 | __block BOOL isSuccess; 22 | 23 | [IMGNotificationRequest unreadNotifications:^(NSArray * notifications) { 24 | 25 | isSuccess = YES; 26 | 27 | } failure:failBlock]; 28 | 29 | expect(isSuccess).willNot.beNil(); 30 | } 31 | 32 | - (void)testLoadNotificationsAndMarkOneAsViewed{ 33 | 34 | __block BOOL isSuccess; 35 | 36 | [IMGNotificationRequest unreadNotifications:^(NSArray * notifications) { 37 | 38 | IMGNotification * first = [notifications firstObject]; 39 | 40 | if(first){ 41 | 42 | //mark first one as viewed 43 | [IMGNotificationRequest notificationViewed:first.notificationID success:^{ 44 | 45 | isSuccess = YES; 46 | 47 | } failure:failBlock]; 48 | } else { 49 | isSuccess = YES; 50 | } 51 | 52 | } failure:failBlock]; 53 | 54 | expect(isSuccess).will.beTruthy(); 55 | } 56 | 57 | - (void)testLoadStaleNotifications{ 58 | 59 | __block BOOL isSuccess; 60 | 61 | [IMGNotificationRequest allNotifications:^(NSArray * notifications) { 62 | 63 | isSuccess = YES; 64 | 65 | } failure:failBlock]; 66 | 67 | expect(isSuccess).willNot.beNil(); 68 | } 69 | 70 | -(void)testSessionRefreshsNotifications{ 71 | __block BOOL isSuccess; 72 | 73 | //force login 74 | [[IMGSession sharedInstance] refreshUserAccount:^(IMGAccount *user) { 75 | 76 | [[IMGSession sharedInstance] checkUserUnreadNotifications:^(NSArray *unreadNotifications) { 77 | 78 | isSuccess = YES; 79 | 80 | } failure:failBlock]; 81 | 82 | } failure:failBlock]; 83 | 84 | expect(isSuccess).will.beTruthy(); 85 | } 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGGalleryProfile.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGGalleryProfile.h 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-15. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGModel.h" 10 | 11 | 12 | @interface IMGGalleryTrophy : IMGModel 13 | 14 | /** 15 | Trophy ID 16 | */ 17 | @property (readonly,nonatomic, copy) NSString * trophyID; 18 | /** 19 | The name of the trophy 20 | */ 21 | @property (readonly,nonatomic, copy) NSString * name; 22 | /** 23 | Can be thought of as the ID of a trophy type 24 | */ 25 | @property (readonly,nonatomic, copy) NSString * type; 26 | /** 27 | A description of the trophy and how it was earned. 28 | */ 29 | @property (readonly,nonatomic, copy) NSString * profileDescription; 30 | /** 31 | The ID of the image or the ID of the comment where the trophy was earned 32 | */ 33 | @property (readonly,nonatomic, copy) NSString * data; 34 | /** 35 | A link to where the trophy was earned 36 | */ 37 | @property (readonly,nonatomic) NSURL * link; 38 | /** 39 | Date the trophy was earned, epoch time 40 | */ 41 | @property (readonly,nonatomic) NSDate *dateAwarded; 42 | /** 43 | Image URL for trophy representation 44 | */ 45 | @property (readonly,nonatomic) NSURL * imageUrl; 46 | 47 | 48 | 49 | @end 50 | 51 | /** 52 | Model object class to represent user gallery profile. https://api.imgur.com/models/gallery_profile 53 | */ 54 | @interface IMGGalleryProfile : IMGModel 55 | 56 | /** 57 | Total number of comments the user has made in the gallery 58 | */ 59 | @property (readonly,nonatomic) NSInteger totalComments; 60 | /** 61 | Total number of images liked by the user in the gallery 62 | */ 63 | @property (readonly,nonatomic) NSInteger totalLikes; 64 | /** 65 | Total number of images submitted by the user. 66 | */ 67 | @property (readonly,nonatomic) NSInteger totalSubmissions; 68 | /** 69 | An array of trophies that the user has. 70 | */ 71 | @property (readonly,nonatomic, copy) NSArray * trophies; 72 | /** 73 | Username for gallery profile 74 | */ 75 | @property (readonly,nonatomic, copy) NSString * userName; 76 | 77 | 78 | 79 | /** 80 | Custom init with username 81 | */ 82 | - (instancetype)initWithUser:(NSString*)username JSONObject:(NSDictionary *)jsonData error:(NSError *__autoreleasing *)error; 83 | 84 | @end 85 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGAlbum.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGAlbum.m 3 | // ImgurSession 4 | // 5 | // Created by Johann Pardanaud on 11/07/13. 6 | // Distributed under the MIT license. 7 | // 8 | 9 | #import "IMGAlbum.h" 10 | #import "IMGImage.h" 11 | 12 | 13 | @implementation IMGAlbum; 14 | 15 | #pragma mark - Init With Json 16 | 17 | - (instancetype)initWithJSONObject:(NSDictionary *)jsonData error:(NSError *__autoreleasing *)error{ 18 | self = [super initWithJSONObject:jsonData error:error]; 19 | 20 | if(self && !*error) { 21 | 22 | if(![jsonData isKindOfClass:[NSDictionary class]]){ 23 | 24 | *error = [NSError errorWithDomain:IMGErrorDomain code:IMGErrorMalformedResponseFormat userInfo:@{@"ImgurClass":[self class]}]; 25 | return nil; 26 | } 27 | 28 | _deletehash = jsonData[@"deletehash"]; 29 | } 30 | return [self trackModels]; 31 | } 32 | 33 | 34 | #pragma mark - Describe 35 | 36 | - (NSString *)description{ 37 | return [NSString stringWithFormat: @"%@ ; deletehash: %@",[super description], self.deletehash]; 38 | } 39 | 40 | -(BOOL)isEqual:(id)object{ 41 | 42 | if (self == object) { 43 | return YES; 44 | } 45 | 46 | if (![object isKindOfClass:[IMGAlbum class]]) { 47 | return NO; 48 | } 49 | 50 | return ([[object albumID] isEqualToString:self.albumID]); 51 | } 52 | 53 | -(NSUInteger)hash{ 54 | 55 | return [self.albumID hash]; 56 | } 57 | 58 | #pragma mark - NSCoding 59 | 60 | - (id)initWithCoder:(NSCoder *)decoder { 61 | 62 | NSString * deletehash = [decoder decodeObjectForKey:@"deletehash"]; 63 | 64 | if (self = [super initWithCoder:decoder]) { 65 | _deletehash = deletehash; 66 | } 67 | return self; 68 | } 69 | 70 | - (void)encodeWithCoder:(NSCoder *)coder { 71 | 72 | [super encodeWithCoder:coder]; 73 | 74 | [coder encodeObject:self.deletehash forKey:@"deletehash"]; 75 | } 76 | 77 | #pragma mark - NSCopying 78 | 79 | - (instancetype)copyWithZone:(NSZone *)zone { 80 | 81 | IMGAlbum * copy = [super copyWithZone:zone]; 82 | 83 | if (copy) { 84 | // Copy NSObject subclasses 85 | [copy setDeletehash:[self.deletehash copyWithZone:zone]]; 86 | } 87 | 88 | return copy; 89 | } 90 | @end 91 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGAccountSettings.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGAccountSettings.h 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-12. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGModel.h" 10 | 11 | #import "IMGBasicAlbum.h" 12 | 13 | 14 | /** 15 | Model object class to represent blocked users from account settings 16 | */ 17 | @interface IMGBlockedUser : IMGModel 18 | 19 | /** 20 | Blocked users Id 21 | */ 22 | @property (nonatomic, readonly, copy) NSString *blockedID; 23 | /** 24 | Blocked users URL for account page 25 | */ 26 | @property (nonatomic, readonly) NSURL *blockedURL; 27 | 28 | //initializer with a dictionary with just two keys 29 | - (instancetype)initWithJSONObject:(NSDictionary *)jsonData error:(NSError * __autoreleasing *)error; 30 | 31 | @end 32 | 33 | 34 | /** 35 | Model object class to represent account settings. https://api.imgur.com/models/account_settings 36 | */ 37 | @interface IMGAccountSettings : IMGModel 38 | 39 | /** 40 | User's email string. 41 | */ 42 | @property (nonatomic, readonly, copy) NSString *email; 43 | /** 44 | User's album privacy setting. If hidden, public cannot see user's albums. 45 | */ 46 | @property (nonatomic, readonly) IMGAlbumPrivacy albumPrivacy; 47 | /** 48 | User allows all images submitted to be accessible to public. 49 | */ 50 | @property (nonatomic, readonly) BOOL publicImages; 51 | /** 52 | Does user have Imgur regulated ability to upload high quality images 53 | */ 54 | @property (nonatomic, readonly) BOOL highQuality; 55 | /** 56 | Expiry date or false if not a pro user 57 | */ 58 | @property (nonatomic, readonly) NSDate * proExpiration; 59 | /** 60 | Has user accepted gallery submission terms? 61 | */ 62 | @property (nonatomic, readonly) BOOL acceptedGalleryTerms; 63 | /** 64 | Array of email strings that are allowed to upload to imgur 65 | */ 66 | @property (nonatomic, readonly, copy) NSArray *activeEmails; 67 | /** 68 | Is user allowing incoming messages 69 | */ 70 | @property (nonatomic, readonly) BOOL messagingEnabled; 71 | /** 72 | Array of blocked users with IMGBLockedUser model object. 73 | */ 74 | @property (nonatomic, readonly, copy) NSArray *blockedUsers; 75 | /** 76 | Username string 77 | */ 78 | @property (nonatomic, readonly, copy) NSString *username; 79 | 80 | /** 81 | Custom init with username 82 | */ 83 | - (instancetype)initWithJSONObject:(NSDictionary *)jsonData withName:(NSString*)username error:(NSError * __autoreleasing *)error; 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /Integrated Tests/IMGConversationTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGConversationTest.m 3 | // ImgurSession 4 | // 5 | // Created by Xtreme Dev on 2014-03-20. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGIntegratedTestCase.h" 10 | 11 | @interface IMGConversationTest : IMGIntegratedTestCase 12 | 13 | @end 14 | 15 | 16 | @implementation IMGConversationTest 17 | 18 | -(void)testLoadConversations{ 19 | 20 | __block BOOL isSuccess; 21 | 22 | [IMGConversationRequest conversations:^(NSArray * messages) { 23 | 24 | IMGConversation * first = [messages firstObject]; 25 | 26 | if(first){ 27 | expect(first.conversationID).beGreaterThan(0); 28 | expect(first.lastMessage).beTruthy(); 29 | 30 | [IMGConversationRequest conversationWithMessageID:first.conversationID success:^(IMGConversation * convseration) { 31 | 32 | isSuccess = YES; 33 | 34 | } failure:failBlock]; 35 | } else { 36 | 37 | isSuccess = YES; 38 | } 39 | 40 | } failure:failBlock]; 41 | 42 | expect(isSuccess).will.beTruthy(); 43 | } 44 | 45 | 46 | -(void)testSendConversatioToSelfAndDelete{ 47 | 48 | __block BOOL didDelete; 49 | 50 | [IMGConversationRequest createMessageWithRecipient:imgurUnitTestParams[@"recipientId"] withBody:@"you must be the designer of this api" success:^{ 51 | 52 | [IMGConversationRequest conversations:^(NSArray * allConvos) { 53 | 54 | [allConvos enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 55 | 56 | IMGConversation * con = obj; 57 | 58 | if([con.fromUsername isEqualToString:@"Ravener"]){ 59 | *stop = YES; 60 | 61 | [IMGConversationRequest deleteConversation:con.conversationID success:^{ 62 | 63 | didDelete = YES; 64 | 65 | } failure:failBlock]; 66 | } 67 | }]; 68 | 69 | } failure:failBlock]; 70 | 71 | } failure:failBlock]; 72 | 73 | expect(didDelete).will.beTruthy(); 74 | } 75 | 76 | 77 | -(void)testBlockSender{ 78 | 79 | //can't figure out how to test this 80 | } 81 | 82 | -(void)testReportSender{ 83 | 84 | //can't figure out how to test this 85 | } 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /ImgurSession/Requests/IMGAlbumRequest.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGAlbumRequest.h 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-15. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGEndpoint.h" 10 | 11 | #import "IMGBasicAlbum.h" 12 | 13 | @class IMGAlbum,IMGImage; 14 | 15 | 16 | /** 17 | Album requests. https://api.imgur.com/endpoints/album 18 | */ 19 | @interface IMGAlbumRequest : IMGEndpoint 20 | 21 | #pragma mark - Load 22 | 23 | /** 24 | Retrieve album details and images 25 | */ 26 | + (void)albumWithID:(NSString *)albumID success:(void (^)(IMGAlbum *album))success failure:(void (^)(NSError *error))failure; 27 | 28 | #pragma mark - Create 29 | 30 | /** 31 | Create an album with an array of imageIDs which currently exist 32 | */ 33 | + (void)createAlbumWithTitle:(NSString *)title imageIDs:(NSArray *)imageIDs success:(void (^)(NSString * albumID, NSString *albumDeleteHash))success failure:(void (^)(NSError *error))failure; 34 | + (void)createAlbumWithTitle:(NSString *)title description:(NSString *)description imageIDs:(NSArray *)imageIDs privacy:(IMGAlbumPrivacy)privacy layout:(IMGAlbumLayout)layout cover:(NSString *)coverID success:(void (^)(NSString * albumID, NSString * albumDeleteHash))success failure:(void (^)(NSError *error))failure; 35 | 36 | #pragma mark - Update 37 | 38 | /** 39 | Update an existing album with optional params. Must be logged in. 40 | */ 41 | + (void)updateAlbumWithID:(NSString*)albumID imageIDs:(NSArray *)imageIDs success:(void (^)())success failure:(void (^)(NSError * error))failure; 42 | /** 43 | Update an anon album with optional params. 44 | */ 45 | + (void)updateAlbumWithID:(NSString*)albumID title:(NSString *)title description:(NSString *)description imageIDs:(NSArray *)imageIDs privacy:(IMGAlbumPrivacy)privacy layout:(IMGAlbumLayout)layout cover:(NSString *)coverID success:(void (^)())success failure:(void (^)(NSError * error))failure; 46 | 47 | #pragma mark - Delete 48 | 49 | /** 50 | Delete an album with an albumID if you are the owner of the album. Must be logged in. 51 | */ 52 | + (void)deleteAlbumWithID:(NSString *)albumID success:(void (^)())success failure:(void (^)(NSError *error))failure; 53 | /** 54 | For anonymous delete, you must pass the deletehash instead 55 | */ 56 | + (void)deleteAlbumWithDeleteHash:(NSString *)deletehash success:(void (^)())success failure:(void (^)(NSError *error))failure; 57 | 58 | 59 | #pragma mark - Favourite 60 | /** 61 | Fav an album. Must be signed in. 62 | */ 63 | +(void)favouriteAlbumWithID:(NSString*)albumID success:(void (^)())success failure:(void (^)(NSError *error))failure; 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGComment.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGComment.h 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-12. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGModel.h" 10 | 11 | #import "IMGGalleryObject.h" 12 | 13 | /** 14 | Model object class to represent comments on images, albums, and comments. https://api.imgur.com/models/comment 15 | */ 16 | @interface IMGComment : IMGModel 17 | 18 | 19 | /** 20 | Comment ID 21 | */ 22 | @property (readonly,nonatomic) NSInteger commentID; 23 | /** 24 | Gallery Object ID comment is associated with (IMGGalleryImage or IMGGalleryAlbum) 25 | */ 26 | @property (readonly,nonatomic, copy) NSString * galleryID; 27 | /** 28 | Actual comment string 29 | */ 30 | @property (readonly,nonatomic, copy) NSString * caption; 31 | /** 32 | Authors username 33 | */ 34 | @property (readonly,nonatomic, copy) NSString * author; 35 | /** 36 | Authors account id 37 | */ 38 | @property (readonly,nonatomic) NSInteger authorID; 39 | /** 40 | Comment on an album, not image 41 | */ 42 | @property (readonly,nonatomic) BOOL onAlbum; 43 | /** 44 | Album Cover Image Id, used for album comments 45 | */ 46 | @property (readonly,nonatomic, copy) NSString * albumCover; 47 | /** 48 | Up-votes 49 | */ 50 | @property (readonly,nonatomic) NSInteger ups; 51 | /** 52 | down-votes 53 | */ 54 | @property (readonly,nonatomic) NSInteger downs; 55 | /** 56 | sum of up-votes minus down-votes 57 | */ 58 | @property (readonly,nonatomic) NSInteger points; 59 | /** 60 | timestamp of creation of comment 61 | */ 62 | @property (readonly,nonatomic) NSDate * datetime; 63 | /** 64 | Parent comment ID, nil if no parent 65 | */ 66 | @property (readonly,nonatomic) NSInteger parentID; 67 | /** 68 | Is comment deleted? Still exists on server 69 | */ 70 | @property (readonly,nonatomic) BOOL deleted; 71 | /** 72 | Responses to this comment. Only included with withReplies=YES 73 | */ 74 | @property (readonly,nonatomic, copy) NSArray * children; 75 | /** 76 | UNDOCUMENTED 77 | Users up or down vote on the comment 78 | */ 79 | @property (nonatomic, readonly) IMGVoteType vote; 80 | 81 | /** 82 | Retrieve constructed gallery object for this comment 83 | */ 84 | -(id )galleryObject; 85 | 86 | 87 | /** 88 | Custom init for when replying to a comment and only the ID is returned by the server 89 | */ 90 | - (instancetype)initUserCommentWithID:(NSInteger)commentID parentID:(NSInteger)parentID caption:(NSString*)caption; 91 | 92 | /** 93 | Set the user's vote for this comment 94 | */ 95 | -(void)setUsersVote:(IMGVoteType)vote; 96 | 97 | @end 98 | -------------------------------------------------------------------------------- /ImgurSession/Requests/IMGCommentRequest.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGCommentRequest.h 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-15. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGEndpoint.h" 10 | #import "IMGVote.h" 11 | 12 | @class IMGComment; 13 | 14 | /** 15 | Comment Requests. https://api.imgur.com/endpoints/comment 16 | */ 17 | @interface IMGCommentRequest : IMGEndpoint 18 | 19 | #pragma mark - Load 20 | /** 21 | Load comment with an ID 22 | @param commentId string Id for comment 23 | @param replies boolean to fetch the replies if YES 24 | */ 25 | + (void)commentWithID:(NSInteger)commentId withReplies:(BOOL)replies success:(void (^)(IMGComment * comment))success failure:(void (^)(NSError *error))failure; 26 | /** 27 | Fetch replies to a parent comment 28 | @param commentID comment ID to fetch replies for 29 | */ 30 | + (void)repliesWithCommentID:(NSInteger)commentID success:(void (^)(NSArray * replies))success failure:(void (^)(NSError *error))failure; 31 | 32 | #pragma mark - Create 33 | /** 34 | Create top-level comment on the image or album. Must be logged in. 35 | @param caption comment string 36 | @param imageID id of object to comment on 37 | */ 38 | + (void)submitComment:(NSString*)caption withImageID:(NSString *)imageID success:(void (^)(NSInteger commentID))success failure:(void (^)(NSError *error))failure; 39 | /** 40 | Reply to a comment. Must be logged in. 41 | @param caption comment string 42 | @param imageId id of image to comment on 43 | @param parentCommentId id of parent comment to reply to 44 | */ 45 | + (void)replyToComment:(NSString*)caption withImageID:(NSString *)imageID withParentCommentID:(NSInteger)parentCommentID success:(void (^)(NSInteger commentID))success failure:(void (^)(NSError * error))failure; 46 | 47 | #pragma mark - Delete 48 | /** 49 | Delete comment. Must be logged in. 50 | @param commentId comment id to delete 51 | */ 52 | + (void)deleteCommentWithID:(NSInteger)commentID success:(void (^)())success failure:(void (^)(NSError *error))failure; 53 | 54 | #pragma mark - Vote 55 | /** 56 | Vote on a comment. Must be logged in. 57 | @param commentId comment id to vote on 58 | @param vote vote to give comment 59 | */ 60 | + (void)voteCommentWithID:(NSInteger)commentID withVote:(IMGVoteType)vote success:(void (^)())success failure:(void (^)(NSError *error))failure; 61 | 62 | #pragma mark - Report 63 | /** 64 | Report a comment as inappropiate. Must be logged in. 65 | @param commentId comment id to report 66 | */ 67 | + (void)reportCommentWithID:(NSInteger)commentID success:(void (^)())success failure:(void (^)(NSError *error))failure; 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /SampleApp/GMMViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // GMMViewController.m 3 | // SampleApp 4 | // 5 | // Created by Xtreme Dev on 2014-05-13. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "GMMViewController.h" 10 | 11 | 12 | 13 | @interface GMMViewController () 14 | 15 | @end 16 | 17 | @implementation GMMViewController 18 | 19 | - (void)viewDidLoad 20 | { 21 | [super viewDidLoad]; 22 | // Do any additional setup after loading the view, typically from a nib. 23 | 24 | } 25 | 26 | -(void)viewWillAppear:(BOOL)animated{ 27 | 28 | [self reload]; 29 | } 30 | 31 | -(void)reload{ 32 | 33 | [IMGGalleryRequest hotGalleryPage:0 withViralSort:YES success:^(NSArray *objects) { 34 | 35 | //random object from gallery 36 | id object = [objects objectAtIndex:arc4random_uniform((u_int32_t)objects.count)]; 37 | NSLog(@"retrieved gallery"); 38 | 39 | //get cover image 40 | IMGImage * cover = [object coverImage]; 41 | //get link to 640x640 cover image 42 | NSURL * coverURL = [cover URLWithSize:IMGLargeThumbnailSize]; 43 | 44 | //set the image view 45 | [self.coverView setImage:[UIImage imageWithData:[NSData dataWithContentsOfURL:coverURL]]]; 46 | 47 | //set the title 48 | self.titleLabel.text = [cover title]; 49 | //description 50 | self.desriptionLabel.text = [cover imageDescription]; 51 | 52 | } failure:^(NSError *error) { 53 | 54 | NSLog(@"gallery request failed - %@" ,error.localizedDescription); 55 | }]; 56 | 57 | if([IMGSession sharedInstance].isAnonymous){ 58 | 59 | self.stateLabel.text = @"Anonymous User"; 60 | } else { 61 | 62 | self.stateLabel.text = [NSString stringWithFormat:@"Logged on as: %@",[IMGSession sharedInstance].user.username]; 63 | } 64 | } 65 | 66 | - (void)didReceiveMemoryWarning 67 | { 68 | [super didReceiveMemoryWarning]; 69 | // Dispose of any resources that can be recreated. 70 | } 71 | 72 | - (IBAction)refresh:(id)sender { 73 | [self reload]; 74 | } 75 | 76 | - (IBAction)loginTapped:(id)sender { 77 | 78 | if([IMGSession sharedInstance].isAnonymous){ 79 | 80 | self.stateLabel.text = @"Logging In"; 81 | 82 | //set your credentials to reset the session to your app 83 | [IMGSession authenticatedSessionWithClientID:@"" secret:@"" authType:IMGCodeAuth withDelegate:(id)[UIApplication sharedApplication].delegate]; 84 | 85 | //force authentication by immediately sending request in reload, which will trigger AppDelegate methods to respond and send to imgur url to sign on 86 | 87 | [self reload]; 88 | } 89 | 90 | 91 | } 92 | 93 | @end 94 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGImage.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGImage.h 3 | // ImgurSession 4 | // 5 | // Created by Johann Pardanaud on 10/07/13. 6 | // Distributed under the MIT license. 7 | // 8 | 9 | #import "IMGModel.h" 10 | #import "IMGObject.h" 11 | 12 | @class IMGBasicAlbum; 13 | 14 | /** 15 | Model object class to represent images posted to Imgur. https://api.imgur.com/models/image 16 | */ 17 | @interface IMGImage : IMGModel 18 | 19 | /** 20 | Image ID to reference in Imgur globally 21 | */ 22 | @property (nonatomic, readonly, copy) NSString *imageID; 23 | /** 24 | Title of image. This is the heading on the imgur image page where the joke usually is. 25 | */ 26 | @property (nonatomic, readonly, copy) NSString *title; 27 | /** 28 | Description of image. Never seen this filled out 29 | */ 30 | @property (nonatomic, readonly, copy) NSString * imageDescription; 31 | /** 32 | Date image was submitted. 33 | */ 34 | @property (nonatomic, readonly) NSDate *datetime; 35 | /** 36 | Type of image from HTTP header. JPEG/GIF, maybe other types 37 | */ 38 | @property (nonatomic, readonly, copy) NSString *type; 39 | /** 40 | Is the image animated 41 | */ 42 | @property (nonatomic, readonly) BOOL animated; 43 | /** 44 | Image width in px 45 | */ 46 | @property (nonatomic, readonly) CGFloat width; 47 | /** 48 | Image height in px 49 | */ 50 | @property (nonatomic, readonly) CGFloat height; 51 | /** 52 | Image size in bytes 53 | */ 54 | @property (nonatomic, readonly) NSInteger size; 55 | /** 56 | Number of page impressions/image views on imgur 57 | */ 58 | @property (nonatomic, readonly) NSInteger views; 59 | /** 60 | Image bandwidth transmitted to users in bytes 61 | */ 62 | @property (nonatomic, readonly) NSInteger bandwidth; 63 | /** 64 | Hash to send DELETE request with to delete image when session is using anonymous authentication. 65 | */ 66 | @property (nonatomic, readonly, copy) NSString *deletehash; 67 | /** 68 | Imgur section the image belongs to if applicable 69 | */ 70 | @property (nonatomic, readonly, copy) NSString *section; 71 | /** 72 | URL to actual image file. Use URLWithSize to retrieve various image sizes. 73 | */ 74 | @property (nonatomic, readonly, copy) NSURL *url; 75 | 76 | 77 | #pragma mark - Display 78 | /** 79 | For creating constructed cover image from the cover paramaters in basicAlbum 80 | */ 81 | -(instancetype)initCoverImageWithAlbum:(IMGBasicAlbum*)album error:(NSError *__autoreleasing *)error; 82 | 83 | /** 84 | For creating constructed image from just a gallery object ID 85 | */ 86 | -(instancetype)initWithGalleryID:(NSString*)objectID error:(NSError *__autoreleasing *)error; 87 | 88 | /** 89 | Retrieves various image sizes using the url with the letter specifier model as described at https://api.imgur.com/models/image 90 | @param size constant to specify requested image size 91 | @return URL for image of that size 92 | */ 93 | - (NSURL *)URLWithSize:(IMGSize)size; 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGGalleryProfile.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGGalleryProfile.m 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-15. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGGalleryProfile.h" 10 | 11 | #import "IMGSession.h" 12 | 13 | 14 | #pragma mark - IMGGalleryTrophy 15 | 16 | @implementation IMGGalleryTrophy 17 | 18 | - (instancetype)initWithJSONObject:(NSDictionary *)jsonData error:(NSError *__autoreleasing *)error{ 19 | 20 | if(self = [super init]) { 21 | _trophyID = jsonData[@"id"]; 22 | _name = jsonData[@"name"]; 23 | _type = jsonData[@"name_clean"];//doesn't really make sense 24 | _profileDescription = jsonData[@"description"]; 25 | _data = jsonData[@"data"]; 26 | _link = [NSURL URLWithString:jsonData[@"data_link"]]; 27 | _dateAwarded = [NSDate dateWithTimeIntervalSince1970:[jsonData[@"datetime"] integerValue]]; 28 | _imageUrl = [NSURL URLWithString:jsonData[@"image"]]; 29 | } 30 | return [self trackModels]; 31 | } 32 | 33 | 34 | @end 35 | 36 | #pragma mark - IMGGalleryProfile 37 | 38 | 39 | @implementation IMGGalleryProfile 40 | 41 | #pragma mark - Init With Json 42 | 43 | - (instancetype)initWithUser:(NSString*)username JSONObject:(NSDictionary *)jsonData error:(NSError *__autoreleasing *)error{ 44 | 45 | if(self = [super init]) { 46 | 47 | if(![jsonData isKindOfClass:[NSDictionary class]]){ 48 | 49 | if(error) 50 | *error = [NSError errorWithDomain:IMGErrorDomain code:IMGErrorMalformedResponseFormat userInfo:@{@"ImgurClass":[self class]}]; 51 | return nil; 52 | } else if (!jsonData[@"score"] || !jsonData[@"ups"] || !jsonData[@"down"]){ 53 | 54 | if(error) 55 | *error = [NSError errorWithDomain:IMGErrorDomain code:IMGErrorResponseMissingParameters userInfo:nil]; 56 | return nil; 57 | } 58 | 59 | _totalComments = [jsonData[@"total_gallery_comments"] integerValue]; 60 | _totalLikes = [jsonData[@"total_gallery_likes"] integerValue]; 61 | _totalSubmissions = [jsonData[@"total_gallery_submissions"] integerValue]; 62 | _userName = username; 63 | 64 | //enumerate all blocked users 65 | NSMutableArray * trophies = [NSMutableArray new]; 66 | for(NSDictionary * trophyJSON in jsonData[@"trophies"]){ 67 | IMGGalleryTrophy * trophy = [[IMGGalleryTrophy alloc] initWithJSONObject:trophyJSON error:nil]; 68 | [trophies addObject:trophy]; 69 | } 70 | _trophies = [NSArray arrayWithArray:trophies]; 71 | } 72 | return [self trackModels]; 73 | } 74 | 75 | #pragma mark - Describe 76 | 77 | - (NSString *)description{ 78 | return [NSString stringWithFormat:@"%@; comments: %ld; likes: %ld; submissions: %ld; trophies: %ld;", [super description],(long)self.totalComments, (long)self.totalLikes, (long)self.totalSubmissions, (long)[self.trophies count]]; 79 | } 80 | 81 | -(BOOL)isEqual:(id)object{ 82 | 83 | if (self == object) { 84 | return YES; 85 | } 86 | 87 | if (![object isKindOfClass:[IMGGalleryProfile class]]) { 88 | return NO; 89 | } 90 | 91 | return ([[object userName] isEqualToString:self.userName]); 92 | } 93 | 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /Stubbed Tests/IMGConversationTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGConversationTest.m 3 | // ImgurSession 4 | // 5 | // Created by Xtreme Dev on 2014-03-20. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGTestCase.h" 10 | 11 | @interface IMGConversationTest : IMGTestCase 12 | 13 | @end 14 | 15 | 16 | @implementation IMGConversationTest 17 | 18 | -(void)testLoadConversations{ 19 | 20 | __block BOOL isSuccess; 21 | [self stubWithFile:@"conversations.json"]; 22 | 23 | [IMGConversationRequest conversations:^(NSArray * messages) { 24 | 25 | expect(messages).haveCountOf(1); 26 | expect([messages firstObject]).beInstanceOf([IMGConversation class]); 27 | IMGConversation * first = [messages firstObject]; 28 | expect(first.lastMessage).beTruthy(); 29 | expect(first.conversationID).beTruthy(); 30 | 31 | IMGConversation * copy = [first copy]; 32 | expect(first.lastMessage).equal(copy.lastMessage); 33 | expect(first.conversationID == copy.conversationID).beTruthy(); 34 | expect(first.authorID == copy.authorID).beTruthy(); 35 | expect(first.messages == copy.messages).beTruthy(); 36 | 37 | NSData * data = [NSKeyedArchiver archivedDataWithRootObject:copy]; 38 | copy = [NSKeyedUnarchiver unarchiveObjectWithData:data]; 39 | 40 | expect(first.lastMessage).equal(copy.lastMessage); 41 | expect(first.conversationID == copy.conversationID).beTruthy(); 42 | expect(first.authorID == copy.authorID).beTruthy(); 43 | expect(first.messages == copy.messages).beTruthy(); 44 | expect(copy).equal(first); 45 | 46 | isSuccess = YES; 47 | 48 | } failure:failBlock]; 49 | 50 | expect(isSuccess).will.beTruthy(); 51 | } 52 | 53 | -(void)testLoadConversationWithID{ 54 | 55 | __block BOOL isSuccess; 56 | [self stubWithFile:@"conversationwithid.json"]; 57 | 58 | [IMGConversationRequest conversationWithMessageID:346346 success:^(IMGConversation * conversation) { 59 | 60 | expect(conversation.lastMessage).beTruthy(); 61 | expect(conversation.conversationID).beTruthy(); 62 | expect(conversation.messageCount).beTruthy(); 63 | 64 | isSuccess = YES; 65 | 66 | } failure:failBlock]; 67 | 68 | expect(isSuccess).will.beTruthy(); 69 | } 70 | 71 | 72 | -(void)testSendConversation{ 73 | 74 | __block BOOL isSuccess; 75 | [self stubWithFile:@"conversationpost.json"]; 76 | 77 | [IMGConversationRequest createMessageWithRecipient:imgurUnitTestParams[@"recipientId"] withBody:@"you must be the designer of this api" success:^{ 78 | 79 | isSuccess = YES; 80 | 81 | } failure:failBlock]; 82 | 83 | expect(isSuccess).will.beTruthy(); 84 | } 85 | 86 | -(void)testDeleteConvo{ 87 | 88 | __block BOOL isSuccess; 89 | [self stubWithFile:@"conversationdelete.json"]; 90 | 91 | [IMGConversationRequest deleteConversation:634436 success:^{ 92 | 93 | isSuccess = YES; 94 | 95 | } failure:failBlock]; 96 | 97 | expect(isSuccess).will.beTruthy(); 98 | } 99 | 100 | 101 | -(void)testBlockSender{ 102 | 103 | //can't figure out how to test this 104 | } 105 | 106 | -(void)testReportSender{ 107 | 108 | //can't figure out how to test this 109 | } 110 | 111 | @end 112 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGBasicAlbum.h: -------------------------------------------------------------------------------- 1 | // 2 | // ImgurPartialAlbum.h 3 | // ImgurSession 4 | // 5 | // Created by Johann Pardanaud on 24/07/13. 6 | // Distributed under the MIT license. 7 | // 8 | 9 | #import "IMGModel.h" 10 | 11 | #import "IMGObject.h" 12 | 13 | 14 | typedef NS_ENUM(NSInteger, IMGAlbumPrivacy){ 15 | IMGAlbumDefault = 1, 16 | IMGAlbumPublic = 1, 17 | IMGAlbumHidden, 18 | IMGAlbumSecret 19 | }; 20 | 21 | typedef NS_ENUM(NSInteger, IMGAlbumLayout){ 22 | IMGDefaultLayout = 1, 23 | IMGBlogLayout = 1, 24 | IMGGridLayout, 25 | IMGHorizontalLayout, 26 | IMGVerticalLayout 27 | }; 28 | 29 | /** 30 | Model object class to represent common denominator properties to gallery and user albums. https://api.imgur.com/models/album 31 | */ 32 | @interface IMGBasicAlbum : IMGModel 33 | 34 | /** 35 | Album ID 36 | */ 37 | @property (nonatomic, readonly, copy) NSString *albumID; 38 | /** 39 | Title of album 40 | */ 41 | @property (nonatomic, readonly, copy) NSString *title; 42 | /** 43 | Album description 44 | */ 45 | @property (nonatomic, readonly, copy) NSString *albumDescription; 46 | /** 47 | Album creation date 48 | */ 49 | @property (nonatomic, readonly) NSDate *datetime; 50 | /** 51 | Image Id for cover of album 52 | */ 53 | @property (nonatomic, readonly, copy) NSString *coverID; 54 | /** 55 | Cover image width in px 56 | */ 57 | @property (nonatomic, readonly) CGFloat coverWidth; 58 | /** 59 | Cover image height in px 60 | */ 61 | @property (nonatomic, readonly) CGFloat coverHeight; 62 | /** 63 | account username of album creator, not a URL but named like this anyway. nil if anonymous 64 | */ 65 | @property (nonatomic, readonly, copy) NSString *accountURL; 66 | /** 67 | Privacy of album 68 | */ 69 | @property (nonatomic, readonly, copy) NSString *privacy; 70 | /** 71 | Type of layout for album 72 | */ 73 | @property (nonatomic, readonly) IMGAlbumLayout layout; 74 | /** 75 | Number of views for album 76 | */ 77 | @property (nonatomic, readonly) NSInteger views; 78 | /** 79 | URL for album link 80 | */ 81 | @property (nonatomic, readonly) NSURL *url; 82 | /** 83 | Number of images in album 84 | */ 85 | @property (nonatomic, readonly) NSInteger imagesCount; // Optional: can be set to nil 86 | /** 87 | Array of images in IMGImage form 88 | */ 89 | @property (nonatomic, readonly, copy) NSArray *images; // Optional: can be set to nil 90 | 91 | /** 92 | For custom init with gallery ID and cover ID 93 | */ 94 | -(instancetype)initWithGalleryID:(NSString*)objectID coverID:(NSString*)coverID error:(NSError *__autoreleasing *)error; 95 | 96 | 97 | #pragma mark - Album Layout setting 98 | /** 99 | Returns string for layout constant 100 | @param layoutType layout constant 101 | @return string for layout constant 102 | */ 103 | +(NSString*)strForLayout:(IMGAlbumLayout)layoutType; 104 | 105 | /** 106 | Returns constant for string layout 107 | @param string for layout constant 108 | @return layout layout constant 109 | */ 110 | +(IMGAlbumLayout)layoutForStr:(NSString*)layoutStr; 111 | 112 | #pragma mark - Album Privacy setting 113 | /** 114 | Returns string for privacy constant 115 | @param privacy privacy constant 116 | @return string for privacy constant 117 | */ 118 | +(NSString*)strForPrivacy:(IMGAlbumPrivacy)privacy; 119 | 120 | /** 121 | Returns constant for privacy layout 122 | @param string for privacy constant 123 | @return privacy privacy constant 124 | */ 125 | +(IMGAlbumPrivacy)privacyForStr:(NSString*)privacyStr; 126 | 127 | 128 | @end 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ImgurSession 2 | 3 | __ImgurSession__ is an Objective-C networking library to easily make [Imgur](http://imgur.com) API requests within iOS and OS X apps. It is built on [AFNetworking's](http://afnetworking.com/) AFHTTPSessionManager baseclass. ImgurSession provides access for V3 of the API. It handles OAuth2 authentication for user-authenticated sessions and also supports basic authentication for anonymous sessions. It covers all documented endpoints on Imgur's [documentation](https://api.imgur.com/). This is being used in production for my free app [Imgurian](http://imgur.com/gallery/63Gv6). 4 | 5 | This project was originally forked from [ImgurKit](https://github.com/Nesk/ImgurKit) by Johann Pardanaud and since refactored. 6 | 7 | ## Features 8 | 9 | - Full Imgur API V3 10 | - Use any of the Request classes in the ImgurSession/Request folder to make requests. Requests use Session singleton created before request was made. 11 | - CRUD actions for images, albums, comments, notifcations, messages, conversations, and accounts. 12 | - Multiple Image upload, upload from url. 13 | 14 | - OAuth2 management. 15 | - Session only needs your app credentials. Handles all authentication except for when it needs a webview. 16 | - Session will refresh your tokens lazily. Feel free to use any request at any time. 17 | 18 | - User account 19 | - Session notifies delegate of replies and messages to the users account automatically every 1 minute. Can be disabled. 20 | - Session refresh the user's account on token expiry every hour. 21 | 22 | 23 | ## Using ImgurSession 24 | 25 | Just import ImgurSession.h and setup the session with your credentials before making any requests. For user authorization, you must register for OAuth 2 authorization with or without a callback URL depending on the token type (Read more at [Imgur](https://api.imgur.com/oauth2)). For authorized login, you must implement the delegate method imgurSessionNeedsExternalWebview: in order to open an external imgur.com page for OAuth authorization. 26 | 27 | ``` 28 | 29 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 30 | { 31 | [IMGSession authenticatedSessionWithClientID:@"clientID" secret:@"secret" authType:IMGCodeAuth withDelegate:self]; 32 | 33 | return YES; 34 | } 35 | 36 | -(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{ 37 | 38 | /* 39 | configure you app URL schema to handle the callback url then call completion with retrieved code from URL 40 | */ 41 | 42 | } 43 | 44 | #pragma mark - IMGSessionDelegate 45 | 46 | -(void)imgurSessionNeedsExternalWebview:(NSURL *)url completion:(void (^)())completion{ 47 | 48 | //open imgur website to authenticate with callback url in safari 49 | [[UIApplication sharedApplication] openURL:url]; 50 | 51 | //save the completion block for later use when imgur responds with url callback 52 | } 53 | 54 | ``` 55 | 56 | Or anonymous session (as configured by registered app on Imgur). 57 | 58 | ``` 59 | [IMGSession anonymousSessionWithClientID:@"anonToken" withDelegate:self]; 60 | ``` 61 | 62 | Anywhere else in the app, make requests which will use the session singleton previously created to handle authentication and error handling. To retrieve the viral gallery: 63 | 64 | 65 | ``` 66 | [IMGGalleryRequest hotGalleryPage:0 success:^(NSArray *objects) { 67 | 68 | //use gallery objects in a table for example 69 | self.tableRows = objects; 70 | [self.tableView reloadData]; 71 | 72 | } failure:^(NSError *error) { 73 | 74 | //handle error 75 | }]; 76 | 77 | ``` 78 | -------------------------------------------------------------------------------- /SampleApp/GMMAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // GMMAppDelegate.m 3 | // SampleApp 4 | // 5 | // Created by Xtreme Dev on 2014-05-13. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "GMMAppDelegate.h" 10 | 11 | @implementation GMMAppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | // Override point for customization after application launch. 16 | [IMGSession anonymousSessionWithClientID:@"e254a73ec4dd21e" withDelegate:self]; 17 | 18 | return YES; 19 | } 20 | 21 | - (void)applicationWillResignActive:(UIApplication *)application 22 | { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | - (void)applicationDidEnterBackground:(UIApplication *)application 28 | { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application 34 | { 35 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 36 | } 37 | 38 | - (void)applicationDidBecomeActive:(UIApplication *)application 39 | { 40 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 41 | } 42 | 43 | - (void)applicationWillTerminate:(UIApplication *)application 44 | { 45 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 46 | } 47 | 48 | 49 | -(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{ 50 | 51 | //app must register url scheme which starts the app at this endpoint with the url containing the code 52 | 53 | 54 | NSMutableDictionary *params = [[NSMutableDictionary alloc] init]; 55 | for (NSString *param in [[url query] componentsSeparatedByString:@"&"]) { 56 | NSArray *elts = [param componentsSeparatedByString:@"="]; 57 | if([elts count] < 2) continue; 58 | [params setObject:[elts objectAtIndex:1] forKey:[elts objectAtIndex:0]]; 59 | } 60 | 61 | NSString * pinCode = params[@"code"]; 62 | 63 | if(!pinCode){ 64 | NSLog(@"error: %@", params[@"error"]); 65 | 66 | self.continueHandler = nil; 67 | 68 | UIAlertView * a = [[UIAlertView alloc] initWithTitle:@"error" message:@"Access was denied by Imgur" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles: @"Try Again",nil]; 69 | [a show]; 70 | 71 | return NO; 72 | } 73 | 74 | [[IMGSession sharedInstance] authenticateWithCode:pinCode]; 75 | 76 | if(_continueHandler) 77 | self.continueHandler(); 78 | 79 | 80 | return YES; 81 | } 82 | 83 | -(void)imgurSessionNeedsExternalWebview:(NSURL *)url completion:(void (^)())completion{ 84 | 85 | self.continueHandler = [completion copy]; 86 | 87 | //go to safari to login, configure your imgur app to redirect to this app using URL scheme. 88 | [[UIApplication sharedApplication] openURL:url]; 89 | 90 | } 91 | 92 | -(void)imgurSessionRateLimitExceeded{ 93 | 94 | 95 | } 96 | @end 97 | -------------------------------------------------------------------------------- /Stubbed Tests/IMGTestCase.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGTestCase.m 3 | // ImgurSession 4 | // 5 | // Created by Xtreme Dev on 2014-03-18. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGTestCase.h" 10 | 11 | //add read-write prop 12 | @interface IMGSession (TestSession) 13 | 14 | @property (readwrite, nonatomic,copy) NSString *clientID; 15 | @property (readwrite, nonatomic, copy) NSString *secret; 16 | @property (readwrite, nonatomic, copy) NSString *refreshToken; 17 | @property (readwrite, nonatomic, copy) NSString *accessToken; 18 | @property (readwrite, nonatomic) NSDate *accessTokenExpiry; 19 | @property (readwrite, nonatomic) IMGAuthType lastAuthType; 20 | 21 | @end 22 | 23 | @implementation IMGTestCase 24 | 25 | - (void)setUp { 26 | [super setUp]; 27 | //run before each test 28 | 29 | //5 second timeout 30 | [Expecta setAsynchronousTestTimeout:5.0]; 31 | 32 | // Storing various testing values 33 | NSDictionary *infos = [[NSBundle bundleForClass:[self class]] infoDictionary]; 34 | 35 | //dummy auth session that will always claim to be properly authenticated even though its not, otherwise we wrongly attempt refresh 36 | [IMGSession authenticatedSessionWithClientID:@"ffdsf" secret:@"dfdsf" authType:IMGPinAuth withDelegate:self]; 37 | [[IMGSession sharedInstance] setRefreshToken:@"efssdfsd"]; 38 | [[IMGSession sharedInstance] setAccessToken:@"efssdfsd"]; 39 | [[IMGSession sharedInstance] setAccessTokenExpiry:[NSDate dateWithTimeIntervalSinceNow:10000000]]; 40 | //no reachability for offline tests 41 | [[IMGSession sharedInstance] setImgurReachability:nil]; 42 | 43 | //need various values such as image title 44 | imgurUnitTestParams = infos[@"imgurUnitTestParams"]; 45 | 46 | 47 | testfileURL = [NSURL fileURLWithPath:[[NSBundle bundleForClass:[self class]] pathForResource:@"image-example" ofType:@"jpg"]]; 48 | testGifURL = [NSURL fileURLWithPath:[[NSBundle bundleForClass:[self class]] pathForResource:@"example" ofType:@"gif"]]; 49 | 50 | //failure block 51 | failBlock = ^(NSError * error) { 52 | 53 | NSLog(@"Error : %@", [error localizedDescription]); 54 | 55 | XCTAssert(nil, @"FAIL"); 56 | }; 57 | } 58 | 59 | - (void)tearDown { 60 | [super tearDown]; 61 | } 62 | 63 | -(void)stubWithFile:(NSString * )filename { 64 | 65 | [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { 66 | return YES; 67 | } withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) { 68 | // Stub it with our "wsresponse.json" stub file 69 | return [OHHTTPStubsResponse responseWithFileAtPath:OHPathForFileInBundle(filename,nil) 70 | statusCode:200 headers:@{@"Content-Type":@"text/json"}]; 71 | }]; 72 | } 73 | 74 | -(void)stubWithFile:(NSString * )filename withHeader:(NSDictionary*)headerDict{ 75 | 76 | [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { 77 | return YES; 78 | } withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) { 79 | 80 | NSMutableDictionary * dict = [NSMutableDictionary dictionaryWithDictionary:@{@"Content-Type":@"text/json"}]; 81 | [dict addEntriesFromDictionary:headerDict]; 82 | 83 | // Stub it with our "wsresponse.json" stub file 84 | return [OHHTTPStubsResponse responseWithFileAtPath:OHPathForFileInBundle(filename,nil) 85 | statusCode:200 headers:[NSDictionary dictionaryWithDictionary:dict]]; 86 | }]; 87 | } 88 | 89 | -(void)stubWithFile:(NSString *)filename withStatusCode:(int)status { 90 | 91 | [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { 92 | return YES; 93 | } withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) { 94 | // Stub it with our "wsresponse.json" stub file 95 | return [OHHTTPStubsResponse responseWithFileAtPath:OHPathForFileInBundle(filename,nil) 96 | statusCode:status headers:@{@"Content-Type":@"text/json"}]; 97 | }]; 98 | } 99 | #pragma mark - IMGSessionDelegate Delegate methods 100 | 101 | -(void)imgurSessionNeedsExternalWebview:(NSURL *)url completion:(void (^)())completion{ 102 | 103 | self.calledImgurView = YES; 104 | } 105 | 106 | -(void)imgurSessionModelFetched:(id)model{ 107 | 108 | NSLog(@"New imgur model fetched: %@", [model description]); 109 | } 110 | 111 | -(void)imgurSessionRateLimitExceeded{ 112 | 113 | NSLog(@"Hit rate limit"); 114 | failBlock(nil); 115 | } 116 | 117 | @end 118 | -------------------------------------------------------------------------------- /ImgurSession/Requests/IMGNotificationRequest.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGNotificationRequest.m 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-15. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGNotificationRequest.h" 10 | #import "IMGNotification.h" 11 | #import "IMGSession.h" 12 | 13 | @implementation IMGNotificationRequest 14 | 15 | #pragma mark - Path Component for requests 16 | 17 | +(NSString*)pathComponent{ 18 | return @"notification"; 19 | } 20 | 21 | #pragma mark - Load 22 | 23 | + (void)unreadNotifications:(void (^)(NSArray * notifications))success failure:(void (^)(NSError * error))failure{ 24 | 25 | return [IMGNotificationRequest notificationsWithUnread:YES success:success failure:failure]; 26 | } 27 | 28 | + (void)allNotifications:(void (^)(NSArray * notifications))success failure:(void (^)(NSError * error))failure{ 29 | 30 | return [IMGNotificationRequest notificationsWithUnread:NO success:success failure:failure]; 31 | } 32 | 33 | + (void)notificationsWithUnread:(BOOL)unreadOnly success:(void (^)(NSArray *))success failure:(void (^)(NSError *))failure{ 34 | NSString *path = [self path]; 35 | 36 | if([[IMGSession sharedInstance] isAnonymous]){ 37 | if(failure) 38 | failure([NSError errorWithDomain:IMGErrorDomain code:IMGErrorRequiresUserAuthentication userInfo:@{IMGErrorServerPath:path}]); 39 | return; 40 | } 41 | 42 | [[IMGSession sharedInstance] GET:path parameters:@{@"new":(unreadOnly ? @"true" : @"false" )} success:^(NSURLSessionDataTask *task, id responseObject) { 43 | 44 | 45 | NSArray * repliesJSON = responseObject[@"replies"]; 46 | NSMutableArray * replies = [NSMutableArray new]; 47 | for(NSDictionary * replyJSON in repliesJSON){ 48 | NSError *JSONError = nil; 49 | IMGNotification * notification = [[IMGNotification alloc] initReplyNotificationWithJSONObject:replyJSON error:&JSONError]; 50 | if(!JSONError && notification) 51 | [replies addObject:notification]; 52 | } 53 | 54 | NSArray * messagesJSON = responseObject[@"messages"]; 55 | NSMutableArray * messages = [NSMutableArray new]; 56 | for(NSDictionary * messageJSON in messagesJSON){ 57 | NSError *JSONError = nil; 58 | IMGNotification * notification = [[IMGNotification alloc] initConversationNotificationWithJSONObject:messageJSON error:&JSONError]; 59 | //API returns duplicates of the same object for some reason 60 | if(!JSONError && notification && ![messages containsObject:notification]) 61 | [messages addObject:notification]; 62 | } 63 | 64 | NSMutableArray * result = [NSMutableArray arrayWithArray:messages]; 65 | [result addObjectsFromArray:replies]; 66 | 67 | if(success) 68 | success([NSArray arrayWithArray:result]); 69 | 70 | } failure:failure]; 71 | } 72 | 73 | + (void)notificationWithID:(NSString*)notificationId success:(void (^)(IMGNotification * notification))success failure:(void (^)(NSError * error))failure{ 74 | NSString *path = [self pathWithID:notificationId]; 75 | 76 | if([[IMGSession sharedInstance] isAnonymous]){ 77 | if(failure) 78 | failure([NSError errorWithDomain:IMGErrorDomain code:IMGErrorRequiresUserAuthentication userInfo:@{IMGErrorServerPath:path}]); 79 | return; 80 | } 81 | 82 | [[IMGSession sharedInstance] GET:path parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) { 83 | 84 | NSError *JSONError = nil; 85 | IMGNotification * notification; 86 | 87 | //is it a reply or message 88 | if(responseObject[@"content"][@"caption"]){ 89 | //reply 90 | notification = [[IMGNotification alloc] initReplyNotificationWithJSONObject:responseObject error:&JSONError]; 91 | } else { 92 | //convo 93 | notification = [[IMGNotification alloc] initConversationNotificationWithJSONObject:responseObject error:&JSONError]; 94 | } 95 | 96 | if(!JSONError && notification) { 97 | if(success) 98 | success(notification); 99 | } 100 | else { 101 | 102 | if(failure) 103 | failure(JSONError); 104 | } 105 | } failure:failure]; 106 | } 107 | 108 | 109 | #pragma mark - Delete 110 | 111 | + (void)notificationViewed:(NSString *)notificationId success:(void (^)())success failure:(void (^)(NSError *))failure{ 112 | NSString *path = [self pathWithID:notificationId]; 113 | 114 | if([[IMGSession sharedInstance] isAnonymous]){ 115 | if(failure) 116 | failure([NSError errorWithDomain:IMGErrorDomain code:IMGErrorRequiresUserAuthentication userInfo:@{IMGErrorServerPath:path}]); 117 | return; 118 | } 119 | 120 | //PUT or POST or DELETE 121 | [[IMGSession sharedInstance] DELETE:path parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) { 122 | 123 | if(success) 124 | success(nil); 125 | 126 | } failure:failure]; 127 | } 128 | @end 129 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGMessage.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGMessage.m 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-15. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGMessage.h" 10 | 11 | #import "IMGSession.h" 12 | 13 | @interface IMGMessage () 14 | 15 | @property (readwrite,nonatomic) NSString *messageID; 16 | @property (readwrite,nonatomic) NSString *fromUsername; 17 | @property (readwrite,nonatomic) NSString * subject; 18 | @property (readwrite,nonatomic) NSString *body; 19 | @property (readwrite,nonatomic) NSDate *datetime; 20 | @property (readwrite,nonatomic) NSInteger conversationID; 21 | @property (readwrite,nonatomic) NSInteger authorID; 22 | 23 | @end 24 | 25 | @implementation IMGMessage 26 | 27 | #pragma mark - Init With Json 28 | 29 | - (instancetype)initWithJSONObject:(NSDictionary *)jsonData error:(NSError *__autoreleasing *)error{ 30 | 31 | if(self = [super init]) { 32 | 33 | if(![jsonData isKindOfClass:[NSDictionary class]]){ 34 | 35 | if(error) 36 | *error = [NSError errorWithDomain:IMGErrorDomain code:IMGErrorMalformedResponseFormat userInfo:@{@"ImgurClass":[self class]}]; 37 | return nil; 38 | } else if (!jsonData[@"id"] || !jsonData[@"conversation_id"]){ 39 | 40 | if(error) 41 | *error = [NSError errorWithDomain:IMGErrorDomain code:IMGErrorResponseMissingParameters userInfo:nil]; 42 | return nil; 43 | } 44 | 45 | _messageID = jsonData[@"id"]; 46 | _fromUsername = jsonData[@"from"]; 47 | _authorID = [jsonData[@"sender_id"] integerValue]; 48 | _subject = jsonData[@"subject"]; 49 | _body = jsonData[@"body"]; 50 | _datetime = [NSDate dateWithTimeIntervalSince1970:[jsonData[@"datetime"] integerValue]]; 51 | _conversationID = [jsonData[@"conversation_id"] integerValue]; 52 | } 53 | return [self trackModels]; 54 | } 55 | 56 | - (instancetype)initWithBody:(NSString*)body{ 57 | 58 | if(self = [super init]) { 59 | 60 | _fromUsername = [[IMGSession sharedInstance] user].username; 61 | _authorID = [[IMGSession sharedInstance] user].accountID; 62 | _body = body; 63 | _datetime = [NSDate date]; 64 | } 65 | return [self trackModels]; 66 | } 67 | 68 | #pragma mark - Describe 69 | 70 | - (NSString *)description{ 71 | return [NSString stringWithFormat:@"%@ ; subject: \"%@\"; author: \"%@\"; message: %@;", [super description], self.subject, self.fromUsername, self.body]; 72 | } 73 | 74 | -(BOOL)isEqual:(id)object{ 75 | 76 | if (self == object) { 77 | return YES; 78 | } 79 | 80 | if (![object isKindOfClass:[IMGMessage class]]) { 81 | return NO; 82 | } 83 | 84 | return ([[object messageID] isEqualToString:self.messageID]); 85 | } 86 | 87 | #pragma mark - NSCoding 88 | 89 | - (id)initWithCoder:(NSCoder *)decoder { 90 | 91 | NSInteger authorID = [[decoder decodeObjectForKey:@"authorID"] integerValue]; 92 | NSInteger conversationID = [[decoder decodeObjectForKey:@"conversationID"] integerValue]; 93 | NSString * messageID = [decoder decodeObjectForKey:@"messageID"]; 94 | NSString * fromUsername = [decoder decodeObjectForKey:@"fromUsername"]; 95 | NSString * subject = [decoder decodeObjectForKey:@"subject"]; 96 | NSString * body = [decoder decodeObjectForKey:@"body"]; 97 | NSDate *datetime = [decoder decodeObjectForKey:@"datetime"]; 98 | 99 | if (self = [super initWithCoder:decoder]) { 100 | _authorID = authorID; 101 | _fromUsername = fromUsername; 102 | _subject = subject; 103 | _conversationID = conversationID; 104 | _messageID = messageID; 105 | _body = body; 106 | _datetime = datetime; 107 | } 108 | return self; 109 | } 110 | 111 | - (void)encodeWithCoder:(NSCoder *)coder { 112 | 113 | [super encodeWithCoder:coder]; 114 | 115 | [coder encodeObject:self.messageID forKey:@"messageID"]; 116 | [coder encodeObject:self.subject forKey:@"subject"]; 117 | [coder encodeObject:self.fromUsername forKey:@"fromUsername"]; 118 | [coder encodeObject:self.body forKey:@"body"]; 119 | [coder encodeObject:self.datetime forKey:@"datetime"]; 120 | 121 | [coder encodeObject:@(self.authorID) forKey:@"authorID"]; 122 | [coder encodeObject:@(self.conversationID) forKey:@"conversationID"]; 123 | } 124 | 125 | #pragma mark - NSCopying 126 | 127 | - (instancetype)copyWithZone:(NSZone *)zone { 128 | 129 | IMGMessage * copy = [[[self class] allocWithZone:zone] init]; 130 | 131 | if (copy) { 132 | // Copy NSObject subclasses 133 | [copy setMessageID:[self.messageID copyWithZone:zone]]; 134 | [copy setFromUsername:[self.fromUsername copyWithZone:zone]]; 135 | [copy setBody:[self.body copyWithZone:zone]]; 136 | [copy setSubject:[self.subject copyWithZone:zone]]; 137 | [copy setDatetime:[self.datetime copyWithZone:zone]]; 138 | 139 | // Set primitives 140 | [copy setAuthorID:self.authorID]; 141 | [copy setConversationID:self.conversationID]; 142 | } 143 | 144 | return copy; 145 | } 146 | 147 | @end 148 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGAccount.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGAccount.m 3 | // ImgurSession 4 | // 5 | // Created by Johann Pardanaud on 10/07/13. 6 | // Distributed under the MIT license. 7 | // 8 | 9 | #import "IMGAccount.h" 10 | #import "IMGImage.h" 11 | #import "IMGGalleryImage.h" 12 | #import "IMGGalleryAlbum.h" 13 | #import "IMGComment.h" 14 | #import "IMGGalleryProfile.h" 15 | 16 | @interface IMGAccount () 17 | @property (readwrite) NSString *username; 18 | @property (readwrite) NSString *bio; 19 | @property (readwrite) NSInteger accountID; 20 | @property (readwrite) NSInteger reputation; 21 | @property (readwrite) NSDate *created; 22 | @end 23 | 24 | @implementation IMGAccount; 25 | 26 | #pragma mark - Init With Json 27 | 28 | - (instancetype)initWithJSONObject:(NSDictionary *)jsonData error:(NSError * __autoreleasing *)error{ 29 | 30 | return [self initWithJSONObject:jsonData withName:@"me" error:error];; 31 | } 32 | 33 | - (instancetype)initWithJSONObject:(NSDictionary *)jsonData withName:(NSString *)username error:(NSError * __autoreleasing *)error{ 34 | 35 | if(self = [super init]) { 36 | 37 | if(![jsonData isKindOfClass:[NSDictionary class]]){ 38 | 39 | if(error) 40 | *error = [NSError errorWithDomain:IMGErrorDomain code:IMGErrorMalformedResponseFormat userInfo:@{@"ImgurClass":[self class]}]; 41 | return nil; 42 | } else if (!jsonData[@"id"] || !jsonData[@"url"] || !jsonData[@"bio"]){ 43 | 44 | if(error) 45 | *error = [NSError errorWithDomain:IMGErrorDomain code:IMGErrorResponseMissingParameters userInfo:nil]; 46 | return nil; 47 | } 48 | 49 | _accountID = [jsonData[@"id"] integerValue]; 50 | //the 'url' is actually the user's name in the API 51 | _username = jsonData[@"url"]; 52 | if([jsonData[@"bio"] isKindOfClass:[NSString class]]) 53 | _bio = jsonData[@"bio"]; 54 | _reputation = [jsonData[@"reputation"] integerValue]; 55 | _created = [NSDate dateWithTimeIntervalSince1970:[jsonData[@"created"] integerValue]]; 56 | } 57 | return [self trackModels]; 58 | } 59 | 60 | 61 | #pragma mark - Convenience 62 | 63 | -(NSString*)notorietyString{ 64 | //based on reputation 65 | 66 | NSString * notoriety; 67 | if(self.reputation >= 20000) 68 | notoriety = @"Glorious"; 69 | if(self.reputation >= 8000) 70 | notoriety = @"Renowned"; 71 | if(self.reputation >= 4000) 72 | notoriety = @"Idolized"; 73 | if(self.reputation >= 2000) 74 | notoriety = @"Trusted"; 75 | if(self.reputation >= 1000) 76 | notoriety = @"Liked"; 77 | if(self.reputation >= 400) 78 | notoriety = @"Accepted"; 79 | if(self.reputation >= 0) 80 | notoriety = @"Neutral"; 81 | if(self.reputation < 0) 82 | notoriety = @"Forever Alone"; 83 | 84 | return notoriety; 85 | } 86 | 87 | #pragma mark - Describe 88 | 89 | - (NSString *)description{ 90 | return [NSString stringWithFormat: @"%@; accountID: %lu; url: \"%@\"; bio: \"%@\"; reputation: %ld; created: %@", [super description], (unsigned long)self.accountID, self.username, self.bio, (long)self.reputation, self.created]; 91 | } 92 | 93 | -(BOOL)isEqual:(id)object{ 94 | 95 | if (self == object) { 96 | return YES; 97 | } 98 | 99 | if (![object isKindOfClass:[IMGAccount class]]) { 100 | return NO; 101 | } 102 | 103 | return ([object accountID] == self.accountID); 104 | } 105 | 106 | #pragma mark - NSCoding 107 | 108 | - (id)initWithCoder:(NSCoder *)decoder { 109 | 110 | NSInteger accountID = [[decoder decodeObjectForKey:@"accountID"] integerValue]; 111 | NSString * username = [decoder decodeObjectForKey:@"username"]; 112 | NSString * bio = [decoder decodeObjectForKey:@"bio"]; 113 | NSInteger reputation = [[decoder decodeObjectForKey:@"reputation"] integerValue]; 114 | NSDate *created = [decoder decodeObjectForKey:@"created"]; 115 | 116 | if (self = [super initWithCoder:decoder]) { 117 | _accountID = accountID; 118 | _bio = bio; 119 | _username = username; 120 | _reputation = reputation; 121 | _created = created; 122 | } 123 | return self; 124 | } 125 | 126 | - (void)encodeWithCoder:(NSCoder *)coder { 127 | 128 | [super encodeWithCoder:coder]; 129 | 130 | [coder encodeObject:@(self.accountID) forKey:@"accountID"]; 131 | [coder encodeObject:self.username forKey:@"username"]; 132 | [coder encodeObject:self.bio forKey:@"bio"]; 133 | [coder encodeObject:@(self.reputation) forKey:@"reputation"]; 134 | [coder encodeObject:self.created forKey:@"created"]; 135 | } 136 | 137 | #pragma mark - NSCopying 138 | 139 | - (id)copyWithZone:(NSZone *)zone { 140 | 141 | IMGAccount * copy = [[[self class] allocWithZone:zone] init]; 142 | 143 | if (copy) { 144 | // Copy NSObject subclasses 145 | [copy setUsername:[self.username copyWithZone:zone]]; 146 | [copy setBio:[self.bio copyWithZone:zone]]; 147 | [copy setCreated:[self.created copyWithZone:zone]]; 148 | 149 | // Set primitives 150 | [copy setAccountID:self.accountID]; 151 | [copy setReputation:self.reputation]; 152 | } 153 | 154 | return copy; 155 | } 156 | 157 | @end 158 | -------------------------------------------------------------------------------- /ImgurSession/Requests/IMGImageRequest.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGImageRequest.h 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-15. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGEndpoint.h" 10 | 11 | @class IMGImage; 12 | 13 | /** 14 | Image requests. https://api.imgur.com/endpoints/image 15 | */ 16 | @interface IMGImageRequest : IMGEndpoint 17 | 18 | #pragma mark - Load 19 | 20 | /** 21 | Retrieves image info from imageID 22 | */ 23 | + (void)imageWithID:(NSString *)imageID 24 | success:(void (^)(IMGImage *))success 25 | failure:(void (^)(NSError *))failure; 26 | 27 | #pragma mark - Upload one image 28 | 29 | /** 30 | Upload a gif with NSData 31 | */ 32 | + (void)uploadImageWithGifData:(NSData *)gifData 33 | title:(NSString *)title 34 | progress:(void (^)(NSProgress *))progress 35 | success:(void (^)(IMGImage *))success 36 | failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; 37 | 38 | + (void)uploadImageWithGifData:(NSData *)gifData 39 | compression:(CGFloat)compression 40 | title:(NSString *)title 41 | description:(NSString *)description 42 | linkToAlbumWithID:(NSString *)albumID 43 | progress:(void (^)(NSProgress *))progress 44 | success:(void (^)(IMGImage *))success 45 | failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; 46 | /** 47 | Upload an image with NSData 48 | */ 49 | + (void)uploadImageWithData:(NSData*)imageData 50 | title:(NSString *)title 51 | progress:(void (^)(NSProgress *))progress 52 | success:(void (^)(IMGImage *))success 53 | failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; 54 | 55 | + (void)uploadImageWithData:(NSData*)imageData 56 | title:(NSString *)title 57 | description:(NSString *)description 58 | linkToAlbumWithID:(NSString *)albumID 59 | progress:(void (^)(NSProgress *))progress 60 | success:(void (^)(IMGImage *))success 61 | failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; 62 | /** 63 | Upload an image with local image URL 64 | */ 65 | + (void)uploadImageWithFileURL:(NSURL *)fileURL 66 | progress:(void (^)(NSProgress *))progress 67 | success:(void (^)(IMGImage *))success 68 | failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; 69 | 70 | + (void)uploadImageWithFileURL:(NSURL *)fileURL 71 | title:(NSString *)title 72 | description:(NSString *)description 73 | linkToAlbumWithID:(NSString *)albumID 74 | progress:(void (^)(NSProgress *))progress 75 | success:(void (^)(IMGImage *))success 76 | failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; 77 | 78 | /** 79 | Upload an image with an external image URL 80 | */ 81 | + (void)uploadImageWithURL:(NSURL *)url success:(void (^)(IMGImage *image))success failure:(void (^)(NSError *error))failure; 82 | + (void)uploadImageWithURL:(NSURL *)url title:(NSString *)title description:(NSString *)description linkToAlbumWithID:(NSString *)albumID success:(void (^)(IMGImage *image))success failure:(void (^)(NSError *error))failure; 83 | 84 | #pragma mark - Upload multiple images - JPEG only 85 | 86 | /** 87 | Upload multiple images using image file url, title and description in NSDictionary in files and optionally link the images to an album 88 | */ 89 | +(void)uploadImages:(NSArray*)files 90 | progress:(void (^)(NSProgress *))progress 91 | success:(void (^)(NSArray *))success 92 | failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; 93 | 94 | +(void)uploadImages:(NSArray*)files 95 | toAlbumWithID:(NSString*)albumID 96 | progress:(void (^)(NSProgress *))progress 97 | success:(void (^)(NSArray *))success 98 | failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; 99 | 100 | #pragma mark - Delete 101 | 102 | /** 103 | Delete an image with an image ID if you are the owner of the image. Must be logged in. 104 | */ 105 | + (void)deleteImageWithID:(NSString *)imageID success:(void (^)())success failure:(void (^)(NSError *error))failure; 106 | /** 107 | Delete an image with an anonymous deletehash 108 | */ 109 | + (void)deleteImageWithHash:(NSString *)deletehash success:(void (^)())success failure:(void (^)(NSError * error))failure; 110 | 111 | #pragma mark - Favourtie 112 | /** 113 | Fav an album. Must be signed in. 114 | */ 115 | +(void)favouriteImageWithID:(NSString*)imageID success:(void (^)())success failure:(void (^)(NSError *error))failure; 116 | 117 | 118 | #pragma mark - Update 119 | 120 | /** 121 | Change image title or description or both. Must be logged In. 122 | */ 123 | + (void)updateImageWithID:(NSString *)imageID title:(NSString*)title description:(NSString*)description success:(void (^)())success failure:(void (^)(NSError * error))failure; 124 | /** 125 | Anonymously change title or description with deletehash 126 | */ 127 | + (void)updateImageWithDeleteHash:(NSString *)deletehash title:(NSString*)title description:(NSString*)description success:(void (^)())success failure:(void (^)(NSError * error))failure; 128 | @end 129 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGNotification.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGNotification.m 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-15. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGNotification.h" 10 | 11 | #import "IMGComment.h" 12 | #import "IMGMessage.h" 13 | #import "IMGConversation.h" 14 | 15 | @interface IMGNotification () 16 | 17 | @property (readwrite,nonatomic) NSString *notificationID; 18 | @property (readwrite,nonatomic) NSInteger accountID; 19 | @property (readwrite,nonatomic) IMGComment * reply; 20 | @property (readwrite,nonatomic) IMGConversation * conversation; 21 | @property (readwrite,nonatomic) BOOL isReply; 22 | @property (readwrite,nonatomic) NSDate * datetime; 23 | 24 | @end 25 | 26 | @implementation IMGNotification 27 | 28 | - (instancetype)initReplyNotificationWithJSONObject:(NSDictionary *)jsonData error:(NSError *__autoreleasing *)error{ 29 | 30 | if(self = [super init]) { 31 | 32 | if(![jsonData isKindOfClass:[NSDictionary class]]){ 33 | 34 | if(error) 35 | *error = [NSError errorWithDomain:IMGErrorDomain code:IMGErrorMalformedResponseFormat userInfo:@{@"ImgurClass":[self class]}]; 36 | return nil; 37 | } else if (!jsonData[@"id"]){ 38 | 39 | if(error) 40 | *error = [NSError errorWithDomain:IMGErrorDomain code:IMGErrorResponseMissingParameters userInfo:nil]; 41 | return nil; 42 | } 43 | 44 | _notificationID = jsonData[@"id"]; 45 | _accountID = [jsonData[@"account_id"] integerValue]; 46 | _isViewed = [jsonData[@"viewed"] boolValue]; 47 | _isReply = YES; 48 | 49 | NSDictionary * content = jsonData[@"content"]; 50 | 51 | IMGComment * comment = [[IMGComment alloc] initWithJSONObject:content error:error]; 52 | _reply = comment; 53 | _datetime = comment.datetime; 54 | } 55 | return [self trackModels]; 56 | } 57 | 58 | - (instancetype)initConversationNotificationWithJSONObject:(NSDictionary *)jsonData error:(NSError *__autoreleasing *)error{ 59 | 60 | if(self = [super init]) { 61 | 62 | _notificationID = jsonData[@"id"]; 63 | _accountID = [jsonData[@"account_id"] integerValue]; 64 | _isViewed = [jsonData[@"viewed"] boolValue]; 65 | _isReply = NO; 66 | 67 | NSDictionary * content = jsonData[@"content"]; 68 | 69 | IMGConversation * convo = [[IMGConversation alloc] initWithJSONObjectFromNotification:content error:error]; 70 | _conversation = convo; 71 | _datetime = convo.datetime; 72 | } 73 | return [self trackModels]; 74 | } 75 | 76 | #pragma mark - Describe 77 | 78 | - (NSString *)description{ 79 | return [NSString stringWithFormat:@"%@ ; notifyId: \"%@\"; accountId: %ld; viewed: %@;", [super description], self.notificationID, (long)self.accountID, (self.isViewed ? @"YES" : @"NO")]; 80 | } 81 | 82 | -(BOOL)isEqual:(id)object{ 83 | 84 | if (self == object) { 85 | return YES; 86 | } 87 | 88 | if (![object isKindOfClass:[IMGNotification class]]) { 89 | return NO; 90 | } 91 | 92 | if(self.isReply){ 93 | 94 | return ([[object reply] isEqual:self.reply]); 95 | } else { 96 | 97 | return ([[object conversation] isEqual:self.conversation]); 98 | } 99 | } 100 | 101 | 102 | #pragma mark - NSCoding 103 | 104 | - (id)initWithCoder:(NSCoder *)decoder { 105 | 106 | NSInteger accountID = [[decoder decodeObjectForKey:@"accountID"] integerValue]; 107 | NSString * notificationID = [decoder decodeObjectForKey:@"notificationID"]; 108 | 109 | IMGComment * reply= [decoder decodeObjectForKey:@"reply"]; 110 | IMGConversation * conversation = [decoder decodeObjectForKey:@"conversation"]; 111 | BOOL isViewed = [[decoder decodeObjectForKey:@"isViewed"] boolValue]; 112 | BOOL isReply = [[decoder decodeObjectForKey:@"isReply"] boolValue]; 113 | NSDate * date = [decoder decodeObjectForKey:@"date"]; 114 | 115 | if (self = [super initWithCoder:decoder]) { 116 | _accountID = accountID; 117 | _notificationID = notificationID; 118 | 119 | _reply = reply; 120 | _conversation = conversation; 121 | _isViewed = isViewed; 122 | _isReply = isReply; 123 | _datetime = date; 124 | } 125 | return self; 126 | } 127 | 128 | - (void)encodeWithCoder:(NSCoder *)coder { 129 | 130 | [super encodeWithCoder:coder]; 131 | 132 | [coder encodeObject:self.notificationID forKey:@"notificationID"]; 133 | [coder encodeObject:self.conversation forKey:@"conversation"]; 134 | [coder encodeObject:self.reply forKey:@"reply"]; 135 | [coder encodeObject:self.datetime forKey:@"date"]; 136 | 137 | [coder encodeObject:@(self.accountID) forKey:@"accountID"]; 138 | [coder encodeObject:@(self.isViewed) forKey:@"isViewed"]; 139 | [coder encodeObject:@(self.isReply) forKey:@"isReply"]; 140 | } 141 | 142 | #pragma mark - NSCopying 143 | 144 | - (instancetype)copyWithZone:(NSZone *)zone { 145 | 146 | IMGNotification * copy = [[[self class] allocWithZone:zone] init]; 147 | 148 | if (copy) { 149 | // Copy NSObject subclasses 150 | [copy setNotificationID:[self.notificationID copyWithZone:zone]]; 151 | [copy setReply:[self.reply copyWithZone:zone]]; 152 | [copy setConversation:[self.conversation copyWithZone:zone]]; 153 | [copy setDatetime:[self.datetime copyWithZone:zone]]; 154 | 155 | // Set primitives 156 | [copy setAccountID:self.accountID]; 157 | [copy setIsReply:self.isReply]; 158 | [copy setIsViewed:self.isViewed]; 159 | } 160 | 161 | return copy; 162 | } 163 | 164 | @end 165 | -------------------------------------------------------------------------------- /ImgurSession/Requests/IMGConversationRequest.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGConversationRequest.m 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-15. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGConversationRequest.h" 10 | #import "IMGMessage.h" 11 | #import "IMGConversation.h" 12 | #import "IMGSession.h" 13 | 14 | @implementation IMGConversationRequest 15 | #pragma mark - Path 16 | 17 | +(NSString *)pathComponent{ 18 | return @"conversations"; 19 | } 20 | 21 | #pragma mark - Load 22 | 23 | + (void)conversations:(void (^)(NSArray *))success failure:(void (^)(NSError *))failure{ 24 | NSString * path = [self path]; 25 | 26 | if([[IMGSession sharedInstance] isAnonymous]){ 27 | if(failure) 28 | failure([NSError errorWithDomain:IMGErrorDomain code:IMGErrorRequiresUserAuthentication userInfo:@{IMGErrorServerPath:path}]); 29 | return; 30 | } 31 | 32 | [[IMGSession sharedInstance] GET:path parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) { 33 | 34 | NSArray * convoJSON = responseObject; 35 | NSMutableArray * convos = [NSMutableArray new]; 36 | 37 | for(NSDictionary * conversationJSON in convoJSON){ 38 | 39 | NSError *JSONError = nil; 40 | IMGConversation *convo = [[IMGConversation alloc] initWithJSONObject:conversationJSON error:&JSONError]; 41 | 42 | if(!JSONError && convo){ 43 | [convos addObject:convo]; 44 | } 45 | } 46 | 47 | if(success) 48 | success([NSArray arrayWithArray:convos]); 49 | 50 | } failure:failure]; 51 | } 52 | 53 | + (void)conversationWithMessageID:(NSInteger)messageId success:(void (^)(IMGConversation *))success failure:(void (^)(NSError *))failure{ 54 | NSString * path = [self pathWithID:[NSString stringWithFormat:@"%lu", (long)messageId]]; 55 | 56 | if([[IMGSession sharedInstance] isAnonymous]){ 57 | if(failure) 58 | failure([NSError errorWithDomain:IMGErrorDomain code:IMGErrorRequiresUserAuthentication userInfo:@{IMGErrorServerPath:path}]); 59 | return; 60 | } 61 | 62 | [[IMGSession sharedInstance] GET:path parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) { 63 | 64 | NSError *JSONError = nil; 65 | IMGConversation *convo = [[IMGConversation alloc] initWithJSONObject:responseObject error:&JSONError]; 66 | 67 | if(!JSONError && convo) { 68 | if(success) 69 | success(convo); 70 | } 71 | else { 72 | if(failure) 73 | failure(JSONError); 74 | } 75 | 76 | } failure:failure]; 77 | } 78 | 79 | #pragma mark - Create 80 | 81 | 82 | + (void)createMessageWithRecipient:(NSString*)recipient withBody:(NSString*)body success:(void (^)())success failure:(void (^)(NSError *))failure{ 83 | NSString *path = [self pathWithID:recipient]; 84 | NSDictionary * params = @{@"recipient":recipient,@"body":body}; 85 | 86 | if([[IMGSession sharedInstance] isAnonymous]){ 87 | if(failure) 88 | failure([NSError errorWithDomain:IMGErrorDomain code:IMGErrorRequiresUserAuthentication userInfo:@{IMGErrorServerPath:path}]); 89 | return; 90 | } 91 | 92 | [[IMGSession sharedInstance] POST:path parameters:params success:^(NSURLSessionDataTask *task, id responseObject) { 93 | 94 | if(success) 95 | success(); 96 | 97 | } failure:failure]; 98 | } 99 | 100 | #pragma mark - Delete 101 | 102 | + (void)deleteConversation:(NSInteger)convoID success:(void (^)())success failure:(void (^)(NSError *))failure{ 103 | NSString *path = [self pathWithID:[NSString stringWithFormat:@"%lu", (long)convoID]]; 104 | 105 | if([[IMGSession sharedInstance] isAnonymous]){ 106 | if(failure) 107 | failure([NSError errorWithDomain:IMGErrorDomain code:IMGErrorRequiresUserAuthentication userInfo:@{IMGErrorServerPath:path}]); 108 | return; 109 | } 110 | 111 | [[IMGSession sharedInstance] DELETE:path parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) { 112 | 113 | if(success) 114 | success(); 115 | 116 | } failure:failure]; 117 | } 118 | 119 | #pragma mark - Report 120 | 121 | + (void)reportSender:(NSString*)username success:(void (^)())success failure:(void (^)(NSError *))failure{ 122 | NSString *path = [self pathWithOption:@"report" withID2:username]; 123 | 124 | if([[IMGSession sharedInstance] isAnonymous]){ 125 | if(failure) 126 | failure([NSError errorWithDomain:IMGErrorDomain code:IMGErrorRequiresUserAuthentication userInfo:@{IMGErrorServerPath:path}]); 127 | return; 128 | } 129 | 130 | [[IMGSession sharedInstance] POST:path parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) { 131 | 132 | if(success) 133 | success(); 134 | 135 | } failure:failure]; 136 | } 137 | 138 | #pragma mark - Block 139 | 140 | + (void)blockSender:(NSString*)username success:(void (^)())success failure:(void (^)(NSError *))failure{ 141 | NSString *path = [self pathWithOption:@"block" withID2:username]; 142 | 143 | if([[IMGSession sharedInstance] isAnonymous]){ 144 | if(failure) 145 | failure([NSError errorWithDomain:IMGErrorDomain code:IMGErrorRequiresUserAuthentication userInfo:@{IMGErrorServerPath:path}]); 146 | return; 147 | } 148 | 149 | [[IMGSession sharedInstance] POST:path parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) { 150 | 151 | if(success) 152 | success(); 153 | 154 | } failure:failure]; 155 | } 156 | @end 157 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGGalleryImage.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGGalleryImage.m 3 | // ImgurSession 4 | // 5 | // Created by Johann Pardanaud on 11/07/13. 6 | // Distributed under the MIT license. 7 | // 8 | 9 | #import "IMGGalleryImage.h" 10 | #import "NSDictionary+IMG.h" 11 | 12 | @interface IMGGalleryImage () 13 | 14 | @property (readwrite, nonatomic) IMGVoteType vote; 15 | @property (readwrite, nonatomic) NSString *accountURL; 16 | @property (readwrite, nonatomic) NSInteger ups; 17 | @property (readwrite, nonatomic) NSInteger downs; 18 | @property (readwrite, nonatomic) NSInteger score; 19 | @property (readwrite, nonatomic) BOOL favorite; 20 | @property (readwrite, nonatomic) BOOL nsfw; 21 | 22 | @end 23 | 24 | @implementation IMGGalleryImage; 25 | 26 | #pragma mark - Init With Json 27 | 28 | - (instancetype)initWithJSONObject:(NSDictionary *)jsonData error:(NSError *__autoreleasing *)error{ 29 | 30 | self = [super initWithJSONObject:jsonData error:error]; 31 | 32 | if(self && !*error) { 33 | 34 | if(![jsonData isKindOfClass:[NSDictionary class]]){ 35 | 36 | *error = [NSError errorWithDomain:IMGErrorDomain code:IMGErrorMalformedResponseFormat userInfo:@{@"ImgurClass":[self class]}]; 37 | return nil; 38 | } 39 | //clean NSNull 40 | jsonData = [jsonData IMG_cleanNull]; 41 | 42 | _ups = [jsonData[@"ups"] integerValue]; 43 | _downs = [jsonData[@"downs"] integerValue]; 44 | _score = [jsonData[@"score"] integerValue]; 45 | _accountURL = jsonData[@"account_url"]; 46 | _vote = [IMGVote voteForStr:jsonData[@"vote"]]; 47 | _nsfw = [jsonData[@"nsfw"] boolValue]; 48 | _favorite = [jsonData[@"favorite"] boolValue]; 49 | } 50 | return [self trackModels]; 51 | } 52 | 53 | -(instancetype)initWithComment:(IMGComment*)comment error:(NSError *__autoreleasing *)error{ 54 | 55 | NSParameterAssert(comment); 56 | 57 | if(self = [super initWithGalleryID:comment.galleryID error:error]){ 58 | 59 | 60 | } 61 | return [self trackModels]; 62 | } 63 | 64 | #pragma mark - Describe 65 | 66 | - (NSString *)description{ 67 | return [NSString stringWithFormat: 68 | @"%@; accountURL: \"%@\"; ups: %ld; downs: %ld; score: %ld; vote: %ld", 69 | [super description], self.accountURL, (long)self.ups, (long)self.downs, (long)self.score, (long)self.vote]; 70 | } 71 | 72 | -(BOOL)isEqual:(id)object{ 73 | 74 | if (self == object) { 75 | return YES; 76 | } 77 | 78 | if (![object isKindOfClass:[IMGImage class]]) { 79 | return NO; 80 | } 81 | return ([[object imageID] isEqualToString:self.imageID]); 82 | } 83 | 84 | -(NSUInteger)hash{ 85 | 86 | return [self.imageID hash]; 87 | } 88 | 89 | #pragma mark - IMGGalleryObjectProtocol 90 | 91 | -(BOOL)isAlbum{ 92 | return NO; 93 | } 94 | 95 | -(IMGVoteType)usersVote{ 96 | return self.vote; 97 | } 98 | 99 | -(BOOL)isFavorite{ 100 | return self.favorite; 101 | } 102 | 103 | -(BOOL)isNSFW{ 104 | return self.nsfw; 105 | } 106 | 107 | -(IMGImage *)coverImage{ 108 | 109 | //for gallery image, the image is the cover image 110 | return self; 111 | } 112 | 113 | -(NSString *)coverID{ 114 | 115 | //for gallery image, the image is the cover image 116 | return self.imageID; 117 | } 118 | 119 | -(NSString *)objectID{ 120 | 121 | return self.imageID; 122 | } 123 | 124 | -(NSString*)galleryDescription{ 125 | 126 | return self.imageDescription; 127 | } 128 | 129 | -(NSString*)fromUsername{ 130 | 131 | return self.accountURL; 132 | } 133 | 134 | -(void)setUsersVote:(IMGVoteType)vote{ 135 | 136 | self.vote = vote; 137 | 138 | } 139 | 140 | -(void)setUsersFav:(BOOL)faved{ 141 | 142 | self.favorite = faved; 143 | } 144 | 145 | #pragma mark - NSCoding 146 | 147 | - (id)initWithCoder:(NSCoder *)decoder { 148 | 149 | NSString * accountURL = [decoder decodeObjectForKey:@"accountURL"]; 150 | IMGVoteType vote = [[decoder decodeObjectForKey:@"vote"] integerValue]; 151 | NSInteger ups = [[decoder decodeObjectForKey:@"ups"] integerValue]; 152 | NSInteger downs = [[decoder decodeObjectForKey:@"downs"] integerValue]; 153 | NSInteger score = [[decoder decodeObjectForKey:@"score"] integerValue]; 154 | BOOL favorite = [[decoder decodeObjectForKey:@"favorite"] boolValue]; 155 | BOOL nsfw = [[decoder decodeObjectForKey:@"nsfw"] boolValue]; 156 | 157 | if (self = [super initWithCoder:decoder]) { 158 | _vote = vote; 159 | _accountURL = accountURL; 160 | _ups = ups; 161 | _downs = downs; 162 | _score = score; 163 | _favorite = favorite; 164 | _nsfw = nsfw; 165 | } 166 | return self; 167 | } 168 | 169 | - (void)encodeWithCoder:(NSCoder *)coder { 170 | 171 | [super encodeWithCoder:coder]; 172 | 173 | [coder encodeObject:self.accountURL forKey:@"accountURL"]; 174 | 175 | [coder encodeObject:@(self.vote) forKey:@"vote"]; 176 | [coder encodeObject:@(self.ups) forKey:@"ups"]; 177 | [coder encodeObject:@(self.downs) forKey:@"downs"]; 178 | [coder encodeObject:@(self.score) forKey:@"score"]; 179 | [coder encodeObject:@(self.favorite) forKey:@"favorite"]; 180 | [coder encodeObject:@(self.nsfw) forKey:@"nsfw"]; 181 | } 182 | 183 | #pragma mark - NSCopying 184 | 185 | - (instancetype)copyWithZone:(NSZone *)zone { 186 | 187 | IMGGalleryImage * copy = [super copyWithZone:zone]; 188 | 189 | if (copy) { 190 | // Copy NSObject subclasses 191 | [copy setAccountURL:[self.accountURL copyWithZone:zone]]; 192 | [copy setVote:self.vote]; 193 | [copy setUps:self.ups]; 194 | [copy setDowns:self.downs]; 195 | [copy setScore:self.score]; 196 | [copy setNsfw:self.nsfw]; 197 | [copy setFavorite:self.favorite]; 198 | } 199 | 200 | return copy; 201 | } 202 | 203 | @end 204 | -------------------------------------------------------------------------------- /ImgurSession/IMGResponseSerializer.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGResponseSerializer.m 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-07. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGResponseSerializer.h" 10 | 11 | #import "IMGSession.h" 12 | #import "IMGModel.h" 13 | 14 | 15 | @interface IMGSession() 16 | 17 | #pragma mark - Rate Limit Tracking 18 | /** 19 | Tracks rate limiting using HTTP headers from the response 20 | @param response HTTP response returned from Imgur call 21 | */ 22 | -(void)updateClientRateLimiting:(NSHTTPURLResponse*)response; 23 | 24 | @end 25 | 26 | @implementation IMGResponseSerializer 27 | 28 | /** 29 | Returns data for IMG Request classes to parse from network request. The json should be parsed from data using the JSON serializer with the super call to responseObjectForResponse. Thus the json should be the basic model described at https://api.imgur.com/models/basic . The 'data' key is all that matters, additonal success and status keys are redundant and not useful for us since we can get those from the http status code. We also use this opportunity to grab the response headers and track rate limiting since this method is called for every single response. 30 | */ 31 | -(id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error{ 32 | 33 | NSHTTPURLResponse * httpRes = (NSHTTPURLResponse *)response; 34 | 35 | //json parsing with AFJSONResponseSerializer, result should be 'basic' model specifed @ https://api.imgur.com/models/basic 36 | NSError * jsonError; 37 | NSDictionary * jsonResult = [super responseObjectForResponse:response data:data error:&jsonError]; 38 | 39 | if(httpRes.statusCode == 200){ 40 | //successful request! 41 | 42 | if(jsonError){ 43 | //decoding error 44 | 45 | NSString * errorDescription = [jsonError localizedDescription]; 46 | NSMutableDictionary * errorDict = [NSMutableDictionary new]; 47 | if(errorDescription) 48 | [errorDict setObject:errorDescription forKey:NSLocalizedDescriptionKey]; 49 | if(jsonError) 50 | [errorDict setObject:jsonError forKey:IMGErrorDecoding]; 51 | 52 | *error = [NSError errorWithDomain:IMGErrorDomain code:IMGErrorMalformedResponseFormat userInfo:[NSDictionary dictionaryWithDictionary:errorDict]]; 53 | return nil; 54 | } 55 | 56 | //decoding successful, continue to API completion 57 | 58 | //we need the data object or if does not exist pass the full JSON object for oauth/token endpoint 59 | if(jsonResult[@"data"]){ 60 | //let response continue processing by ImgurSession completion blocks 61 | 62 | //update rate limit tracking in the session 63 | [[IMGSession sharedInstance] updateClientRateLimiting:httpRes]; 64 | 65 | //pass back only "data" for simplicity to request subclasses 66 | return jsonResult[@"data"]; 67 | 68 | } else { 69 | //the basic model is not respected for oauth calls, still need to respond with JSON 70 | //cannot handle client rate limiting in this case, headers are not sent 71 | return jsonResult; 72 | } 73 | 74 | } else { 75 | 76 | /** 77 | Error handling detailed @ https://api.imgur.com/errorhandling 78 | 79 | Status Code: 200 80 | The request has succeeded and there were no errors. Congrats! 81 | Status Code: 400 82 | This error indicates that a required parameter is missing or a parameter has a value that is out of bounds or otherwise incorrect. This status code is also returned when image uploads fail due to images that are corrupt or do not meet the format requirements. 83 | Status Code: 401 84 | The request requires user authentication. Either you didn't send send OAuth credentials, or the ones you sent were invalid. 85 | Status Code: 403 86 | Forbidden. You don't have access to this action. If you're getting this error, check that you haven't run out of API credits or make sure you're sending the OAuth headers correctly and have valid tokens/secrets. 87 | Status Code: 404 88 | Resource does not exist. This indicates you have requested a resource that does not exist. For example, requesting an image that doesn't exist. 89 | Status Code: 429 90 | Rate limiting. This indicates you have hit either the rate limiting on the application or on the user's IP address. 91 | Status Code: 500 92 | Unexpected internal error. What it says. We'll strive NOT to return these but your app should be prepared to see it. It basically means that something is broken with the Imgur service. 93 | **/ 94 | 95 | //construct error to inform original API request of exact issue. 96 | //Special cases are needed for 401,403,429 as detaileed in NSError+IMGError and IMGSession 97 | 98 | NSString * errorDescription = jsonResult[@"data"][@"error"]; 99 | NSString * errorPath = jsonResult[@"data"][@"request"]; 100 | NSString * errorMethod = jsonResult[@"data"][@"method"]; 101 | 102 | NSMutableDictionary * errorDict = [NSMutableDictionary new]; 103 | if(errorDescription) 104 | [errorDict setObject:errorDescription forKey:IMGErrorServerDescription]; 105 | if(errorPath) 106 | [errorDict setObject:errorPath forKey:IMGErrorServerPath]; 107 | if(errorMethod) 108 | [errorDict setObject:errorMethod forKey:IMGErrorServerMethod]; 109 | 110 | 111 | *error = [NSError errorWithDomain:IMGErrorDomain code:httpRes.statusCode userInfo:[NSDictionary dictionaryWithDictionary:errorDict]]; 112 | return nil; 113 | } 114 | } 115 | @end 116 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGConversation.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGConversation.m 3 | // ImgurSession 4 | // 5 | // Created by Xtreme Dev on 2014-03-20. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGConversation.h" 10 | #import "IMGMessage.h" 11 | 12 | @interface IMGConversation () 13 | 14 | @property (readwrite,nonatomic) NSArray *messages; 15 | @property (readwrite,nonatomic) NSString *fromUsername; 16 | @property (readwrite,nonatomic) NSString *lastMessage; 17 | @property (readwrite,nonatomic) NSDate *datetime; 18 | @property (readwrite,nonatomic) NSInteger conversationID; 19 | @property (readwrite,nonatomic) NSInteger authorID; 20 | @property (readwrite,nonatomic) NSInteger messageCount; 21 | 22 | @end 23 | 24 | @implementation IMGConversation 25 | 26 | #pragma mark - Init With Json 27 | 28 | - (instancetype)initWithJSONObject:(NSDictionary *)jsonData error:(NSError *__autoreleasing *)error{ 29 | 30 | if(self = [super init]) { 31 | 32 | if(![jsonData isKindOfClass:[NSDictionary class]]){ 33 | 34 | if(error) 35 | *error = [NSError errorWithDomain:IMGErrorDomain code:IMGErrorMalformedResponseFormat userInfo:@{@"ImgurClass":[self class]}]; 36 | return nil; 37 | } else if (!jsonData[@"id"] || !jsonData[@"with_account"]){ 38 | 39 | if(error) 40 | *error = [NSError errorWithDomain:IMGErrorDomain code:IMGErrorResponseMissingParameters userInfo:nil]; 41 | return nil; 42 | } 43 | 44 | _conversationID = [jsonData[@"id"] integerValue]; 45 | _fromUsername = jsonData[@"with_account"]; 46 | _authorID = [jsonData[@"with_account_id"] integerValue]; 47 | _lastMessage = jsonData[@"last_message_preview"]; 48 | _messageCount = [jsonData[@"message_count"] integerValue]; 49 | _datetime = [NSDate dateWithTimeIntervalSince1970:[jsonData[@"datetime"] integerValue]]; 50 | 51 | NSMutableArray * msgs = [NSMutableArray new]; 52 | for(NSDictionary * json in jsonData[@"messages"]){ 53 | 54 | NSError * JSONError; 55 | IMGMessage * msg = [[IMGMessage alloc] initWithJSONObject:json error:&JSONError]; 56 | 57 | if(msg && !JSONError){ 58 | [msgs addObject:msg]; 59 | } 60 | } 61 | _messages = [NSArray arrayWithArray:msgs]; 62 | } 63 | return [self trackModels]; 64 | } 65 | 66 | - (instancetype)initWithJSONObjectFromNotification:(NSDictionary *)jsonData error:(NSError *__autoreleasing *)error{ 67 | 68 | if(self = [super init]) { 69 | 70 | _conversationID = [jsonData[@"id"] integerValue]; 71 | _fromUsername = jsonData[@"from"]; 72 | _authorID = [jsonData[@"with_account"] integerValue]; 73 | _lastMessage = jsonData[@"last_message"]; 74 | _messageCount = [jsonData[@"message_num"] integerValue]; 75 | _datetime = [NSDate dateWithTimeIntervalSince1970:[jsonData[@"datetime"] integerValue]]; 76 | } 77 | return [self trackModels]; 78 | } 79 | 80 | #pragma mark - Describe 81 | 82 | - (NSString *)description{ 83 | return [NSString stringWithFormat:@"%@ author: \"%@\"; last message: %@; count: %lu;", [super description], self.fromUsername, self.lastMessage, (long)self.messageCount]; 84 | } 85 | 86 | -(BOOL)isEqual:(id)object{ 87 | 88 | if (self == object) { 89 | return YES; 90 | } 91 | 92 | if (![object isKindOfClass:[IMGConversation class]]) { 93 | return NO; 94 | } 95 | 96 | return ([object conversationID] == self.conversationID); 97 | } 98 | 99 | #pragma mark - NSCoding 100 | 101 | - (id)initWithCoder:(NSCoder *)decoder { 102 | 103 | NSInteger conversationID = [[decoder decodeObjectForKey:@"conversationID"] integerValue]; 104 | NSInteger authorID = [[decoder decodeObjectForKey:@"authorID"] integerValue]; 105 | NSInteger messageCount = [[decoder decodeObjectForKey:@"messageCount"] integerValue]; 106 | NSString * fromUsername = [decoder decodeObjectForKey:@"fromUsername"]; 107 | NSString * lastMessage = [decoder decodeObjectForKey:@"lastMessage"]; 108 | NSDate *datetime = [decoder decodeObjectForKey:@"datetime"]; 109 | NSArray *messages = [decoder decodeObjectForKey:@"messages"]; 110 | 111 | if (self = [super initWithCoder:decoder]) { 112 | _conversationID = conversationID; 113 | _fromUsername = fromUsername; 114 | _authorID = authorID; 115 | _lastMessage = lastMessage; 116 | _datetime = datetime; 117 | _messageCount = messageCount; 118 | _messages = messages; 119 | } 120 | return self; 121 | } 122 | 123 | - (void)encodeWithCoder:(NSCoder *)coder { 124 | 125 | [super encodeWithCoder:coder]; 126 | 127 | [coder encodeObject:self.messages forKey:@"messages"]; 128 | [coder encodeObject:self.fromUsername forKey:@"fromUsername"]; 129 | [coder encodeObject:self.lastMessage forKey:@"lastMessage"]; 130 | [coder encodeObject:self.datetime forKey:@"datetime"]; 131 | 132 | [coder encodeObject:@(self.conversationID) forKey:@"conversationID"]; 133 | [coder encodeObject:@(self.authorID) forKey:@"authorID"]; 134 | [coder encodeObject:@(self.messageCount) forKey:@"messageCount"]; 135 | } 136 | 137 | #pragma mark - NSCopying 138 | 139 | - (instancetype)copyWithZone:(NSZone *)zone { 140 | 141 | IMGConversation * copy = [[[self class] allocWithZone:zone] init]; 142 | 143 | if (copy) { 144 | // Copy NSObject subclasses 145 | [copy setFromUsername:[self.fromUsername copyWithZone:zone]]; 146 | [copy setLastMessage:[self.lastMessage copyWithZone:zone]]; 147 | [copy setMessages:[self.messages copyWithZone:zone]]; 148 | [copy setDatetime:[self.datetime copyWithZone:zone]]; 149 | 150 | // Set primitives 151 | [copy setAuthorID:self.authorID]; 152 | [copy setConversationID:self.conversationID]; 153 | [copy setMessageCount:self.messageCount]; 154 | } 155 | 156 | return copy; 157 | } 158 | 159 | @end 160 | -------------------------------------------------------------------------------- /Integrated Tests/IMGGalleryTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGGalleryTests.m 3 | // ImgurSession 4 | // 5 | // Created by Xtreme Dev on 2014-03-19. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGIntegratedTestCase.h" 10 | 11 | @interface IMGGalleryTests : IMGIntegratedTestCase 12 | 13 | @end 14 | 15 | @implementation IMGGalleryTests 16 | 17 | 18 | - (void)testGalleryHot{ 19 | 20 | __block BOOL isSuccess; 21 | __block NSMutableOrderedSet * j = [NSMutableOrderedSet new]; 22 | 23 | [IMGGalleryRequest hotGalleryPage:0 success:^(NSArray * images) { 24 | 25 | [j addObjectsFromArray:images]; 26 | 27 | } failure:failBlock]; 28 | 29 | 30 | [IMGGalleryRequest hotGalleryPage:1 success:^(NSArray * images) { 31 | 32 | [j addObjectsFromArray:images]; 33 | 34 | } failure:failBlock]; 35 | 36 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 37 | 38 | [IMGGalleryRequest hotGalleryPage:2 success:^(NSArray * images) { 39 | 40 | [j addObjectsFromArray:images]; 41 | 42 | if(j.count == images.count) 43 | failBlock(nil); 44 | else 45 | isSuccess = YES; 46 | 47 | } failure:failBlock]; 48 | }); 49 | 50 | expect(isSuccess).will.beTruthy(); 51 | } 52 | 53 | - (void)testGalleryTop{ 54 | 55 | __block BOOL isSuccess; 56 | __block NSMutableOrderedSet * j = [NSMutableOrderedSet new]; 57 | 58 | [IMGGalleryRequest topGalleryPage:0 withWindow:IMGTopGalleryWindowDay success:^(NSArray * images) { 59 | 60 | [j addObjectsFromArray:images]; 61 | 62 | } failure:failBlock]; 63 | 64 | 65 | [IMGGalleryRequest topGalleryPage:1 withWindow:IMGTopGalleryWindowDay success:^(NSArray * images) { 66 | 67 | [j addObjectsFromArray:images]; 68 | 69 | } failure:failBlock]; 70 | 71 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 72 | 73 | [IMGGalleryRequest topGalleryPage:2 withWindow:IMGTopGalleryWindowDay success:^(NSArray * images) { 74 | 75 | [j addObjectsFromArray:images]; 76 | 77 | if(j.count == images.count) 78 | failBlock(nil); 79 | else 80 | isSuccess = YES; 81 | 82 | } failure:failBlock]; 83 | }); 84 | 85 | expect(isSuccess).will.beTruthy(); 86 | } 87 | 88 | - (void)testGalleryUser{ 89 | 90 | __block BOOL isSuccess; 91 | __block NSMutableOrderedSet * j = [NSMutableOrderedSet new]; 92 | 93 | [IMGGalleryRequest userGalleryPage:0 withViralSort:YES showViral:YES success:^(NSArray * images) { 94 | 95 | [j addObjectsFromArray:images]; 96 | 97 | } failure:failBlock]; 98 | 99 | [IMGGalleryRequest userGalleryPage:1 withViralSort:YES showViral:YES success:^(NSArray * images) { 100 | 101 | [j addObjectsFromArray:images]; 102 | 103 | } failure:failBlock]; 104 | 105 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 106 | 107 | [IMGGalleryRequest userGalleryPage:2 withViralSort:YES showViral:YES success:^(NSArray * images) { 108 | 109 | [j addObjectsFromArray:images]; 110 | 111 | if(j.count == images.count) 112 | failBlock(nil); 113 | else 114 | isSuccess = YES; 115 | 116 | } failure:failBlock]; 117 | }); 118 | 119 | expect(isSuccess).will.beTruthy(); 120 | } 121 | 122 | -(void)testImageNotFound{ 123 | 124 | __block BOOL isSuccess = NO; 125 | 126 | //should fail request with not found 127 | 128 | [IMGImageRequest imageWithID:@"fdsfdsfdsa" success:^(IMGImage *image) { 129 | 130 | //should not success 131 | failBlock([NSError errorWithDomain:IMGErrorDomain code:0 userInfo:nil]); 132 | 133 | } failure:^(NSError *error) { 134 | 135 | //imgur sometimes responds with previous account requests for some reasons saying it is a cache hit even though it is a different URL 136 | //in this case this test will fail with code 1 == IMGErrorResponseMissingParameters 137 | 138 | expect(error.code == 404).beTruthy(); 139 | expect([error.userInfo[IMGErrorServerMethod] isEqualToString:@"GET"]).beTruthy(); 140 | 141 | //should go here 142 | isSuccess = YES; 143 | 144 | }]; 145 | 146 | expect(isSuccess).will.beTruthy(); 147 | } 148 | 149 | 150 | // no longer working since POST does not retry authentications 151 | 152 | - (void)testPostAndDeleteGalleryImage{ 153 | 154 | __block BOOL isDeleted; 155 | 156 | [self postTestGalleryImage:^(IMGGalleryImage * image, void(^success)()) { 157 | 158 | success(); 159 | isDeleted = YES; 160 | }]; 161 | 162 | expect(isDeleted).will.beTruthy(); 163 | } 164 | 165 | - (void)testPostAndDeleteGalleryAlbum{ 166 | 167 | __block BOOL isDeleted; 168 | [self postTestGalleryAlbumWithOneImage:^(IMGGalleryAlbum * album, void(^success)()) { 169 | 170 | success(); 171 | isDeleted = YES; 172 | }]; 173 | expect(isDeleted).will.beTruthy(); 174 | } 175 | 176 | - (void)testPostAndDeleteImage{ 177 | 178 | __block BOOL isDeleted; 179 | [self postTestImage:^(IMGImage * image, void(^success)()) { 180 | 181 | success(); 182 | isDeleted = YES; 183 | }]; 184 | expect(isDeleted).will.beTruthy(); 185 | } 186 | 187 | - (void)testPostAndDeleteAlbum{ 188 | 189 | __block BOOL isDeleted; 190 | [self postTestAlbumWithOneImage:^(IMGAlbum * album, void(^success)()) { 191 | 192 | success(); 193 | isDeleted = YES; 194 | }]; 195 | expect(isDeleted).will.beTruthy(); 196 | } 197 | 198 | @end 199 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGGalleryAlbum.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGGalleryAlbum.m 3 | // ImgurSession 4 | // 5 | // Created by Johann Pardanaud on 11/07/13. 6 | // Distributed under the MIT license. 7 | // 8 | 9 | #import "IMGGalleryAlbum.h" 10 | #import "NSDictionary+IMG.h" 11 | 12 | @interface IMGGalleryAlbum () 13 | 14 | @property (readwrite, nonatomic) IMGVoteType vote; 15 | @property (readwrite, nonatomic) NSString *section; 16 | @property (readwrite, nonatomic) NSInteger ups; 17 | @property (readwrite, nonatomic) NSInteger downs; 18 | @property (readwrite, nonatomic) NSInteger score; 19 | @property (readwrite, nonatomic) BOOL favorite; 20 | @property (readwrite, nonatomic) BOOL nsfw; 21 | 22 | @end 23 | 24 | @implementation IMGGalleryAlbum; 25 | 26 | #pragma mark - Init With Json 27 | 28 | - (instancetype)initWithJSONObject:(NSDictionary *)jsonData error:(NSError *__autoreleasing *)error 29 | { 30 | self = [super initWithJSONObject:jsonData error:error]; 31 | 32 | if(self && !*error) { 33 | 34 | if(![jsonData isKindOfClass:[NSDictionary class]]){ 35 | 36 | *error = [NSError errorWithDomain:IMGErrorDomain code:IMGErrorMalformedResponseFormat userInfo:@{@"ImgurClass":[self class]}]; 37 | return nil; 38 | } 39 | jsonData = [jsonData IMG_cleanNull]; 40 | 41 | _ups = [jsonData[@"ups"] integerValue]; 42 | _downs = [jsonData[@"downs"] integerValue]; 43 | _score = [jsonData[@"score"] integerValue]; 44 | _vote = [IMGVote voteForStr:jsonData[@"vote"]]; 45 | 46 | _section = jsonData[@"section"]; 47 | _nsfw = [jsonData[@"nsfw"] boolValue]; 48 | _favorite = [jsonData[@"favorite"] boolValue]; 49 | } 50 | return [self trackModels]; 51 | } 52 | 53 | -(instancetype)initWithComment:(IMGComment*)comment error:(NSError *__autoreleasing *)error{ 54 | 55 | NSParameterAssert(comment); 56 | 57 | if(self = [super initWithGalleryID:comment.galleryID coverID:comment.albumCover error:error]){ 58 | 59 | 60 | } 61 | return [self trackModels]; 62 | } 63 | 64 | #pragma mark - Describe 65 | 66 | - (NSString *)description{ 67 | 68 | return [NSString stringWithFormat: @"%@; ups: %ld; downs: %ld; score: %ld; vote: %ld", [super description], (long)self.ups, (long)self.downs, (long)self.score, (long)self.vote]; 69 | } 70 | 71 | -(BOOL)isEqual:(id)object{ 72 | 73 | if (self == object) { 74 | return YES; 75 | } 76 | 77 | if (![object isKindOfClass:[IMGBasicAlbum class]]) { 78 | return NO; 79 | } 80 | 81 | return ([[object albumID] isEqualToString:self.albumID]); 82 | } 83 | 84 | -(NSUInteger)hash{ 85 | 86 | return [self.albumID hash]; 87 | } 88 | 89 | #pragma mark - IMGGalleryObjectProtocol 90 | 91 | -(BOOL)isAlbum{ 92 | return YES; 93 | } 94 | 95 | -(IMGVoteType)usersVote{ 96 | return self.vote; 97 | } 98 | 99 | -(BOOL)isFavorite{ 100 | return self.favorite; 101 | } 102 | 103 | -(BOOL)isNSFW{ 104 | return self.nsfw; 105 | } 106 | 107 | -(IMGImage *)coverImage{ 108 | 109 | //image should be included in the images array 110 | __block IMGImage * cover = nil; 111 | 112 | [self.images enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 113 | IMGImage * img = obj; 114 | 115 | if([self.coverID isEqualToString:img.imageID]){ 116 | //this is the cover 117 | cover = img; 118 | *stop = YES; 119 | } 120 | }]; 121 | 122 | return cover; 123 | } 124 | 125 | -(NSString *)objectID{ 126 | 127 | return self.albumID; 128 | 129 | } 130 | 131 | -(NSString*)galleryDescription{ 132 | 133 | return self.albumDescription; 134 | } 135 | 136 | -(NSString*)fromUsername{ 137 | 138 | return self.accountURL; 139 | } 140 | 141 | -(void)setUsersVote:(IMGVoteType)vote{ 142 | 143 | self.vote = vote; 144 | 145 | } 146 | 147 | -(void)setUsersFav:(BOOL)faved{ 148 | 149 | self.favorite = faved; 150 | } 151 | 152 | 153 | #pragma mark - NSCoding 154 | 155 | - (id)initWithCoder:(NSCoder *)decoder { 156 | 157 | NSString * section = [decoder decodeObjectForKey:@"section"]; 158 | IMGVoteType vote = [[decoder decodeObjectForKey:@"vote"] integerValue]; 159 | NSInteger ups = [[decoder decodeObjectForKey:@"ups"] integerValue]; 160 | NSInteger downs = [[decoder decodeObjectForKey:@"downs"] integerValue]; 161 | NSInteger score = [[decoder decodeObjectForKey:@"score"] integerValue]; 162 | BOOL favorite = [[decoder decodeObjectForKey:@"favorite"] boolValue]; 163 | BOOL nsfw = [[decoder decodeObjectForKey:@"nsfw"] boolValue]; 164 | 165 | if (self = [super initWithCoder:decoder]) { 166 | _vote = vote; 167 | _section = section; 168 | _ups = ups; 169 | _downs = downs; 170 | _score = score; 171 | _favorite = favorite; 172 | _nsfw = nsfw; 173 | } 174 | return self; 175 | } 176 | 177 | - (void)encodeWithCoder:(NSCoder *)coder { 178 | 179 | [super encodeWithCoder:coder]; 180 | 181 | [coder encodeObject:self.section forKey:@"section"]; 182 | 183 | [coder encodeObject:@(self.vote) forKey:@"vote"]; 184 | [coder encodeObject:@(self.ups) forKey:@"ups"]; 185 | [coder encodeObject:@(self.downs) forKey:@"downs"]; 186 | [coder encodeObject:@(self.score) forKey:@"score"]; 187 | [coder encodeObject:@(self.favorite) forKey:@"favorite"]; 188 | [coder encodeObject:@(self.nsfw) forKey:@"nsfw"]; 189 | } 190 | 191 | #pragma mark - NSCopying 192 | 193 | - (instancetype)copyWithZone:(NSZone *)zone { 194 | 195 | IMGGalleryAlbum * copy = [super copyWithZone:zone]; 196 | 197 | if (copy) { 198 | // Copy NSObject subclasses 199 | [copy setSection:[self.section copyWithZone:zone]]; 200 | [copy setVote:self.vote]; 201 | [copy setUps:self.ups]; 202 | [copy setDowns:self.downs]; 203 | [copy setScore:self.score]; 204 | [copy setNsfw:self.nsfw]; 205 | [copy setFavorite:self.favorite]; 206 | } 207 | 208 | return copy; 209 | } 210 | 211 | @end 212 | -------------------------------------------------------------------------------- /ImgurSession/Requests/IMGCommentRequest.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGCommentRequest.m 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-15. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGCommentRequest.h" 10 | #import "IMGComment.h" 11 | #import "IMGSession.h" 12 | 13 | @implementation IMGCommentRequest 14 | 15 | #pragma mark - Path Component for requests 16 | 17 | +(NSString*)pathComponent{ 18 | return @"comment"; 19 | } 20 | 21 | #pragma mark - Load 22 | 23 | + (void)commentWithID:(NSInteger)commentID withReplies:(BOOL)replies success:(void (^)(IMGComment * comment))success failure:(void (^)(NSError * error))failure{ 24 | NSString *path = [self pathWithID:[NSString stringWithFormat:@"%lu", (unsigned long)commentID]]; 25 | 26 | if(replies) 27 | path = [self pathWithID:[NSString stringWithFormat:@"%lu", (unsigned long)commentID] withOption:@"replies"]; 28 | 29 | [[IMGSession sharedInstance] GET:path parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) { 30 | 31 | NSError *JSONError = nil; 32 | IMGComment *comment = [[IMGComment alloc] initWithJSONObject:responseObject error:&JSONError]; 33 | 34 | if(!JSONError && comment) { 35 | if(success) 36 | success(comment); 37 | } 38 | else { 39 | if(failure) 40 | failure(JSONError); 41 | } 42 | 43 | } failure:failure]; 44 | } 45 | 46 | + (void)repliesWithCommentID:(NSInteger)commentID success:(void (^)(NSArray * replies))success failure:(void (^)(NSError * error))failure{ 47 | 48 | NSString *path = [self pathWithID:[NSString stringWithFormat:@"%lu", (unsigned long)commentID] withOption:@"replies"]; 49 | 50 | [[IMGSession sharedInstance] GET:path parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) { 51 | 52 | NSArray * jsonArray = responseObject[@"children"]; 53 | NSMutableArray * comments = [NSMutableArray new]; 54 | 55 | for(NSDictionary * json in jsonArray){ 56 | 57 | NSError *JSONError = nil; 58 | IMGComment * comment = [[IMGComment alloc] initWithJSONObject:json error:&JSONError]; 59 | if(!JSONError && comment) 60 | [comments addObject:comment]; 61 | } 62 | if(success) 63 | success([NSArray arrayWithArray:comments]); 64 | 65 | } failure:failure]; 66 | } 67 | 68 | #pragma mark - Create 69 | 70 | + (void)submitComment:(NSString*)caption withImageID:(NSString *)imageId success:(void (^)(NSInteger commentID))success failure:(void (^)(NSError *))failure{ 71 | NSString *path = [self pathWithID:imageId]; 72 | 73 | if([[IMGSession sharedInstance] isAnonymous]){ 74 | if(failure) 75 | failure([NSError errorWithDomain:IMGErrorDomain code:IMGErrorRequiresUserAuthentication userInfo:@{IMGErrorServerPath:path}]); 76 | return; 77 | } 78 | 79 | NSDictionary * params = @{@"image_id":imageId,@"comment":caption}; 80 | 81 | [[IMGSession sharedInstance] POST:path parameters:params success:^(NSURLSessionDataTask *task, id responseObject) { 82 | 83 | //returns string in dictionary for some reason 84 | NSNumberFormatter * f = [[NSNumberFormatter alloc] init]; 85 | [f setNumberStyle:NSNumberFormatterDecimalStyle]; 86 | NSNumber * commentId = [f numberFromString:responseObject[@"id"]]; 87 | 88 | if(success) 89 | success([commentId integerValue]); 90 | 91 | } failure:failure]; 92 | } 93 | 94 | + (void)replyToComment:(NSString*)caption withImageID:(NSString*)imageId withParentCommentID:(NSInteger)parentCommentId success:(void (^)(NSInteger))success failure:(void (^)(NSError *))failure{ 95 | NSString *path = [self pathWithID:[NSString stringWithFormat:@"%ld",(long)parentCommentId]]; 96 | 97 | if([[IMGSession sharedInstance] isAnonymous]){ 98 | if(failure) 99 | failure([NSError errorWithDomain:IMGErrorDomain code:IMGErrorRequiresUserAuthentication userInfo:@{IMGErrorServerPath:path}]); 100 | return; 101 | } 102 | 103 | NSDictionary * params = @{@"image_id":imageId,@"comment":caption}; 104 | 105 | [[IMGSession sharedInstance] POST:path parameters:params success:^(NSURLSessionDataTask *task, id responseObject) { 106 | 107 | //returns string in dictionary for some reason 108 | NSNumberFormatter * f = [[NSNumberFormatter alloc] init]; 109 | [f setNumberStyle:NSNumberFormatterDecimalStyle]; 110 | NSNumber * commentId = [f numberFromString:responseObject[@"id"]]; 111 | 112 | if(success) 113 | success([commentId integerValue]); 114 | 115 | } failure:failure]; 116 | } 117 | 118 | #pragma mark - Delete 119 | 120 | + (void)deleteCommentWithID:(NSInteger)commentID success:(void (^)())success failure:(void (^)(NSError *))failure{ 121 | NSString *path = [self pathWithID:[NSString stringWithFormat:@"%lu", (unsigned long)commentID]]; 122 | 123 | if([[IMGSession sharedInstance] isAnonymous]){ 124 | if(failure) 125 | failure([NSError errorWithDomain:IMGErrorDomain code:IMGErrorRequiresUserAuthentication userInfo:@{IMGErrorServerPath:path}]); 126 | return; 127 | } 128 | 129 | [[IMGSession sharedInstance] DELETE:path parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) { 130 | 131 | if(success) 132 | success(); 133 | } failure:failure]; 134 | } 135 | 136 | #pragma mark - Vote 137 | 138 | + (void)voteCommentWithID:(NSInteger)commentID withVote:(IMGVoteType)vote success:(void (^)())success failure:(void (^)(NSError *))failure{ 139 | NSString *path = [self pathWithID:[NSString stringWithFormat:@"%lu", (unsigned long)commentID] withOption:@"vote" withID2:[IMGVote strForVote:vote]]; 140 | 141 | if([[IMGSession sharedInstance] isAnonymous]){ 142 | if(failure) 143 | failure([NSError errorWithDomain:IMGErrorDomain code:IMGErrorRequiresUserAuthentication userInfo:@{IMGErrorServerPath:path}]); 144 | return; 145 | } 146 | 147 | [[IMGSession sharedInstance] POST:path parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) { 148 | 149 | if(success) 150 | success(); 151 | 152 | } failure:failure]; 153 | } 154 | 155 | 156 | #pragma mark - Report 157 | 158 | + (void)reportCommentWithID:(NSInteger)commentID success:(void (^)())success failure:(void (^)(NSError *))failure{ 159 | NSString *path = [self pathWithID:[NSString stringWithFormat:@"%lu", (unsigned long)commentID] withOption:@"vote/report"]; 160 | 161 | if([[IMGSession sharedInstance] isAnonymous]){ 162 | if(failure) 163 | failure([NSError errorWithDomain:IMGErrorDomain code:IMGErrorRequiresUserAuthentication userInfo:@{IMGErrorServerPath:path}]); 164 | return; 165 | } 166 | 167 | [[IMGSession sharedInstance] POST:path parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) { 168 | 169 | if(success) 170 | success(); 171 | 172 | } failure:failure]; 173 | 174 | } 175 | 176 | @end 177 | -------------------------------------------------------------------------------- /SampleApp/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 30 | 36 | 46 | 52 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /ImgurSession/Requests/IMGAccountRequest.h: -------------------------------------------------------------------------------- 1 | // 2 | // IMGAccountRequest.h 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-15. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | #import "IMGEndpoint.h" 9 | 10 | #import "IMGSession.h" 11 | #import "IMGBasicAlbum.h" 12 | 13 | @class IMGAccount,IMGAccountSettings,IMGAlbum,IMGImage,IMGComment,IMGGalleryProfile; 14 | 15 | 16 | /** 17 | Account requests. https://api.imgur.com/endpoints/account 18 | */ 19 | @interface IMGAccountRequest : IMGEndpoint 20 | 21 | 22 | #pragma mark - Load 23 | 24 | //call IMGSession refreshUserAccount to refresh the current user's account. Retrieve at the 'user' property 25 | 26 | /** 27 | Request standard user information. If you need the username for the account that is logged in, it is returned in the request for an access token. 28 | @param username username to fetch 29 | */ 30 | + (void)accountWithUser:(NSString *)username success:(void (^)(IMGAccount *account))success failure:(void (^)(NSError *error))failure; 31 | 32 | #pragma mark - Favourites 33 | /** 34 | Return the images the user has favorited in the gallery. 35 | @param username name of account 36 | @param page page of requests 37 | */ 38 | + (void)accountFavouritesWithUser:(NSString *)username withPage:(NSInteger)page success:(void (^)(NSArray * favs))success failure:(void (^)(NSError *error))failure; 39 | 40 | #pragma mark - Gallery Profile 41 | /** 42 | Returns the totals for the gallery profile. 43 | @param username name of account 44 | */ 45 | + (void)accountGalleryProfileWithUser:(NSString *)username success:(void (^)(IMGGalleryProfile * profile))success failure:(void (^)(NSError *error))failure; 46 | 47 | #pragma mark - Verify User Email 48 | /** 49 | Sends the verification email to user 50 | */ 51 | +(void)sendUserEmailVerification:(void (^)())success failure:(void (^)(NSError * error))failure; 52 | /** 53 | Determines whether the user has verified their email 54 | */ 55 | +(void)isUserEmailVerification:(void (^)(BOOL verified))success failure:(void (^)(NSError * error))failure; 56 | 57 | #pragma mark - Submissions 58 | /** 59 | Retrieve account submissions. 60 | @param page pagination, page number to retrieve 61 | @param username name of account 62 | */ 63 | + (void)accountSubmissionsWithUser:(NSString*)username withPage:(NSInteger)page success:(void (^)(NSArray * submissions))success failure:(void (^)(NSError *error))failure; 64 | 65 | 66 | #pragma mark - Load settings 67 | /** 68 | Retrieve account settings only for current user. Must be logged in. 69 | */ 70 | + (void)accountSettings:(void (^)(IMGAccountSettings *settings))success failure:(void (^)(NSError *error))failure; 71 | 72 | #pragma mark - Update settings 73 | /** 74 | Update current account settings with new values. Must be logged in. 75 | */ 76 | + (void)changeAccountWithBio:(NSString*)bio success:(void (^)())success failure:(void (^)(NSError *error))failure; 77 | /** 78 | Update current account settings with new values. Must be logged in. 79 | */ 80 | + (void)changeAccountWithBio:(NSString*)bio messagingEnabled:(BOOL)msgEnabled publicImages:(BOOL)publicImages albumPrivacy:(IMGAlbumPrivacy)privacy acceptedGalleryTerms:(BOOL)galTerms success:(void (^)())success failure:(void (^)(NSError *error))failure; 81 | 82 | 83 | #pragma mark - Albums associated with account 84 | 85 | /** 86 | Get all the albums associated with the account. Must be logged in as the user to see secret and hidden albums. 87 | */ 88 | + (void)accountAlbumsWithUser:(NSString*)username withPage:(NSInteger)page success:(void (^)(NSArray * albums))success failure:(void (^)(NSError * error))failure; 89 | /** 90 | Return an array of all of the album IDs. 91 | */ 92 | + (void)accountAlbumIDsWithUser:(NSString*)username success:(void (^)(NSArray * albumIDs))success failure:(void (^)(NSError * error))failure; 93 | /** 94 | Get additional information about an album, this endpoint works the same as the Album Endpoint. You can also use any of the additional routes that are used on an album in the album endpoint. 95 | */ 96 | + (void)accountAlbumWithID:(NSString*)albumID success:(void (^)(IMGAlbum * album))success failure:(void (^)(NSError * error))failure; 97 | /** 98 | Return the total number of albums associated with the account. 99 | */ 100 | + (void)accountAlbumCountWithUser:(NSString*)username success:(void (^)(NSInteger albumCount))success failure:(void (^)(NSError * error))failure; 101 | /** 102 | Delete an Album with a given id. Must be logged in. 103 | */ 104 | + (void)accountDeleteAlbumWithID:(NSString*)albumID success:(void (^)())success failure:(void (^)(NSError * error))failure; 105 | 106 | 107 | 108 | #pragma mark - Images associated with account 109 | 110 | /** 111 | Return all of the images associated with the account. You can page through the images by setting the page, this defaults to 0. 112 | */ 113 | + (void)accountImagesWithUser:(NSString*)username withPage:(NSInteger)page success:(void (^)(NSArray * images))success failure:(void (^)(NSError * error))failure; 114 | /** 115 | Returns an array of Image IDs that are associated with the account.. 116 | */ 117 | + (void)accountImageIDsWithUser:(NSString*)username success:(void (^)(NSArray * imageIDs))success failure:(void (^)(NSError * error))failure; 118 | /** 119 | Return information about a specific image. This endpoint works the same as the Image Endpoint. You can use any of the additional actions that the image endpoint with this endpoint. 120 | */ 121 | + (void)accountImageWithID:(NSString*)imageId success:(void (^)(IMGImage * image))success failure:(void (^)(NSError * error))failure; 122 | /** 123 | Returns the total number of images associated with the account. 124 | */ 125 | + (void)accountImageCount:(NSString*)username success:(void (^)(NSInteger imageCount))success failure:(void (^)(NSError * error))failure; 126 | /** 127 | Deletes an Image. This requires a delete hash rather than an ID. 128 | */ 129 | + (void)accountDeleteImageWithUser:(NSString*)username deletehash:(NSString*)deleteHash success:(void (^)())success failure:(void (^)(NSError * error))failure; 130 | 131 | 132 | #pragma mark - Comments associated with account 133 | 134 | /** 135 | Return the comments the current user has created. 136 | */ 137 | + (void)accountCommentsWithUser:(NSString*)username success:(void (^)(NSArray * comments))success failure:(void (^)(NSError * error))failure; 138 | /** 139 | Return an array of all of the comment IDs. 140 | */ 141 | + (void)accountCommentIDsWithUser:(NSString*)username success:(void (^)(NSArray * commentIDs))success failure:(void (^)(NSError * error))failure; 142 | /** 143 | Return information about a specific comment. This endpoint works the same as the Comment Endpoint. You can use any of the additional actions that the comment endpoint allows on this end point. 144 | */ 145 | + (void)accountCommentWithID:(NSInteger)commentID success:(void (^)(IMGComment * comment))success failure:(void (^)(NSError * error))failure; 146 | /** 147 | Return a count of all of the comments associated with the current account. 148 | */ 149 | + (void)accountCommentCount:(NSString*)username success:(void (^)(NSInteger commentCount))success failure:(void (^)(NSError * error))failure; 150 | /** 151 | Delete a comment from the current account. Must be logged in. 152 | */ 153 | + (void)accountDeleteCommentWithID:(NSInteger)commentID success:(void (^)())success failure:(void (^)(NSError * error))failure; 154 | 155 | 156 | #pragma mark - Replies associated with account 157 | 158 | /** 159 | Returns all reply notification for the current account 160 | */ 161 | + (void)accountAllReplies:(void (^)(NSArray *))success failure:(void (^)(NSError *))failure; 162 | /** 163 | Returns all unread reply notifications for the current account 164 | */ 165 | + (void)accountUnreadReplies:(void (^)(NSArray *))success failure:(void (^)(NSError *))failure; 166 | 167 | @end 168 | -------------------------------------------------------------------------------- /ImgurSession/Models/IMGAccountSettings.m: -------------------------------------------------------------------------------- 1 | // 2 | // IMGAccountSettings.m 3 | // ImgurSession 4 | // 5 | // Created by Geoff MacDonald on 2014-03-12. 6 | // Copyright (c) 2014 GeoffMacDonald. All rights reserved. 7 | // 8 | 9 | #import "IMGAccount.h" 10 | #import "IMGAccountSettings.h" 11 | #import "IMGBasicAlbum.h" 12 | 13 | 14 | @interface IMGAccountSettings () 15 | @property (readwrite) NSString *email; 16 | @property (readwrite) NSString *username; 17 | @property (readwrite) IMGAlbumPrivacy albumPrivacy; 18 | @property (readwrite) BOOL publicImages; 19 | @property (readwrite) BOOL highQuality; 20 | @property (readwrite) NSDate * proExpiration; 21 | @property (readwrite) BOOL acceptedGalleryTerms; 22 | @property (readwrite) NSArray *activeEmails; 23 | @property (readwrite) BOOL messagingEnabled; 24 | @property (readwrite) NSArray *blockedUsers; 25 | @end 26 | 27 | @implementation IMGBlockedUser 28 | 29 | #pragma mark - Init With Json 30 | 31 | - (instancetype)initWithJSONObject:(NSDictionary *)jsonData error:(NSError * __autoreleasing *)error{ 32 | 33 | if(self = [super init]) { 34 | _blockedID = jsonData[@"blocked_id"]; 35 | _blockedURL = [NSURL URLWithString:jsonData[@"blocked_url"]]; 36 | } 37 | return [self trackModels]; 38 | } 39 | 40 | @end 41 | 42 | @implementation IMGAccountSettings 43 | 44 | #pragma mark - Init With Json 45 | 46 | - (instancetype)initWithJSONObject:(NSDictionary *)jsonData error:(NSError * __autoreleasing *)error{ 47 | 48 | if(self = [super init]) { 49 | 50 | if(![jsonData isKindOfClass:[NSDictionary class]]){ 51 | 52 | if(error) 53 | *error = [NSError errorWithDomain:IMGErrorDomain code:IMGErrorMalformedResponseFormat userInfo:@{@"ImgurClass":[self class]}]; 54 | return nil; 55 | } else if (!jsonData[@"email"] || !jsonData[@"album_privacy"] || !jsonData[@"accepted_gallery_terms"]){ 56 | 57 | if(error) 58 | *error = [NSError errorWithDomain:IMGErrorDomain code:IMGErrorResponseMissingParameters userInfo:nil]; 59 | return nil; 60 | } 61 | 62 | _email = jsonData[@"email"]; 63 | _albumPrivacy = [IMGBasicAlbum privacyForStr:jsonData[@"album_privacy"]]; 64 | _publicImages = [jsonData[@"public_images"] integerValue]; 65 | _highQuality = [jsonData[@"high_quality"] integerValue]; 66 | if([jsonData[@"pro_expiration"] integerValue]) 67 | _proExpiration = [NSDate dateWithTimeIntervalSince1970:[jsonData[@"pro_expiration"] integerValue]]; 68 | else 69 | _proExpiration = nil; 70 | _acceptedGalleryTerms = [jsonData[@"accepted_gallery_terms"] integerValue]; 71 | 72 | //enumerate all active emails 73 | NSMutableArray * activeEmails = [NSMutableArray new]; 74 | for(NSString * email in jsonData[@"active_emails"]){ 75 | [activeEmails addObject:email]; 76 | } 77 | _activeEmails = [NSArray arrayWithArray:activeEmails]; 78 | _messagingEnabled = [jsonData[@"messaging_enabled"] integerValue]; 79 | 80 | //enumerate all blocked users 81 | NSMutableArray * blockedUsers = [NSMutableArray new]; 82 | for(NSDictionary * user in jsonData[@"blocked_users"]){ 83 | IMGBlockedUser * blocked = [[IMGBlockedUser alloc] initWithJSONObject:user error:nil]; 84 | if(blocked) 85 | [blockedUsers addObject:blocked]; 86 | } 87 | _blockedUsers = [NSArray arrayWithArray:blockedUsers]; 88 | } 89 | return [self trackModels]; 90 | } 91 | 92 | - (instancetype)initWithJSONObject:(NSDictionary *)jsonData withName:(NSString*)username error:(NSError * __autoreleasing *)error{ 93 | 94 | self = [self initWithJSONObject:jsonData error:error]; 95 | 96 | if(self) 97 | _username = username; 98 | 99 | return self; 100 | } 101 | 102 | #pragma mark - Describe 103 | 104 | - (NSString *)description{ 105 | return [NSString stringWithFormat: @"%@; email: \"%@\"; high quality: \"%@\"; album_privact: \"%@\"", [super description], self.email, (self.highQuality ? @"YES" : @"NO"), [IMGBasicAlbum strForPrivacy:self.albumPrivacy]]; 106 | } 107 | 108 | -(BOOL)isEqual:(id)object{ 109 | 110 | if (self == object) { 111 | return YES; 112 | } 113 | 114 | if (![object isKindOfClass:[IMGAccountSettings class]]) { 115 | return NO; 116 | } 117 | 118 | return ([[object username] isEqualToString:self.username]); 119 | } 120 | 121 | #pragma mark - NSCoding 122 | 123 | - (id)initWithCoder:(NSCoder *)decoder { 124 | 125 | IMGAlbumPrivacy albumPrivacy = [[decoder decodeObjectForKey:@"albumPrivacy"] integerValue]; 126 | NSString * email = [decoder decodeObjectForKey:@"email"]; 127 | NSString * username = [decoder decodeObjectForKey:@"username"]; 128 | NSDate * proExpiration = [decoder decodeObjectForKey:@"proExpiration"]; 129 | 130 | BOOL publicImages = [[decoder decodeObjectForKey:@"publicImages"] boolValue]; 131 | BOOL highQuality = [[decoder decodeObjectForKey:@"highQuality"] boolValue]; 132 | BOOL acceptedGalleryTerms = [[decoder decodeObjectForKey:@"acceptedGalleryTerms"] boolValue]; 133 | BOOL messagingEnabled = [[decoder decodeObjectForKey:@"messagingEnabled"] boolValue]; 134 | 135 | NSArray * blockUsers = [decoder decodeObjectForKey:@"blockUsers"]; 136 | NSArray * activeEmails = [decoder decodeObjectForKey:@"activeEmails"]; 137 | 138 | if (self = [super init]) { 139 | _email = email; 140 | _username = username; 141 | _albumPrivacy = albumPrivacy; 142 | _proExpiration = proExpiration; 143 | _blockedUsers = blockUsers; 144 | _activeEmails = activeEmails; 145 | 146 | _messagingEnabled = messagingEnabled; 147 | _acceptedGalleryTerms = acceptedGalleryTerms; 148 | _publicImages = publicImages; 149 | _highQuality = highQuality; 150 | 151 | } 152 | return self; 153 | } 154 | 155 | - (void)encodeWithCoder:(NSCoder *)coder { 156 | 157 | [super encodeWithCoder:coder]; 158 | 159 | [coder encodeObject:@(self.albumPrivacy) forKey:@"albumPrivacy"]; 160 | [coder encodeObject:self.email forKey:@"email"]; 161 | [coder encodeObject:self.username forKey:@"username"]; 162 | [coder encodeObject:self.proExpiration forKey:@"proExpiration"]; 163 | [coder encodeObject:self.blockedUsers forKey:@"blockedUsers"]; 164 | [coder encodeObject:self.activeEmails forKey:@"activeEmails"]; 165 | [coder encodeObject:@(self.albumPrivacy) forKey:@"albumPrivacy"]; 166 | [coder encodeObject:@(self.publicImages) forKey:@"publicImages"]; 167 | [coder encodeObject:@(self.messagingEnabled) forKey:@"messagingEnabled"]; 168 | [coder encodeObject:@(self.highQuality) forKey:@"highQuality"];; 169 | [coder encodeObject:@(self.acceptedGalleryTerms) forKey:@"acceptedGalleryTerms"];; 170 | } 171 | 172 | #pragma mark - NSCopying 173 | 174 | - (id)copyWithZone:(NSZone *)zone { 175 | 176 | IMGAccountSettings * copy = [[[self class] allocWithZone:zone] init]; 177 | 178 | if (copy) { 179 | // Copy NSObject subclasses 180 | [copy setEmail:[self.email copyWithZone:zone]]; 181 | [copy setBlockedUsers:[self.blockedUsers copyWithZone:zone]]; 182 | [copy setActiveEmails:[self.activeEmails copyWithZone:zone]]; 183 | [copy setProExpiration:self.proExpiration]; 184 | [copy setUsername:self.username]; 185 | 186 | // Set primitives 187 | [copy setAcceptedGalleryTerms:self.acceptedGalleryTerms]; 188 | [copy setMessagingEnabled:self.messagingEnabled]; 189 | [copy setPublicImages:self.publicImages]; 190 | [copy setHighQuality:self.highQuality]; 191 | [copy setAlbumPrivacy:self.albumPrivacy]; 192 | } 193 | 194 | return copy; 195 | } 196 | 197 | 198 | @end 199 | --------------------------------------------------------------------------------