├── .gitignore ├── .gitmodules ├── AppledocSettings.plist ├── Documentation ├── Classes │ ├── SPComment.html │ ├── SPPagination.html │ ├── SPPlayer.html │ ├── SPRequest.html │ └── SPShot.html ├── css │ └── styles.css ├── hierarchy.html ├── img │ ├── button_bar_background.png │ ├── disclosure.png │ ├── disclosure_open.png │ └── title_background.png └── index.html ├── README.md ├── Spectttator.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── Spectttator ├── SPComment.h ├── SPComment.m ├── SPMethods.h ├── SPMethods.m ├── SPObject.h ├── SPObject.m ├── SPPagination.h ├── SPPagination.m ├── SPPlayer.h ├── SPPlayer.m ├── SPRequest.h ├── SPRequest.m ├── SPShot.h ├── SPShot.m ├── Spectttator-Info.plist ├── Spectttator-Prefix.pch ├── Spectttator-iOS-Prefix.pch └── Spectttator.h ├── SpectttatorTest-iOS ├── MainWindow.xib ├── RootViewController.h ├── RootViewController.m ├── RootViewController.xib ├── ShotCell.h ├── ShotCell.m ├── ShotCell.xib ├── SpectttatorTest-iOS-Info.plist ├── SpectttatorTest-iOS-Prefix.pch ├── SpectttatorTest-iOS.png ├── SpectttatorTest_iOSAppDelegate.h ├── SpectttatorTest_iOSAppDelegate.m └── main.m ├── SpectttatorTest.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata └── SpectttatorTest ├── Default-568h@2x.png ├── MainMenu.xib ├── NSAttributedString+Hyperlink.h ├── NSAttributedString+Hyperlink.m ├── SpectttatorTest-Info.plist ├── SpectttatorTest-Prefix.pch ├── SpectttatorTest.png ├── SpectttatorTestAppDelegate.h ├── SpectttatorTestAppDelegate.m └── main.m /.gitignore: -------------------------------------------------------------------------------- 1 | xcuserdata 2 | .DS_Store 3 | html 4 | appledoc 5 | SBJson 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Spectttator/AFNetworking"] 2 | path = Spectttator/AFNetworking 3 | url = https://github.com/AFNetworking/AFNetworking.git 4 | -------------------------------------------------------------------------------- /AppledocSettings.plist: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | --project-name 7 | Spectttator 8 | --project-company 9 | David Keegan 10 | --company-id 11 | com.inscopeapps 12 | --input 13 | 14 | ./Spectttator/SPRequest.h 15 | ./Spectttator/SPPagination.h 16 | ./Spectttator/SPPlayer.h 17 | ./Spectttator/SPShot.h 18 | ./Spectttator/SPComment.h 19 | 20 | --create-html 21 | 22 | --output 23 | ./Documentation 24 | --verbose 25 | 4 26 | --logformat 27 | 1 28 | 29 | -------------------------------------------------------------------------------- /Documentation/Classes/SPComment.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SPComment Class Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 16 | 61 |
62 | 107 |
108 |
109 | 110 | 116 | 121 |
122 | 123 |
124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 |
Inherits fromNSObject
Declared inSPComment.h
132 | 133 | 134 | 135 | 136 |
137 | 138 |

Overview

139 |

The SPComment class provides a programmatic interface for interacting 140 | with Dribbble comments.

141 | 142 |

The following snippet demonstrates how to retrieve comments for a shot.

143 | 144 |
#import <Spectttator/Spectttator.h>
145 | 
146 | [SPRequest shotInformationForIdentifier:199295 runOnMainThread:NO withBlock:^(SPShot *shot){
147 |     [shot commentsWithPagination:nil runOnMainThread:NO withBlock:^(NSArray *comments, SPPagination *pagination){
148 |         NSLog(@"Comments for '%@': %@", shot.title, comments);
149 |     }];
150 | }];
151 | 
152 | 153 |

This is non-blocking, NSLog will run whenever the comment data has finished loading, 154 | but the block still has access to everything in the scope from where it was defined. 155 | If the block is updating UI elements make sure to set runOnMainThread:YES, the Dribbble 156 | requests will still be asynchronous but the passed in block will be executed on the main thread.

157 |
158 | 159 | 160 | 161 | 162 | 163 |
164 | 165 |

Tasks

166 | 167 | 168 | 169 |

Other Methods

170 | 171 |
    172 |
  • 173 | 174 |   identifier 175 |

    The unique id of the comment.

    176 |
    177 | property 178 | 179 |
  • 180 | 181 |   body 182 |

    The text of the comment.

    183 |
    184 | property 185 | 186 |
  • 187 | 188 |   likesCount 189 |

    The number of players who liked the comment.

    190 |
    191 | property 192 | 193 |
  • 194 | 195 |   createdAt 196 |

    The date the comment was created.

    197 |
    198 | property 199 | 200 |
  • 201 | 202 |   player 203 |

    The player who posted the comment.

    204 |
    205 | property 206 | 207 |
  • 208 |
209 | 210 | 211 | 212 |

Initializing a SPComment Object

213 | 214 |
    215 |
  • 216 | 217 | – initWithDictionary: 218 |

    Returns a Spectttator comment object initialized with the given comment data.

    219 |
    220 | 221 | 222 |
  • 223 |
224 | 225 |
226 | 227 | 228 | 229 | 230 | 231 |
232 | 233 |

Properties

234 | 235 |
236 | 237 |

body

238 | 239 | 240 | 241 |
242 |

The text of the comment.

243 |
244 | 245 | 246 |
@property (readonly) NSString *body
247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 |
262 |

Declared In

263 | SPComment.h
264 |
265 | 266 | 267 |
268 | 269 |
270 | 271 |

createdAt

272 | 273 | 274 | 275 |
276 |

The date the comment was created.

277 |
278 | 279 | 280 |
@property (readonly) NSDate *createdAt
281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 |
296 |

Declared In

297 | SPComment.h
298 |
299 | 300 | 301 |
302 | 303 |
304 | 305 |

identifier

306 | 307 | 308 | 309 |
310 |

The unique id of the comment.

311 |
312 | 313 | 314 |
@property (readonly) NSUInteger identifier
315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 |
330 |

Declared In

331 | SPComment.h
332 |
333 | 334 | 335 |
336 | 337 |
338 | 339 |

likesCount

340 | 341 | 342 | 343 |
344 |

The number of players who liked the comment.

345 |
346 | 347 | 348 |
@property (readonly) NSUInteger likesCount
349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 |
364 |

Declared In

365 | SPComment.h
366 |
367 | 368 | 369 |
370 | 371 |
372 | 373 |

player

374 | 375 | 376 | 377 |
378 |

The player who posted the comment.

379 |
380 | 381 | 382 |
@property (readonly) SPPlayer *player
383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 |
396 |

See Also

397 | 402 |
403 | 404 | 405 | 406 |
407 |

Declared In

408 | SPComment.h
409 |
410 | 411 | 412 |
413 | 414 |
415 | 416 | 417 | 418 | 419 | 420 |
421 | 422 |

Instance Methods

423 | 424 |
425 | 426 |

initWithDictionary:

427 | 428 | 429 | 430 |
431 |

Returns a Spectttator comment object initialized with the given comment data.

432 |
433 | 434 | 435 |
- (id)initWithDictionary:(NSDictionary *)dictionary
436 | 437 | 438 |
439 |

Parameters

440 | 441 |
442 |
dictionary
443 |

A dictionary of comment data.

444 |
445 | 446 |
447 | 448 | 449 | 450 |
451 |

Return Value

452 |

An initialized SPComment object.

453 |
454 | 455 | 456 | 457 |
458 |

Discussion

459 | 460 |

There is no need to call this method directly, it is used by 461 | higher level methods like [SPShot commentsWithPagination:runOnMainThread:withBlock:].

462 |
463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 |
471 |

Declared In

472 | SPComment.h
473 |
474 | 475 | 476 |
477 | 478 |
479 | 480 | 481 |
482 | 488 | 497 |
498 |
499 | 582 | 583 | -------------------------------------------------------------------------------- /Documentation/Classes/SPPagination.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SPPagination Class Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 16 | 70 |
71 | 128 |
129 |
130 | 131 | 137 | 142 |
143 | 144 |
145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 |
Inherits fromNSObject
Declared inSPPagination.h
153 | 154 | 155 | 156 | 157 |
158 | 159 |

Overview

160 |

Most Dribbble api calls take page and per_page to define which page of 161 | data to return and how many items should be contained in the return. 162 | In Spectttator this can be defined by passing a dictionary with page 163 | and per_page to the andPagination: parameter. 164 | SPPagination contains helper functions for creating this dictionary.

165 | 166 |

The SPPagination class is used in two ways. First it provides helper functions for creating 167 | pagination dictionary to pass to methods. Secondly it provides an object that is passed to blocks, 168 | this object contains the returned pagination information: page, pages, perPage, total.

169 | 170 |
#import <Spectttator/Spectttator.h>
171 | 
172 | NSString *username = @"inscopeapps";
173 | 
174 | [SPRequest shotsForPlayer:user 
175 |            withPagination:[SPPagination perPage:20]
176 |           runOnMainThread:NO
177 |                 withBlock:^(NSArray *shots, SPPagination *pagination){
178 |                     NSLog(@"Received shot data for %@", user);
179 |                     NSLog(@"With pagination: %@", pagination);
180 |                 }];
181 | 
182 | 183 |

This is non-blocking, NSLog will run whenever the comment data has finished loading, 184 | but the block still has access to everything in the scope from where it was defined. 185 | If the block is updating UI elements make sure to set runOnMainThread:YES, the Dribbble 186 | requests will still be asynchronous but the passed in block will be executed on the main thread.

187 |
188 | 189 | 190 | 191 | 192 | 193 |
194 | 195 |

Tasks

196 | 197 | 198 | 199 |

Other Methods

200 | 201 |
    202 |
  • 203 | 204 |   page 205 |

    The current page number.

    206 |
    207 | property 208 | 209 |
  • 210 | 211 |   pages 212 |

    The total number of pages.

    213 |
    214 | property 215 | 216 |
  • 217 | 218 |   perPage 219 |

    The number of items per-page.

    220 |
    221 | property 222 | 223 |
  • 224 | 225 |   total 226 |

    The total number of items.

    227 |
    228 | property 229 | 230 |
  • 231 |
232 | 233 | 234 | 235 |

Initializing a SPPagination Object

236 | 237 |
    238 |
  • 239 | 240 | + paginationWithDictionary: 241 |

    Returns an autoreleased SPPagination object initialized with the given pagination data.

    242 |
    243 | 244 | 245 |
  • 246 | 247 | – initWithDictionary: 248 |

    Returns a Spectttator pagination object initialized with the given pagination data.

    249 |
    250 | 251 | 252 |
  • 253 |
254 | 255 | 256 | 257 |

Pagination Dictionary

258 | 259 |
    260 |
  • 261 | 262 | + page: 263 |

    A helper method for creating a pagination dictionary.

    264 |
    265 | 266 | 267 |
  • 268 | 269 | + perPage: 270 |

    A helper method for creating a pagination dictionary.

    271 |
    272 | 273 | 274 |
  • 275 | 276 | + page:perPage: 277 |

    A helper method for creating a pagination dictionary.

    278 |
    279 | 280 | 281 |
  • 282 |
283 | 284 |
285 | 286 | 287 | 288 | 289 | 290 |
291 | 292 |

Properties

293 | 294 |
295 | 296 |

page

297 | 298 | 299 | 300 |
301 |

The current page number.

302 |
303 | 304 | 305 |
@property (readonly) NSUInteger page
306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 |
321 |

Declared In

322 | SPPagination.h
323 |
324 | 325 | 326 |
327 | 328 |
329 | 330 |

pages

331 | 332 | 333 | 334 |
335 |

The total number of pages.

336 |
337 | 338 | 339 |
@property (readonly) NSUInteger pages
340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 |
355 |

Declared In

356 | SPPagination.h
357 |
358 | 359 | 360 |
361 | 362 |
363 | 364 |

perPage

365 | 366 | 367 | 368 |
369 |

The number of items per-page.

370 |
371 | 372 | 373 |
@property (readonly) NSUInteger perPage
374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 |
389 |

Declared In

390 | SPPagination.h
391 |
392 | 393 | 394 |
395 | 396 |
397 | 398 |

total

399 | 400 | 401 | 402 |
403 |

The total number of items.

404 |
405 | 406 | 407 |
@property (readonly) NSUInteger total
408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 |
422 |

Declared In

423 | SPPagination.h
424 |
425 | 426 | 427 |
428 | 429 |
430 | 431 | 432 | 433 |
434 | 435 |

Class Methods

436 | 437 |
438 | 439 |

page:

440 | 441 | 442 | 443 |
444 |

A helper method for creating a pagination dictionary.

445 |
446 | 447 | 448 |
+ (NSDictionary *)page:(NSUInteger)page
449 | 450 | 451 |
452 |

Parameters

453 | 454 |
455 |
page
456 |

The page number.

457 |
458 | 459 |
460 | 461 | 462 | 463 |
464 |

Return Value

465 |

A pagination dictionary with the specified page value.

466 |
467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 |
477 |

See Also

478 | 483 |
484 | 485 | 486 | 487 |
488 |

Declared In

489 | SPPagination.h
490 |
491 | 492 | 493 |
494 | 495 |
496 | 497 |

page:perPage:

498 | 499 | 500 | 501 |
502 |

A helper method for creating a pagination dictionary.

503 |
504 | 505 | 506 |
+ (NSDictionary *)page:(NSUInteger)page perPage:(NSUInteger)perPage
507 | 508 | 509 |
510 |

Parameters

511 | 512 |
513 |
page
514 |

The page number.

515 |
516 | 517 |
518 |
perPage
519 |

The number of items per-page.

520 |
521 | 522 |
523 | 524 | 525 | 526 |
527 |

Return Value

528 |

A pagination dictionary with the specified page and per_page values.

529 |
530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 |
540 |

See Also

541 | 546 |
547 | 548 | 549 | 550 |
551 |

Declared In

552 | SPPagination.h
553 |
554 | 555 | 556 |
557 | 558 |
559 | 560 |

paginationWithDictionary:

561 | 562 | 563 | 564 |
565 |

Returns an autoreleased SPPagination object initialized with the given pagination data.

566 |
567 | 568 | 569 |
+ (id)paginationWithDictionary:(NSDictionary *)dictionary
570 | 571 | 572 |
573 |

Parameters

574 | 575 |
576 |
dictionary
577 |

A dictionary of comment data.

578 |
579 | 580 |
581 | 582 | 583 | 584 |
585 |

Return Value

586 |

An autoreleased SPPagination object.

587 |
588 | 589 | 590 | 591 |
592 |

Discussion

593 |

There is no need to call this method directly, higher level 594 | methods use this to return pagination data to blocks.

595 |
596 | 597 | 598 | 599 | 600 | 601 |
602 |

See Also

603 | 608 |
609 | 610 | 611 | 612 |
613 |

Declared In

614 | SPPagination.h
615 |
616 | 617 | 618 |
619 | 620 |
621 | 622 |

perPage:

623 | 624 | 625 | 626 |
627 |

A helper method for creating a pagination dictionary.

628 |
629 | 630 | 631 |
+ (NSDictionary *)perPage:(NSUInteger)perPage
632 | 633 | 634 |
635 |

Parameters

636 | 637 |
638 |
perPage
639 |

The number of items per-page.

640 |
641 | 642 |
643 | 644 | 645 | 646 |
647 |

Return Value

648 |

A pagination dictionary with the specified per_page value.

649 |
650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 |
660 |

See Also

661 | 666 |
667 | 668 | 669 | 670 |
671 |

Declared In

672 | SPPagination.h
673 |
674 | 675 | 676 |
677 | 678 |
679 | 680 | 681 | 682 |
683 | 684 |

Instance Methods

685 | 686 |
687 | 688 |

initWithDictionary:

689 | 690 | 691 | 692 |
693 |

Returns a Spectttator pagination object initialized with the given pagination data.

694 |
695 | 696 | 697 |
- (id)initWithDictionary:(NSDictionary *)dictionary
698 | 699 | 700 |
701 |

Parameters

702 | 703 |
704 |
dictionary
705 |

A dictionary of comment data.

706 |
707 | 708 |
709 | 710 | 711 | 712 |
713 |

Return Value

714 |

An initialized SPPagination object.

715 |
716 | 717 | 718 | 719 |
720 |

Discussion

721 |

There is no need to call this method directly, higher level 722 | methods use this to return pagination data to blocks.

723 |
724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 |
732 |

Declared In

733 | SPPagination.h
734 |
735 | 736 | 737 |
738 | 739 |
740 | 741 | 742 |
743 | 749 | 758 |
759 |
760 | 843 | 844 | -------------------------------------------------------------------------------- /Documentation/css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; 3 | font-size: 13px; 4 | color: #000; 5 | } 6 | 7 | code { 8 | font-family: Courier, Consolas, monospace; 9 | font-size: 13px; 10 | color: #666; 11 | } 12 | 13 | pre { 14 | font-family: Courier, Consolas, monospace; 15 | font-size: 13px; 16 | line-height: 18px; 17 | tab-interval: 0.5em; 18 | border: 1px solid #C7CFD5; 19 | background-color: #F1F5F9; 20 | color: #666; 21 | padding: 0.3em 1em; 22 | } 23 | 24 | ul { 25 | list-style-type: square; 26 | } 27 | 28 | li { 29 | margin-bottom: 10px; 30 | } 31 | 32 | p:first-child { 33 | margin-top: 0.5em; 34 | } 35 | 36 | a code, 37 | a { 38 | text-decoration: none; 39 | color: #36C; 40 | } 41 | 42 | a:hover code, 43 | a:hover { 44 | text-decoration: underline; 45 | color: #36C; 46 | } 47 | 48 | /* @group Common page elements */ 49 | 50 | #top_header { 51 | height: 55px; 52 | left: 0; 53 | min-width: 598px; 54 | position: absolute; 55 | right: 0; 56 | top: 0; 57 | z-index: 900; 58 | } 59 | 60 | #footer { 61 | clear: both; 62 | padding-top: 20px; 63 | text-align: center; 64 | } 65 | 66 | #contents { 67 | border-top: 1px solid #2B334F; 68 | position: absolute; 69 | top: 56px; 70 | left: 0; 71 | right: 0; 72 | bottom: 0; 73 | overflow-x: hidden; 74 | overflow-y: auto; 75 | padding-left: 2em; 76 | padding-right: 2em; 77 | padding-top: 1em; 78 | min-width: 550px; 79 | } 80 | 81 | #contents.isShowingTOC { 82 | left: 230px; 83 | min-width: 320px; 84 | } 85 | 86 | #overview_contents { 87 | padding-left: 2em; 88 | padding-right: 2em; 89 | padding-top: 1em; 90 | min-width: 550px; 91 | } 92 | 93 | .copyright { 94 | font-size: 12px; 95 | } 96 | 97 | .generator { 98 | font-size: 11px; 99 | } 100 | 101 | .main-navigation ul li { 102 | display: inline; 103 | margin-left: 15px; 104 | list-style: none; 105 | } 106 | 107 | .navigation-top { 108 | clear: both; 109 | float: right; 110 | } 111 | 112 | .navigation-bottom { 113 | clear: both; 114 | float: right; 115 | margin-top: 20px; 116 | margin-bottom: -10px; 117 | } 118 | 119 | .open > .disclosure { 120 | background-image: url("../img/disclosure_open.png"); 121 | } 122 | 123 | .disclosure { 124 | background: url("../img/disclosure.png") no-repeat scroll 0 0; 125 | } 126 | 127 | .disclosure, .nodisclosure { 128 | display: inline-block; 129 | height: 8px; 130 | margin-right: 5px; 131 | position: relative; 132 | width: 9px; 133 | } 134 | 135 | /* @end */ 136 | 137 | /* @group Header */ 138 | 139 | #top_header #title { 140 | background: url("../img/title_background.png") repeat-x 0 0 #8A98A9; 141 | border-bottom: 1px solid #B6B6B6; 142 | height: 25px; 143 | overflow: hidden; 144 | } 145 | 146 | #top_header h1 { 147 | font-size: 115%; 148 | font-weight: normal; 149 | margin: 0; 150 | padding: 3px 0 2px; 151 | text-align: center; 152 | text-shadow: 0 1px 0 #D5D5D5; 153 | white-space: nowrap; 154 | } 155 | 156 | #headerButtons { 157 | background-color: #D8D8D8; 158 | background-image: url("../img/button_bar_background.png"); 159 | border-bottom: 1px solid #EDEDED; 160 | border-top: 1px solid #2B334F; 161 | font-size: 8pt; 162 | height: 28px; 163 | left: 0; 164 | list-style: none outside none; 165 | margin: 0; 166 | overflow: hidden; 167 | padding: 0; 168 | position: absolute; 169 | right: 0; 170 | top: 26px; 171 | } 172 | 173 | #headerButtons li { 174 | background-repeat: no-repeat; 175 | display: inline; 176 | margin-top: 0; 177 | margin-bottom: 0; 178 | padding: 0; 179 | } 180 | 181 | #toc_button button { 182 | border-color: #ACACAC; 183 | border-style: none solid none none; 184 | border-width: 0 1px 0 0; 185 | height: 28px; 186 | margin: 0; 187 | padding-left: 30px; 188 | text-align: left; 189 | width: 230px; 190 | } 191 | 192 | li#jumpto_button { 193 | left: 230px; 194 | margin-left: 0; 195 | position: absolute; 196 | } 197 | 198 | li#jumpto_button select { 199 | height: 22px; 200 | margin: 5px 2px 0 10px; 201 | max-width: 300px; 202 | } 203 | 204 | /* @end */ 205 | 206 | /* @group Table of contents */ 207 | 208 | #tocContainer.isShowingTOC { 209 | border-right: 1px solid #ACACAC; 210 | display: block; 211 | overflow-x: hidden; 212 | overflow-y: auto; 213 | padding: 0; 214 | } 215 | 216 | #tocContainer { 217 | background-color: #E4EBF7; 218 | border-top: 1px solid #2B334F; 219 | bottom: 0; 220 | display: none; 221 | left: 0; 222 | overflow: hidden; 223 | position: absolute; 224 | top: 56px; 225 | width: 229px; 226 | } 227 | 228 | #tocContainer > ul#toc { 229 | font-size: 11px; 230 | margin: 0; 231 | padding: 12px 0 18px; 232 | width: 209px; 233 | -moz-user-select: none; 234 | -webkit-user-select: none; 235 | user-select: none; 236 | } 237 | 238 | #tocContainer > ul#toc > li { 239 | margin: 0; 240 | padding: 0 0 7px 30px; 241 | text-indent: -15px; 242 | } 243 | 244 | #tocContainer > ul#toc > li > .sectionName a { 245 | color: #000000; 246 | font-weight: bold; 247 | } 248 | 249 | #tocContainer > ul#toc > li > .sectionName a:hover { 250 | text-decoration: none; 251 | } 252 | 253 | #tocContainer > ul#toc li.children > ul { 254 | display: none; 255 | height: 0; 256 | } 257 | 258 | #tocContainer > ul#toc > li > ul { 259 | margin: 0; 260 | padding: 0; 261 | } 262 | 263 | #tocContainer > ul#toc > li > ul, ul#toc > li > ul > li { 264 | margin-left: 0; 265 | margin-bottom: 0; 266 | padding-left: 15px; 267 | } 268 | 269 | #tocContainer > ul#toc > li ul { 270 | list-style: none; 271 | margin-right: 0; 272 | padding-right: 0; 273 | } 274 | 275 | #tocContainer > ul#toc li.children.open > ul { 276 | display: block; 277 | height: auto; 278 | margin-left: -15px; 279 | padding-left: 0; 280 | } 281 | 282 | #tocContainer > ul#toc > li > ul, ul#toc > li > ul > li { 283 | margin-left: 0; 284 | padding-left: 15px; 285 | } 286 | 287 | #tocContainer li ul li { 288 | margin-top: 0.583em; 289 | overflow: hidden; 290 | text-overflow: ellipsis; 291 | white-space: nowrap; 292 | } 293 | 294 | #tocContainer li ul li span.sectionName { 295 | white-space: normal; 296 | } 297 | 298 | #tocContainer > ul#toc > li > ul > li > .sectionName a { 299 | font-weight: bold; 300 | } 301 | 302 | #tocContainer > ul#toc > li > ul a { 303 | color: #4F4F4F; 304 | } 305 | 306 | /* @end */ 307 | 308 | /* @group Index formatting */ 309 | 310 | .index-title { 311 | font-size: 13px; 312 | font-weight: normal; 313 | } 314 | 315 | .index-column { 316 | float: left; 317 | width: 30%; 318 | min-width: 200px; 319 | font-size: 11px; 320 | } 321 | 322 | .index-column ul { 323 | margin: 8px 0 0 0; 324 | padding: 0; 325 | list-style: none; 326 | } 327 | 328 | .index-column ul li { 329 | margin: 0 0 3px 0; 330 | padding: 0; 331 | } 332 | 333 | .hierarchy-column { 334 | min-width: 400px; 335 | } 336 | 337 | .hierarchy-column ul { 338 | margin: 3px 0 0 15px; 339 | } 340 | 341 | .hierarchy-column ul li { 342 | list-style-type: square; 343 | } 344 | 345 | /* @end */ 346 | 347 | /* @group Common formatting elements */ 348 | 349 | .title { 350 | font-weight: normal; 351 | font-size: 250%; 352 | margin-top:0; 353 | } 354 | 355 | .subtitle, 356 | .index-overview h1 { 357 | font-weight: normal; 358 | font-size: 180%; 359 | color: #3C4C6C; 360 | border-bottom: 1px solid #5088C5; 361 | } 362 | 363 | .subsubtitle { 364 | font-weight: normal; 365 | font-size: 145%; 366 | height: 0.7em; 367 | } 368 | 369 | .warning { 370 | border: 1px solid #5088C5; 371 | background-color: #F0F3F7; 372 | margin-bottom: 0.5em; 373 | padding: 0em 0.8em; 374 | } 375 | 376 | .bug { 377 | border: 1px solid #000; 378 | background-color: #ffffcc; 379 | margin-bottom: 0.5em; 380 | padding: 0em 0.8em; 381 | } 382 | 383 | /* @end */ 384 | 385 | /* @group Common layout */ 386 | 387 | .section { 388 | margin-top: 3em; 389 | } 390 | 391 | /* @end */ 392 | 393 | /* @group Object specification section */ 394 | 395 | .section-specification { 396 | margin-left: 2.5em; 397 | margin-right: 2.5em; 398 | font-size: 12px; 399 | } 400 | 401 | .section-specification table { 402 | border-top: 1px solid #d6e0e5; 403 | } 404 | 405 | .section-specification td { 406 | vertical-align: top; 407 | border-bottom: 1px solid #d6e0e5; 408 | padding: .6em; 409 | } 410 | 411 | .section-specification .specification-title { 412 | font-weight: bold; 413 | } 414 | 415 | /* @end */ 416 | 417 | /* @group Tasks section */ 418 | 419 | .task-list { 420 | list-style-type: none; 421 | padding-left: 0px; 422 | } 423 | 424 | .task-list li { 425 | margin-bottom: 3px; 426 | } 427 | 428 | .task-item-suffix { 429 | color: #996; 430 | font-size: 12px; 431 | font-style: italic; 432 | margin-left: 0.5em; 433 | } 434 | 435 | span.tooltip span.tooltip { 436 | font-size: 1.0em; 437 | display: none; 438 | padding: 0.2em 0.3em; 439 | border: 1px solid #aaa; 440 | background-color: #fdfec8; 441 | color: #000; 442 | text-align: left; 443 | } 444 | 445 | span.tooltip span.tooltip p { 446 | margin: 0.1em 0.3em; 447 | } 448 | 449 | span.tooltip:hover span.tooltip { 450 | display: block; 451 | position: absolute; 452 | margin-left: 2em; 453 | } 454 | 455 | /* @end */ 456 | 457 | /* @group Method section */ 458 | 459 | .section-method { 460 | margin-top: 2.3em; 461 | } 462 | 463 | .method-title { 464 | margin-bottom: 1.5em; 465 | } 466 | 467 | .method-subtitle { 468 | margin-top: 0.7em; 469 | margin-bottom: 0.2em; 470 | } 471 | 472 | .method-subsection p { 473 | margin-top: 0.4em; 474 | margin-bottom: 0.8em; 475 | } 476 | 477 | .method-declaration { 478 | margin-top:1.182em; 479 | margin-bottom:.909em; 480 | } 481 | 482 | .method-declaration code { 483 | font:14px Courier, Consolas, monospace; 484 | color:#000; 485 | } 486 | 487 | .declaration { 488 | color: #000; 489 | } 490 | 491 | .argument-def { 492 | margin-top: 0.3em; 493 | margin-bottom: 0.3em; 494 | } 495 | 496 | .argument-def dd { 497 | margin-left: 1.25em; 498 | } 499 | 500 | .see-also-section ul { 501 | list-style-type: none; 502 | padding-left: 0px; 503 | margin-top: 0; 504 | } 505 | 506 | .see-also-section li { 507 | margin-bottom: 3px; 508 | } 509 | 510 | .see-also-section p { 511 | margin-top: 0; 512 | margin-bottom: 0; 513 | } 514 | 515 | .declared-in-ref { 516 | color: #666; 517 | } 518 | 519 | /* @end */ 520 | 521 | -------------------------------------------------------------------------------- /Documentation/hierarchy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Spectttator Hierarchy 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 | 15 | 20 |
21 | 22 |
23 |

Class Hierarchy

24 | 25 | 44 | 45 |
46 | 47 | 48 | 49 |
50 | 53 | 63 |
64 |
65 | 66 | -------------------------------------------------------------------------------- /Documentation/img/button_bar_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kgn/Spectttator/b6154b14bab5e27cf68a228bac695b8ea8fce5cc/Documentation/img/button_bar_background.png -------------------------------------------------------------------------------- /Documentation/img/disclosure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kgn/Spectttator/b6154b14bab5e27cf68a228bac695b8ea8fce5cc/Documentation/img/disclosure.png -------------------------------------------------------------------------------- /Documentation/img/disclosure_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kgn/Spectttator/b6154b14bab5e27cf68a228bac695b8ea8fce5cc/Documentation/img/disclosure_open.png -------------------------------------------------------------------------------- /Documentation/img/title_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kgn/Spectttator/b6154b14bab5e27cf68a228bac695b8ea8fce5cc/Documentation/img/title_background.png -------------------------------------------------------------------------------- /Documentation/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Spectttator Reference 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 | 15 | 20 |
21 | 22 | 23 | 24 |
25 |

Class References

26 | 39 |
40 | 41 | 42 | 43 |
44 | 47 | 57 |
58 |
59 | 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Spectttator 2 | ======== 3 | 4 | Spectttator is an Objective-C framework for OSX and iOS that uses 5 | [blocks](http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Blocks/Articles/00_Introduction.html) 6 | to provide an easy to use asynchronous interface to the [Dribbble api](http://dribbble.com/api). 7 | 8 | This framework has been heavily veted by the Mac App [Play by Play](http://playbyplayapp.com). 9 | 10 | Below is a quick overview of the framework, to get an in depth look check out the [documentation](http://kgn.github.com/Spectttator). 11 | 12 | Spectttator requires either 13 | [iOS 4.0](http://developer.apple.com/library/ios/#releasenotes/General/WhatsNewIniPhoneOS/Articles/iPhoneOS4.html%23//apple_ref/doc/uid/TP40009559-SW1) 14 | and above, or [OSX 10.6](http://developer.apple.com/library/mac/#releasenotes/MacOSX/WhatsNewInOSX/Articles/MacOSX10_6.html#//apple_ref/doc/uid/TP40008898-SW7) 15 | and above. 16 | 17 | How To 18 | -------- 19 | 20 | Spectttator uses [AFNetworking](https://github.com/AFNetworking/AFNetworking) 21 | and [JSONKit](https://github.com/johnezang/JSONKit). 22 | These libraries are included as submodules, so make sure to clone the repo with the `--recursive` flag. 23 | 24 | git clone --recursive https://github.com/kgn/Spectttator.git 25 | 26 | Example 27 | -------- 28 | 29 | The following snippet demonstrates how to get the last 10 shots a player liked. 30 | 31 | ``` obj-c 32 | #import 33 | NSString *username = @"kgn"; 34 | [SPRequest shotsForPlayerLikes:username 35 | withPagination:[SPPagination perPage:10] 36 | runOnMainThread:NO 37 | withBlock:^(NSArray *shots, SPPagination *pagination){ 38 | NSLog(@"Shot %@ likes: %@", username, shots); 39 | }]; 40 | ``` 41 | 42 | This is non-blocking, `NSLog` will run whenever the comment data has finished loading, but the block still has access 43 | to everything in the scope from where it was defined. If the block is updating UI elements make sure to set 44 | `runOnMainThread:YES`, the Dribbble requests will still be asynchronous but the passed in block will be executed on the main thread. 45 | 46 | SpectttatorTest 47 | -------- 48 | 49 | SpectttatorTest is a sample application that demonstrates how to use Spectttator to create a non-blocking user 50 | interface that displays information from Dribbble. It also runs every method, this was used during testing and 51 | development and is a great way to see how to use Spectttator. 52 | 53 | Check out [this video](http://vimeo.com/25704164) to see SpectttatorTest in action. 54 | 55 | ![SpectttatorTest](https://github.com/kgn/Spectttator/raw/master/SpectttatorTest/SpectttatorTest.png) 56 | 57 | SpectttatorTest-iOS 58 | -------- 59 | 60 | SpectttatorTest-iOS is a simple iPhone app that demonstrates how to use Spectttator in iOS. 61 | 62 | ![SpectttatorTest](https://github.com/kgn/Spectttator/raw/master/SpectttatorTest-iOS/SpectttatorTest-iOS.png) 63 | 64 | Change Log 65 | -------- 66 | 67 | * **0.3.1** - Switching to use [AFNetworking](https://github.com/AFNetworking/AFNetworking) and [JSONKit](https://github.com/johnezang/JSONKit), no public API change. 68 | * **0.3.0** - Renaming SPManager to SPRequest. SPRequest is no longer a singleton and all it's methods are now simply class methods. 69 | * **0.2.1** - Switching to 64bit, this caused no interface changes. 70 | * **0.2.0** - Overhaul of all the methods by adding runOnMainThread to make it easy to update UI elements inside the block on the main thread. 71 | * **0.1.0** - Initial release with support for the full Dribbble api. 72 | 73 | License 74 | -------- 75 | 76 | Copyright (c) 2011 David Keegan 77 | 78 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 79 | to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 80 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 81 | 82 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 83 | 84 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 85 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 86 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 87 | IN THE SOFTWARE. 88 | -------------------------------------------------------------------------------- /Spectttator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Spectttator/SPComment.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPComment.h 3 | // Spectttator 4 | // 5 | // Created by David Keegan on 6/27/11. 6 | // Copyright 2011 David Keegan. 7 | // 8 | 9 | #import 10 | #import "SPObject.h" 11 | 12 | @class SPPlayer; 13 | 14 | /** The `SPComment` class provides a programmatic interface for interacting 15 | with Dribbble comments. 16 | 17 | The following snippet demonstrates how to retrieve comments for a shot. 18 | 19 | #import 20 | 21 | [SPRequest shotInformationForIdentifier:199295 runOnMainThread:NO withBlock:^(SPShot *shot){ 22 | [shot commentsWithPagination:nil runOnMainThread:NO withBlock:^(NSArray *comments, SPPagination *pagination){ 23 | NSLog(@"Comments for '%@': %@", shot.title, comments); 24 | }]; 25 | }]; 26 | 27 | This is non-blocking, `NSLog` will run whenever the comment data has finished loading, 28 | but the block still has access to everything in the scope from where it was defined. 29 | If the block is updating UI elements make sure to set `runOnMainThread:YES`, the Dribbble 30 | requests will still be asynchronous but the passed in block will be executed on the main thread. 31 | */ 32 | 33 | @interface SPComment : SPObject 34 | 35 | /// The text of the comment. 36 | @property (strong, nonatomic, readonly) NSString *body; 37 | /// The number of players who liked the comment. 38 | @property (nonatomic, readonly) NSUInteger likesCount; 39 | /** The player who posted the comment. 40 | @see SPPlayer 41 | */ 42 | @property (strong, nonatomic, readonly) SPPlayer *player; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /Spectttator/SPComment.m: -------------------------------------------------------------------------------- 1 | // 2 | // SPComment.m 3 | // Spectttator 4 | // 5 | // Created by David Keegan on 6/27/11. 6 | // Copyright 2011 David Keegan. 7 | // 8 | 9 | #import "SPComment.h" 10 | #import "SPPlayer.h" 11 | 12 | @interface SPComment() 13 | @property (strong, nonatomic, readwrite) NSString *body; 14 | @property (nonatomic, readwrite) NSUInteger likesCount; 15 | @property (strong, nonatomic, readwrite) SPPlayer *player; 16 | @end 17 | 18 | @implementation SPComment 19 | 20 | - (id)initWithDictionary:(NSDictionary *)dictionary{ 21 | if((self = [super initWithDictionary:dictionary])){ 22 | self.body = [dictionary stringSafelyFromKey:@"body"]; 23 | self.likesCount = [dictionary uintSafelyFromKey:@"likes_count"]; 24 | 25 | NSDictionary *player = [dictionary objectSafelyFromKey:@"player"]; 26 | if(player != nil){ 27 | self.player = [[SPPlayer alloc] initWithDictionary:player]; 28 | } 29 | } 30 | 31 | return self; 32 | } 33 | 34 | - (NSString *)description{ 35 | return [NSString stringWithFormat:@"<%@ %lu Username='%@' Body=%@>", 36 | [self class], (unsigned long)self.identifier, self.player.username, self.body]; 37 | } 38 | 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Spectttator/SPMethods.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPMethods.h 3 | // Spectttator 4 | // 5 | // Created by David Keegan on 6/27/11. 6 | // Copyright 2011 David Keegan. 7 | // 8 | 9 | #import 10 | 11 | @class SPPagination; 12 | 13 | #if TARGET_OS_IPHONE 14 | #define SPImage UIImage 15 | #else 16 | #define SPImage NSImage 17 | #endif 18 | 19 | @interface NSDictionary(Spectttator) 20 | 21 | - (NSUInteger)uintSafelyFromKey:(id)key; 22 | - (NSString *)stringSafelyFromKey:(id)key; 23 | - (NSURL *)URLSafelyFromKey:(id)key; 24 | - (id)objectSafelyFromKey:(id)key; 25 | 26 | @end 27 | 28 | @interface SPMethods : NSObject 29 | 30 | + (NSString *)pagination:(NSDictionary *)pagination; 31 | 32 | + (void)requestPlayersWithURL:(NSURL *)url 33 | runOnMainThread:(BOOL)runOnMainThread 34 | withBlock:(void (^)(NSArray *players, SPPagination *pagination))block; 35 | 36 | + (void)requestShotsWithURL:(NSURL *)url 37 | runOnMainThread:(BOOL)runOnMainThread 38 | withBlock:(void (^)(NSArray *shots, SPPagination *pagination))block; 39 | 40 | + (void)requestCommentsWithURL:(NSURL *)url 41 | runOnMainThread:(BOOL)runOnMainThread 42 | withBlock:(void (^)(NSArray *comments, SPPagination *pagination))block; 43 | 44 | + (void)requestImageWithURL:(NSURL *)url 45 | runOnMainThread:(BOOL)runOnMainThread 46 | withBlock:(void (^)(SPImage *image))block; 47 | 48 | + (void)requestDataWithURL:(NSURL *)url 49 | runOnMainThread:(BOOL)runOnMainThread 50 | withBlock:(void (^)(NSData *data))block; 51 | @end 52 | -------------------------------------------------------------------------------- /Spectttator/SPMethods.m: -------------------------------------------------------------------------------- 1 | // 2 | // SPMethods.m 3 | // Spectttator 4 | // 5 | // Created by David Keegan on 6/27/11. 6 | // Copyright 2011 David Keegan. 7 | // 8 | 9 | #import "SPMethods.h" 10 | #import "SPPlayer.h" 11 | #import "SPComment.h" 12 | #import "SPShot.h" 13 | #import "SPPagination.h" 14 | #import "AFJSONRequestOperation.h" 15 | #import "AFImageRequestOperation.h" 16 | #import "AFHTTPRequestOperation.h" 17 | 18 | @implementation NSDictionary(Spectttator) 19 | 20 | - (NSUInteger)uintSafelyFromKey:(id)key{ 21 | if([self objectForKey:key] == [NSNull null] || [self objectForKey:key] == nil){ 22 | return NSNotFound; 23 | } 24 | return [[self objectForKey:key] integerValue]; 25 | } 26 | 27 | - (NSString *)stringSafelyFromKey:(id)key{ 28 | if([self objectForKey:key] == [NSNull null] || [self objectForKey:key] == nil){ 29 | return nil; 30 | } 31 | return [NSString stringWithString:[self objectForKey:key]]; 32 | } 33 | 34 | - (NSURL *)URLSafelyFromKey:(id)key{ 35 | if([self objectForKey:key] == [NSNull null] || [self objectForKey:key] == nil){ 36 | return nil; 37 | } 38 | NSString *urlString = 39 | [[self objectForKey:key] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 40 | return [NSURL URLWithString:urlString]; 41 | } 42 | 43 | - (id)objectSafelyFromKey:(id)key{ 44 | if([self objectForKey:key] == [NSNull null] || [self objectForKey:key] == nil){ 45 | return nil; 46 | } 47 | return [self objectForKey:key]; 48 | } 49 | 50 | @end 51 | 52 | @implementation SPMethods 53 | 54 | + (NSString *)pagination:(NSDictionary *)pagination{ 55 | NSNumber *page = [pagination objectForKey:@"page"]; 56 | NSNumber *perPage = [pagination objectForKey:@"perPage"]; 57 | if(page && perPage){ 58 | return [NSString stringWithFormat:@"?page=%lu&per_page=%lu", [page longValue], [perPage longValue]]; 59 | }else if(page){ 60 | return [NSString stringWithFormat:@"?page=%lu", [page longValue]]; 61 | }else if(perPage){ 62 | return [NSString stringWithFormat:@"?per_page=%lu", [perPage longValue]]; 63 | } 64 | return @""; 65 | } 66 | 67 | + (void)requestPlayersWithURL:(NSURL *)url 68 | runOnMainThread:(BOOL)runOnMainThread 69 | withBlock:(void (^)(NSArray *players, SPPagination *pagination))block{ 70 | [[AFJSONRequestOperation 71 | JSONRequestOperationWithRequest:[NSURLRequest requestWithURL:url] 72 | success:^(NSURLRequest *request, NSHTTPURLResponse *response, NSDictionary *json){ 73 | NSArray *players = [json objectForKey:@"players"]; 74 | NSMutableArray *mplayers = [[NSMutableArray alloc] initWithCapacity:[players count]]; 75 | for(NSDictionary *playerData in players){ 76 | @autoreleasepool{ 77 | SPPlayer *player = [[SPPlayer alloc] initWithDictionary:playerData]; 78 | [mplayers addObject:player]; 79 | } 80 | } 81 | if([mplayers count] == 0){ 82 | mplayers = nil; 83 | } 84 | if(runOnMainThread){ 85 | dispatch_async(dispatch_get_main_queue(), ^{ 86 | block(mplayers, [SPPagination paginationWithDictionary:json]); 87 | }); 88 | }else{ 89 | block(mplayers, [SPPagination paginationWithDictionary:json]); 90 | } 91 | } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON){ 92 | if(runOnMainThread){ 93 | dispatch_async(dispatch_get_main_queue(), ^{ 94 | block(nil, nil); 95 | }); 96 | }else{ 97 | block(nil, nil); 98 | } 99 | }] start]; 100 | } 101 | 102 | + (void)requestShotsWithURL:(NSURL *)url 103 | runOnMainThread:(BOOL)runOnMainThread 104 | withBlock:(void (^)(NSArray *shots, SPPagination *pagination))block{ 105 | [[AFJSONRequestOperation 106 | JSONRequestOperationWithRequest:[NSURLRequest requestWithURL:url] 107 | success:^(NSURLRequest *request, NSHTTPURLResponse *response, NSDictionary *json){ 108 | NSArray *shots = [json objectForKey:@"shots"]; 109 | NSMutableArray *mshots = [[NSMutableArray alloc] initWithCapacity:[shots count]]; 110 | for(NSDictionary *shotData in shots){ 111 | @autoreleasepool{ 112 | SPShot *shot = [[SPShot alloc] initWithDictionary:shotData]; 113 | [mshots addObject:shot]; 114 | } 115 | } 116 | if([mshots count] == 0){ 117 | mshots = nil; 118 | } 119 | if(runOnMainThread){ 120 | dispatch_async(dispatch_get_main_queue(), ^{ 121 | block(mshots, [SPPagination paginationWithDictionary:json]); 122 | }); 123 | }else{ 124 | block(mshots, [SPPagination paginationWithDictionary:json]); 125 | } 126 | } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON){ 127 | if(runOnMainThread){ 128 | dispatch_async(dispatch_get_main_queue(), ^{ 129 | block(nil, nil); 130 | }); 131 | }else{ 132 | block(nil, nil); 133 | } 134 | }] start]; 135 | } 136 | 137 | + (void)requestCommentsWithURL:(NSURL *)url 138 | runOnMainThread:(BOOL)runOnMainThread 139 | withBlock:(void (^)(NSArray *comments, SPPagination *pagination))block{ 140 | [[AFJSONRequestOperation 141 | JSONRequestOperationWithRequest:[NSURLRequest requestWithURL:url] 142 | success:^(NSURLRequest *request, NSHTTPURLResponse *response, NSDictionary *json){ 143 | NSArray *comments = [json objectForKey:@"comments"]; 144 | NSMutableArray *mcomments = [[NSMutableArray alloc] initWithCapacity:[comments count]]; 145 | for(NSDictionary *commentData in comments){ 146 | @autoreleasepool{ 147 | SPComment *comment = [[SPComment alloc] initWithDictionary:commentData]; 148 | [mcomments addObject:comment]; 149 | } 150 | } 151 | if([mcomments count] == 0){ 152 | mcomments = nil; 153 | } 154 | if(runOnMainThread){ 155 | dispatch_async(dispatch_get_main_queue(), ^{ 156 | block(mcomments, [SPPagination paginationWithDictionary:json]); 157 | }); 158 | }else{ 159 | block(mcomments, [SPPagination paginationWithDictionary:json]); 160 | } 161 | } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON){ 162 | if(runOnMainThread){ 163 | dispatch_async(dispatch_get_main_queue(), ^{ 164 | block(nil, nil); 165 | }); 166 | }else{ 167 | block(nil, nil); 168 | } 169 | }] start]; 170 | } 171 | 172 | + (void)requestImageWithURL:(NSURL *)url 173 | runOnMainThread:(BOOL)runOnMainThread 174 | withBlock:(void (^)(SPImage *image))block{ 175 | [[AFImageRequestOperation 176 | imageRequestOperationWithRequest:[NSURLRequest requestWithURL:url] 177 | imageProcessingBlock:nil 178 | success:^(NSURLRequest *request, NSHTTPURLResponse *response, SPImage *image){ 179 | if(runOnMainThread){ 180 | dispatch_async(dispatch_get_main_queue(), ^{ 181 | block(image); 182 | }); 183 | }else{ 184 | block(image); 185 | } 186 | } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){ 187 | if(runOnMainThread){ 188 | dispatch_async(dispatch_get_main_queue(), ^{ 189 | block(nil); 190 | }); 191 | }else{ 192 | block(nil); 193 | } 194 | }] start]; 195 | } 196 | 197 | + (void)requestDataWithURL:(NSURL *)url 198 | runOnMainThread:(BOOL)runOnMainThread 199 | withBlock:(void (^)(NSData *data))block{ 200 | AFHTTPRequestOperation *operation = 201 | [[AFHTTPRequestOperation alloc] initWithRequest:[NSURLRequest requestWithURL:url]]; 202 | [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject){ 203 | if(runOnMainThread){ 204 | dispatch_async(dispatch_get_main_queue(), ^{ 205 | block(operation.responseData); 206 | }); 207 | }else{ 208 | block(operation.responseData); 209 | } 210 | } failure:^(AFHTTPRequestOperation *operation, NSError *error){ 211 | if(runOnMainThread){ 212 | dispatch_async(dispatch_get_main_queue(), ^{ 213 | block(nil); 214 | }); 215 | }else{ 216 | block(nil); 217 | } 218 | }]; 219 | [operation start]; 220 | } 221 | 222 | @end 223 | -------------------------------------------------------------------------------- /Spectttator/SPObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPObject.h 3 | // Spectttator 4 | // 5 | // Created by David Keegan on 12/19/11. 6 | // Copyright (c) 2011 David Keegan. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SPMethods.h" 11 | 12 | @interface SPObject : NSObject 13 | 14 | /// The unique id of the object. 15 | @property (nonatomic, readonly) NSUInteger identifier; 16 | /// The date the object was created. 17 | @property (strong, nonatomic, readonly) NSDate *createdAt; 18 | 19 | ///---------------------------- 20 | /// @name Initializing a SPObject Object 21 | ///---------------------------- 22 | 23 | /** 24 | Returns a Spectttator object initialized with the given data. 25 | 26 | There is no need to call this method directly, it is used by 27 | higher level methods. 28 | @param dictionary A dictionary of data. 29 | @return An initialized `SPObject` object. 30 | */ 31 | - (id)initWithDictionary:(NSDictionary *)dictionary; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /Spectttator/SPObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // SPObject.m 3 | // Spectttator 4 | // 5 | // Created by David Keegan on 12/19/11. 6 | // Copyright (c) 2011 David Keegan. All rights reserved. 7 | // 8 | 9 | #import "SPObject.h" 10 | 11 | @interface SPObject() 12 | @property (nonatomic, readwrite) NSUInteger identifier; 13 | @property (strong, nonatomic, readwrite) NSDate *createdAt; 14 | @end 15 | 16 | @implementation SPObject 17 | 18 | - (id)initWithDictionary:(NSDictionary *)dictionary{ 19 | if((self = [super init])){ 20 | self.identifier = [dictionary uintSafelyFromKey:@"id"]; 21 | NSString *createdAt = [dictionary stringSafelyFromKey:@"created_at"]; 22 | if(createdAt != nil){ 23 | NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; 24 | [formatter setDateFormat:@"yyyy/MM/dd HH:mm:ss ZZZZ"]; 25 | self.createdAt = [formatter dateFromString:createdAt]; 26 | } 27 | } 28 | 29 | return self; 30 | } 31 | 32 | - (NSUInteger)hash{ 33 | return self.identifier; 34 | } 35 | 36 | - (BOOL)isEqual:(id)object{ 37 | if([object isKindOfClass:[self class]]){ 38 | return (self.identifier == [(SPObject *)object identifier]); 39 | } 40 | return NO; 41 | } 42 | 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /Spectttator/SPPagination.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPPagination.h 3 | // Spectttator 4 | // 5 | // Created by David Keegan on 6/26/11. 6 | // Copyright 2011 David Keegan. 7 | // 8 | 9 | #import 10 | 11 | extern NSUInteger const SPPerPageDefault; 12 | extern NSUInteger const SPPerPageMax; 13 | 14 | /** Most Dribbble api calls take `page` and `per_page` to define which page of 15 | data to return and how many items should be contained in the return. 16 | In Spectttator this can be defined by passing a dictionary with `page` 17 | and `per_page` to the `andPagination:` parameter. 18 | `SPPagination` contains helper functions for creating this dictionary. 19 | 20 | The `SPPagination` class is used in two ways. First it provides helper functions for creating 21 | pagination dictionary to pass to methods. Secondly it provides an object that is passed to blocks, 22 | this object contains the returned pagination information: `page`, `pages`, `perPage`, `total`. 23 | 24 | #import 25 | 26 | NSString *username = @"inscopeapps"; 27 | 28 | [SPRequest shotsForPlayer:user 29 | withPagination:SPPerPageMax 30 | runOnMainThread:NO 31 | withBlock:^(NSArray *shots, SPPagination *pagination){ 32 | NSLog(@"Received shot data for %@", user); 33 | NSLog(@"With pagination: %@", pagination); 34 | }]; 35 | 36 | This is non-blocking, `NSLog` will run whenever the comment data has finished loading, 37 | but the block still has access to everything in the scope from where it was defined. 38 | If the block is updating UI elements make sure to set `runOnMainThread:YES`, the Dribbble 39 | requests will still be asynchronous but the passed in block will be executed on the main thread. 40 | */ 41 | 42 | @interface SPPagination : NSObject 43 | 44 | /// The current page number. 45 | @property(nonatomic, readonly) NSUInteger page; 46 | /// The total number of pages. 47 | @property(nonatomic, readonly) NSUInteger pages; 48 | /// The number of items per-page. 49 | @property(nonatomic, readonly) NSUInteger perPage; 50 | /// The total number of items. 51 | @property(nonatomic, readonly) NSUInteger total; 52 | 53 | ///---------------------------- 54 | /// @name Initializing a SPPagination Object 55 | ///---------------------------- 56 | 57 | /** 58 | Returns an autoreleased `SPPagination` object initialized with the given pagination data. 59 | 60 | There is no need to call this method directly, higher level 61 | methods use this to return pagination data to blocks. 62 | @param dictionary A dictionary of comment data. 63 | @return An autoreleased `SPPagination` object. 64 | @see initWithDictionary: 65 | @see [SPRequest shotsForPlayerLikes:withBlock:] 66 | */ 67 | + (id)paginationWithDictionary:(NSDictionary *)dictionary; 68 | 69 | /** 70 | Returns a Spectttator pagination object initialized with the given pagination data. 71 | 72 | There is no need to call this method directly, higher level 73 | methods use this to return pagination data to blocks. 74 | @param dictionary A dictionary of comment data. 75 | @return An initialized `SPPagination` object. 76 | @see [SPRequest shotsForPlayerLikes:withBlock:] 77 | */ 78 | - (id)initWithDictionary:(NSDictionary *)dictionary; 79 | 80 | ///---------------------------- 81 | /// @name Pagination Dictionary 82 | ///---------------------------- 83 | 84 | /** 85 | A helper method for creating a pagination dictionary. 86 | @param page The page number. 87 | @return A pagination dictionary with the specified `page` value. 88 | @see page:perPage: 89 | */ 90 | + (NSDictionary *)page:(NSUInteger)page; 91 | 92 | /** 93 | A helper method for creating a pagination dictionary. 94 | @param perPage The number of items per-page. 95 | @return A pagination dictionary with the specified `per_page` value. 96 | @see page:perPage: 97 | */ 98 | + (NSDictionary *)perPage:(NSUInteger)perPage; 99 | 100 | /** 101 | A helper method for creating a pagination dictionary. 102 | @param page The page number. 103 | @param perPage The number of items per-page. 104 | @return A pagination dictionary with the specified `page` and `per_page` values. 105 | @see page:perPage: 106 | */ 107 | + (NSDictionary *)page:(NSUInteger)page perPage:(NSUInteger)perPage; 108 | 109 | @end 110 | -------------------------------------------------------------------------------- /Spectttator/SPPagination.m: -------------------------------------------------------------------------------- 1 | // 2 | // SPPagination.m 3 | // Spectttator 4 | // 5 | // Created by David Keegan on 6/26/11. 6 | // Copyright 2011 David Keegan. 7 | // 8 | 9 | #import "SPPagination.h" 10 | #import "SPMethods.h" 11 | 12 | NSUInteger const SPPerPageDefault = 15; 13 | NSUInteger const SPPerPageMax = 50; 14 | 15 | @interface SPPagination() 16 | @property(nonatomic, readwrite) NSUInteger page; 17 | @property(nonatomic, readwrite) NSUInteger pages; 18 | @property(nonatomic, readwrite) NSUInteger perPage; 19 | @property(nonatomic, readwrite) NSUInteger total; 20 | @end 21 | 22 | @implementation SPPagination 23 | 24 | + (NSDictionary *)page:(NSUInteger)page{ 25 | return [SPPagination page:page perPage:NSNotFound]; 26 | } 27 | 28 | + (NSDictionary *)perPage:(NSUInteger)perPage{ 29 | return [SPPagination page:NSNotFound perPage:perPage]; 30 | } 31 | 32 | + (NSDictionary *)page:(NSUInteger)page perPage:(NSUInteger)perPage{ 33 | NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; 34 | if(page != NSNotFound && page > 0){ 35 | [dictionary setObject:@(page) forKey:@"page"]; 36 | } 37 | if(perPage != NSNotFound && perPage > 0){ 38 | [dictionary setObject:@(perPage) forKey:@"perPage"]; 39 | } 40 | return dictionary; 41 | } 42 | 43 | + (id)paginationWithDictionary:(NSDictionary *)dictionary{ 44 | return [[SPPagination alloc] initWithDictionary:dictionary]; 45 | } 46 | 47 | - (id)initWithDictionary:(NSDictionary *)dictionary{ 48 | if((self = [super init])){ 49 | self.page = [dictionary uintSafelyFromKey:@"page"]; 50 | self.pages = [dictionary uintSafelyFromKey:@"pages"]; 51 | self.perPage = [dictionary uintSafelyFromKey:@"per_page"]; 52 | self.total = [dictionary uintSafelyFromKey:@"total"]; 53 | } 54 | return self; 55 | } 56 | 57 | - (NSString *)description{ 58 | return [NSString stringWithFormat:@"<%@ Page=%lu Pages=%lu PerPage=%lu Total=%lu>", 59 | [self class], (unsigned long)self.page, (unsigned long)self.pages, 60 | (unsigned long)self.perPage, (unsigned long)self.total]; 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /Spectttator/SPPlayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPPlayer.h 3 | // Spectttator 4 | // 5 | // Created by David Keegan on 6/26/11. 6 | // Copyright 2011 David Keegan. 7 | // 8 | 9 | #import 10 | #import "SPObject.h" 11 | 12 | /** The `SPPlayer` class provides a programmatic interface for interacting 13 | with Dribbble players. 14 | 15 | The following snippet demonstrates how to retrieve information about a player by their username. 16 | 17 | #import 18 | 19 | NSString *username = @"inscopeapps"; 20 | 21 | [SPRequest playerInformationForUsername:username 22 | runOnMainThread:NO 23 | withBlock:^(SPPlayer *player){ 24 | NSLog(@"Player information for %@: %@", username, player); 25 | }]; 26 | 27 | This is non-blocking, `NSLog` will run whenever the comment data has finished loading, 28 | but the block still has access to everything in the scope from where it was defined. 29 | If the block is updating UI elements make sure to set `runOnMainThread:YES`, the Dribbble 30 | requests will still be asynchronous but the passed in block will be executed on the main thread. 31 | */ 32 | 33 | @interface SPPlayer : SPObject 34 | 35 | /// The real name of the player. 36 | @property (strong, nonatomic, readonly) NSString *name; 37 | /// The username of the player. 38 | @property (strong, nonatomic, readonly) NSString *username; 39 | /// The url of the player's profile. 40 | @property (strong, nonatomic, readonly) NSURL *url; 41 | /// The url of the player's avatar. 42 | @property (strong, nonatomic, readonly) NSURL *avatarUrl; 43 | /// The location of the player. 44 | @property (strong, nonatomic, readonly) NSString *location; 45 | /// The player's twitter name. 46 | @property (strong, nonatomic, readonly) NSString *twitterScreenName; 47 | /** The id of this player who drafted this player. 48 | 49 | If this player was not drafted the value is `NSNotFound`. 50 | */ 51 | @property (nonatomic, readonly) NSUInteger draftedByPlayerId; 52 | /// The number of shots the player has posted. 53 | @property (nonatomic, readonly) NSUInteger shotsCount; 54 | /// The number of players the player has drafted. 55 | @property (nonatomic, readonly) NSUInteger drafteesCount; 56 | /// The number of people the player follows. 57 | @property (nonatomic, readonly) NSUInteger followersCount; 58 | /// The number of followers the player has. 59 | @property (nonatomic, readonly) NSUInteger followingCount; 60 | /// The number of comments the player has posted. 61 | @property (nonatomic, readonly) NSUInteger commentsCount; 62 | /// The number of comments the player's shots have received. 63 | @property (nonatomic, readonly) NSUInteger commentsReceivedCount; 64 | /// The number of shots the player has liked. 65 | @property (nonatomic, readonly) NSUInteger likesCount; 66 | /// The number of likes the player's shots have received. 67 | @property (nonatomic, readonly) NSUInteger likesReceivedCount; 68 | /// The number of rebounds the player has posted. 69 | @property (nonatomic, readonly) NSUInteger reboundsCount; 70 | /// The number of rebounds the player's shots have received. 71 | @property (nonatomic, readonly) NSUInteger reboundsReceivedCount; 72 | 73 | ///---------------------------- 74 | /// @name Avatar 75 | ///---------------------------- 76 | 77 | /** 78 | Retrieves the player's avatar. 79 | @param runOnMainThread Specifies if the passed in block should be run on the main thread. 80 | If UI elements are being updated in the block this should be `YES`. 81 | @param block The block to be executed once the data has been retrieved. 82 | Depending on the platform an `NSImage` or `UIImage` object for the 83 | avatar is passed to the block. 84 | */ 85 | - (void)avatarRunOnMainThread:(BOOL)runOnMainThread 86 | withBlock:(void (^)(SPImage *image))block; 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /Spectttator/SPPlayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // SPPlayer.m 3 | // Spectttator 4 | // 5 | // Created by David Keegan on 6/26/11. 6 | // Copyright 2011 David Keegan. 7 | // 8 | 9 | #import "SPPlayer.h" 10 | 11 | @interface SPPlayer() 12 | @property (strong, nonatomic, readwrite) NSString *name; 13 | @property (strong, nonatomic, readwrite) NSString *username; 14 | @property (strong, nonatomic, readwrite) NSURL *url; 15 | @property (strong, nonatomic, readwrite) NSURL *avatarUrl; 16 | @property (strong, nonatomic, readwrite) NSString *location; 17 | @property (strong, nonatomic, readwrite) NSString *twitterScreenName; 18 | @property (nonatomic, readwrite) NSUInteger draftedByPlayerId; 19 | @property (nonatomic, readwrite) NSUInteger shotsCount; 20 | @property (nonatomic, readwrite) NSUInteger drafteesCount; 21 | @property (nonatomic, readwrite) NSUInteger followersCount; 22 | @property (nonatomic, readwrite) NSUInteger followingCount; 23 | @property (nonatomic, readwrite) NSUInteger commentsCount; 24 | @property (nonatomic, readwrite) NSUInteger commentsReceivedCount; 25 | @property (nonatomic, readwrite) NSUInteger likesCount; 26 | @property (nonatomic, readwrite) NSUInteger likesReceivedCount; 27 | @property (nonatomic, readwrite) NSUInteger reboundsCount; 28 | @property (nonatomic, readwrite) NSUInteger reboundsReceivedCount; 29 | @end 30 | 31 | @implementation SPPlayer 32 | 33 | - (void)avatarRunOnMainThread:(BOOL)runOnMainThread 34 | withBlock:(void (^)(SPImage *image))block{ 35 | [SPMethods requestImageWithURL:self.avatarUrl 36 | runOnMainThread:runOnMainThread 37 | withBlock:block]; 38 | } 39 | 40 | - (id)initWithDictionary:(NSDictionary *)dictionary{ 41 | if((self = [super initWithDictionary:dictionary])){ 42 | self.name = [dictionary stringSafelyFromKey:@"name"]; 43 | self.username = [dictionary stringSafelyFromKey:@"username"]; 44 | self.url = [dictionary URLSafelyFromKey:@"url"]; 45 | self.avatarUrl = [dictionary URLSafelyFromKey:@"avatar_url"]; 46 | self.location = [dictionary stringSafelyFromKey:@"location"]; 47 | self.twitterScreenName = [dictionary stringSafelyFromKey:@"twitter_screen_name"]; 48 | self.draftedByPlayerId = [dictionary uintSafelyFromKey:@"drafted_by_player_id"]; 49 | self.shotsCount = [dictionary uintSafelyFromKey:@"shots_count"]; 50 | self.drafteesCount = [dictionary uintSafelyFromKey:@"draftees_count"]; 51 | self.followersCount = [dictionary uintSafelyFromKey:@"followers_count"]; 52 | self.followingCount = [dictionary uintSafelyFromKey:@"following_count"]; 53 | self.commentsCount = [dictionary uintSafelyFromKey:@"comments_count"]; 54 | self.commentsReceivedCount = [dictionary uintSafelyFromKey:@"comments_received_count"]; 55 | self.likesCount = [dictionary uintSafelyFromKey:@"likes_count"]; 56 | self.likesReceivedCount = [dictionary uintSafelyFromKey:@"likes_received_count"]; 57 | self.reboundsCount = [dictionary uintSafelyFromKey:@"rebounds_count"]; 58 | self.reboundsReceivedCount = [dictionary uintSafelyFromKey:@"rebounds_received_count"]; 59 | } 60 | 61 | return self; 62 | } 63 | 64 | - (NSString *)description{ 65 | return [NSString stringWithFormat:@"<%@ %lu Name='%@' Username='%@' URL=%@>", 66 | [self class], (unsigned long)self.identifier, self.name, self.username, self.url]; 67 | } 68 | 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /Spectttator/SPRequest.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPRequest.h 3 | // Spectttator 4 | // 5 | // Created by David Keegan on 6/25/11. 6 | // Copyright 2011 David Keegan. 7 | // 8 | 9 | #import 10 | #import "SPShot.h" 11 | #import "SPPlayer.h" 12 | #import "SPPagination.h" 13 | 14 | extern NSString *const SPListDebuts; 15 | extern NSString *const SPListEveryone; 16 | extern NSString *const SPListPopular; 17 | 18 | /** The `SPRequest` class provides a programmatic interface for interacting 19 | with the majority of [Dribbble api](http://dribbble.com/api) calls, there are some shot centric 20 | methods implemented on the `SPShot` object. 21 | 22 | The following snippet demonstrates how to get the last 10 shots a player liked. 23 | 24 | #import 25 | 26 | NSString *username = @"kgn"; 27 | [SPRequest shotsForPlayerLikes:username 28 | withPagination:[SPPagination perPage:10] 29 | runOnMainThread:NO 30 | withBlock:^(NSArray *shots, SPPagination *pagination){ 31 | NSLog(@"Shot %@ likes: %@", username, shots); 32 | }]; 33 | 34 | This is non-blocking, `NSLog` will run whenever the comment data has finished loading, 35 | but the block still has access to everything in the scope from where it was defined. 36 | If the block is updating UI elements make sure to set `runOnMainThread:YES`, the Dribbble 37 | requests will still be asynchronous but the passed in block will be executed on the main thread. 38 | */ 39 | 40 | @interface SPRequest : NSObject 41 | 42 | ///---------------------------- 43 | /// @name Players 44 | ///---------------------------- 45 | 46 | /** 47 | Retrieves profile details for a player specified by _username_. 48 | @param username The username of the player. 49 | @param runOnMainThread Specifies if the passed in block should be run on the main thread. 50 | If UI elements are being updated in the block this should be `YES`. 51 | @param block The block to be executed once the data has been retrieved. 52 | A `SPPlayer` object is passed to the block. 53 | @see SPPlayer 54 | */ 55 | + (void)playerInformationForUsername:(NSString *)username 56 | runOnMainThread:(BOOL)runOnMainThread 57 | withBlock:(void (^)(SPPlayer *player))block; 58 | 59 | /** 60 | Retrieves the list of followers for a player specified by _username_. 61 | @param username The username of the player. 62 | @param pagination A NSDictionary with pagination data, the best way to 63 | create this dictionary is with the helper functions on `SPPagination`. 64 | If `nil` the default pagination will be used as defined by the 65 | [dribbble api](http://dribbble.com/api#pagination). 66 | @param runOnMainThread Specifies if the passed in block should be run on the main thread. 67 | If UI elements are being updated in the block this should be `YES`. 68 | @param block The block to be executed once the data has been retrieved. 69 | An `NSArray` of `SPPlayer` objects and a `SPPagination` objects are passed to the block. 70 | @see playerFollowers:withBlock: 71 | @see SPPagination 72 | @see SPPlayer 73 | */ 74 | + (void)playerFollowers:(NSString *)username 75 | withPagination:(NSDictionary *)pagination 76 | runOnMainThread:(BOOL)runOnMainThread 77 | withBlock:(void (^)(NSArray *players, SPPagination *pagination))block; 78 | 79 | /** 80 | Retrieves the list of followers for a player specified by _username_. 81 | @param username The username of the player. 82 | @param pagination A NSDictionary with pagination data, the best way to 83 | create this dictionary is with the helper functions on `SPPagination`. 84 | If `nil` the default pagination will be used as defined by the 85 | [dribbble api](http://dribbble.com/api#pagination). 86 | @param runOnMainThread Specifies if the passed in block should be run on the main thread. 87 | If UI elements are being updated in the block this should be `YES`. 88 | @param block The block to be executed once the data has been retrieved. 89 | An `NSArray` of `SPPlayer` objects and a `SPPagination` objects are passed to the block. 90 | @see playerFollowing:withBlock: 91 | @see SPPagination 92 | @see SPPlayer 93 | */ 94 | + (void)playerFollowing:(NSString *)username 95 | withPagination:(NSDictionary *)pagination 96 | runOnMainThread:(BOOL)runOnMainThread 97 | withBlock:(void (^)(NSArray *players, SPPagination *pagination))block; 98 | 99 | /** 100 | Retrieves the list of players drafted by the player specified by _username_. 101 | @param username The username of the player. 102 | @param pagination A NSDictionary with pagination data, the best way to 103 | create this dictionary is with the helper functions on `SPPagination`. 104 | If `nil` the default pagination will be used as defined by the 105 | [dribbble api](http://dribbble.com/api#pagination). 106 | @param runOnMainThread Specifies if the passed in block should be run on the main thread. 107 | If UI elements are being updated in the block this should be `YES`. 108 | @param block The block to be executed once the data has been retrieved. 109 | An `NSArray` of `SPPlayer` objects and a `SPPagination` objects are passed to the block. 110 | @see playerDraftees:withBlock: 111 | @see SPPagination 112 | @see SPPlayer 113 | */ 114 | + (void)playerDraftees:(NSString *)username 115 | withPagination:(NSDictionary *)pagination 116 | runOnMainThread:(BOOL)runOnMainThread 117 | withBlock:(void (^)(NSArray *players, SPPagination *pagination))block; 118 | 119 | 120 | ///---------------------------- 121 | /// @name Shots 122 | ///---------------------------- 123 | 124 | /** 125 | Retrieves details for a shot specified by _id_. 126 | @param identifier The shot identifier number. 127 | @param runOnMainThread Specifies if the passed in block should be run on the main thread. 128 | If UI elements are being updated in the block this should be `YES`. 129 | @param block The block to be executed once the data has been retrieved. 130 | A `SPShot` object is passed to the block. 131 | @see SPShot 132 | */ 133 | + (void)shotInformationForIdentifier:(NSUInteger)identifier 134 | runOnMainThread:(BOOL)runOnMainThread 135 | withBlock:(void (^)(SPShot *shot))block; 136 | 137 | /** 138 | Retrieves the specified list of shots. 139 | @param list The list to retrieve shots from, must be one of the following values: 140 | 141 | - `SPDebutsList` 142 | - `SPEveryoneList` 143 | - `SPPopularList` 144 | @param pagination A NSDictionary with pagination data, the best way to 145 | create this dictionary is with the helper functions on `SPPagination`. 146 | If `nil` the default pagination will be used as defined by the 147 | [dribbble api](http://dribbble.com/api#pagination). 148 | @param runOnMainThread Specifies if the passed in block should be run on the main thread. 149 | If UI elements are being updated in the block this should be `YES`. 150 | @param block The block to be executed once the data has been retrieved. 151 | An `NSArray` of `SPShot` objects and a `SPPagination` objects are passed to the block. 152 | @see shotsForList:withBlock: 153 | @see SPPagination 154 | @see SPShot 155 | */ 156 | + (void)shotsForList:(NSString *)list 157 | withPagination:(NSDictionary *)pagination 158 | runOnMainThread:(BOOL)runOnMainThread 159 | withBlock:(void (^)(NSArray *shots, SPPagination *pagination))block; 160 | 161 | /** 162 | Retrieves the most recent shots for the player specified by _username_. 163 | @param username The username of the player. 164 | @param pagination A NSDictionary with pagination data, the best way to 165 | create this dictionary is with the helper functions on `SPPagination`. 166 | If `nil` the default pagination will be used as defined by the 167 | [dribbble api](http://dribbble.com/api#pagination). 168 | @param runOnMainThread Specifies if the passed in block should be run on the main thread. 169 | If UI elements are being updated in the block this should be `YES`. 170 | @param block The block to be executed once the data has been retrieved. 171 | An `NSArray` of `SPShot` objects and a `SPPagination` objects are passed to the block. 172 | @see shotsForPlayer:withBlock: 173 | @see SPPagination 174 | @see SPShot 175 | */ 176 | + (void)shotsForPlayer:(NSString *)username 177 | withPagination:(NSDictionary *)pagination 178 | runOnMainThread:(BOOL)runOnMainThread 179 | withBlock:(void (^)(NSArray *shots, SPPagination *pagination))block; 180 | 181 | /** 182 | Retrieves the most recent shots published by those the player specified by _username_ is following. 183 | @param username The username of the player. 184 | @param pagination A NSDictionary with pagination data, the best way to 185 | create this dictionary is with the helper functions on `SPPagination`. 186 | If `nil` the default pagination will be used as defined by the 187 | [dribbble api](http://dribbble.com/api#pagination). 188 | @param runOnMainThread Specifies if the passed in block should be run on the main thread. 189 | If UI elements are being updated in the block this should be `YES`. 190 | @param block The block to be executed once the data has been retrieved. 191 | An `NSArray` of `SPShot` objects and a `SPPagination` objects are passed to the block. 192 | @see shotsForPlayerFollowing:withBlock: 193 | @see SPPagination 194 | @see SPShot 195 | */ 196 | + (void)shotsForPlayerFollowing:(NSString *)username 197 | withPagination:(NSDictionary *)pagination 198 | runOnMainThread:(BOOL)runOnMainThread 199 | withBlock:(void (^)(NSArray *shots, SPPagination *pagination))block; 200 | 201 | /** 202 | Retrieves shots liked by the player specified by _username_. 203 | @param username The username of the player. 204 | @param pagination A NSDictionary with pagination data, the best way to 205 | create this dictionary is with the helper functions on `SPPagination`. 206 | If `nil` the default pagination will be used as defined by the 207 | [dribbble api](http://dribbble.com/api#pagination). 208 | @param runOnMainThread Specifies if the passed in block should be run on the main thread. 209 | If UI elements are being updated in the block this should be `YES`. 210 | @param block The block to be executed once the data has been retrieved. 211 | An `NSArray` of `SPShot` objects and a `SPPagination` objects are passed to the block. 212 | @see shotsForPlayerLikes:withBlock: 213 | @see SPPagination 214 | @see SPShot 215 | */ 216 | + (void)shotsForPlayerLikes:(NSString *)username 217 | withPagination:(NSDictionary *)pagination 218 | runOnMainThread:(BOOL)runOnMainThread 219 | withBlock:(void (^)(NSArray *shots, SPPagination *pagination))block; 220 | 221 | @end 222 | -------------------------------------------------------------------------------- /Spectttator/SPRequest.m: -------------------------------------------------------------------------------- 1 | // 2 | // SPRequest.m 3 | // Spectttator 4 | // 5 | // Created by David Keegan on 6/25/11. 6 | // Copyright 2011 David Keegan. 7 | // 8 | 9 | #import "SPRequest.h" 10 | #import "SPMethods.h" 11 | #import "AFJSONRequestOperation.h" 12 | 13 | NSString *const SPListDebuts = @"debuts"; 14 | NSString *const SPListEveryone = @"everyone"; 15 | NSString *const SPListPopular = @"popular"; 16 | 17 | @implementation SPRequest 18 | 19 | #pragma mark Players 20 | #pragma mark - 21 | 22 | + (void)playerInformationForUsername:(NSString *)username 23 | runOnMainThread:(BOOL)runOnMainThread 24 | withBlock:(void (^)(SPPlayer *player))block{ 25 | NSString *urlString = [NSString stringWithFormat:@"http://api.dribbble.com/players/%@", username]; 26 | [[AFJSONRequestOperation 27 | JSONRequestOperationWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlString]] 28 | success:^(NSURLRequest *request, NSHTTPURLResponse *response, NSDictionary *json){ 29 | SPPlayer *player = nil; 30 | if(![[json objectForKey:@"message"] isEqualToString:@"Not found"]){ 31 | player = [[SPPlayer alloc] initWithDictionary:json]; 32 | } 33 | if(runOnMainThread){ 34 | dispatch_async(dispatch_get_main_queue(), ^{ 35 | block(player); 36 | }); 37 | }else{ 38 | block(player); 39 | } 40 | } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON){ 41 | if(runOnMainThread){ 42 | dispatch_async(dispatch_get_main_queue(), ^{ 43 | block(nil); 44 | }); 45 | }else{ 46 | block(nil); 47 | } 48 | }] start]; 49 | } 50 | 51 | + (void)playerFollowers:(NSString *)username 52 | withPagination:(NSDictionary *)pagination 53 | runOnMainThread:(BOOL)runOnMainThread 54 | withBlock:(void (^)(NSArray *players, SPPagination *pagination))block{ 55 | NSString *urlString = [NSString stringWithFormat: 56 | @"http://api.dribbble.com/players/%@/followers%@", 57 | username, [SPMethods pagination:pagination]]; 58 | [SPMethods requestPlayersWithURL:[NSURL URLWithString:urlString] 59 | runOnMainThread:runOnMainThread 60 | withBlock:block]; 61 | } 62 | 63 | + (void)playerFollowing:(NSString *)username 64 | withPagination:(NSDictionary *)pagination 65 | runOnMainThread:(BOOL)runOnMainThread 66 | withBlock:(void (^)(NSArray *players, SPPagination *pagination))block{ 67 | NSString *urlString = [NSString stringWithFormat: 68 | @"http://api.dribbble.com/players/%@/following%@", 69 | username, [SPMethods pagination:pagination]]; 70 | [SPMethods requestPlayersWithURL:[NSURL URLWithString:urlString] 71 | runOnMainThread:runOnMainThread 72 | withBlock:block]; 73 | } 74 | 75 | + (void)playerDraftees:(NSString *)username 76 | withPagination:(NSDictionary *)pagination 77 | runOnMainThread:(BOOL)runOnMainThread 78 | withBlock:(void (^)(NSArray *players, SPPagination *pagination))block{ 79 | NSString *urlString = [NSString stringWithFormat: 80 | @"http://api.dribbble.com/players/%@/draftees%@", 81 | username, [SPMethods pagination:pagination]]; 82 | [SPMethods requestPlayersWithURL:[NSURL URLWithString:urlString] 83 | runOnMainThread:runOnMainThread 84 | withBlock:block]; 85 | } 86 | 87 | #pragma mark Shots 88 | #pragma mark - 89 | 90 | + (void)shotInformationForIdentifier:(NSUInteger)identifier 91 | runOnMainThread:(BOOL)runOnMainThread 92 | withBlock:(void (^)(SPShot *shot))block{ 93 | NSString *urlString = [NSString stringWithFormat:@"http://api.dribbble.com/shots/%lu", (unsigned long)identifier]; 94 | [[AFJSONRequestOperation 95 | JSONRequestOperationWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlString]] 96 | success:^(NSURLRequest *request, NSHTTPURLResponse *response, NSDictionary *json){ 97 | SPShot *shot = nil; 98 | if(![[json objectForKey:@"message"] isEqualToString:@"Not found"]){ 99 | shot = [[SPShot alloc] initWithDictionary:json]; 100 | } 101 | if(runOnMainThread){ 102 | dispatch_async(dispatch_get_main_queue(), ^{ 103 | block(shot); 104 | }); 105 | }else{ 106 | block(shot); 107 | } 108 | } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON){ 109 | if(runOnMainThread){ 110 | dispatch_async(dispatch_get_main_queue(), ^{ 111 | block(nil); 112 | }); 113 | }else{ 114 | block(nil); 115 | } 116 | }] start]; 117 | } 118 | 119 | + (void)shotsForList:(NSString *)list 120 | withPagination:(NSDictionary *)pagination 121 | runOnMainThread:(BOOL)runOnMainThread 122 | withBlock:(void (^)(NSArray *shots, SPPagination *pagination))block{ 123 | NSString *urlString = [NSString stringWithFormat: 124 | @"http://api.dribbble.com/shots/%@%@", 125 | list, [SPMethods pagination:pagination]]; 126 | [SPMethods requestShotsWithURL:[NSURL URLWithString:urlString] 127 | runOnMainThread:runOnMainThread 128 | withBlock:block]; 129 | } 130 | 131 | + (void)shotsForPlayer:(NSString *)username 132 | withPagination:(NSDictionary *)pagination 133 | runOnMainThread:(BOOL)runOnMainThread 134 | withBlock:(void (^)(NSArray *shots, SPPagination *pagination))block{ 135 | NSString *urlString = [NSString stringWithFormat: 136 | @"http://api.dribbble.com/players/%@/shots%@", 137 | username, [SPMethods pagination:pagination]]; 138 | [SPMethods requestShotsWithURL:[NSURL URLWithString:urlString] 139 | runOnMainThread:runOnMainThread 140 | withBlock:block]; 141 | } 142 | 143 | + (void)shotsForPlayerFollowing:(NSString *)username 144 | withPagination:(NSDictionary *)pagination 145 | runOnMainThread:(BOOL)runOnMainThread 146 | withBlock:(void (^)(NSArray *shots, SPPagination *pagination))block{ 147 | NSString *urlString = [NSString stringWithFormat: 148 | @"http://api.dribbble.com/players/%@/shots/following%@", 149 | username, [SPMethods pagination:pagination]]; 150 | [SPMethods requestShotsWithURL:[NSURL URLWithString:urlString] 151 | runOnMainThread:runOnMainThread 152 | withBlock:block]; 153 | } 154 | 155 | + (void)shotsForPlayerLikes:(NSString *)username 156 | withPagination:(NSDictionary *)pagination 157 | runOnMainThread:(BOOL)runOnMainThread 158 | withBlock:(void (^)(NSArray *shots, SPPagination *pagination))block{ 159 | NSString *urlString = [NSString stringWithFormat: 160 | @"http://api.dribbble.com/players/%@/shots/likes%@", 161 | username, [SPMethods pagination:pagination]]; 162 | [SPMethods requestShotsWithURL:[NSURL URLWithString:urlString] 163 | runOnMainThread:runOnMainThread 164 | withBlock:block]; 165 | } 166 | 167 | @end 168 | -------------------------------------------------------------------------------- /Spectttator/SPShot.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPShot.h 3 | // Spectttator 4 | // 5 | // Created by David Keegan on 6/26/11. 6 | // Copyright 2011 David Keegan. 7 | // 8 | 9 | #import 10 | #import "SPObject.h" 11 | 12 | @class SPPagination; 13 | @class SPPlayer; 14 | 15 | /** The `SPShot` class provides a programmatic interface for interacting 16 | with Dribbble shots. 17 | 18 | The following snippet demonstrates how to retrieve a shot from an id and then get rebounds and comments on the shot. 19 | 20 | #import 21 | 22 | [SPRequest shotInformationForIdentifier:199295 runOnMainThread:NO withBlock:^(SPShot *shot){ 23 | NSLog(@"Shot Information: %@", shot); 24 | [shot reboundsWithPagination:nil 25 | runOnMainThread:NO 26 | withBlock:^(NSArray *rebounds, SPPagination *pagination){ 27 | NSLog(@"Rebounds for '%@': %@", shot.title, rebounds); 28 | }]; 29 | [shot commentsWithPagination:nil 30 | runOnMainThread:NO 31 | withBlock:^(NSArray *comments, SPPagination *pagination){ 32 | NSLog(@"Comments for '%@': %@", shot.title, comments); 33 | }]; 34 | }]; 35 | 36 | This is non-blocking, `NSLog` will run whenever the comment data has finished loading, 37 | but the block still has access to everything in the scope from where it was defined. 38 | If the block is updating UI elements make sure to set `runOnMainThread:YES`, the Dribbble 39 | requests will still be asynchronous but the passed in block will be executed on the main thread. 40 | */ 41 | 42 | @interface SPShot : SPObject 43 | 44 | /// The title of the shot. 45 | @property (strong, nonatomic, readonly) NSString *title; 46 | /// The full url to the shot. 47 | @property (strong, nonatomic, readonly) NSURL *url; 48 | /// The short url to the shot. 49 | @property (strong, nonatomic, readonly) NSURL *shortUrl; 50 | /// The url to the shot's image. 51 | @property (strong, nonatomic, readonly) NSURL *imageUrl; 52 | /// The url to the shot's 400px wide image. 53 | @property (strong, nonatomic, readonly) NSURL *image400Url; 54 | /// The url to the shot's teaser image. 55 | @property (strong, nonatomic, readonly) NSURL *imageTeaserUrl; 56 | /// The width of the shot. 57 | @property (nonatomic, readonly) NSUInteger width; 58 | /// The height of the shot. 59 | @property (nonatomic, readonly) NSUInteger height; 60 | /// The number of views the shot has. 61 | @property (nonatomic, readonly) NSUInteger viewsCount; 62 | /// The number of likes the shot has. 63 | @property (nonatomic, readonly) NSUInteger likesCount; 64 | /// The number of comments the shot has. 65 | @property (nonatomic, readonly) NSUInteger commentsCount; 66 | /// The number of rebounds the shot has. 67 | @property (nonatomic, readonly) NSUInteger reboundsCount; 68 | /** The id of this shot this shot is a rebound of. 69 | 70 | If it is not a rebound this value is `NSNotFound`. 71 | */ 72 | @property (nonatomic, readonly) NSUInteger reboundSourceId; 73 | /** The player who posted the shot. 74 | @see SPPlayer 75 | */ 76 | @property (strong, nonatomic, readonly) SPPlayer *player; 77 | 78 | 79 | ///---------------------------- 80 | /// @name Retrieving Images 81 | ///---------------------------- 82 | 83 | /** 84 | Retrieves the shot's image. 85 | @param runOnMainThread Specifies if the passed in block should be run on the main thread. 86 | If UI elements are being updated in the block this should be `YES`. 87 | @param block The block to be executed once the data has been retrieved. 88 | Depending on the platform an `NSImage` or `UIImage` object for the 89 | shot is passed to the block. 90 | */ 91 | - (void)imageRunOnMainThread:(BOOL)runOnMainThread 92 | withBlock:(void (^)(SPImage *image))block; 93 | 94 | /** 95 | Retrieves the shot's 400px wide image. 96 | @param runOnMainThread Specifies if the passed in block should be run on the main thread. 97 | If UI elements are being updated in the block this should be `YES`. 98 | @param block The block to be executed once the data has been retrieved. 99 | Depending on the platform an `NSImage` or `UIImage` object for the 100 | shot is passed to the block. 101 | */ 102 | - (void)image400RunOnMainThread:(BOOL)runOnMainThread 103 | withBlock:(void (^)(SPImage *image))block; 104 | 105 | /** 106 | Retrieves the shot's teaser image. 107 | @param runOnMainThread Specifies if the passed in block should be run on the main thread. 108 | If UI elements are being updated in the block this should be `YES`. 109 | @param block The block to be executed once the data has been retrieved. 110 | Depending on the platform an `NSImage` or `UIImage` object for the 111 | teaser is passed to the block. 112 | */ 113 | - (void)imageTeaserRunOnMainThread:(BOOL)runOnMainThread 114 | withBlock:(void (^)(SPImage *image))block; 115 | 116 | ///---------------------------- 117 | /// @name Rebounds and Comments 118 | ///---------------------------- 119 | 120 | /** 121 | Retrieves the set of rebounds (shots in response to a shot) for the shot. 122 | @param pagination A NSDictionary with pagination data, the best way to 123 | create this dictionary is with the helper functions on `SPPagination`. 124 | If `nil` the default pagination will be used as defined by the 125 | [dribbble api](http://dribbble.com/api#pagination). 126 | @param runOnMainThread Specifies if the passed in block should be run on the main thread. 127 | If UI elements are being updated in the block this should be `YES`. 128 | @param block The block to be executed once the data has been retrieved. 129 | An `NSArray` of `SPShot` objects and a `SPPagination` objects are passed to the block. 130 | @see reboundsWithBlock:withBlock: 131 | @see SPPagination 132 | */ 133 | - (void)reboundsWithPagination:(NSDictionary *)pagination 134 | runOnMainThread:(BOOL)runOnMainThread 135 | withBlock:(void (^)(NSArray *shots, SPPagination *pagination))block; 136 | 137 | /** 138 | Retrieves the set of rebounds (shots in response to a shot) for the shot. 139 | @param pagination A NSDictionary with pagination data, the best way to 140 | create this dictionary is with the helper functions on `SPPagination`. 141 | If `nil` the default pagination will be used as defined by the 142 | [dribbble api](http://dribbble.com/api#pagination). 143 | @param runOnMainThread Specifies if the passed in block should be run on the main thread. 144 | If UI elements are being updated in the block this should be `YES`. 145 | @param block The block to be executed once the data has been retrieved. 146 | An `NSArray` of `SPComment` objects and a `SPPagination` objects are passed to the block. 147 | @param pagination A `NSDictionary` with pagination data, the best way to 148 | create this dictionary is with the helper functions on `SPPagination`. 149 | @see commentsWithBlock:withBlock: 150 | @see SPComment 151 | @see SPPagination 152 | */ 153 | - (void)commentsWithPagination:(NSDictionary *)pagination 154 | runOnMainThread:(BOOL)runOnMainThread 155 | withBlock:(void (^)(NSArray *comments, SPPagination *pagination))block; 156 | 157 | @end 158 | -------------------------------------------------------------------------------- /Spectttator/SPShot.m: -------------------------------------------------------------------------------- 1 | // 2 | // SPShot.m 3 | // Spectttator 4 | // 5 | // Created by David Keegan on 6/26/11. 6 | // Copyright 2011 David Keegan. 7 | // 8 | 9 | #import "SPShot.h" 10 | #import "SPComment.h" 11 | #import "SPPagination.h" 12 | #import "SPPlayer.h" 13 | 14 | @interface SPShot() 15 | @property (strong, nonatomic, readwrite) NSString *title; 16 | @property (strong, nonatomic, readwrite) NSURL *url; 17 | @property (strong, nonatomic, readwrite) NSURL *shortUrl; 18 | @property (strong, nonatomic, readwrite) NSURL *imageUrl; 19 | @property (strong, nonatomic, readwrite) NSURL *image400Url; 20 | @property (strong, nonatomic, readwrite) NSURL *imageTeaserUrl; 21 | @property (nonatomic, readwrite) NSUInteger width; 22 | @property (nonatomic, readwrite) NSUInteger height; 23 | @property (nonatomic, readwrite) NSUInteger viewsCount; 24 | @property (nonatomic, readwrite) NSUInteger likesCount; 25 | @property (nonatomic, readwrite) NSUInteger commentsCount; 26 | @property (nonatomic, readwrite) NSUInteger reboundsCount; 27 | @property (nonatomic, readwrite) NSUInteger reboundSourceId; 28 | @property (strong, nonatomic, readwrite) SPPlayer *player; 29 | @end 30 | 31 | @implementation SPShot 32 | 33 | - (void)imageRunOnMainThread:(BOOL)runOnMainThread 34 | withBlock:(void (^)(SPImage *image))block{ 35 | [SPMethods requestImageWithURL:self.imageUrl 36 | runOnMainThread:runOnMainThread 37 | withBlock:block]; 38 | } 39 | 40 | - (void)image400RunOnMainThread:(BOOL)runOnMainThread 41 | withBlock:(void (^)(SPImage *image))block{ 42 | [SPMethods requestImageWithURL:self.image400Url 43 | runOnMainThread:runOnMainThread 44 | withBlock:block]; 45 | } 46 | 47 | - (void)imageTeaserRunOnMainThread:(BOOL)runOnMainThread 48 | withBlock:(void (^)(SPImage *image))block{ 49 | [SPMethods requestImageWithURL:self.imageTeaserUrl 50 | runOnMainThread:runOnMainThread 51 | withBlock:block]; 52 | } 53 | 54 | - (void)reboundsWithPagination:(NSDictionary *)pagination 55 | runOnMainThread:(BOOL)runOnMainThread 56 | withBlock:(void (^)(NSArray *shots, SPPagination *pagination))block{ 57 | NSString *urlString = [NSString stringWithFormat: 58 | @"http://api.dribbble.com/shots/%lu/rebounds%@", 59 | (unsigned long)self.identifier, [SPMethods pagination:pagination]]; 60 | [SPMethods requestShotsWithURL:[NSURL URLWithString:urlString] 61 | runOnMainThread:runOnMainThread 62 | withBlock:block]; 63 | } 64 | 65 | - (void)commentsWithPagination:(NSDictionary *)pagination 66 | runOnMainThread:(BOOL)runOnMainThread 67 | withBlock:(void (^)(NSArray *comments, SPPagination *pagination))block{ 68 | NSString *urlString = [NSString stringWithFormat: 69 | @"http://api.dribbble.com/shots/%lu/comments%@", 70 | (unsigned long)self.identifier, [SPMethods pagination:pagination]]; 71 | [SPMethods requestCommentsWithURL:[NSURL URLWithString:urlString] 72 | runOnMainThread:runOnMainThread 73 | withBlock:block]; 74 | } 75 | 76 | - (id)initWithDictionary:(NSDictionary *)dictionary{ 77 | if((self = [super initWithDictionary:dictionary])){ 78 | self.title = [dictionary stringSafelyFromKey:@"title"]; 79 | self.url = [dictionary URLSafelyFromKey:@"url"]; 80 | self.shortUrl = [dictionary URLSafelyFromKey:@"short_url"]; 81 | self.imageUrl = [dictionary URLSafelyFromKey:@"image_url"]; 82 | self.image400Url = [dictionary URLSafelyFromKey:@"image_400_url"]; 83 | self.imageTeaserUrl = [dictionary URLSafelyFromKey:@"image_teaser_url"]; 84 | self.width = [dictionary uintSafelyFromKey:@"width"]; 85 | self.height = [dictionary uintSafelyFromKey:@"height"]; 86 | self.viewsCount = [dictionary uintSafelyFromKey:@"views_count"]; 87 | self.likesCount = [dictionary uintSafelyFromKey:@"likes_count"]; 88 | self.commentsCount = [dictionary uintSafelyFromKey:@"comments_count"]; 89 | self.reboundsCount = [dictionary uintSafelyFromKey:@"rebounds_count"]; 90 | self.reboundSourceId = [dictionary uintSafelyFromKey:@"rebound_source_id"]; 91 | 92 | NSDictionary *player = [dictionary objectSafelyFromKey:@"player"]; 93 | if(player != nil){ 94 | self.player = [[SPPlayer alloc] initWithDictionary:player]; 95 | } 96 | } 97 | 98 | return self; 99 | } 100 | 101 | - (NSString *)description{ 102 | return [NSString stringWithFormat:@"<%@ %lu Title='%@' Player=%@ URL=%@>", 103 | [self class], (unsigned long)self.identifier, self.title, self.player.username, self.url]; 104 | } 105 | 106 | 107 | @end 108 | -------------------------------------------------------------------------------- /Spectttator/Spectttator-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.InScopeApps.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | NSHumanReadableCopyright 24 | Copyright © 2011 David Keegan 25 | 26 | 27 | -------------------------------------------------------------------------------- /Spectttator/Spectttator-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'Spectttator' target in the 'Spectttator' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #import 8 | #endif 9 | -------------------------------------------------------------------------------- /Spectttator/Spectttator-iOS-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'Spectttator-iOS' target in the 'Spectttator-iOS' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #import 8 | #import 9 | #endif 10 | -------------------------------------------------------------------------------- /Spectttator/Spectttator.h: -------------------------------------------------------------------------------- 1 | // 2 | // Spectttator.h 3 | // Spectttator 4 | // 5 | // Created by David Keegan on 6/25/11. 6 | // Copyright 2011 David Keegan. 7 | // 8 | 9 | #import "SPRequest.h" 10 | #import "SPPagination.h" 11 | #import "SPShot.h" 12 | #import "SPPlayer.h" 13 | #import "SPComment.h" 14 | #import "SPMethods.h" 15 | -------------------------------------------------------------------------------- /SpectttatorTest-iOS/RootViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // RootViewController.h 3 | // SpectttatorTest-iOS 4 | // 5 | // Created by David Keegan on 6/29/11. 6 | // Copyright 2011 David Keegan. 7 | // 8 | 9 | #import 10 | #import "ShotCell.h" 11 | 12 | @interface RootViewController : UITableViewController 13 | 14 | @property (retain, nonatomic) NSString *list; 15 | @property (retain, nonatomic) NSArray *shots; 16 | @property (retain, nonatomic) NSMutableSet *imageRetrievedCache; 17 | @property (retain, nonatomic) NSMutableDictionary *imageCache; 18 | 19 | @property (retain, nonatomic) UIBarButtonItem *listButton; 20 | @property (retain, nonatomic) UIBarButtonItem *refreshButton; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /SpectttatorTest-iOS/RootViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // RootViewController.m 3 | // SpectttatorTest-iOS 4 | // 5 | // Created by David Keegan on 6/29/11. 6 | // Copyright 2011 David Keegan. 7 | // 8 | 9 | #import "RootViewController.h" 10 | #import "Spectttator.h" 11 | 12 | @implementation RootViewController 13 | 14 | @synthesize list = _list; 15 | @synthesize shots = _shots; 16 | @synthesize imageRetrievedCache = _imageRetrievedCache; 17 | @synthesize imageCache = _imageCache; 18 | 19 | @synthesize listButton = _listButton; 20 | @synthesize refreshButton = _refreshButton; 21 | 22 | - (void)refreshWithList:(NSString *)aList{ 23 | if(aList != nil){ 24 | self.list = aList; 25 | } 26 | self.title = [self.list capitalizedString]; 27 | 28 | [self.listButton setEnabled:NO]; 29 | [self.refreshButton setEnabled:NO]; 30 | 31 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; 32 | [SPRequest shotsForList:self.list 33 | withPagination:[SPPagination perPage:SPPerPageMax] 34 | runOnMainThread:YES 35 | withBlock:^(NSArray *theShots, SPPagination *thsPagination){ 36 | self.shots = theShots; 37 | self.imageCache = nil; 38 | self.imageRetrievedCache = nil; 39 | self.imageRetrievedCache = [[NSMutableSet alloc] initWithCapacity:[theShots count]]; 40 | self.imageCache = [[NSMutableDictionary alloc] initWithCapacity:[theShots count]]; 41 | 42 | [self.listButton setEnabled:YES]; 43 | [self.refreshButton setEnabled:YES]; 44 | [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; 45 | 46 | [self.tableView reloadData]; 47 | }]; 48 | } 49 | 50 | - (void)refresh{ 51 | [self refreshWithList:nil]; 52 | } 53 | 54 | -(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{ 55 | if(buttonIndex == 0){ 56 | [self refreshWithList:SPListPopular]; 57 | }else if(buttonIndex == 1){ 58 | [self refreshWithList:SPListEveryone]; 59 | }else if(buttonIndex == 2){ 60 | [self refreshWithList:SPListDebuts]; 61 | } 62 | } 63 | 64 | - (void)changeList{ 65 | UIActionSheet *listSheet = [[UIActionSheet alloc] initWithTitle:@"Lists" 66 | delegate:self 67 | cancelButtonTitle:nil 68 | destructiveButtonTitle:nil 69 | otherButtonTitles:[SPListPopular capitalizedString], 70 | [SPListEveryone capitalizedString], 71 | [SPListDebuts capitalizedString], 72 | nil]; 73 | [listSheet setActionSheetStyle:UIActionSheetStyleBlackOpaque]; 74 | [listSheet showInView:self.view]; 75 | [listSheet release]; 76 | } 77 | 78 | - (void)viewDidLoad{ 79 | [self refreshWithList:SPListPopular]; 80 | 81 | self.refreshButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh 82 | target:self 83 | action:@selector(refresh)]; 84 | self.navigationItem.rightBarButtonItem = self.refreshButton; 85 | 86 | self.listButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemBookmarks 87 | target:self 88 | action:@selector(changeList)]; 89 | self.navigationItem.leftBarButtonItem = self.listButton; 90 | 91 | [super viewDidLoad]; 92 | } 93 | 94 | - (void)viewDidAppear:(BOOL)animated{ 95 | [self refresh]; 96 | [super viewDidAppear:animated]; 97 | } 98 | 99 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ 100 | return 68.0f; 101 | } 102 | 103 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ 104 | return 1; 105 | } 106 | 107 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ 108 | return[self.shots count]; 109 | } 110 | 111 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 112 | { 113 | static NSString *CellIdentifier = @"ShotCell"; 114 | 115 | ShotCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 116 | if (cell == nil) { 117 | NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil]; 118 | cell = [topLevelObjects objectAtIndex:0]; 119 | } 120 | 121 | SPShot *aShot = [self.shots objectAtIndex:indexPath.row]; 122 | 123 | NSNumber *identifier = [NSNumber numberWithLong:aShot.identifier]; 124 | [cell loadShot:aShot withImage:[self.imageCache objectForKey:identifier]]; 125 | 126 | // A little image cache so we only retrieve the image once, 127 | // and we update the row the first time it's loaded. 128 | if(![self.imageRetrievedCache member:identifier]){ 129 | [self.imageRetrievedCache addObject:identifier]; 130 | [aShot imageTeaserRunOnMainThread:YES withBlock:^(UIImage *image){ 131 | [self.imageCache setObject:image forKey:identifier]; 132 | [tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] 133 | withRowAnimation:UITableViewRowAnimationNone]; 134 | }]; 135 | } 136 | 137 | // Configure the cell. 138 | return cell; 139 | } 140 | 141 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ 142 | // Open the shot in safari 143 | SPShot *aShot = [self.shots objectAtIndex:indexPath.row]; 144 | [[UIApplication sharedApplication] openURL:aShot.url]; 145 | } 146 | 147 | - (void)dealloc{ 148 | [_shots release]; 149 | [_imageRetrievedCache release]; 150 | [_imageCache release]; 151 | 152 | [_refreshButton release]; 153 | [_listButton release]; 154 | 155 | [super dealloc]; 156 | } 157 | 158 | @end 159 | -------------------------------------------------------------------------------- /SpectttatorTest-iOS/RootViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 784 5 | 10D541 6 | 760 7 | 1038.29 8 | 460.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 81 12 | 13 | 14 | YES 15 | 16 | 17 | 18 | YES 19 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 20 | 21 | 22 | YES 23 | 24 | YES 25 | 26 | 27 | YES 28 | 29 | 30 | 31 | YES 32 | 33 | IBFilesOwner 34 | IBCocoaTouchFramework 35 | 36 | 37 | IBFirstResponder 38 | IBCocoaTouchFramework 39 | 40 | 41 | 42 | 274 43 | {320, 247} 44 | 45 | 46 | 3 47 | MQA 48 | 49 | NO 50 | YES 51 | NO 52 | IBCocoaTouchFramework 53 | NO 54 | 1 55 | 0 56 | YES 57 | 44 58 | 22 59 | 22 60 | 61 | 62 | 63 | 64 | YES 65 | 66 | 67 | view 68 | 69 | 70 | 71 | 3 72 | 73 | 74 | 75 | dataSource 76 | 77 | 78 | 79 | 4 80 | 81 | 82 | 83 | delegate 84 | 85 | 86 | 87 | 5 88 | 89 | 90 | 91 | 92 | YES 93 | 94 | 0 95 | 96 | 97 | 98 | 99 | 100 | -1 101 | 102 | 103 | File's Owner 104 | 105 | 106 | -2 107 | 108 | 109 | 110 | 111 | 2 112 | 113 | 114 | 115 | 116 | 117 | 118 | YES 119 | 120 | YES 121 | -1.CustomClassName 122 | -2.CustomClassName 123 | 2.IBEditorWindowLastContentRect 124 | 2.IBPluginDependency 125 | 126 | 127 | YES 128 | RootViewController 129 | UIResponder 130 | {{144, 609}, {320, 247}} 131 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 132 | 133 | 134 | 135 | YES 136 | 137 | 138 | YES 139 | 140 | 141 | 142 | 143 | YES 144 | 145 | 146 | YES 147 | 148 | 149 | 150 | 5 151 | 152 | 153 | 154 | YES 155 | 156 | RootViewController 157 | UITableViewController 158 | 159 | IBProjectSource 160 | RootViewController.h 161 | 162 | 163 | 164 | 165 | YES 166 | 167 | NSObject 168 | 169 | IBFrameworkSource 170 | Foundation.framework/Headers/NSError.h 171 | 172 | 173 | 174 | NSObject 175 | 176 | IBFrameworkSource 177 | Foundation.framework/Headers/NSFileManager.h 178 | 179 | 180 | 181 | NSObject 182 | 183 | IBFrameworkSource 184 | Foundation.framework/Headers/NSKeyValueCoding.h 185 | 186 | 187 | 188 | NSObject 189 | 190 | IBFrameworkSource 191 | Foundation.framework/Headers/NSKeyValueObserving.h 192 | 193 | 194 | 195 | NSObject 196 | 197 | IBFrameworkSource 198 | Foundation.framework/Headers/NSKeyedArchiver.h 199 | 200 | 201 | 202 | NSObject 203 | 204 | IBFrameworkSource 205 | Foundation.framework/Headers/NSNetServices.h 206 | 207 | 208 | 209 | NSObject 210 | 211 | IBFrameworkSource 212 | Foundation.framework/Headers/NSObject.h 213 | 214 | 215 | 216 | NSObject 217 | 218 | IBFrameworkSource 219 | Foundation.framework/Headers/NSPort.h 220 | 221 | 222 | 223 | NSObject 224 | 225 | IBFrameworkSource 226 | Foundation.framework/Headers/NSRunLoop.h 227 | 228 | 229 | 230 | NSObject 231 | 232 | IBFrameworkSource 233 | Foundation.framework/Headers/NSStream.h 234 | 235 | 236 | 237 | NSObject 238 | 239 | IBFrameworkSource 240 | Foundation.framework/Headers/NSThread.h 241 | 242 | 243 | 244 | NSObject 245 | 246 | IBFrameworkSource 247 | Foundation.framework/Headers/NSURL.h 248 | 249 | 250 | 251 | NSObject 252 | 253 | IBFrameworkSource 254 | Foundation.framework/Headers/NSURLConnection.h 255 | 256 | 257 | 258 | NSObject 259 | 260 | IBFrameworkSource 261 | Foundation.framework/Headers/NSXMLParser.h 262 | 263 | 264 | 265 | NSObject 266 | 267 | IBFrameworkSource 268 | UIKit.framework/Headers/UIAccessibility.h 269 | 270 | 271 | 272 | NSObject 273 | 274 | IBFrameworkSource 275 | UIKit.framework/Headers/UINibLoading.h 276 | 277 | 278 | 279 | NSObject 280 | 281 | IBFrameworkSource 282 | UIKit.framework/Headers/UIResponder.h 283 | 284 | 285 | 286 | UIResponder 287 | NSObject 288 | 289 | 290 | 291 | UIScrollView 292 | UIView 293 | 294 | IBFrameworkSource 295 | UIKit.framework/Headers/UIScrollView.h 296 | 297 | 298 | 299 | UISearchBar 300 | UIView 301 | 302 | IBFrameworkSource 303 | UIKit.framework/Headers/UISearchBar.h 304 | 305 | 306 | 307 | UISearchDisplayController 308 | NSObject 309 | 310 | IBFrameworkSource 311 | UIKit.framework/Headers/UISearchDisplayController.h 312 | 313 | 314 | 315 | UITableView 316 | UIScrollView 317 | 318 | IBFrameworkSource 319 | UIKit.framework/Headers/UITableView.h 320 | 321 | 322 | 323 | UITableViewController 324 | UIViewController 325 | 326 | IBFrameworkSource 327 | UIKit.framework/Headers/UITableViewController.h 328 | 329 | 330 | 331 | UIView 332 | 333 | IBFrameworkSource 334 | UIKit.framework/Headers/UITextField.h 335 | 336 | 337 | 338 | UIView 339 | UIResponder 340 | 341 | IBFrameworkSource 342 | UIKit.framework/Headers/UIView.h 343 | 344 | 345 | 346 | UIViewController 347 | 348 | IBFrameworkSource 349 | UIKit.framework/Headers/UINavigationController.h 350 | 351 | 352 | 353 | UIViewController 354 | 355 | IBFrameworkSource 356 | UIKit.framework/Headers/UITabBarController.h 357 | 358 | 359 | 360 | UIViewController 361 | UIResponder 362 | 363 | IBFrameworkSource 364 | UIKit.framework/Headers/UIViewController.h 365 | 366 | 367 | 368 | 369 | 0 370 | IBCocoaTouchFramework 371 | 372 | com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS 373 | 374 | 375 | 376 | com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 377 | 378 | 379 | YES 380 | SpectttatorTest-iOS.xcodeproj 381 | 3 382 | 81 383 | 384 | 385 | -------------------------------------------------------------------------------- /SpectttatorTest-iOS/ShotCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ShotCell.h 3 | // SpectttatorTest-iOS 4 | // 5 | // Created by David Keegan on 6/30/11. 6 | // Copyright 2011 David Keegan. 7 | // 8 | 9 | #import 10 | #import "Spectttator.h" 11 | 12 | @interface ShotCell : UITableViewCell { 13 | UILabel *_title; 14 | UILabel *_player; 15 | UILabel *_info; 16 | UIImageView *_shot; 17 | } 18 | 19 | @property (nonatomic, retain) IBOutlet UILabel *title; 20 | @property (nonatomic, retain) IBOutlet UILabel *player; 21 | @property (nonatomic, retain) IBOutlet UILabel *info; 22 | @property (nonatomic, retain) IBOutlet UIImageView *shot; 23 | 24 | - (void)loadShot:(SPShot *)aShot withImage:(UIImage *)image; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /SpectttatorTest-iOS/ShotCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ShotCell.m 3 | // SpectttatorTest-iOS 4 | // 5 | // Created by David Keegan on 6/30/11. 6 | // Copyright 2011 David Keegan. 7 | // 8 | 9 | #import "ShotCell.h" 10 | 11 | @implementation ShotCell 12 | 13 | @synthesize title = _title; 14 | @synthesize player = _player; 15 | @synthesize info = _info; 16 | @synthesize shot = _shot; 17 | 18 | - (void)loadShot:(SPShot *)aShot withImage:(UIImage *)image{ 19 | self.title.text = aShot.title; 20 | self.player.text = aShot.player.name; 21 | self.info.text = [NSString stringWithFormat:@"👀%lu 💓%lu 📢%lu", 22 | (unsigned long)aShot.viewsCount, (unsigned long)aShot.likesCount, 23 | (unsigned long)aShot.commentsCount]; 24 | self.shot.image = image; 25 | } 26 | 27 | - (void)dealloc { 28 | [_title release]; 29 | [_player release]; 30 | [_info release]; 31 | [_shot release]; 32 | [super dealloc]; 33 | } 34 | @end 35 | -------------------------------------------------------------------------------- /SpectttatorTest-iOS/ShotCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1280 5 | 10K540 6 | 1881 7 | 1038.36 8 | 461.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 876 12 | 13 | 14 | YES 15 | IBUITableViewCell 16 | IBUIImageView 17 | IBUILabel 18 | IBProxyObject 19 | 20 | 21 | YES 22 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 23 | 24 | 25 | PluginDependencyRecalculationVersion 26 | 27 | 28 | 29 | YES 30 | 31 | IBFilesOwner 32 | IBCocoaTouchFramework 33 | 34 | 35 | IBFirstResponder 36 | IBCocoaTouchFramework 37 | 38 | 39 | 40 | 292 41 | 42 | YES 43 | 44 | 45 | 256 46 | 47 | YES 48 | 49 | 50 | 292 51 | {{2, 2}, {85, 64}} 52 | 53 | 54 | 55 | NO 56 | IBCocoaTouchFramework 57 | 58 | 59 | 60 | 290 61 | {{95, 2}, {205, 21}} 62 | 63 | 64 | 65 | NO 66 | YES 67 | 7 68 | NO 69 | IBCocoaTouchFramework 70 | Label 71 | 72 | 1 73 | MCAwIDAAA 74 | 75 | 76 | 3 77 | MQA 78 | 79 | 1 80 | 14 81 | 82 | 2 83 | 17 84 | 85 | 86 | Helvetica-Bold 87 | 17 88 | 16 89 | 90 | 91 | 92 | 93 | 290 94 | {{95, 20}, {205, 21}} 95 | 96 | 97 | 98 | NO 99 | YES 100 | 7 101 | NO 102 | IBCocoaTouchFramework 103 | Label 104 | 105 | 106 | 1 107 | 10 108 | 109 | 1 110 | 4 111 | 112 | 113 | Helvetica 114 | 14 115 | 16 116 | 117 | 118 | 119 | 120 | 266 121 | {{95, 42}, {205, 21}} 122 | 123 | 124 | 125 | NO 126 | YES 127 | 7 128 | NO 129 | IBCocoaTouchFramework 130 | Label 131 | 132 | 3 133 | MC42NjY2NjY2NjY3AA 134 | 135 | 136 | 1 137 | 10 138 | 139 | 140 | 141 | 142 | {320, 68} 143 | 144 | 145 | 146 | 147 | 3 148 | MCAwAA 149 | 150 | NO 151 | YES 152 | 4 153 | YES 154 | IBCocoaTouchFramework 155 | 156 | 157 | {320, 68} 158 | 159 | 160 | 161 | 162 | 1 163 | MSAxIDEAA 164 | 165 | IBCocoaTouchFramework 166 | 1 167 | 168 | 169 | 170 | ShotCell 171 | 172 | 173 | 174 | 175 | YES 176 | 177 | 178 | title 179 | 180 | 181 | 182 | 7 183 | 184 | 185 | 186 | player 187 | 188 | 189 | 190 | 8 191 | 192 | 193 | 194 | info 195 | 196 | 197 | 198 | 9 199 | 200 | 201 | 202 | shot 203 | 204 | 205 | 206 | 10 207 | 208 | 209 | 210 | 211 | YES 212 | 213 | 0 214 | 215 | YES 216 | 217 | 218 | 219 | 220 | 221 | -1 222 | 223 | 224 | File's Owner 225 | 226 | 227 | -2 228 | 229 | 230 | 231 | 232 | 2 233 | 234 | 235 | YES 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 3 245 | 246 | 247 | 248 | 249 | 4 250 | 251 | 252 | 253 | 254 | 5 255 | 256 | 257 | 258 | 259 | 6 260 | 261 | 262 | 263 | 264 | 265 | 266 | YES 267 | 268 | YES 269 | -1.IBPluginDependency 270 | -2.CustomClassName 271 | -2.IBPluginDependency 272 | 2.CustomClassName 273 | 2.IBPluginDependency 274 | 3.IBPluginDependency 275 | 4.IBPluginDependency 276 | 5.IBPluginDependency 277 | 6.IBPluginDependency 278 | 279 | 280 | YES 281 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 282 | UIResponder 283 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 284 | ShotCell 285 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 286 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 287 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 288 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 289 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 290 | 291 | 292 | 293 | YES 294 | 295 | 296 | 297 | 298 | 299 | YES 300 | 301 | 302 | 303 | 304 | 11 305 | 306 | 307 | 308 | YES 309 | 310 | ShotCell 311 | UITableViewCell 312 | 313 | YES 314 | 315 | YES 316 | info 317 | player 318 | shot 319 | title 320 | 321 | 322 | YES 323 | UILabel 324 | UILabel 325 | UIImageView 326 | UILabel 327 | 328 | 329 | 330 | YES 331 | 332 | YES 333 | info 334 | player 335 | shot 336 | title 337 | 338 | 339 | YES 340 | 341 | info 342 | UILabel 343 | 344 | 345 | player 346 | UILabel 347 | 348 | 349 | shot 350 | UIImageView 351 | 352 | 353 | title 354 | UILabel 355 | 356 | 357 | 358 | 359 | IBProjectSource 360 | ./Classes/ShotCell.h 361 | 362 | 363 | 364 | 365 | 0 366 | IBCocoaTouchFramework 367 | 368 | com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 369 | 370 | 371 | YES 372 | 3 373 | 876 374 | 375 | 376 | -------------------------------------------------------------------------------- /SpectttatorTest-iOS/SpectttatorTest-iOS-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIconFile 12 | 13 | CFBundleIdentifier 14 | com.InScopeApps.${PRODUCT_NAME:rfc1034identifier} 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | ${PRODUCT_NAME} 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleSignature 24 | ???? 25 | CFBundleVersion 26 | 1.0 27 | LSRequiresIPhoneOS 28 | 29 | NSMainNibFile 30 | MainWindow 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /SpectttatorTest-iOS/SpectttatorTest-iOS-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'SpectttatorTest-iOS' target in the 'SpectttatorTest-iOS' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_3_0 8 | #warning "This project uses features only available in iPhone SDK 3.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /SpectttatorTest-iOS/SpectttatorTest-iOS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kgn/Spectttator/b6154b14bab5e27cf68a228bac695b8ea8fce5cc/SpectttatorTest-iOS/SpectttatorTest-iOS.png -------------------------------------------------------------------------------- /SpectttatorTest-iOS/SpectttatorTest_iOSAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SpectttatorTest_iOSAppDelegate.h 3 | // SpectttatorTest-iOS 4 | // 5 | // Created by David Keegan on 6/29/11. 6 | // Copyright 2011 David Keegan. 7 | // 8 | 9 | #import 10 | 11 | @interface SpectttatorTest_iOSAppDelegate : NSObject 12 | 13 | @property (nonatomic, retain) IBOutlet UIWindow *window; 14 | @property (nonatomic, retain) IBOutlet UINavigationController *navigationController; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /SpectttatorTest-iOS/SpectttatorTest_iOSAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // SpectttatorTest_iOSAppDelegate.m 3 | // SpectttatorTest-iOS 4 | // 5 | // Created by David Keegan on 6/29/11. 6 | // Copyright 2011 David Keegan. 7 | // 8 | 9 | #import "SpectttatorTest_iOSAppDelegate.h" 10 | 11 | @implementation SpectttatorTest_iOSAppDelegate 12 | 13 | @synthesize window = _window; 14 | @synthesize navigationController = _navigationController; 15 | 16 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ 17 | self.window.rootViewController = self.navigationController; 18 | [self.window makeKeyAndVisible]; 19 | return YES; 20 | } 21 | 22 | - (void)dealloc{ 23 | [_window release]; 24 | [_navigationController release]; 25 | [super dealloc]; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /SpectttatorTest-iOS/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SpectttatorTest-iOS 4 | // 5 | // Created by David Keegan on 6/29/11. 6 | // Copyright 2011 David Keegan. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, char *argv[]){ 12 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 13 | int retVal = UIApplicationMain(argc, argv, nil, nil); 14 | [pool release]; 15 | return retVal; 16 | } 17 | -------------------------------------------------------------------------------- /SpectttatorTest.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 730EB4D7161D67E600745929 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 730EB4D6161D67E600745929 /* Default-568h@2x.png */; }; 11 | 73698B8913B6820100922D1A /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 73698B7113B681F400922D1A /* Cocoa.framework */; }; 12 | 73698B9113B6820100922D1A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 73698B9013B6820100922D1A /* main.m */; }; 13 | 73698B9813B6820100922D1A /* SpectttatorTestAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 73698B9713B6820100922D1A /* SpectttatorTestAppDelegate.m */; }; 14 | 7388115513C2F05E004B037C /* Spectttator.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7388114C13C2F04B004B037C /* Spectttator.framework */; }; 15 | 7388115613C2F064004B037C /* Spectttator.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 7388114C13C2F04B004B037C /* Spectttator.framework */; }; 16 | 7388115913C2F075004B037C /* libSpectttator-iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7388114E13C2F04B004B037C /* libSpectttator-iOS.a */; }; 17 | 739A44B013B86ADB00E7783A /* NSAttributedString+Hyperlink.m in Sources */ = {isa = PBXBuildFile; fileRef = 739A449713B826A000E7783A /* NSAttributedString+Hyperlink.m */; }; 18 | 73C01CC313C2EC4A00A8ECDB /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 73C01CC213C2EC4A00A8ECDB /* main.m */; }; 19 | 73C01CC713C2EC4A00A8ECDB /* SpectttatorTest_iOSAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 73C01CC613C2EC4A00A8ECDB /* SpectttatorTest_iOSAppDelegate.m */; }; 20 | 73C01CCA13C2EC4A00A8ECDB /* RootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 73C01CC913C2EC4A00A8ECDB /* RootViewController.m */; }; 21 | 73C01CDB13C2ED3000A8ECDB /* ShotCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 73C01CD913C2ED3000A8ECDB /* ShotCell.m */; }; 22 | 73C01CDC13C2ED3000A8ECDB /* ShotCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 73C01CDA13C2ED3000A8ECDB /* ShotCell.xib */; }; 23 | 73C01CDE13C2ED3C00A8ECDB /* RootViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 73C01CDD13C2ED3B00A8ECDB /* RootViewController.xib */; }; 24 | 73C01CE113C2ED7000A8ECDB /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 73C01CAD13C2EBBC00A8ECDB /* UIKit.framework */; }; 25 | 73C01CE313C2ED9800A8ECDB /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 73C01CE213C2ED9800A8ECDB /* Foundation.framework */; }; 26 | 73D2C9E613C2EF2D009C0419 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 73D2C9E513C2EF2D009C0419 /* MainMenu.xib */; }; 27 | D688AD2113C70A6E00278A6D /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = D688AD2013C70A6E00278A6D /* MainWindow.xib */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXContainerItemProxy section */ 31 | 7388114B13C2F04B004B037C /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = 7388113F13C2F04B004B037C /* Spectttator.xcodeproj */; 34 | proxyType = 2; 35 | remoteGlobalIDString = 73698B6E13B681F400922D1A; 36 | remoteInfo = Spectttator; 37 | }; 38 | 7388114D13C2F04B004B037C /* PBXContainerItemProxy */ = { 39 | isa = PBXContainerItemProxy; 40 | containerPortal = 7388113F13C2F04B004B037C /* Spectttator.xcodeproj */; 41 | proxyType = 2; 42 | remoteGlobalIDString = 73C01C7B13C2EA8600A8ECDB; 43 | remoteInfo = "Spectttator-iOS"; 44 | }; 45 | 7388115313C2F058004B037C /* PBXContainerItemProxy */ = { 46 | isa = PBXContainerItemProxy; 47 | containerPortal = 7388113F13C2F04B004B037C /* Spectttator.xcodeproj */; 48 | proxyType = 1; 49 | remoteGlobalIDString = 73698B6D13B681F400922D1A; 50 | remoteInfo = Spectttator; 51 | }; 52 | 7388115713C2F070004B037C /* PBXContainerItemProxy */ = { 53 | isa = PBXContainerItemProxy; 54 | containerPortal = 7388113F13C2F04B004B037C /* Spectttator.xcodeproj */; 55 | proxyType = 1; 56 | remoteGlobalIDString = 73C01C7A13C2EA8600A8ECDB; 57 | remoteInfo = "Spectttator-iOS"; 58 | }; 59 | /* End PBXContainerItemProxy section */ 60 | 61 | /* Begin PBXCopyFilesBuildPhase section */ 62 | 73698BA313B6821B00922D1A /* Copy Frameworks */ = { 63 | isa = PBXCopyFilesBuildPhase; 64 | buildActionMask = 2147483647; 65 | dstPath = ""; 66 | dstSubfolderSpec = 10; 67 | files = ( 68 | 7388115613C2F064004B037C /* Spectttator.framework in Copy Frameworks */, 69 | ); 70 | name = "Copy Frameworks"; 71 | runOnlyForDeploymentPostprocessing = 0; 72 | }; 73 | /* End PBXCopyFilesBuildPhase section */ 74 | 75 | /* Begin PBXFileReference section */ 76 | 730EB4D6161D67E600745929 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "SpectttatorTest/Default-568h@2x.png"; sourceTree = SOURCE_ROOT; }; 77 | 73698B7113B681F400922D1A /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 78 | 73698B7613B681F400922D1A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 79 | 73698B8713B6820100922D1A /* SpectttatorTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SpectttatorTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; 80 | 73698B8C13B6820100922D1A /* SpectttatorTest-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SpectttatorTest-Info.plist"; sourceTree = ""; }; 81 | 73698B9013B6820100922D1A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 82 | 73698B9213B6820100922D1A /* SpectttatorTest-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SpectttatorTest-Prefix.pch"; sourceTree = ""; }; 83 | 73698B9613B6820100922D1A /* SpectttatorTestAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SpectttatorTestAppDelegate.h; sourceTree = ""; }; 84 | 73698B9713B6820100922D1A /* SpectttatorTestAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SpectttatorTestAppDelegate.m; sourceTree = ""; }; 85 | 7388113F13C2F04B004B037C /* Spectttator.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = Spectttator.xcodeproj; sourceTree = ""; }; 86 | 739A449613B826A000E7783A /* NSAttributedString+Hyperlink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSAttributedString+Hyperlink.h"; sourceTree = ""; }; 87 | 739A449713B826A000E7783A /* NSAttributedString+Hyperlink.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSAttributedString+Hyperlink.m"; sourceTree = ""; }; 88 | 73C01CAD13C2EBBC00A8ECDB /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; 89 | 73C01CB313C2EC4A00A8ECDB /* SpectttatorTest-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "SpectttatorTest-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 90 | 73C01CBE13C2EC4A00A8ECDB /* SpectttatorTest-iOS-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SpectttatorTest-iOS-Info.plist"; sourceTree = ""; }; 91 | 73C01CC213C2EC4A00A8ECDB /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 92 | 73C01CC413C2EC4A00A8ECDB /* SpectttatorTest-iOS-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SpectttatorTest-iOS-Prefix.pch"; sourceTree = ""; }; 93 | 73C01CC513C2EC4A00A8ECDB /* SpectttatorTest_iOSAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SpectttatorTest_iOSAppDelegate.h; sourceTree = ""; }; 94 | 73C01CC613C2EC4A00A8ECDB /* SpectttatorTest_iOSAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SpectttatorTest_iOSAppDelegate.m; sourceTree = ""; }; 95 | 73C01CC813C2EC4A00A8ECDB /* RootViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RootViewController.h; sourceTree = ""; }; 96 | 73C01CC913C2EC4A00A8ECDB /* RootViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RootViewController.m; sourceTree = ""; }; 97 | 73C01CD813C2ED3000A8ECDB /* ShotCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShotCell.h; sourceTree = ""; }; 98 | 73C01CD913C2ED3000A8ECDB /* ShotCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShotCell.m; sourceTree = ""; }; 99 | 73C01CDA13C2ED3000A8ECDB /* ShotCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ShotCell.xib; sourceTree = ""; }; 100 | 73C01CDD13C2ED3B00A8ECDB /* RootViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RootViewController.xib; sourceTree = ""; }; 101 | 73C01CE213C2ED9800A8ECDB /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 102 | 73D2C9E513C2EF2D009C0419 /* MainMenu.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = ""; }; 103 | D688AD2013C70A6E00278A6D /* MainWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainWindow.xib; sourceTree = ""; }; 104 | /* End PBXFileReference section */ 105 | 106 | /* Begin PBXFrameworksBuildPhase section */ 107 | 73698B8413B6820100922D1A /* Frameworks */ = { 108 | isa = PBXFrameworksBuildPhase; 109 | buildActionMask = 2147483647; 110 | files = ( 111 | 7388115513C2F05E004B037C /* Spectttator.framework in Frameworks */, 112 | 73698B8913B6820100922D1A /* Cocoa.framework in Frameworks */, 113 | ); 114 | runOnlyForDeploymentPostprocessing = 0; 115 | }; 116 | 73C01CB013C2EC4A00A8ECDB /* Frameworks */ = { 117 | isa = PBXFrameworksBuildPhase; 118 | buildActionMask = 2147483647; 119 | files = ( 120 | 7388115913C2F075004B037C /* libSpectttator-iOS.a in Frameworks */, 121 | 73C01CE313C2ED9800A8ECDB /* Foundation.framework in Frameworks */, 122 | 73C01CE113C2ED7000A8ECDB /* UIKit.framework in Frameworks */, 123 | ); 124 | runOnlyForDeploymentPostprocessing = 0; 125 | }; 126 | /* End PBXFrameworksBuildPhase section */ 127 | 128 | /* Begin PBXGroup section */ 129 | 73698B6213B681F400922D1A = { 130 | isa = PBXGroup; 131 | children = ( 132 | 7388113F13C2F04B004B037C /* Spectttator.xcodeproj */, 133 | 73698B8A13B6820100922D1A /* SpectttatorTest */, 134 | 73C01CBC13C2EC4A00A8ECDB /* SpectttatorTest-iOS */, 135 | 73698B7013B681F400922D1A /* Mac Frameworks */, 136 | 73C01C7C13C2EA8600A8ECDB /* iOS Frameworks */, 137 | 73698B6F13B681F400922D1A /* Products */, 138 | ); 139 | sourceTree = ""; 140 | }; 141 | 73698B6F13B681F400922D1A /* Products */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | 73698B8713B6820100922D1A /* SpectttatorTest.app */, 145 | 73C01CB313C2EC4A00A8ECDB /* SpectttatorTest-iOS.app */, 146 | ); 147 | name = Products; 148 | sourceTree = ""; 149 | }; 150 | 73698B7013B681F400922D1A /* Mac Frameworks */ = { 151 | isa = PBXGroup; 152 | children = ( 153 | 73698B7613B681F400922D1A /* Foundation.framework */, 154 | 73698B7113B681F400922D1A /* Cocoa.framework */, 155 | ); 156 | name = "Mac Frameworks"; 157 | sourceTree = ""; 158 | }; 159 | 73698B8A13B6820100922D1A /* SpectttatorTest */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | 73698B9613B6820100922D1A /* SpectttatorTestAppDelegate.h */, 163 | 73698B9713B6820100922D1A /* SpectttatorTestAppDelegate.m */, 164 | 739A449613B826A000E7783A /* NSAttributedString+Hyperlink.h */, 165 | 739A449713B826A000E7783A /* NSAttributedString+Hyperlink.m */, 166 | 73D2C9E513C2EF2D009C0419 /* MainMenu.xib */, 167 | 73698B8B13B6820100922D1A /* Supporting Files */, 168 | ); 169 | path = SpectttatorTest; 170 | sourceTree = ""; 171 | }; 172 | 73698B8B13B6820100922D1A /* Supporting Files */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | 73698B8C13B6820100922D1A /* SpectttatorTest-Info.plist */, 176 | 73698B9013B6820100922D1A /* main.m */, 177 | 73698B9213B6820100922D1A /* SpectttatorTest-Prefix.pch */, 178 | ); 179 | name = "Supporting Files"; 180 | sourceTree = ""; 181 | }; 182 | 7388114013C2F04B004B037C /* Products */ = { 183 | isa = PBXGroup; 184 | children = ( 185 | 7388114C13C2F04B004B037C /* Spectttator.framework */, 186 | 7388114E13C2F04B004B037C /* libSpectttator-iOS.a */, 187 | ); 188 | name = Products; 189 | sourceTree = ""; 190 | }; 191 | 73C01C7C13C2EA8600A8ECDB /* iOS Frameworks */ = { 192 | isa = PBXGroup; 193 | children = ( 194 | 73C01CE213C2ED9800A8ECDB /* Foundation.framework */, 195 | 73C01CAD13C2EBBC00A8ECDB /* UIKit.framework */, 196 | ); 197 | name = "iOS Frameworks"; 198 | sourceTree = ""; 199 | }; 200 | 73C01CBC13C2EC4A00A8ECDB /* SpectttatorTest-iOS */ = { 201 | isa = PBXGroup; 202 | children = ( 203 | 73C01CC513C2EC4A00A8ECDB /* SpectttatorTest_iOSAppDelegate.h */, 204 | 73C01CC613C2EC4A00A8ECDB /* SpectttatorTest_iOSAppDelegate.m */, 205 | 73C01CC813C2EC4A00A8ECDB /* RootViewController.h */, 206 | 73C01CC913C2EC4A00A8ECDB /* RootViewController.m */, 207 | 73C01CDD13C2ED3B00A8ECDB /* RootViewController.xib */, 208 | 73C01CD813C2ED3000A8ECDB /* ShotCell.h */, 209 | 73C01CD913C2ED3000A8ECDB /* ShotCell.m */, 210 | 73C01CDA13C2ED3000A8ECDB /* ShotCell.xib */, 211 | D688AD2013C70A6E00278A6D /* MainWindow.xib */, 212 | 73C01CBD13C2EC4A00A8ECDB /* Supporting Files */, 213 | ); 214 | path = "SpectttatorTest-iOS"; 215 | sourceTree = ""; 216 | }; 217 | 73C01CBD13C2EC4A00A8ECDB /* Supporting Files */ = { 218 | isa = PBXGroup; 219 | children = ( 220 | 730EB4D6161D67E600745929 /* Default-568h@2x.png */, 221 | 73C01CBE13C2EC4A00A8ECDB /* SpectttatorTest-iOS-Info.plist */, 222 | 73C01CC213C2EC4A00A8ECDB /* main.m */, 223 | 73C01CC413C2EC4A00A8ECDB /* SpectttatorTest-iOS-Prefix.pch */, 224 | ); 225 | name = "Supporting Files"; 226 | sourceTree = ""; 227 | }; 228 | /* End PBXGroup section */ 229 | 230 | /* Begin PBXNativeTarget section */ 231 | 73698B8613B6820100922D1A /* SpectttatorTest */ = { 232 | isa = PBXNativeTarget; 233 | buildConfigurationList = 73698B9C13B6820100922D1A /* Build configuration list for PBXNativeTarget "SpectttatorTest" */; 234 | buildPhases = ( 235 | 73698B8313B6820100922D1A /* Sources */, 236 | 73698B8413B6820100922D1A /* Frameworks */, 237 | 73698B8513B6820100922D1A /* Resources */, 238 | 73698BA313B6821B00922D1A /* Copy Frameworks */, 239 | ); 240 | buildRules = ( 241 | ); 242 | dependencies = ( 243 | 7388115413C2F058004B037C /* PBXTargetDependency */, 244 | ); 245 | name = SpectttatorTest; 246 | productName = SpectttatorTest; 247 | productReference = 73698B8713B6820100922D1A /* SpectttatorTest.app */; 248 | productType = "com.apple.product-type.application"; 249 | }; 250 | 73C01CB213C2EC4A00A8ECDB /* SpectttatorTest-iOS */ = { 251 | isa = PBXNativeTarget; 252 | buildConfigurationList = 73C01CD513C2EC4A00A8ECDB /* Build configuration list for PBXNativeTarget "SpectttatorTest-iOS" */; 253 | buildPhases = ( 254 | 73C01CAF13C2EC4A00A8ECDB /* Sources */, 255 | 73C01CB013C2EC4A00A8ECDB /* Frameworks */, 256 | 73C01CB113C2EC4A00A8ECDB /* Resources */, 257 | ); 258 | buildRules = ( 259 | ); 260 | dependencies = ( 261 | 7388115813C2F070004B037C /* PBXTargetDependency */, 262 | ); 263 | name = "SpectttatorTest-iOS"; 264 | productName = "SpectttatorTest-iOS"; 265 | productReference = 73C01CB313C2EC4A00A8ECDB /* SpectttatorTest-iOS.app */; 266 | productType = "com.apple.product-type.application"; 267 | }; 268 | /* End PBXNativeTarget section */ 269 | 270 | /* Begin PBXProject section */ 271 | 73698B6413B681F400922D1A /* Project object */ = { 272 | isa = PBXProject; 273 | attributes = { 274 | LastUpgradeCheck = 0420; 275 | ORGANIZATIONNAME = "David Keegan"; 276 | }; 277 | buildConfigurationList = 73698B6713B681F400922D1A /* Build configuration list for PBXProject "SpectttatorTest" */; 278 | compatibilityVersion = "Xcode 3.2"; 279 | developmentRegion = English; 280 | hasScannedForEncodings = 0; 281 | knownRegions = ( 282 | en, 283 | ); 284 | mainGroup = 73698B6213B681F400922D1A; 285 | productRefGroup = 73698B6F13B681F400922D1A /* Products */; 286 | projectDirPath = ""; 287 | projectReferences = ( 288 | { 289 | ProductGroup = 7388114013C2F04B004B037C /* Products */; 290 | ProjectRef = 7388113F13C2F04B004B037C /* Spectttator.xcodeproj */; 291 | }, 292 | ); 293 | projectRoot = ""; 294 | targets = ( 295 | 73698B8613B6820100922D1A /* SpectttatorTest */, 296 | 73C01CB213C2EC4A00A8ECDB /* SpectttatorTest-iOS */, 297 | ); 298 | }; 299 | /* End PBXProject section */ 300 | 301 | /* Begin PBXReferenceProxy section */ 302 | 7388114C13C2F04B004B037C /* Spectttator.framework */ = { 303 | isa = PBXReferenceProxy; 304 | fileType = wrapper.framework; 305 | path = Spectttator.framework; 306 | remoteRef = 7388114B13C2F04B004B037C /* PBXContainerItemProxy */; 307 | sourceTree = BUILT_PRODUCTS_DIR; 308 | }; 309 | 7388114E13C2F04B004B037C /* libSpectttator-iOS.a */ = { 310 | isa = PBXReferenceProxy; 311 | fileType = archive.ar; 312 | path = "libSpectttator-iOS.a"; 313 | remoteRef = 7388114D13C2F04B004B037C /* PBXContainerItemProxy */; 314 | sourceTree = BUILT_PRODUCTS_DIR; 315 | }; 316 | /* End PBXReferenceProxy section */ 317 | 318 | /* Begin PBXResourcesBuildPhase section */ 319 | 73698B8513B6820100922D1A /* Resources */ = { 320 | isa = PBXResourcesBuildPhase; 321 | buildActionMask = 2147483647; 322 | files = ( 323 | 73D2C9E613C2EF2D009C0419 /* MainMenu.xib in Resources */, 324 | ); 325 | runOnlyForDeploymentPostprocessing = 0; 326 | }; 327 | 73C01CB113C2EC4A00A8ECDB /* Resources */ = { 328 | isa = PBXResourcesBuildPhase; 329 | buildActionMask = 2147483647; 330 | files = ( 331 | 73C01CDC13C2ED3000A8ECDB /* ShotCell.xib in Resources */, 332 | 73C01CDE13C2ED3C00A8ECDB /* RootViewController.xib in Resources */, 333 | D688AD2113C70A6E00278A6D /* MainWindow.xib in Resources */, 334 | 730EB4D7161D67E600745929 /* Default-568h@2x.png in Resources */, 335 | ); 336 | runOnlyForDeploymentPostprocessing = 0; 337 | }; 338 | /* End PBXResourcesBuildPhase section */ 339 | 340 | /* Begin PBXSourcesBuildPhase section */ 341 | 73698B8313B6820100922D1A /* Sources */ = { 342 | isa = PBXSourcesBuildPhase; 343 | buildActionMask = 2147483647; 344 | files = ( 345 | 73698B9113B6820100922D1A /* main.m in Sources */, 346 | 73698B9813B6820100922D1A /* SpectttatorTestAppDelegate.m in Sources */, 347 | 739A44B013B86ADB00E7783A /* NSAttributedString+Hyperlink.m in Sources */, 348 | ); 349 | runOnlyForDeploymentPostprocessing = 0; 350 | }; 351 | 73C01CAF13C2EC4A00A8ECDB /* Sources */ = { 352 | isa = PBXSourcesBuildPhase; 353 | buildActionMask = 2147483647; 354 | files = ( 355 | 73C01CC313C2EC4A00A8ECDB /* main.m in Sources */, 356 | 73C01CC713C2EC4A00A8ECDB /* SpectttatorTest_iOSAppDelegate.m in Sources */, 357 | 73C01CCA13C2EC4A00A8ECDB /* RootViewController.m in Sources */, 358 | 73C01CDB13C2ED3000A8ECDB /* ShotCell.m in Sources */, 359 | ); 360 | runOnlyForDeploymentPostprocessing = 0; 361 | }; 362 | /* End PBXSourcesBuildPhase section */ 363 | 364 | /* Begin PBXTargetDependency section */ 365 | 7388115413C2F058004B037C /* PBXTargetDependency */ = { 366 | isa = PBXTargetDependency; 367 | name = Spectttator; 368 | targetProxy = 7388115313C2F058004B037C /* PBXContainerItemProxy */; 369 | }; 370 | 7388115813C2F070004B037C /* PBXTargetDependency */ = { 371 | isa = PBXTargetDependency; 372 | name = "Spectttator-iOS"; 373 | targetProxy = 7388115713C2F070004B037C /* PBXContainerItemProxy */; 374 | }; 375 | /* End PBXTargetDependency section */ 376 | 377 | /* Begin XCBuildConfiguration section */ 378 | 73698B7E13B681F400922D1A /* Debug */ = { 379 | isa = XCBuildConfiguration; 380 | buildSettings = { 381 | ALWAYS_SEARCH_USER_PATHS = NO; 382 | ARCHS = "$(ARCHS_STANDARD_64_BIT)"; 383 | COPY_PHASE_STRIP = NO; 384 | GCC_C_LANGUAGE_STANDARD = gnu99; 385 | GCC_DYNAMIC_NO_PIC = NO; 386 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 387 | GCC_OPTIMIZATION_LEVEL = 0; 388 | GCC_PREPROCESSOR_DEFINITIONS = ( 389 | "DEBUG=1", 390 | "$(inherited)", 391 | ); 392 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 393 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 394 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 395 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 396 | GCC_WARN_UNUSED_VARIABLE = YES; 397 | MACOSX_DEPLOYMENT_TARGET = 10.6; 398 | ONLY_ACTIVE_ARCH = YES; 399 | SDKROOT = macosx; 400 | }; 401 | name = Debug; 402 | }; 403 | 73698B7F13B681F400922D1A /* Release */ = { 404 | isa = XCBuildConfiguration; 405 | buildSettings = { 406 | ALWAYS_SEARCH_USER_PATHS = NO; 407 | ARCHS = "$(ARCHS_STANDARD_64_BIT)"; 408 | COPY_PHASE_STRIP = YES; 409 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 410 | GCC_C_LANGUAGE_STANDARD = gnu99; 411 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 412 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 413 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 414 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 415 | GCC_WARN_UNUSED_VARIABLE = YES; 416 | MACOSX_DEPLOYMENT_TARGET = 10.6; 417 | SDKROOT = macosx; 418 | }; 419 | name = Release; 420 | }; 421 | 73698B9D13B6820100922D1A /* Debug */ = { 422 | isa = XCBuildConfiguration; 423 | buildSettings = { 424 | CLANG_ENABLE_OBJC_ARC = YES; 425 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 426 | GCC_PREFIX_HEADER = "SpectttatorTest/SpectttatorTest-Prefix.pch"; 427 | INFOPLIST_FILE = "SpectttatorTest/SpectttatorTest-Info.plist"; 428 | MACOSX_DEPLOYMENT_TARGET = 10.6.8; 429 | PRODUCT_NAME = "$(TARGET_NAME)"; 430 | WRAPPER_EXTENSION = app; 431 | }; 432 | name = Debug; 433 | }; 434 | 73698B9E13B6820100922D1A /* Release */ = { 435 | isa = XCBuildConfiguration; 436 | buildSettings = { 437 | CLANG_ENABLE_OBJC_ARC = YES; 438 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 439 | GCC_PREFIX_HEADER = "SpectttatorTest/SpectttatorTest-Prefix.pch"; 440 | INFOPLIST_FILE = "SpectttatorTest/SpectttatorTest-Info.plist"; 441 | MACOSX_DEPLOYMENT_TARGET = 10.6.8; 442 | PRODUCT_NAME = "$(TARGET_NAME)"; 443 | WRAPPER_EXTENSION = app; 444 | }; 445 | name = Release; 446 | }; 447 | 73C01CD613C2EC4A00A8ECDB /* Debug */ = { 448 | isa = XCBuildConfiguration; 449 | buildSettings = { 450 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 451 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 452 | FRAMEWORK_SEARCH_PATHS = ( 453 | "$(inherited)", 454 | "\"$(DEVELOPER_FRAMEWORKS_DIR)\"", 455 | ); 456 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 457 | GCC_PREFIX_HEADER = "SpectttatorTest-iOS/SpectttatorTest-iOS-Prefix.pch"; 458 | HEADER_SEARCH_PATHS = Spectttator; 459 | INFOPLIST_FILE = "SpectttatorTest-iOS/SpectttatorTest-iOS-Info.plist"; 460 | IPHONEOS_DEPLOYMENT_TARGET = 4.3; 461 | OTHER_LDFLAGS = "-ObjC"; 462 | PRODUCT_NAME = "$(TARGET_NAME)"; 463 | SDKROOT = iphoneos; 464 | WRAPPER_EXTENSION = app; 465 | }; 466 | name = Debug; 467 | }; 468 | 73C01CD713C2EC4A00A8ECDB /* Release */ = { 469 | isa = XCBuildConfiguration; 470 | buildSettings = { 471 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 472 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 473 | FRAMEWORK_SEARCH_PATHS = ( 474 | "$(inherited)", 475 | "\"$(DEVELOPER_FRAMEWORKS_DIR)\"", 476 | ); 477 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 478 | GCC_PREFIX_HEADER = "SpectttatorTest-iOS/SpectttatorTest-iOS-Prefix.pch"; 479 | HEADER_SEARCH_PATHS = Spectttator; 480 | INFOPLIST_FILE = "SpectttatorTest-iOS/SpectttatorTest-iOS-Info.plist"; 481 | IPHONEOS_DEPLOYMENT_TARGET = 4.3; 482 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 483 | OTHER_LDFLAGS = "-ObjC"; 484 | PRODUCT_NAME = "$(TARGET_NAME)"; 485 | SDKROOT = iphoneos; 486 | VALIDATE_PRODUCT = YES; 487 | WRAPPER_EXTENSION = app; 488 | }; 489 | name = Release; 490 | }; 491 | /* End XCBuildConfiguration section */ 492 | 493 | /* Begin XCConfigurationList section */ 494 | 73698B6713B681F400922D1A /* Build configuration list for PBXProject "SpectttatorTest" */ = { 495 | isa = XCConfigurationList; 496 | buildConfigurations = ( 497 | 73698B7E13B681F400922D1A /* Debug */, 498 | 73698B7F13B681F400922D1A /* Release */, 499 | ); 500 | defaultConfigurationIsVisible = 0; 501 | defaultConfigurationName = Release; 502 | }; 503 | 73698B9C13B6820100922D1A /* Build configuration list for PBXNativeTarget "SpectttatorTest" */ = { 504 | isa = XCConfigurationList; 505 | buildConfigurations = ( 506 | 73698B9D13B6820100922D1A /* Debug */, 507 | 73698B9E13B6820100922D1A /* Release */, 508 | ); 509 | defaultConfigurationIsVisible = 0; 510 | defaultConfigurationName = Release; 511 | }; 512 | 73C01CD513C2EC4A00A8ECDB /* Build configuration list for PBXNativeTarget "SpectttatorTest-iOS" */ = { 513 | isa = XCConfigurationList; 514 | buildConfigurations = ( 515 | 73C01CD613C2EC4A00A8ECDB /* Debug */, 516 | 73C01CD713C2EC4A00A8ECDB /* Release */, 517 | ); 518 | defaultConfigurationIsVisible = 0; 519 | defaultConfigurationName = Release; 520 | }; 521 | /* End XCConfigurationList section */ 522 | }; 523 | rootObject = 73698B6413B681F400922D1A /* Project object */; 524 | } 525 | -------------------------------------------------------------------------------- /SpectttatorTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SpectttatorTest/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kgn/Spectttator/b6154b14bab5e27cf68a228bac695b8ea8fce5cc/SpectttatorTest/Default-568h@2x.png -------------------------------------------------------------------------------- /SpectttatorTest/NSAttributedString+Hyperlink.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSAttributedString+Hyperlink.h 3 | // Spectttator 4 | // 5 | // Created by David Keegan on 6/26/11. 6 | // 7 | // From http://developer.apple.com/library/mac/#qa/qa1487/_index.html 8 | // 9 | 10 | #import 11 | 12 | @interface NSAttributedString (Hyperlink) 13 | 14 | +(id)hyperlinkFromString:(NSString*)inString withURL:(NSURL*)aURL; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /SpectttatorTest/NSAttributedString+Hyperlink.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSAttributedString+Hyperlink.m 3 | // Spectttator 4 | // 5 | // Created by David Keegan on 6/26/11. 6 | // 7 | 8 | #import "NSAttributedString+Hyperlink.h" 9 | 10 | @implementation NSAttributedString (Hyperlink) 11 | 12 | + (NSAttributedString *)hyperlinkFromString:(NSString *)inString withURL:(NSURL *)aURL{ 13 | NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString: inString]; 14 | NSRange range = NSMakeRange(0, [attrString length]); 15 | 16 | [attrString beginEditing]; 17 | [attrString addAttribute:NSLinkAttributeName value:[aURL absoluteString] range:range]; 18 | 19 | // make the text appear in blue 20 | [attrString addAttribute:NSForegroundColorAttributeName value:[NSColor blueColor] range:range]; 21 | 22 | // next make the text appear with an underline 23 | [attrString addAttribute: 24 | NSUnderlineStyleAttributeName value:@(NSSingleUnderlineStyle) range:range]; 25 | 26 | [attrString endEditing]; 27 | 28 | return attrString; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /SpectttatorTest/SpectttatorTest-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.InScopeApps.${PRODUCT_NAME:rfc1034identifier} 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 25 | LSMinimumSystemVersion 26 | ${MACOSX_DEPLOYMENT_TARGET} 27 | NSHumanReadableCopyright 28 | Copyright © 2011 David Keegan 29 | NSMainNibFile 30 | MainMenu 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /SpectttatorTest/SpectttatorTest-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'SpectttatorTest' target in the 'SpectttatorTest' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /SpectttatorTest/SpectttatorTest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kgn/Spectttator/b6154b14bab5e27cf68a228bac695b8ea8fce5cc/SpectttatorTest/SpectttatorTest.png -------------------------------------------------------------------------------- /SpectttatorTest/SpectttatorTestAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SpectttatorTestAppDelegate.h 3 | // SpectttatorTest 4 | // 5 | // Created by David Keegan on 6/25/11. 6 | // Copyright 2011 David Keegan. 7 | // 8 | 9 | #import 10 | 11 | @interface SpectttatorTestAppDelegate : NSObject 12 | 13 | @property (strong, nonatomic) IBOutlet NSWindow *window; 14 | @property (strong, nonatomic) IBOutlet NSProgressIndicator *spinner; 15 | @property (strong, nonatomic) IBOutlet NSTextField *username; 16 | @property (strong, nonatomic) IBOutlet NSTextView *shots; 17 | @property (strong, nonatomic) IBOutlet NSPopUpButton *listPopup; 18 | @property (strong, nonatomic) IBOutlet NSTextView *listShots; 19 | @property (strong, nonatomic) IBOutlet NSImageView *lastPlayerShot; 20 | @property (strong, nonatomic) IBOutlet NSImageView *lastListShot; 21 | @property (strong, nonatomic) IBOutlet NSImageView *avatar; 22 | @property BOOL userUpdating, listUpdating; 23 | 24 | - (IBAction)userChanged:(id)sender; 25 | - (IBAction)listChanged:(id)sender; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /SpectttatorTest/SpectttatorTestAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // SpectttatorTestAppDelegate.m 3 | // SpectttatorTest 4 | // 5 | // Created by David Keegan on 6/25/11. 6 | // Copyright 2011 David Keegan. 7 | // 8 | 9 | #import "SpectttatorTestAppDelegate.h" 10 | #import "NSAttributedString+Hyperlink.h" 11 | #import 12 | 13 | @implementation SpectttatorTestAppDelegate 14 | 15 | @synthesize window = _window; 16 | @synthesize spinner = _spinner; 17 | @synthesize username = _username; 18 | @synthesize shots = _shots; 19 | @synthesize listPopup = _listPopup; 20 | @synthesize listShots = _listShots; 21 | @synthesize lastPlayerShot = _lastPlayerShot; 22 | @synthesize lastListShot = _lastListShot; 23 | @synthesize avatar = _avatar; 24 | @synthesize userUpdating = _userUpdating; 25 | @synthesize listUpdating = _listUpdating; 26 | 27 | - (void)applicationDidFinishLaunching:(NSNotification *)notification{ 28 | NSString *username = @"kgn"; 29 | 30 | //populate the ui 31 | [self.username setStringValue:username]; 32 | [self userChanged:self.username]; 33 | [self listChanged:self.listPopup]; 34 | 35 | //run commands that don't appear in the ui 36 | [SPRequest shotInformationForIdentifier:199295 runOnMainThread:NO withBlock:^(SPShot *shot){ 37 | NSLog(@"Shot Information: %@", shot); 38 | [shot reboundsWithPagination:nil 39 | runOnMainThread:NO 40 | withBlock:^(NSArray *rebounds, SPPagination *pagination){ 41 | NSLog(@"Rebounds for '%@': %@", shot.title, rebounds); 42 | }]; 43 | [shot commentsWithPagination:nil 44 | runOnMainThread:NO 45 | withBlock:^(NSArray *comments, SPPagination *pagination){ 46 | NSLog(@"Comments for '%@': %@", shot.title, comments); 47 | }]; 48 | }]; 49 | 50 | [SPRequest playerInformationForUsername:username 51 | runOnMainThread:NO 52 | withBlock:^(SPPlayer *player){ 53 | NSLog(@"Player information for %@: %@", username, player); 54 | }]; 55 | 56 | [SPRequest playerFollowers:@"simplebits" 57 | withPagination:[SPPagination page:2 perPage:20] 58 | runOnMainThread:NO 59 | withBlock:^(NSArray *players, SPPagination *pagination){ 60 | NSLog(@"Players following %@: %@", username, players); 61 | }]; 62 | 63 | [SPRequest shotsForPlayerFollowing:username 64 | withPagination:nil 65 | runOnMainThread:NO 66 | withBlock:^(NSArray *shots, SPPagination *pagination){ 67 | NSLog(@"Shot by player %@ is following: %@", username, shots); 68 | }]; 69 | 70 | [SPRequest shotsForPlayerLikes:username 71 | withPagination:[SPPagination perPage:10] 72 | runOnMainThread:NO 73 | withBlock:^(NSArray *shots, SPPagination *pagination){ 74 | NSLog(@"Shot %@ likes: %@", username, shots); 75 | }]; 76 | 77 | [SPRequest playerFollowing:username 78 | withPagination:[SPPagination page:2] 79 | runOnMainThread:NO 80 | withBlock:^(NSArray *shots, SPPagination *pagination){ 81 | NSLog(@"Shot %@ likes: %@", username, shots); 82 | }]; 83 | 84 | [SPRequest playerDraftees:username 85 | withPagination:nil 86 | runOnMainThread:NO 87 | withBlock:^(NSArray *players, SPPagination *pagination){ 88 | NSLog(@"Players %@ drafted: %@", username, players); 89 | }]; 90 | } 91 | 92 | #pragma mark - 93 | #pragma mark Actions 94 | 95 | // These actions actions get data from dribbble then 96 | // update the UI on the main thread with runOnMainThread:YES 97 | 98 | - (IBAction)userChanged:(id)sender{ 99 | NSString *user = [sender stringValue]; 100 | [self.shots setString:@""]; 101 | [self.lastPlayerShot setImage:nil]; 102 | [self.avatar setImage:nil]; 103 | if(![user length]){ 104 | return; 105 | } 106 | 107 | [self.spinner startAnimation:nil]; 108 | [self.username setEnabled:NO]; 109 | self.userUpdating = YES; 110 | 111 | //get shots from player 112 | [SPRequest shotsForPlayer:user 113 | withPagination:[SPPagination perPage:SPPerPageMax] 114 | runOnMainThread:YES 115 | withBlock:^(NSArray *shots, SPPagination *pagination){ 116 | NSLog(@"Received shot data for %@", user); 117 | NSLog(@"With pagination: %@", pagination); 118 | 119 | //get the last shot uploaded by the player 120 | if([shots count]){ 121 | [shots[0] imageRunOnMainThread:YES withBlock:^(NSImage *image){ 122 | [self.lastPlayerShot setImage:image]; 123 | }]; 124 | } 125 | 126 | //get the player's avatar 127 | if([shots count]){ 128 | [[shots[0] player] avatarRunOnMainThread:YES withBlock:^(NSImage *image){ 129 | [self.avatar setImage:image]; 130 | }]; 131 | } 132 | 133 | @autoreleasepool { 134 | for(SPShot *shot in shots){ 135 | NSString *string = [NSString stringWithFormat:@"%@\n", shot.title]; 136 | NSMutableAttributedString *shotString = [NSAttributedString hyperlinkFromString:string 137 | withURL:shot.url]; 138 | [[self.shots textStorage] appendAttributedString:shotString]; 139 | } 140 | } 141 | 142 | self.userUpdating = NO; 143 | if(!self.listUpdating){ 144 | [self.spinner stopAnimation:nil]; 145 | } 146 | [self.username setEnabled:YES]; 147 | }]; 148 | } 149 | 150 | - (IBAction)listChanged:(id)sender { 151 | NSString *list = [[sender titleOfSelectedItem] lowercaseString]; 152 | [self.listShots setString:@""]; 153 | [self.lastListShot setImage:nil]; 154 | [self.spinner startAnimation:nil]; 155 | [self.listPopup setEnabled:NO]; 156 | self.listUpdating = YES; 157 | 158 | //get shots from a list 159 | [SPRequest shotsForList:list 160 | withPagination:nil 161 | runOnMainThread:YES 162 | withBlock:^(NSArray *shots, SPPagination *pagination){ 163 | NSLog(@"Received list data for %@", list); 164 | NSLog(@"With pagination: %@", pagination); 165 | 166 | //get the last shot uploaded to the list 167 | if([shots count]){ 168 | [shots[0] imageRunOnMainThread:NO withBlock:^(NSImage *image){ 169 | [self.lastListShot setImage:image]; 170 | }]; 171 | } 172 | 173 | @autoreleasepool { 174 | for(SPShot *shot in shots){ 175 | NSString *string = [NSString stringWithFormat:@"%@\n", shot.title]; 176 | NSMutableAttributedString *shotString = [NSAttributedString hyperlinkFromString:string 177 | withURL:shot.url]; 178 | [[self.listShots textStorage] appendAttributedString:shotString]; 179 | } 180 | } 181 | 182 | self.listUpdating = NO; 183 | if(!self.userUpdating){ 184 | [self.spinner stopAnimation:nil]; 185 | } 186 | [self.listPopup setEnabled:YES]; 187 | }]; 188 | } 189 | 190 | @end 191 | -------------------------------------------------------------------------------- /SpectttatorTest/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SpectttatorTest 4 | // 5 | // Created by David Keegan on 6/25/11. 6 | // Copyright 2011 David Keegan. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, char *argv[]){ 12 | return NSApplicationMain(argc, (const char **)argv); 13 | } 14 | --------------------------------------------------------------------------------