Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial release.

  • Loading branch information...
commit c2fc35121235106eb075e043ee2fdba6a914da5a 0 parents
@iKenndac iKenndac authored
Showing with 22,218 additions and 0 deletions.
  1. +18 −0 .gitignore
  2. +6 −0 .gitmodules
  3. +509 −0 CocoaLibSpotify.xcodeproj/project.pbxproj
  4. +28 −0 CocoaLibSpotify/AppledocSettings.plist
  5. +30 −0 CocoaLibSpotify/CocoaLibSpotify-Info.plist
  6. +7 −0 CocoaLibSpotify/CocoaLibSpotify-Prefix.pch
  7. +52 −0 CocoaLibSpotify/CocoaLibSpotify.h
  8. +149 −0 CocoaLibSpotify/SPAlbum.h
  9. +189 −0 CocoaLibSpotify/SPAlbum.m
  10. +131 −0 CocoaLibSpotify/SPAlbumBrowse.h
  11. +161 −0 CocoaLibSpotify/SPAlbumBrowse.m
  12. +103 −0 CocoaLibSpotify/SPArtist.h
  13. +123 −0 CocoaLibSpotify/SPArtist.m
  14. +139 −0 CocoaLibSpotify/SPArtistBrowse.h
  15. +201 −0 CocoaLibSpotify/SPArtistBrowse.m
  16. +48 −0 CocoaLibSpotify/SPErrorExtensions.h
  17. +63 −0 CocoaLibSpotify/SPErrorExtensions.m
  18. +124 −0 CocoaLibSpotify/SPImage.h
  19. +207 −0 CocoaLibSpotify/SPImage.m
  20. +290 −0 CocoaLibSpotify/SPPlaylist.h
  21. +621 −0 CocoaLibSpotify/SPPlaylist.m
  22. +121 −0 CocoaLibSpotify/SPPlaylistContainer.h
  23. +244 −0 CocoaLibSpotify/SPPlaylistContainer.m
  24. +40 −0 CocoaLibSpotify/SPPlaylistContainerInternal.h
  25. +96 −0 CocoaLibSpotify/SPPlaylistFolder.h
  26. +425 −0 CocoaLibSpotify/SPPlaylistFolder.m
  27. +49 −0 CocoaLibSpotify/SPPlaylistFolderInternal.h
  28. +141 −0 CocoaLibSpotify/SPPostTracksToInboxOperation.h
  29. +148 −0 CocoaLibSpotify/SPPostTracksToInboxOperation.m
  30. +258 −0 CocoaLibSpotify/SPSearch.h
  31. +356 −0 CocoaLibSpotify/SPSearch.m
  32. +547 −0 CocoaLibSpotify/SPSession.h
  33. +950 −0 CocoaLibSpotify/SPSession.m
  34. +154 −0 CocoaLibSpotify/SPToplist.h
  35. +303 −0 CocoaLibSpotify/SPToplist.m
  36. +167 −0 CocoaLibSpotify/SPTrack.h
  37. +224 −0 CocoaLibSpotify/SPTrack.m
  38. +73 −0 CocoaLibSpotify/SPURLExtensions.h
  39. +85 −0 CocoaLibSpotify/SPURLExtensions.m
  40. +151 −0 CocoaLibSpotify/SPUser.h
  41. +151 −0 CocoaLibSpotify/SPUser.m
  42. +2 −0  CocoaLibSpotify/en.lproj/InfoPlist.strings
  43. +24 −0 LICENSE.txt
  44. +23 −0 README.txt
  45. +446 −0 Sample Projects/Guess The Intro/Guess The Intro.xcodeproj/project.pbxproj
  46. +1 −0  Sample Projects/Guess The Intro/Guess The Intro/CoCA
  47. +44 −0 Sample Projects/Guess The Intro/Guess The Intro/Guess The Intro-Info.plist
  48. +7 −0 Sample Projects/Guess The Intro/Guess The Intro/Guess The Intro-Prefix.pch
  49. +149 −0 Sample Projects/Guess The Intro/Guess The Intro/Guess_The_IntroAppDelegate.h
  50. +570 −0 Sample Projects/Guess The Intro/Guess The Intro/Guess_The_IntroAppDelegate.m
  51. +39 −0 Sample Projects/Guess The Intro/Guess The Intro/SPArrayExtensions.h
  52. +52 −0 Sample Projects/Guess The Intro/Guess The Intro/SPArrayExtensions.m
  53. +94 −0 Sample Projects/Guess The Intro/Guess The Intro/SPCircularBuffer.h
  54. +160 −0 Sample Projects/Guess The Intro/Guess The Intro/SPCircularBuffer.m
  55. +106 −0 Sample Projects/Guess The Intro/Guess The Intro/SPPlaybackManager.h
  56. +288 −0 Sample Projects/Guess The Intro/Guess The Intro/SPPlaybackManager.m
  57. +29 −0 Sample Projects/Guess The Intro/Guess The Intro/en.lproj/Credits.rtf
  58. +2 −0  Sample Projects/Guess The Intro/Guess The Intro/en.lproj/InfoPlist.strings
  59. +6,156 −0 Sample Projects/Guess The Intro/Guess The Intro/en.lproj/MainMenu.xib
  60. +14 −0 Sample Projects/Guess The Intro/Guess The Intro/main.m
  61. +24 −0 Sample Projects/Sample Projects README.txt
  62. +457 −0 Sample Projects/SimplePlayer/SimplePlayer.xcodeproj/project.pbxproj
  63. +1 −0  Sample Projects/SimplePlayer/SimplePlayer/CoCA
  64. +94 −0 Sample Projects/SimplePlayer/SimplePlayer/SPCircularBuffer.h
  65. +160 −0 Sample Projects/SimplePlayer/SimplePlayer/SPCircularBuffer.m
  66. +106 −0 Sample Projects/SimplePlayer/SimplePlayer/SPPlaybackManager.h
  67. +288 −0 Sample Projects/SimplePlayer/SimplePlayer/SPPlaybackManager.m
  68. +44 −0 Sample Projects/SimplePlayer/SimplePlayer/SimplePlayer-Info.plist
  69. +7 −0 Sample Projects/SimplePlayer/SimplePlayer/SimplePlayer-Prefix.pch
  70. +67 −0 Sample Projects/SimplePlayer/SimplePlayer/SimplePlayerAppDelegate.h
  71. +190 −0 Sample Projects/SimplePlayer/SimplePlayer/SimplePlayerAppDelegate.m
  72. +29 −0 Sample Projects/SimplePlayer/SimplePlayer/en.lproj/Credits.rtf
  73. +2 −0  Sample Projects/SimplePlayer/SimplePlayer/en.lproj/InfoPlist.strings
  74. +4,647 −0 Sample Projects/SimplePlayer/SimplePlayer/en.lproj/MainMenu.xib
  75. +14 −0 Sample Projects/SimplePlayer/SimplePlayer/main.m
18 .gitignore
@@ -0,0 +1,18 @@
+.DS_Store
+obf-*.cpp
+obf-*.h
+*.swp
+*.Po
+*.orig
+*~
+*.pyc
+/config.user.h
+Thumbs.db
+/build/
+xcuserdata
+*.xcworkspace
+*.mode1v3
+*.pbxuser
+libspotify-0.0.*
+appkey.c
+
6 .gitmodules
@@ -0,0 +1,6 @@
+[submodule "Sample Projects/Guess The Intro/Guess The Intro/CoCA"]
+ path = Sample Projects/Guess The Intro/Guess The Intro/CoCA
+ url = git://github.com/iKenndac/CoCA.git
+[submodule "Sample Projects/SimplePlayer/SimplePlayer/CoCA"]
+ path = Sample Projects/SimplePlayer/SimplePlayer/CoCA
+ url = git://github.com/iKenndac/CoCA.git
509 CocoaLibSpotify.xcodeproj/project.pbxproj
@@ -0,0 +1,509 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 5004B3C7133E39460098C7B4 /* SPURLExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 5004B3C5133E39460098C7B4 /* SPURLExtensions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5004B3C8133E39460098C7B4 /* SPURLExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 5004B3C6133E39460098C7B4 /* SPURLExtensions.m */; };
+ 503D631013258041007772CA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5028EC8B13092B7C00BF0B22 /* Cocoa.framework */; };
+ 503D631B13258062007772CA /* SPErrorExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 5028ECBE130947B800BF0B22 /* SPErrorExtensions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 503D631C13258068007772CA /* SPErrorExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 5028ECBF130947B800BF0B22 /* SPErrorExtensions.m */; };
+ 503D631D1325806E007772CA /* SPUser.h in Headers */ = {isa = PBXBuildFile; fileRef = 50AB044C1312D00400357CD2 /* SPUser.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 503D631E1325806E007772CA /* SPUser.m in Sources */ = {isa = PBXBuildFile; fileRef = 50AB044D1312D00900357CD2 /* SPUser.m */; };
+ 503D631F1325806E007772CA /* SPSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 5028ECB6130937B200BF0B22 /* SPSession.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 503D63201325806E007772CA /* SPSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 5028ECB7130937B200BF0B22 /* SPSession.m */; };
+ 503D63211325806E007772CA /* SPPlaylist.h in Headers */ = {isa = PBXBuildFile; fileRef = 5028ECB913093B5800BF0B22 /* SPPlaylist.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 503D63221325806E007772CA /* SPPlaylist.m in Sources */ = {isa = PBXBuildFile; fileRef = 5028ECBA13093B5800BF0B22 /* SPPlaylist.m */; };
+ 503D63231325806E007772CA /* SPPlaylistFolder.h in Headers */ = {isa = PBXBuildFile; fileRef = 503D56AF131086DB00894014 /* SPPlaylistFolder.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 503D63251325806E007772CA /* SPTrack.h in Headers */ = {isa = PBXBuildFile; fileRef = 50D1B50E130FC21A00F4EB93 /* SPTrack.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 503D63261325806E007772CA /* SPTrack.m in Sources */ = {isa = PBXBuildFile; fileRef = 50D1B50F130FC21A00F4EB93 /* SPTrack.m */; };
+ 503D63271325806E007772CA /* SPPlaylistContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 503D56AB13107D4500894014 /* SPPlaylistContainer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 503D63281325806E007772CA /* SPPlaylistContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 503D56AC13107D4500894014 /* SPPlaylistContainer.m */; };
+ 503D63291325807D007772CA /* SPAlbum.h in Headers */ = {isa = PBXBuildFile; fileRef = 50CE7626131161BD0047543F /* SPAlbum.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 503D632A1325807D007772CA /* SPArtist.h in Headers */ = {isa = PBXBuildFile; fileRef = 50CE762A13117F880047543F /* SPArtist.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 503D632B1325807D007772CA /* SPImage.h in Headers */ = {isa = PBXBuildFile; fileRef = 50CE7623131149AD0047543F /* SPImage.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 503D632C13258084007772CA /* SPAlbum.m in Sources */ = {isa = PBXBuildFile; fileRef = 50CE7627131161BE0047543F /* SPAlbum.m */; };
+ 503D632D13258084007772CA /* SPArtist.m in Sources */ = {isa = PBXBuildFile; fileRef = 50CE762B13117F890047543F /* SPArtist.m */; };
+ 503D632E13258084007772CA /* SPImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 50CE7624131149AD0047543F /* SPImage.m */; };
+ 504379031370564D001DD605 /* SPPlaylistContainerInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 504379011370564C001DD605 /* SPPlaylistContainerInternal.h */; };
+ 50437908137057D9001DD605 /* SPPlaylistFolderInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 50437906137057D7001DD605 /* SPPlaylistFolderInternal.h */; };
+ 506359451369833500B90B67 /* SPToplist.h in Headers */ = {isa = PBXBuildFile; fileRef = 506359431369833500B90B67 /* SPToplist.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 506359461369833500B90B67 /* SPToplist.m in Sources */ = {isa = PBXBuildFile; fileRef = 506359441369833500B90B67 /* SPToplist.m */; };
+ 5099D84C136436D3005C7CDC /* SPPostTracksToInboxOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 5099D84A136436D3005C7CDC /* SPPostTracksToInboxOperation.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5099D84D136436D3005C7CDC /* SPPostTracksToInboxOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 5099D84B136436D3005C7CDC /* SPPostTracksToInboxOperation.m */; };
+ 5099D87913644E71005C7CDC /* SPArtistBrowse.h in Headers */ = {isa = PBXBuildFile; fileRef = 5099D87713644E71005C7CDC /* SPArtistBrowse.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5099D87A13644E71005C7CDC /* SPArtistBrowse.m in Sources */ = {isa = PBXBuildFile; fileRef = 5099D87813644E71005C7CDC /* SPArtistBrowse.m */; };
+ 5099D88413647D4F005C7CDC /* SPAlbumBrowse.h in Headers */ = {isa = PBXBuildFile; fileRef = 5099D88213647D4F005C7CDC /* SPAlbumBrowse.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 5099D88513647D4F005C7CDC /* SPAlbumBrowse.m in Sources */ = {isa = PBXBuildFile; fileRef = 5099D88313647D4F005C7CDC /* SPAlbumBrowse.m */; };
+ 50B7850E136EC15400D51152 /* SPPlaylistFolder.m in Sources */ = {isa = PBXBuildFile; fileRef = 503D56B0131086DB00894014 /* SPPlaylistFolder.m */; };
+ 50D9158F1382EBB3008FF7C1 /* libspotify.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50D9158E1382EBB3008FF7C1 /* libspotify.framework */; };
+ 50D915901382EBCD008FF7C1 /* libspotify.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 50D9158E1382EBB3008FF7C1 /* libspotify.framework */; };
+ 50D9864713605B8000DA5542 /* SPSearch.h in Headers */ = {isa = PBXBuildFile; fileRef = 50D9864513605B8000DA5542 /* SPSearch.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 50D9864813605B8000DA5542 /* SPSearch.m in Sources */ = {isa = PBXBuildFile; fileRef = 50D9864613605B8000DA5542 /* SPSearch.m */; };
+ 50E65EB11325862700E66AFA /* CocoaLibSpotify.h in Headers */ = {isa = PBXBuildFile; fileRef = 50E65EB01325862700E66AFA /* CocoaLibSpotify.h */; settings = {ATTRIBUTES = (Public, ); }; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 503D63311325811E007772CA /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ 50D915901382EBCD008FF7C1 /* libspotify.framework in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 5004B3C5133E39460098C7B4 /* SPURLExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPURLExtensions.h; sourceTree = "<group>"; };
+ 5004B3C6133E39460098C7B4 /* SPURLExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPURLExtensions.m; sourceTree = "<group>"; };
+ 5028EC8B13092B7C00BF0B22 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
+ 5028EC8E13092B7C00BF0B22 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
+ 5028EC8F13092B7C00BF0B22 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
+ 5028EC9013092B7C00BF0B22 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+ 5028EC9313092B7C00BF0B22 /* CocoaLibSpotify-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CocoaLibSpotify-Info.plist"; sourceTree = "<group>"; };
+ 5028EC9513092B7C00BF0B22 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ 5028EC9713092B7C00BF0B22 /* CocoaLibSpotify-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CocoaLibSpotify-Prefix.pch"; sourceTree = "<group>"; };
+ 5028ECB6130937B200BF0B22 /* SPSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = SPSession.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
+ 5028ECB7130937B200BF0B22 /* SPSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSession.m; sourceTree = "<group>"; };
+ 5028ECB913093B5800BF0B22 /* SPPlaylist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = SPPlaylist.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
+ 5028ECBA13093B5800BF0B22 /* SPPlaylist.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPPlaylist.m; sourceTree = "<group>"; };
+ 5028ECBE130947B800BF0B22 /* SPErrorExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = SPErrorExtensions.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
+ 5028ECBF130947B800BF0B22 /* SPErrorExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPErrorExtensions.m; sourceTree = "<group>"; };
+ 503D56AB13107D4500894014 /* SPPlaylistContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = SPPlaylistContainer.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
+ 503D56AC13107D4500894014 /* SPPlaylistContainer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPPlaylistContainer.m; sourceTree = "<group>"; };
+ 503D56AF131086DB00894014 /* SPPlaylistFolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPPlaylistFolder.h; sourceTree = "<group>"; };
+ 503D56B0131086DB00894014 /* SPPlaylistFolder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPPlaylistFolder.m; sourceTree = "<group>"; };
+ 503D630F13258041007772CA /* CocoaLibSpotify.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CocoaLibSpotify.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 504379011370564C001DD605 /* SPPlaylistContainerInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPPlaylistContainerInternal.h; sourceTree = "<group>"; };
+ 50437906137057D7001DD605 /* SPPlaylistFolderInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPPlaylistFolderInternal.h; sourceTree = "<group>"; };
+ 506359431369833500B90B67 /* SPToplist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPToplist.h; sourceTree = "<group>"; };
+ 506359441369833500B90B67 /* SPToplist.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPToplist.m; sourceTree = "<group>"; };
+ 5099D84A136436D3005C7CDC /* SPPostTracksToInboxOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPPostTracksToInboxOperation.h; sourceTree = "<group>"; };
+ 5099D84B136436D3005C7CDC /* SPPostTracksToInboxOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPPostTracksToInboxOperation.m; sourceTree = "<group>"; };
+ 5099D87713644E71005C7CDC /* SPArtistBrowse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPArtistBrowse.h; sourceTree = "<group>"; };
+ 5099D87813644E71005C7CDC /* SPArtistBrowse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPArtistBrowse.m; sourceTree = "<group>"; };
+ 5099D88213647D4F005C7CDC /* SPAlbumBrowse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPAlbumBrowse.h; sourceTree = "<group>"; };
+ 5099D88313647D4F005C7CDC /* SPAlbumBrowse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPAlbumBrowse.m; sourceTree = "<group>"; };
+ 50AB044C1312D00400357CD2 /* SPUser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = SPUser.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
+ 50AB044D1312D00900357CD2 /* SPUser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPUser.m; sourceTree = "<group>"; };
+ 50CE7623131149AD0047543F /* SPImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = SPImage.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
+ 50CE7624131149AD0047543F /* SPImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPImage.m; sourceTree = "<group>"; };
+ 50CE7626131161BD0047543F /* SPAlbum.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = SPAlbum.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
+ 50CE7627131161BE0047543F /* SPAlbum.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPAlbum.m; sourceTree = "<group>"; };
+ 50CE762A13117F880047543F /* SPArtist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = SPArtist.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
+ 50CE762B13117F890047543F /* SPArtist.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPArtist.m; sourceTree = "<group>"; };
+ 50D1B50E130FC21A00F4EB93 /* SPTrack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = SPTrack.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
+ 50D1B50F130FC21A00F4EB93 /* SPTrack.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTrack.m; sourceTree = "<group>"; };
+ 50D9158E1382EBB3008FF7C1 /* libspotify.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = libspotify.framework; path = "libspotify-0.0.8-Darwin/libspotify.framework"; sourceTree = "<group>"; };
+ 50D9864513605B8000DA5542 /* SPSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSearch.h; sourceTree = "<group>"; };
+ 50D9864613605B8000DA5542 /* SPSearch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSearch.m; sourceTree = "<group>"; };
+ 50E65EB01325862700E66AFA /* CocoaLibSpotify.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CocoaLibSpotify.h; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 503D630B13258041007772CA /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 503D631013258041007772CA /* Cocoa.framework in Frameworks */,
+ 50D9158F1382EBB3008FF7C1 /* libspotify.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 5028EC7C13092B7B00BF0B22 = {
+ isa = PBXGroup;
+ children = (
+ 5028EC9113092B7C00BF0B22 /* CocoaLibSpotify */,
+ 5028EC8A13092B7B00BF0B22 /* Frameworks */,
+ 5028EC8813092B7B00BF0B22 /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ 5028EC8813092B7B00BF0B22 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 503D630F13258041007772CA /* CocoaLibSpotify.framework */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 5028EC8A13092B7B00BF0B22 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 50D9158E1382EBB3008FF7C1 /* libspotify.framework */,
+ 5028EC8B13092B7C00BF0B22 /* Cocoa.framework */,
+ 5028EC8D13092B7C00BF0B22 /* Other Frameworks */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ 5028EC8D13092B7C00BF0B22 /* Other Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 5028EC8E13092B7C00BF0B22 /* AppKit.framework */,
+ 5028EC8F13092B7C00BF0B22 /* CoreData.framework */,
+ 5028EC9013092B7C00BF0B22 /* Foundation.framework */,
+ );
+ name = "Other Frameworks";
+ sourceTree = "<group>";
+ };
+ 5028EC9113092B7C00BF0B22 /* CocoaLibSpotify */ = {
+ isa = PBXGroup;
+ children = (
+ 50E65EB01325862700E66AFA /* CocoaLibSpotify.h */,
+ 5028ECBC1309479600BF0B22 /* Extensions */,
+ 5028ECB5130931D000BF0B22 /* Model */,
+ 5028EC9213092B7C00BF0B22 /* Supporting Files */,
+ );
+ path = CocoaLibSpotify;
+ sourceTree = "<group>";
+ };
+ 5028EC9213092B7C00BF0B22 /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ 5028EC9313092B7C00BF0B22 /* CocoaLibSpotify-Info.plist */,
+ 5028EC9413092B7C00BF0B22 /* InfoPlist.strings */,
+ 5028EC9713092B7C00BF0B22 /* CocoaLibSpotify-Prefix.pch */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+ 5028ECB5130931D000BF0B22 /* Model */ = {
+ isa = PBXGroup;
+ children = (
+ 50CE762D13117F940047543F /* Metadata */,
+ 50AB044C1312D00400357CD2 /* SPUser.h */,
+ 50AB044D1312D00900357CD2 /* SPUser.m */,
+ 5028ECB6130937B200BF0B22 /* SPSession.h */,
+ 5028ECB7130937B200BF0B22 /* SPSession.m */,
+ 5028ECB913093B5800BF0B22 /* SPPlaylist.h */,
+ 5028ECBA13093B5800BF0B22 /* SPPlaylist.m */,
+ 503D56AF131086DB00894014 /* SPPlaylistFolder.h */,
+ 50437906137057D7001DD605 /* SPPlaylistFolderInternal.h */,
+ 503D56B0131086DB00894014 /* SPPlaylistFolder.m */,
+ 50D1B50E130FC21A00F4EB93 /* SPTrack.h */,
+ 50D1B50F130FC21A00F4EB93 /* SPTrack.m */,
+ 503D56AB13107D4500894014 /* SPPlaylistContainer.h */,
+ 504379011370564C001DD605 /* SPPlaylistContainerInternal.h */,
+ 503D56AC13107D4500894014 /* SPPlaylistContainer.m */,
+ 50D9864513605B8000DA5542 /* SPSearch.h */,
+ 50D9864613605B8000DA5542 /* SPSearch.m */,
+ 5099D84A136436D3005C7CDC /* SPPostTracksToInboxOperation.h */,
+ 5099D84B136436D3005C7CDC /* SPPostTracksToInboxOperation.m */,
+ );
+ name = Model;
+ sourceTree = "<group>";
+ };
+ 5028ECBC1309479600BF0B22 /* Extensions */ = {
+ isa = PBXGroup;
+ children = (
+ 5028ECBE130947B800BF0B22 /* SPErrorExtensions.h */,
+ 5028ECBF130947B800BF0B22 /* SPErrorExtensions.m */,
+ 5004B3C5133E39460098C7B4 /* SPURLExtensions.h */,
+ 5004B3C6133E39460098C7B4 /* SPURLExtensions.m */,
+ );
+ name = Extensions;
+ sourceTree = "<group>";
+ };
+ 50CE762D13117F940047543F /* Metadata */ = {
+ isa = PBXGroup;
+ children = (
+ 50CE7626131161BD0047543F /* SPAlbum.h */,
+ 50CE7627131161BE0047543F /* SPAlbum.m */,
+ 50CE762A13117F880047543F /* SPArtist.h */,
+ 50CE762B13117F890047543F /* SPArtist.m */,
+ 50CE7623131149AD0047543F /* SPImage.h */,
+ 50CE7624131149AD0047543F /* SPImage.m */,
+ 5099D87713644E71005C7CDC /* SPArtistBrowse.h */,
+ 5099D87813644E71005C7CDC /* SPArtistBrowse.m */,
+ 5099D88213647D4F005C7CDC /* SPAlbumBrowse.h */,
+ 5099D88313647D4F005C7CDC /* SPAlbumBrowse.m */,
+ 506359431369833500B90B67 /* SPToplist.h */,
+ 506359441369833500B90B67 /* SPToplist.m */,
+ );
+ name = Metadata;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ 503D630C13258041007772CA /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 503D631B13258062007772CA /* SPErrorExtensions.h in Headers */,
+ 503D631D1325806E007772CA /* SPUser.h in Headers */,
+ 503D631F1325806E007772CA /* SPSession.h in Headers */,
+ 503D63211325806E007772CA /* SPPlaylist.h in Headers */,
+ 503D63231325806E007772CA /* SPPlaylistFolder.h in Headers */,
+ 503D63251325806E007772CA /* SPTrack.h in Headers */,
+ 503D63271325806E007772CA /* SPPlaylistContainer.h in Headers */,
+ 503D63291325807D007772CA /* SPAlbum.h in Headers */,
+ 503D632A1325807D007772CA /* SPArtist.h in Headers */,
+ 503D632B1325807D007772CA /* SPImage.h in Headers */,
+ 50E65EB11325862700E66AFA /* CocoaLibSpotify.h in Headers */,
+ 5004B3C7133E39460098C7B4 /* SPURLExtensions.h in Headers */,
+ 50D9864713605B8000DA5542 /* SPSearch.h in Headers */,
+ 5099D84C136436D3005C7CDC /* SPPostTracksToInboxOperation.h in Headers */,
+ 5099D87913644E71005C7CDC /* SPArtistBrowse.h in Headers */,
+ 5099D88413647D4F005C7CDC /* SPAlbumBrowse.h in Headers */,
+ 506359451369833500B90B67 /* SPToplist.h in Headers */,
+ 504379031370564D001DD605 /* SPPlaylistContainerInternal.h in Headers */,
+ 50437908137057D9001DD605 /* SPPlaylistFolderInternal.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+ 503D630E13258041007772CA /* CocoaLibSpotify */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 503D631813258041007772CA /* Build configuration list for PBXNativeTarget "CocoaLibSpotify" */;
+ buildPhases = (
+ 50D9158C1382E8D5008FF7C1 /* Run Script (Download libspotify if needed) */,
+ 503D630A13258041007772CA /* Sources */,
+ 503D630C13258041007772CA /* Headers */,
+ 503D630D13258041007772CA /* Resources */,
+ 503D630B13258041007772CA /* Frameworks */,
+ 503D63311325811E007772CA /* CopyFiles */,
+ 50E65EC31325915100E66AFA /* Run Script (Set up symbolic links) */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = CocoaLibSpotify;
+ productName = CocoaLibSpotify;
+ productReference = 503D630F13258041007772CA /* CocoaLibSpotify.framework */;
+ productType = "com.apple.product-type.framework";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 5028EC7E13092B7B00BF0B22 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0410;
+ ORGANIZATIONNAME = Spotify;
+ };
+ buildConfigurationList = 5028EC8113092B7B00BF0B22 /* Build configuration list for PBXProject "CocoaLibSpotify" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ );
+ mainGroup = 5028EC7C13092B7B00BF0B22;
+ productRefGroup = 5028EC8813092B7B00BF0B22 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 503D630E13258041007772CA /* CocoaLibSpotify */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 503D630D13258041007772CA /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 50D9158C1382E8D5008FF7C1 /* Run Script (Download libspotify if needed) */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script (Download libspotify if needed)";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /usr/bin/python;
+ shellScript = "import urllib\nimport zipfile\nimport os\nimport sys\nimport commands\n\nlibspotifyFileName = \"libspotify-0.0.8-Darwin.zip\"\nlibspotifyRemoteLocation = \"http://developer.spotify.com/download/libspotify/\"\nprojectDir = os.environ.get('PROJECT_DIR')\nlibspotifyDirectoryDir = os.path.join(projectDir, \"libspotify-0.0.8-Darwin\")\nlibspotifyZipDir = os.path.join(projectDir, libspotifyFileName)\n\nif (os.path.exists(libspotifyDirectoryDir)):\n print \"LibSpotify is present, no download needed.\"\n sys.exit(0)\n\nprint \"LibSpotify not present, downloading...\"\n\ntry:\n urllib.urlretrieve(libspotifyRemoteLocation + libspotifyFileName, libspotifyZipDir)\nexcept OSError:\n print \"Could not download \" + libspotifyRemoteLocation + libspotifyFileName + \".\"\n sys.exit(1)\n\nunzipCommand = 'unzip -q ' + libspotifyZipDir + ' -d \"' + projectDir + '\"'\nunzipResult = commands.getstatusoutput(unzipCommand)\n\nif (unzipResult[0] != 0):\n print \"Could not unzip \" + libspotifyFileName + \".\"\n sys.exit(1)\n\ntry:\n os.remove(libspotifyZipDir)\nexcept OSError:\n print \"Could not remove downloaded zip file.\"\n\nprint \"Complete.\"";
+ };
+ 50E65EC31325915100E66AFA /* Run Script (Set up symbolic links) */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script (Set up symbolic links)";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "FRAMEWORK_DIR=\"$BUILT_PRODUCTS_DIR/CocoaLibSpotify.framework/Versions\"\nFRAMEWORKS_LINK_LOCATION=\"$FRAMEWORK_DIR/Frameworks\"\n\nif ( ! [ -e \"$FRAMEWORKS_LINK_LOCATION\" ]); then\ncd \"$FRAMEWORK_DIR\"\nln -s \"Current/Frameworks\" \"Frameworks\"\nfi";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 503D630A13258041007772CA /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 503D631C13258068007772CA /* SPErrorExtensions.m in Sources */,
+ 503D631E1325806E007772CA /* SPUser.m in Sources */,
+ 503D63201325806E007772CA /* SPSession.m in Sources */,
+ 503D63221325806E007772CA /* SPPlaylist.m in Sources */,
+ 503D63261325806E007772CA /* SPTrack.m in Sources */,
+ 503D63281325806E007772CA /* SPPlaylistContainer.m in Sources */,
+ 503D632C13258084007772CA /* SPAlbum.m in Sources */,
+ 503D632D13258084007772CA /* SPArtist.m in Sources */,
+ 503D632E13258084007772CA /* SPImage.m in Sources */,
+ 5004B3C8133E39460098C7B4 /* SPURLExtensions.m in Sources */,
+ 50D9864813605B8000DA5542 /* SPSearch.m in Sources */,
+ 5099D84D136436D3005C7CDC /* SPPostTracksToInboxOperation.m in Sources */,
+ 5099D87A13644E71005C7CDC /* SPArtistBrowse.m in Sources */,
+ 5099D88513647D4F005C7CDC /* SPAlbumBrowse.m in Sources */,
+ 506359461369833500B90B67 /* SPToplist.m in Sources */,
+ 50B7850E136EC15400D51152 /* SPPlaylistFolder.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 5028EC9413092B7C00BF0B22 /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 5028EC9513092B7C00BF0B22 /* en */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 5028ECA313092B7C00BF0B22 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_ENABLE_OBJC_GC = supported;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = DEBUG;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ };
+ name = Debug;
+ };
+ 5028ECA413092B7C00BF0B22 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_ENABLE_OBJC_GC = supported;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
+ SDKROOT = macosx;
+ };
+ name = Release;
+ };
+ 503D631913258041007772CA /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(SRCROOT)\"/**",
+ );
+ FRAMEWORK_VERSION = A;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "CocoaLibSpotify/CocoaLibSpotify-Prefix.pch";
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ INFOPLIST_FILE = "CocoaLibSpotify/CocoaLibSpotify-Info.plist";
+ INSTALL_PATH = "@rpath";
+ OTHER_LDFLAGS = (
+ "-sub_umbrella",
+ libspotify,
+ );
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = framework;
+ };
+ name = Debug;
+ };
+ 503D631A13258041007772CA /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(SRCROOT)\"/**",
+ );
+ FRAMEWORK_VERSION = A;
+ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "CocoaLibSpotify/CocoaLibSpotify-Prefix.pch";
+ INFOPLIST_FILE = "CocoaLibSpotify/CocoaLibSpotify-Info.plist";
+ INSTALL_PATH = "@rpath";
+ OTHER_LDFLAGS = (
+ "-sub_umbrella",
+ libspotify,
+ );
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = framework;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 5028EC8113092B7B00BF0B22 /* Build configuration list for PBXProject "CocoaLibSpotify" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 5028ECA313092B7C00BF0B22 /* Debug */,
+ 5028ECA413092B7C00BF0B22 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 503D631813258041007772CA /* Build configuration list for PBXNativeTarget "CocoaLibSpotify" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 503D631913258041007772CA /* Debug */,
+ 503D631A13258041007772CA /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 5028EC7E13092B7B00BF0B22 /* Project object */;
+}
28 CocoaLibSpotify/AppledocSettings.plist
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>--company-id</key>
+ <string>com.spotify</string>
+ <key>--ignore</key>
+ <array>
+ <string>ThirdParty</string>
+ <string>Libraries</string>
+ <string>Frameworks</string>
+ <string>Testing</string>
+ <string>libspotify-0.0.8-Darwin</string>
+ </array>
+ <key>--logformat</key>
+ <integer>1</integer>
+ <key>--print-settings</key>
+ <true/>
+ <key>--project-company</key>
+ <string>Spotify AB</string>
+ <key>--project-name</key>
+ <string>CocoaLibSpotify</string>
+ <key>--repeat-first-par</key>
+ <false/>
+ <key>--verbose</key>
+ <integer>3</integer>
+</dict>
+</plist>
30 CocoaLibSpotify/CocoaLibSpotify-Info.plist
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.spotify.${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>Copyright © 2011 Spotify. All rights reserved.</string>
+ <key>NSPrincipalClass</key>
+ <string></string>
+</dict>
+</plist>
7 CocoaLibSpotify/CocoaLibSpotify-Prefix.pch
@@ -0,0 +1,7 @@
+//
+// Prefix header for all source files of the 'CocoaLibSpotify' target in the 'CocoaLibSpotify' project
+//
+
+#ifdef __OBJC__
+ #import <Foundation/Foundation.h>
+#endif
52 CocoaLibSpotify/CocoaLibSpotify.h
@@ -0,0 +1,52 @@
+//
+// CocoaLibSpotify.h
+// CocoaLibSpotify
+//
+// Created by Daniel Kennett on 3/7/11.
+/*
+Copyright (c) 2011, Spotify AB
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Spotify AB nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL SPOTIFY AB BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#import <Foundation/Foundation.h>
+
+#import <CocoaLibSpotify/SPErrorExtensions.h>
+#import <CocoaLibSpotify/SPURLExtensions.h>
+#import <CocoaLibSpotify/SPAlbum.h>
+#import <CocoaLibSpotify/SPArtist.h>
+#import <CocoaLibSpotify/SPImage.h>
+#import <CocoaLibSpotify/SPUser.h>
+#import <CocoaLibSpotify/SPSession.h>
+#import <CocoaLibSpotify/SPPlaylist.h>
+#import <CocoaLibSpotify/SPPlaylistFolder.h>
+#import <CocoaLibSpotify/SPTrack.h>
+#import <CocoaLibSpotify/SPPlaylistContainer.h>
+#import <CocoaLibSpotify/SPSearch.h>
+#import <CocoaLibSpotify/SPPostTracksToInboxOperation.h>
+#import <CocoaLibSpotify/SPArtistBrowse.h>
+#import <CocoaLibSpotify/SPAlbumBrowse.h>
+#import <CocoaLibSpotify/SPToplist.h>
+
+#import <libspotify/api.h>
149 CocoaLibSpotify/SPAlbum.h
@@ -0,0 +1,149 @@
+//
+// SPAlbum.h
+// CocoaLibSpotify
+//
+// Created by Daniel Kennett on 2/20/11.
+/*
+Copyright (c) 2011, Spotify AB
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Spotify AB nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL SPOTIFY AB BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/** Represents an album on Spotify.
+
+ SPAlbum is roughly analogous to the sp_album struct in the C LibSpotify API.
+ */
+
+#import <Foundation/Foundation.h>
+#import <libspotify/api.h>
+
+@class SPSession;
+@class SPImage;
+@class SPArtist;
+
+@interface SPAlbum : NSObject {
+ @private
+ sp_album *album;
+ SPSession *session;
+ SPImage *cover;
+ SPArtist *artist;
+}
+
+///----------------------------
+/// @name Creating and Initializing Albums
+///----------------------------
+
+/** Creates an SPAlbum from the given opaque sp_album struct.
+
+ This convenience method creates an SPAlbum object if one doesn't exist, or
+ returns a cached SPAlbum if one already exists for the given struct.
+
+ @param anAlbum The sp_album struct to create an SPAlbum for.
+ @param aSession The SPSession the album should exist in.
+ @return Returns the created SPAlbum object.
+ */
++(SPAlbum *)albumWithAlbumStruct:(sp_album *)anAlbum inSession:(SPSession *)aSession;
+
+/** Creates an SPAlbum from the given Spotify album URL.
+
+ This convenience method creates an SPAlbum object if one doesn't exist, or
+ returns a cached SPAlbum if one already exists for the given URL.
+
+ @warning *Important:* If you pass in an invalid album URL (i.e., any URL not
+ starting `spotify:album:`, this method will return `nil`.
+
+ @param aURL The album URL to create an SPAlbum for.
+ @param aSession The SPSession the album should exist in.
+ @return Returns the created SPAlbum object, or `nil` if given an invalid album URL.
+ */
++(SPAlbum *)albumWithAlbumURL:(NSURL *)aURL inSession:(SPSession *)aSession;
+
+/** Initializes a new SPAlbum from the given opaque sp_album struct.
+
+ @warning *Important:* For better performance and built-in caching, it is recommended
+ you create SPAlbum objects using +[SPAlbum albumWithAlbumStruct:inSession:],
+ +[SPAlbum albumWithAlbumURL:inSession:] or the instance methods on SPSession.
+
+ @param anAlbum The sp_album struct to create an SPAlbum for.
+ @param aSession The SPSession the album should exist in.
+ @return Returns the created SPAlbum object.
+ */
+-(id)initWithAlbumStruct:(sp_album *)anAlbum inSession:(SPSession *)aSession;
+
+///----------------------------
+/// @name Properties
+///----------------------------
+
+/** Returns the opaque structure used by the C LibSpotify API.
+
+ @warning *Important:* This should only be used if you plan to directly use the
+ C LibSpotify API. The behaviour of CocoaLibSpotify is undefined if you use the C
+ API directly on items that have CocoaLibSpotify objects associated with them.
+ */
+@property (readonly) sp_album *album;
+
+/** Returns `YES` if the album metadata has finished loading. */
+@property (readonly, getter=isLoaded) BOOL loaded;
+
+/** Returns the session the album's metadata is loaded in. */
+@property (readonly, retain) SPSession *session;
+
+///----------------------------
+/// @name Metadata
+///----------------------------
+
+/** Returns the album's artist, or `nil` if the metadata isn't loaded yet. */
+@property (readonly, retain) SPArtist *artist;
+
+/** Returns the album's cover image. Returns `nil` if the metadata isn't loaded yet, or if the album doesn't have a cover image. */
+@property (readonly, retain) SPImage *cover;
+
+/** Returns `YES` if the album is available in the logged-in user's region. */
+@property (readonly, getter=isAvailable) BOOL available;
+
+/** Returns the name of the album. */
+@property (readonly) NSString *name;
+
+/** Returns the album type.
+
+ Possible values:
+
+ SP_ALBUMTYPE_ALBUM
+ Normal album.
+
+ SP_ALBUMTYPE_SINGLE
+ Single.
+
+ SP_ALBUMTYPE_COMPILATION
+ Compilation.
+
+ SP_ALBUMTYPE_UNKNOWN
+ Unknown type.
+ */
+@property (readonly) sp_albumtype type;
+
+/** Returns the release year of the album. */
+@property (readonly) NSUInteger year;
+
+@end
189 CocoaLibSpotify/SPAlbum.m
@@ -0,0 +1,189 @@
+//
+// SPAlbum.m
+// CocoaLibSpotify
+//
+// Created by Daniel Kennett on 2/20/11.
+/*
+Copyright (c) 2011, Spotify AB
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Spotify AB nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL SPOTIFY AB BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#import "SPAlbum.h"
+#import "SPSession.h"
+#import "SPImage.h"
+#import "SPArtist.h"
+#import "SPURLExtensions.h"
+
+@interface SPAlbum ()
+
+@property (readwrite) sp_album *album;
+@property (readwrite, retain) SPSession *session;
+@property (readwrite, retain) SPImage *cover;
+@property (readwrite, retain) SPArtist *artist;
+
+-(void)loadAlbumData;
+
+@end
+
+@implementation SPAlbum
+
+static NSMutableDictionary *albumCache;
+
++(SPAlbum *)albumWithAlbumStruct:(sp_album *)anAlbum inSession:(SPSession *)aSession {
+
+ if (albumCache == nil) {
+ albumCache = [[NSMutableDictionary alloc] init];
+ }
+
+ NSValue *ptrValue = [NSValue valueWithPointer:anAlbum];
+
+ SPAlbum *cachedAlbum = [albumCache objectForKey:ptrValue];
+
+ if (cachedAlbum != nil) {
+ return cachedAlbum;
+ }
+
+ cachedAlbum = [[SPAlbum alloc] initWithAlbumStruct:anAlbum
+ inSession:aSession];
+
+ [albumCache setObject:cachedAlbum forKey:ptrValue];
+ return [cachedAlbum autorelease];
+}
+
++(SPAlbum *)albumWithAlbumURL:(NSURL *)aURL inSession:(SPSession *)aSession {
+
+ if ([aURL spotifyLinkType] == SP_LINKTYPE_ALBUM) {
+ sp_album *album = sp_link_as_album([aURL createSpotifyLink]);
+ if (album != NULL) {
+ SPAlbum *spAlbum = [self albumWithAlbumStruct:album inSession:aSession];
+ sp_album_release(album);
+ return spAlbum;
+ }
+ }
+ return nil;
+}
+
+-(id)initWithAlbumStruct:(sp_album *)anAlbum inSession:(SPSession *)aSession {
+ if ((self = [super init])) {
+ album = anAlbum;
+ sp_album_add_ref(album);
+ self.session = aSession;
+
+ if (!sp_album_is_loaded(album)) {
+ [self performSelector:@selector(checkLoaded)
+ withObject:nil
+ afterDelay:.25];
+ } else {
+ [self loadAlbumData];
+ }
+ }
+ return self;
+}
+
+-(void)checkLoaded {
+ BOOL loaded = sp_album_is_loaded(album);
+ if (!loaded) {
+ [self performSelector:_cmd
+ withObject:nil
+ afterDelay:.25];
+ } else {
+ [self loadAlbumData];
+ }
+}
+
+-(void)loadAlbumData {
+ const byte *imageId = sp_album_cover(album);
+
+ if (imageId != NULL) {
+ [self setCover:[SPImage imageWithImageId:imageId
+ inSession:session]];
+ }
+
+ sp_artist *spArtist = sp_album_artist(album);
+ if (spArtist != NULL) {
+ [self setArtist:[SPArtist artistWithArtistStruct:spArtist]];
+ }
+
+ // Fire KVO notifications
+ [self willChangeValueForKey:@"year"];
+ [self didChangeValueForKey:@"year"];
+
+ [self willChangeValueForKey:@"type"];
+ [self didChangeValueForKey:@"type"];
+
+ [self willChangeValueForKey:@"name"];
+ [self didChangeValueForKey:@"name"];
+
+ [self willChangeValueForKey:@"available"];
+ [self didChangeValueForKey:@"available"];
+}
+
+-(NSString *)description {
+ return [NSString stringWithFormat:@"%@: %@ by %@", [super description], self.name, self.artist.name];
+}
+
+@synthesize album;
+@synthesize session;
+@synthesize cover;
+@synthesize artist;
+
+-(BOOL)isAvailable {
+ return (BOOL)sp_album_is_available(album);
+}
+
+-(BOOL)isLoaded {
+ return (BOOL)sp_album_is_loaded(album);
+}
+
+-(NSUInteger)year {
+ return (NSUInteger)sp_album_year(album);
+}
+
+-(sp_albumtype)type {
+ return sp_album_type(album);
+}
+
+-(NSString *)name {
+ const char *name = sp_album_name(album);
+ if (name != NULL) {
+ NSString *nameString = [NSString stringWithUTF8String:name];
+ return [nameString length] > 0 ? nameString : nil;
+ } else {
+ return nil;
+ }
+}
+
+-(void)dealloc {
+
+ self.session = nil;
+ [self setCover:nil];
+ [self setArtist:nil];
+
+ sp_album_release(album);
+
+ [super dealloc];
+}
+
+@end
131 CocoaLibSpotify/SPAlbumBrowse.h
@@ -0,0 +1,131 @@
+//
+// SPAlbumBrowse.h
+// CocoaLibSpotify
+//
+// Created by Daniel Kennett on 4/24/11.
+/*
+Copyright (c) 2011, Spotify AB
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Spotify AB nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL SPOTIFY AB BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/** Represents an "album browse" of an album on the Spotify service.
+
+ An "album browse" fetches detailed information about an album from the Spotify
+ service, including a review, copyright information and a list of the album's tracks.
+
+ Artist or album browses are required for certain SPTrack metadata to be available -
+ see the SPTrack documentation for details.
+ */
+
+#import <Foundation/Foundation.h>
+#import <libspotify/api.h>
+
+@class SPAlbum;
+@class SPSession;
+@class SPArtist;
+
+@interface SPAlbumBrowse : NSObject {
+@private
+ BOOL loaded;
+ NSError *loadError;
+ SPSession *session;
+ SPAlbum *album;
+ SPArtist *artist;
+ NSArray *tracks;
+ NSArray *copyrights;
+ NSString *review;
+ sp_albumbrowse *browseOperation;
+}
+
+///----------------------------
+/// @name Creating and Initializing Album Browses
+///----------------------------
+
+/** Creates an SPAlbumBrowse from the given SPAlbum.
+
+ This convenience method is simply returns a new, autoreleased SPAlbumBrowse
+ object. No caching is performed.
+
+ @param anAlbum The SPAlbum to make an SPAlbumBrowse for.
+ @param aSession The SPSession the browse should exist in.
+ @return Returns the created SPAlbumBrowse object.
+ */
++(SPAlbumBrowse *)browseAlbum:(SPAlbum *)anAlbum inSession:(SPSession *)aSession;
+
+/** Creates an SPAlbumBrowse from the given album URL.
+
+ This convenience method is simply returns a new, autoreleased SPAlbumBrowse
+ object. No caching is performed.
+
+ @warning *Important:* If you pass in an invalid album URL (i.e., any URL not
+ starting `spotify:album:`, this method will return `nil`.
+
+ @param albumURL The album URL to make an SPAlbumBrowse for.
+ @param aSession The SPSession the browse should exist in.
+ @return Returns the created SPAlbumBrowse object.
+ */
++(SPAlbumBrowse *)browseAlbumAtURL:(NSURL *)albumURL inSession:(SPSession *)aSession;
+
+/** Initializes a new SPAlbumBrowse from the given SPAlbum.
+
+ @param anAlbum The SPAlbum to make an SPAlbumBrowse for.
+ @param aSession The SPSession the browse should exist in.
+ @return Returns the created SPAlbumBrowse object.
+ */
+-(id)initWithAlbum:(SPAlbum *)anAlbum inSession:(SPSession *)aSession;
+
+///----------------------------
+/// @name Properties
+///----------------------------
+
+/** Returns `YES` if the album metadata has finished loading. */
+@property (nonatomic, readonly, getter=isLoaded) BOOL loaded;
+
+/** Returns the error that occurred during loading, or `nil` if no error occurred. */
+@property (nonatomic, readonly, copy) NSError *loadError;
+
+/** Returns the session the album's metadata is loaded in. */
+@property (nonatomic, readonly, retain) SPSession *session;
+
+///----------------------------
+/// @name Metadata
+///----------------------------
+
+/** Returns the browse operation's album. */
+@property (nonatomic, readonly, retain) SPAlbum *album;
+
+/** Returns the album's artist, or `nil` if the metadata isn't loaded yet. */
+@property (nonatomic, readonly, retain) SPArtist *artist;
+
+/** Returns the album's copyrights as an array of NSString, or `nil` if the metadata isn't loaded yet. */
+@property (nonatomic, readonly, retain) NSArray *copyrights;
+
+/** Returns the album's review, or `nil` if the metadata isn't loaded yet. */
+@property (nonatomic, readonly, copy) NSString *review;
+
+/** Returns the album's tracks, or `nil` if the metadata isn't loaded yet. */
+@property (nonatomic, readonly, retain) NSArray *tracks;
+
+@end
161 CocoaLibSpotify/SPAlbumBrowse.m
@@ -0,0 +1,161 @@
+//
+// SPAlbumBrowse.m
+// CocoaLibSpotify
+//
+// Created by Daniel Kennett on 4/24/11.
+/*
+Copyright (c) 2011, Spotify AB
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Spotify AB nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL SPOTIFY AB BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#import "SPAlbumBrowse.h"
+#import "SPSession.h"
+#import "SPAlbum.h"
+#import "SPArtist.h"
+#import "SPErrorExtensions.h"
+#import "SPTrack.h"
+
+// IMPORTANT: This class was implemented while enjoying a lovely spring afternoon by a lake
+// in Sweden. This is my view right now: http://twitpic.com/4oy9zn
+
+@interface SPAlbumBrowse ()
+
+@property (nonatomic, readwrite, getter=isLoaded) BOOL loaded;
+@property (nonatomic, readwrite, copy) NSError *loadError;
+@property (nonatomic, readwrite, retain) SPSession *session;
+
+@property (nonatomic, readwrite, retain) SPAlbum *album;
+@property (nonatomic, readwrite, retain) SPArtist *artist;
+@property (nonatomic, readwrite, retain) NSArray *tracks;
+
+@property (nonatomic, readwrite, retain) NSArray *copyrights;
+@property (nonatomic, readwrite, copy) NSString *review;
+
+@end
+
+void albumbrowse_complete (sp_albumbrowse *result, void *userdata);
+void albumbrowse_complete (sp_albumbrowse *result, void *userdata) {
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ SPAlbumBrowse *albumBrowse = userdata;
+
+ albumBrowse.loaded = sp_albumbrowse_is_loaded(result);
+ sp_error errorCode = sp_albumbrowse_error(result);
+
+ if (errorCode != SP_ERROR_OK) {
+ albumBrowse.loadError = [NSError spotifyErrorWithCode:errorCode];
+ } else {
+ albumBrowse.loadError = nil;
+ }
+
+ if (albumBrowse.isLoaded) {
+
+ albumBrowse.review = [NSString stringWithUTF8String:sp_albumbrowse_review(result)];
+ albumBrowse.artist = [SPArtist artistWithArtistStruct:sp_albumbrowse_artist(result)];
+
+ int trackCount = sp_albumbrowse_num_tracks(result);
+ NSMutableArray *tracks = [NSMutableArray arrayWithCapacity:trackCount];
+ for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
+ sp_track *track = sp_albumbrowse_track(result, currentTrack);
+ if (track != NULL) {
+ [tracks addObject:[SPTrack trackForTrackStruct:track inSession:albumBrowse.session]];
+ }
+ }
+
+ albumBrowse.tracks = [NSArray arrayWithArray:tracks];
+
+ int copyrightCount = sp_albumbrowse_num_copyrights(result);
+ NSMutableArray *copyrights = [NSMutableArray arrayWithCapacity:copyrightCount];
+ for (int currentCopyright = 0; currentCopyright < copyrightCount; currentCopyright++) {
+ const char *copyright = sp_albumbrowse_copyright(result, currentCopyright);
+ [copyrights addObject:[NSString stringWithUTF8String:copyright]];
+ }
+
+ albumBrowse.copyrights = [NSArray arrayWithArray:copyrights];
+ }
+
+ [pool drain];
+}
+
+@implementation SPAlbumBrowse
+
++(SPAlbumBrowse *)browseAlbum:(SPAlbum *)anAlbum inSession:(SPSession *)aSession {
+ return [[[SPAlbumBrowse alloc] initWithAlbum:anAlbum inSession:aSession] autorelease];
+}
+
++(SPAlbumBrowse *)browseAlbumAtURL:(NSURL *)albumURL inSession:(SPSession *)aSession {
+ return [[[SPAlbumBrowse alloc] initWithAlbum:[SPAlbum albumWithAlbumURL:albumURL inSession:aSession]
+ inSession:aSession] autorelease];
+
+}
+
+-(id)initWithAlbum:(SPAlbum *)anAlbum inSession:(SPSession *)aSession; {
+
+ if (anAlbum == nil || aSession == nil) {
+ [self release];
+ return nil;
+ }
+
+ if ((self = [super init])) {
+ self.session = aSession;
+ self.album = anAlbum;
+
+ sp_albumbrowse *albumBrowse = sp_albumbrowse_create(self.session.session,
+ self.album.album,
+ &albumbrowse_complete,
+ self);
+ if (albumBrowse != NULL) {
+ browseOperation = albumBrowse;
+ }
+ }
+
+ return self;
+}
+
+@synthesize loaded;
+@synthesize loadError;
+@synthesize session;
+@synthesize album;
+@synthesize artist;
+@synthesize tracks;
+@synthesize copyrights;
+@synthesize review;
+
+- (void)dealloc {
+ self.loadError = nil;
+ self.session = nil;
+ self.album = nil;
+ self.artist = nil;
+ self.tracks = nil;
+ self.copyrights = nil;
+ self.review = nil;
+
+ if (browseOperation != NULL)
+ sp_albumbrowse_release(browseOperation);
+
+ [super dealloc];
+}
+
+@end
103 CocoaLibSpotify/SPArtist.h
@@ -0,0 +1,103 @@
+//
+// SPArtist.h
+// CocoaLibSpotify
+//
+// Created by Daniel Kennett on 2/20/11.
+/*
+Copyright (c) 2011, Spotify AB
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Spotify AB nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL SPOTIFY AB BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/** Represents an artist on Spotify.
+
+ SPArtist is roughly analogous to the sp_artist struct in the C LibSpotify API.
+ */
+
+#import <Foundation/Foundation.h>
+#import <libspotify/api.h>
+
+@interface SPArtist : NSObject {
+ @private
+ sp_artist *artist;
+}
+
+///----------------------------
+/// @name Creating and Initializing Artists
+///----------------------------
+
+/** Creates an SPArtist from the given opaque sp_artist struct.
+
+ This convenience method creates an SPArtist object if one doesn't exist, or
+ returns a cached SPArtist if one already exists for the given struct.
+
+ @param anArtist The sp_artist struct to create an SPArtist for.
+ @return Returns the created SPArtist object.
+ */
++(SPArtist *)artistWithArtistStruct:(sp_artist *)anArtist;
+
+/** Creates an SPArtist from the given Spotify artist URL.
+
+ This convenience method creates an SPArtist object if one doesn't exist, or
+ returns a cached SPArtist if one already exists for the given URL.
+
+ @warning *Important:* If you pass in an invalid artist URL (i.e., any URL not
+ starting `spotify:artist:`, this method will return `nil`.
+
+ @param aURL The artist URL to create an SPArtist for.
+ @return Returns the created SPArtist object, or `nil` if given an invalid artist URL.
+ */
++(SPArtist *)artistWithArtistURL:(NSURL *)aURL;
+
+/** Initializes a new SPArtist from the given opaque sp_artist struct.
+
+ @warning *Important:* For better performance and built-in caching, it is recommended
+ you create SPArtist objects using +[SPArtist artistWithArtistStruct:],
+ +[SPArtist artistWithArtistURL:] or the instance methods on SPSession.
+
+ @param anArtist The sp_artist struct to create an SPArtist for.
+ @return Returns the created SPArtist object.
+ */
+-(id)initWithArtistStruct:(sp_artist *)anArtist;
+
+///----------------------------
+/// @name Properties
+///----------------------------
+
+/** Returns the opaque structure used by the C LibSpotify API.
+
+ @warning *Important:* This should only be used if you plan to directly use the
+ C LibSpotify API. The behaviour of CocoaLibSpotify is undefined if you use the C
+ API directly on items that have CocoaLibSpotify objects associated with them.
+ */
+@property (readonly) sp_artist *artist;
+
+///----------------------------
+/// @name Metadata
+///----------------------------
+
+/** Returns the artist's name. */
+@property (readonly) NSString *name;
+
+@end
123 CocoaLibSpotify/SPArtist.m
@@ -0,0 +1,123 @@
+//
+// SPArtist.m
+// CocoaLibSpotify
+//
+// Created by Daniel Kennett on 2/20/11.
+/*
+Copyright (c) 2011, Spotify AB
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Spotify AB nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL SPOTIFY AB BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#import "SPArtist.h"
+#import "SPURLExtensions.h"
+
+@implementation SPArtist
+
+static NSMutableDictionary *artistCache;
+
++(SPArtist *)artistWithArtistStruct:(sp_artist *)anArtist {
+
+ if (artistCache == nil) {
+ artistCache = [[NSMutableDictionary alloc] init];
+ }
+
+ NSValue *ptrValue = [NSValue valueWithPointer:anArtist];
+ SPArtist *cachedArtist = [artistCache objectForKey:ptrValue];
+
+ if (cachedArtist != nil) {
+ return cachedArtist;
+ }
+
+ cachedArtist = [[SPArtist alloc] initWithArtistStruct:anArtist];
+
+ [artistCache setObject:cachedArtist forKey:ptrValue];
+ return [cachedArtist autorelease];
+}
+
++(SPArtist *)artistWithArtistURL:(NSURL *)aURL {
+
+ if ([aURL spotifyLinkType] == SP_LINKTYPE_ARTIST) {
+ sp_artist *artist = sp_link_as_artist([aURL createSpotifyLink]);
+ if (artist != NULL) {
+ SPArtist *spArtist = [self artistWithArtistStruct:artist];
+ sp_artist_release(artist);
+ return spArtist;
+ }
+ }
+ return nil;
+}
+
+#pragma mark -
+
+-(id)initWithArtistStruct:(sp_artist *)anArtist {
+ if ((self = [super init])) {
+ artist = anArtist;
+ sp_artist_add_ref(artist);
+
+ if (!sp_artist_is_loaded(artist)) {
+ [self performSelector:@selector(checkLoaded)
+ withObject:nil
+ afterDelay:.25];
+ }
+ }
+ return self;
+}
+
+-(void)checkLoaded {
+ BOOL loaded = sp_artist_is_loaded(artist);
+ if (!loaded) {
+ [self performSelector:_cmd
+ withObject:nil
+ afterDelay:.25];
+ } else {
+
+ // Fire KVO notifications
+ [self willChangeValueForKey:@"name"];
+ [self didChangeValueForKey:@"name"];
+ }
+}
+
+-(NSString *)description {
+ return [NSString stringWithFormat:@"%@: %@", [super description], self.name];
+}
+
+@synthesize artist;
+
+-(NSString *)name {
+ const char *name = sp_artist_name(artist);
+ if (name != NULL) {
+ NSString *nameString = [NSString stringWithUTF8String:name];
+ return [nameString length] > 0 ? nameString : nil;
+ } else {
+ return nil;
+ }
+}
+
+-(void)dealloc {
+ sp_artist_release(artist);
+ [super dealloc];
+}
+
+@end
139 CocoaLibSpotify/SPArtistBrowse.h
@@ -0,0 +1,139 @@
+//
+// SPArtistBrowse.h
+// CocoaLibSpotify
+//
+// Created by Daniel Kennett on 4/24/11.
+/*
+Copyright (c) 2011, Spotify AB
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Spotify AB nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL SPOTIFY AB BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/** Represents an "artist browse" of an artist on the Spotify service.
+
+ An "artist browse" fetches detailed information about an artist from the Spotify
+ service, including a biography, portrait images, related artists and a list of
+ their tracks and albums.
+
+ Artist or album browses are required for certain SPTrack metadata to be available -
+ see the SPTrack documentation for details.
+ */
+
+#import <Cocoa/Cocoa.h>
+#import <libspotify/api.h>
+
+@class SPArtist;
+@class SPSession;
+
+@interface SPArtistBrowse : NSObject {
+@private
+ BOOL loaded;
+ NSError *loadError;
+ SPArtist *artist;
+ SPSession *session;
+ NSArray *portraits;
+ NSImage *firstPortrait;
+ NSArray *tracks;
+ NSArray *albums;
+ NSArray *relatedArtists;
+ NSString *biography;
+ sp_artistbrowse *browseOperation;
+}
+
+///----------------------------
+/// @name Creating and Initializing Artist Browses
+///----------------------------
+
+/** Creates an SPArtistBrowse from the given SPArtist.
+
+ This convenience method is simply returns a new, autoreleased SPArtistBrowse
+ object. No caching is performed.
+
+ @param anArtist The SPArtist to make an SPArtistBrowse for.
+ @param aSession The SPSession the browse should exist in.
+ @return Returns the created SPArtistBrowse object.
+ */
++(SPArtistBrowse *)browseArtist:(SPArtist *)anArtist inSession:(SPSession *)aSession;
+
+/** Creates an SPArtistBrowse from the given artist URL.
+
+ This convenience method is simply returns a new, autoreleased SPArtistBrowse
+ object. No caching is performed.
+
+ @warning *Important:* If you pass in an invalid artist URL (i.e., any URL not
+ starting `spotify:artist:`, this method will return `nil`.
+
+ @param artistURL The artist URL to make an SPArtistBrowse for.
+ @param aSession The SPSession the browse should exist in.
+ @return Returns the created SPArtistBrowse object.
+ */
++(SPArtistBrowse *)browseArtistAtURL:(NSURL *)artistURL inSession:(SPSession *)aSession;
+
+/** Initializes a new SPArtistBrowse from the given SPArtist.
+
+ @param anArtist The SPArtist to make an SPArtistBrowse for.
+ @param aSession The SPSession the browse should exist in.
+ @return Returns the created SPArtistBrowse object.
+ */
+-(id)initWithArtist:(SPArtist *)anArtist inSession:(SPSession *)aSession;
+
+///----------------------------
+/// @name Properties
+///----------------------------
+
+/** Returns `YES` if the artist metadata has finished loading. */
+@property (nonatomic, readonly, getter=isLoaded) BOOL loaded;
+
+/** Returns the error that occurred during loading, or `nil` if no error occurred. */
+@property (nonatomic, readonly, copy) NSError *loadError;
+
+/** Returns the session the artist's metadata is loaded in. */
+@property (nonatomic, readonly, retain) SPSession *session;
+
+///----------------------------
+/// @name Metadata
+///----------------------------
+
+/** Returns a list of albums by this artist, or `nil` if the metadata isn't loaded yet. */
+@property (nonatomic, readonly, retain) NSArray *albums;
+
+/** Returns the browse operation's artist. */
+@property (nonatomic, readonly, retain) SPArtist *artist;
+
+/** Returns the artist's biography, or `nil` if the metadata isn't loaded yet. */
+@property (nonatomic, readonly, copy) NSString *biography;
+
+/** Returns the first artist portrait image, or `nil` if the metadata isn't loaded yet or there are no images. */
+@property (nonatomic, readonly, retain) NSImage *firstPortrait;
+
+/** Returns the artist's portrait images, or `nil` if the metadata isn't loaded yet. */
+@property (nonatomic, readonly, retain) NSArray *portraits;
+
+/** Returns a list of related artists for this artist, or `nil` if the metadata isn't loaded yet. */
+@property (nonatomic, readonly, retain) NSArray *relatedArtists;
+
+/** Returns a list of tracks by this artist, or `nil` if the metadata isn't loaded yet. */
+@property (nonatomic, readonly, retain) NSArray *tracks;
+
+@end
201 CocoaLibSpotify/SPArtistBrowse.m
@@ -0,0 +1,201 @@
+//
+// SPArtistBrowse.m
+// CocoaLibSpotify
+//
+// Created by Daniel Kennett on 4/24/11.
+/*
+Copyright (c) 2011, Spotify AB
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Spotify AB nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL SPOTIFY AB BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+// IMPORTANT: This class was implemented while enjoying a lovely spring afternoon by a lake
+// in Sweden. This is my view right now: http://twitpic.com/4oy9zn
+
+#import "SPArtistBrowse.h"
+#import "SPErrorExtensions.h"
+#import "SPTrack.h"
+#import "SPAlbum.h"
+#import "SPArtist.h"
+#import "SPImage.h"
+#import "SPSession.h"
+
+@interface SPArtistBrowse ()
+
+@property (nonatomic, readwrite, getter=isLoaded) BOOL loaded;
+@property (nonatomic, readwrite, copy) NSError *loadError;
+@property (nonatomic, readwrite, retain) SPArtist *artist;
+@property (nonatomic, readwrite, retain) SPSession *session;
+
+@property (nonatomic, readwrite, retain) NSArray *portraits;
+
+@property (nonatomic, readwrite, retain) NSArray *tracks;
+@property (nonatomic, readwrite, retain) NSArray *albums;
+@property (nonatomic, readwrite, retain) NSArray *relatedArtists;
+
+@property (nonatomic, readwrite, copy) NSString *biography;
+
+@end
+
+void artistbrowse_complete(sp_artistbrowse *result, void *userdata);
+void artistbrowse_complete(sp_artistbrowse *result, void *userdata) {
+
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ SPArtistBrowse *artistBrowse = userdata;
+
+ artistBrowse.loaded = sp_artistbrowse_is_loaded(result);
+ sp_error errorCode = sp_artistbrowse_error(result);
+
+ if (errorCode != SP_ERROR_OK) {
+ artistBrowse.loadError = [NSError spotifyErrorWithCode:errorCode];
+ } else {
+ artistBrowse.loadError = nil;
+ }
+
+ if (artistBrowse.isLoaded) {
+
+ artistBrowse.biography = [NSString stringWithUTF8String:sp_artistbrowse_biography(result)];
+
+ int trackCount = sp_artistbrowse_num_tracks(result);
+ NSMutableArray *tracks = [NSMutableArray arrayWithCapacity:trackCount];
+ for (int currentTrack = 0; currentTrack < trackCount; currentTrack++) {
+ sp_track *track = sp_artistbrowse_track(result, currentTrack);
+ if (track != NULL) {
+ [tracks addObject:[SPTrack trackForTrackStruct:track inSession:artistBrowse.session]];
+ }
+ }
+
+ artistBrowse.tracks = [NSArray arrayWithArray:tracks];
+
+ int albumCount = sp_artistbrowse_num_albums(result);
+ NSMutableArray *albums = [NSMutableArray arrayWithCapacity:albumCount];
+ for (int currentAlbum = 0; currentAlbum < albumCount; currentAlbum++) {
+ sp_album *album = sp_artistbrowse_album(result, currentAlbum);
+ if (album != NULL) {
+ [albums addObject:[SPAlbum albumWithAlbumStruct:album inSession:artistBrowse.session]];
+ }
+ }
+
+ artistBrowse.albums = [NSArray arrayWithArray:albums];
+
+ int relatedArtistCount = sp_artistbrowse_num_similar_artists(result);
+ NSMutableArray *relatedArtists = [NSMutableArray arrayWithCapacity:relatedArtistCount];
+ for (int currentArtist = 0; currentArtist < relatedArtistCount; currentArtist++) {
+ sp_artist *artist = sp_artistbrowse_similar_artist(result, currentArtist);
+ if (artist != NULL) {
+ [relatedArtists addObject:[SPArtist artistWithArtistStruct:artist]];
+ }
+ }
+
+ artistBrowse.relatedArtists = [NSArray arrayWithArray:relatedArtists];
+
+ int portraitCount = sp_artistbrowse_num_portraits(result);
+ NSMutableArray *portraits = [NSMutableArray arrayWithCapacity:portraitCount];
+ for (int currentPortrait = 0; currentPortrait < portraitCount; currentPortrait++) {
+ const byte *portraitId = sp_artistbrowse_portrait(result, currentPortrait);
+ SPImage *portrait = [SPImage imageWithImageId:portraitId inSession:artistBrowse.session];
+ if (portrait != nil) {
+ [portraits addObject:portrait];
+ }
+ }
+
+ artistBrowse.portraits = [NSArray arrayWithArray:portraits];
+ }
+
+ [pool drain];
+}
+
+@implementation SPArtistBrowse
+
++(SPArtistBrowse *)browseArtist:(SPArtist *)anArtist inSession:(SPSession *)aSession {
+ return [[[SPArtistBrowse alloc] initWithArtist:anArtist inSession:aSession] autorelease];
+}
+
++(SPArtistBrowse *)browseArtistAtURL:(NSURL *)artistURL inSession:(SPSession *)aSession {
+ return [[[SPArtistBrowse alloc] initWithArtist:[SPArtist artistWithArtistURL:artistURL]
+ inSession:aSession] autorelease];
+}
+
+-(id)initWithArtist:(SPArtist *)anArtist inSession:(SPSession *)aSession {
+
+ if (anArtist == nil || aSession == nil) {
+ [self release];
+ return nil;
+ }
+
+ if ((self = [super init])) {
+ self.session = aSession;
+ self.artist = anArtist;
+
+ sp_artistbrowse *artistBrowse = sp_artistbrowse_create(self.session.session,
+ self.artist.artist,
+ &artistbrowse_complete,
+ self);
+ if (artistBrowse != NULL) {
+ browseOperation = artistBrowse;
+ }
+ }
+
+ return self;
+}
+
+@synthesize loaded;
+@synthesize loadError;
+@synthesize artist;
+@synthesize session;
+@synthesize portraits;
+@synthesize tracks;
+@synthesize albums;
+@synthesize relatedArtists;
+@synthesize biography;
+
++(NSSet *)keyPathsForValuesAffectingFirstPortrait {
+ return [NSSet setWithObject:@"portraits"];
+}
+
+-(SPImage *)firstPortrait {
+ if (self.portraits.count > 0) {
+ return [self.portraits objectAtIndex:0];
+ }
+ return nil;
+}
+
+- (void)dealloc {
+ self.loadError = nil;
+ self.artist = nil;
+ self.session = nil;
+ self.portraits = nil;
+ self.tracks = nil;
+ self.albums = nil;
+ self.relatedArtists = nil;
+ self.biography = nil;
+
+ if (browseOperation != NULL)
+ sp_artistbrowse_release(browseOperation);
+
+ [super dealloc];
+}
+
+@end
48 CocoaLibSpotify/SPErrorExtensions.h
@@ -0,0 +1,48 @@
+//
+// SPErrorExtensions.h
+// CocoaLibSpotify
+//
+// Created by Daniel Kennett on 2/14/11.
+/*
+Copyright (c) 2011, Spotify AB
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Spotify AB nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL SPOTIFY AB BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/** Contains convenience methods for working with Spotify error codes (`sp_error`). */
+
+#import <Foundation/Foundation.h>
+#import <libspotify/api.h>
+
+static NSString * const kCocoaLibSpotifyErrorDomain = @"com.spotify.CocoaLibSpotify.error";
+
+@interface NSError (SPErrorExtensions)
+
++ (NSError *)spotifyErrorWithDescription:(NSString *)msg code:(NSInteger)code;
++ (NSError *)spotifyErrorWithCode:(sp_error)code;
++ (NSError *)spotifyErrorWithDescription:(NSString *)msg;
++ (NSError *)spotifyErrorWithCode:(NSInteger)code format:(NSString *)format, ...;
++ (NSError *)spotifyErrorWithFormat:(NSString *)format, ...;
+
+@end
63 CocoaLibSpotify/SPErrorExtensions.m
@@ -0,0 +1,63 @@
+//
+// SPErrorExtensions.m
+// CocoaLibSpotify
+//
+// Created by Daniel Kennett on 2/14/11.
+/*
+Copyright (c) 2011, Spotify AB
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Spotify AB nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL SPOTIFY AB BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#import "SPErrorExtensions.h"
+
+@implementation NSError (SCAdditions)
+
++ (NSError *)spotifyErrorWithDescription:(NSString *)msg code:(NSInteger)code {
+ return [NSError errorWithDomain:kCocoaLibSpotifyErrorDomain code:code userInfo:[NSDictionary dictionaryWithObject:msg forKey:NSLocalizedDescriptionKey]];
+}
++ (NSError *)spotifyErrorWithCode:(sp_error)code {
+ return [NSError spotifyErrorWithDescription:[NSString stringWithUTF8String:sp_error_message(code)] code:code];
+}
++ (NSError *)spotifyErrorWithDescription:(NSString *)msg {
+ return [NSError spotifyErrorWithDescription:msg code:0];
+}
++ (NSError *)spotifyErrorWithCode:(NSInteger)code format:(NSString *)format, ... {
+ va_list src, dest;
+ va_start(src, format);
+ va_copy(dest, src);
+ va_end(src);
+ NSString *msg = [[NSString alloc] initWithFormat:format arguments:dest];
+ return [NSError spotifyErrorWithDescription:msg code:code];
+}
++ (NSError *)spotifyErrorWithFormat:(NSString *)format, ... {
+ va_list src, dest;
+ va_start(src, format);
+ va_copy(dest, src);
+ va_end(src);
+ NSString *msg = [[NSString alloc] initWithFormat:format arguments:dest];
+ return [NSError spotifyErrorWithDescription:msg code:0];
+}
+
+@end
124 CocoaLibSpotify/SPImage.h
@@ -0,0 +1,124 @@
+//
+// SPImage.h
+// CocoaLibSpotify
+//
+// Created by Daniel Kennett on 2/20/11.
+/*
+Copyright (c) 2011, Spotify AB
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Spotify AB nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL SPOTIFY AB BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/** Represents an image from the Spotify service. */
+
+#import <Cocoa/Cocoa.h>
+#import <libspotify/api.h>
+
+@class SPSession;
+
+@interface SPImage : NSObject {
+ @private
+ NSImage *image;
+ sp_image *spImage;
+ const byte *imageId;
+ BOOL loaded;
+ BOOL hasRequestedImage;
+ __weak SPSession *session;
+}
+
+///----------------------------
+/// @name Creating and Initializing Images
+///----------------------------
+
+/** Creates an SPImage from the given ID.
+
+ This convenience method creates an SPImage object if one doesn't exist, or
+ returns a cached SPImage if one already exists for the given ID.
+
+ @param imageId The image ID to create an SPImage for.
+ @param aSession The SPSession the image should exist in.
+ @return Returns the created SPImage object.
+ */
++(SPImage *)imageWithImageId:(const byte[])imageId inSession:(SPSession *)aSession;
+
+/** Creates an SPImage from the given URL.
+
+ This convenience method creates an SPImage object if one doesn't exist, or
+ returns a cached SPImage if one already exists for the given URL.
+
+ @param imageURL The image URL to create an SPImage for.
+ @param aSession The SPSession the image should exist in.
+ @return Returns the created SPImage object.
+ */
++(SPImage *)imageWithImageURL:(NSURL *)imageURL inSession:(SPSession *)aSession;
+
+/** Initializes a new SPImage from the given struct and ID.
+
+ @warning *Important:* For better performance and built-in caching, it is recommended
+ you create SPImage objects using +[SPImage imageWithImageId:inSession:].
+
+ @param anImage The sp_image struct to create an SPImage for, or NULL if the image hasn't been loaded yet.
+ @param anId The ID of the image.
+ @param aSession The SPSession the image should exist in.
+ @return Returns the created SPImage object.
+ */
+-(id)initWithImageStruct:(sp_image *)anImage imageId:(const byte[20])anId inSession:(SPSession *)aSession;
+
+///----------------------------
+/// @name Loading Images
+///----------------------------
+
+/** Begins loading the image if it hasn't already been loaded.
+
+ This is called automatically if you request the image property. */
+-(void)beginLoading;
+
+///----------------------------
+/// @name Properties
+///----------------------------
+
+/** Returns an NSImage representation of the image, or `nil` if the image has yet to be loaded. */
+@property (readonly, retain) NSImage *image;
+
+/** Returns the ID of the image. */
+-(const byte *)imageId;
+
+/** Returns `YES` if the image has finished loading and all data is available. */
+@property (readonly, getter=isLoaded) BOOL loaded;
+
+/** Returns the session the image was loaded in. */
+@property (readonly) __weak SPSession *session;
+
+/** Returns the opaque structure used by the C LibSpotify API, or NULL if the image has yet to be loaded.
+
+ @warning *Important:* This should only be used if you plan to directly use the
+ C LibSpotify API. The behaviour of CocoaLibSpotify is undefined if you use the C
+ API directly on items that have CocoaLibSpotify objects associated with them.
+ */
+@property (readonly) sp_image *spImage;
+
+/** Returns the Spotify URL of the image. */
+-(NSURL *)spotifyURL;
+
+@end
207 CocoaLibSpotify/SPImage.m
@@ -0,0 +1,207 @@
+//
+// SPImage.m
+// CocoaLibSpotify
+//
+// Created by Daniel Kennett on 2/20/11.
+/*
+Copyright (c) 2011, Spotify AB
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Spotify AB nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL SPOTIFY AB BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#import "SPImage.h"
+#import "SPSession.h"
+#import "SPURLExtensions.h"
+
+@interface SPImage ()
+
+@property (readwrite, retain) NSImage *image;
+@property (readwrite) sp_image *spImage;
+@property (readwrite, getter=isLoaded) BOOL loaded;
+@property (readwrite) __weak SPSession *session;
+
+@end
+
+static void image_loaded(sp_image *image, void *userdata) {
+ [(SPImage *)userdata setLoaded:sp_image_is_loaded(image)];
+}
+
+static NSString * const kSPImageKVOContext = @"kSPImageKVOContext";
+
+@implementation SPImage
+
+static NSMutableDictionary *imageCache;
+
++(SPImage *)imageWithImageId:(const byte[])imageId inSession:(SPSession *)aSession {
+
+ if (imageCache == nil) {
+ imageCache = [[NSMutableDictionary alloc] init];
+ }
+
+ if (imageId == NULL) {
+ return nil;
+ }
+
+ NSData *imageIdAsData = [NSData dataWithBytes:imageId length:sizeof(imageId)];
+ SPImage *cachedImage = [imageCache objectForKey:imageIdAsData];
+
+ if (cachedImage != nil)
+ return cachedImage;
+
+ cachedImage = [[SPImage alloc] initWithImageStruct:NULL
+ imageId:imageId
+ inSession:aSession];
+ [imageCache setObject:cachedImage forKey:imageIdAsData];
+ return [cachedImage autorelease];
+}
+
++(SPImage *)imageWithImageURL:(NSURL *)imageURL inSession:(SPSession *)aSession {
+
+ if ([imageURL spotifyLinkType] == SP_LINKTYPE_IMAGE) {
+ sp_link *link = [imageURL createSpotifyLink];
+ sp_image *image = sp_image_create_from_link(aSession.session, link);
+
+ if (link != NULL)
+ sp_link_release(link);
+
+ if (image != NULL) {
+ SPImage *spImage = [self imageWithImageId:sp_image_image_id(image) inSession:aSession];
+ sp_image_release(image);
+ return spImage;
+ }
+ }
+ return nil;
+}
+
+#pragma mark -
+
+-(id)initWithImageStruct:(sp_image *)anImage imageId:(const byte[20])anId inSession:aSession {
+
+ if ((self = [super init])) {
+
+ self.session = aSession;
+ imageId = anId;
+
+ [self addObserver:self
+ forKeyPath:@"loaded"
+ options:0
+ context:kSPImageKVOContext];
+
+ if (anImage != NULL) {
+ spImage = anImage;
+ sp_image_add_ref(spImage);
+ sp_image_add_load_callback(spImage,
+ &image_loaded,
+ self);
+
+ self.loaded = sp_image_is_loaded(spImage);
+ }
+ }
+ return self;
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
+ if (context == kSPImageKVOContext) {
+ if ([keyPath isEqualToString:@"loaded"] && [self isLoaded] && ([self image] == nil)) {
+ if (sp_image_format(spImage) == SP_IMAGE_FORMAT_JPEG) {
+
+ size_t size;
+ const byte *data = sp_image_data(spImage, &size);
+
+ if (size > 0) {
+ NSData *imageData = [NSData dataWithBytes:data length:size];
+ [self setImage:[[[NSImage alloc] initWithData:imageData] autorelease]];
+ }
+ }
+ }
+ } else {
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+ }
+}
+
+@synthesize spImage;
+@synthesize loaded;
+@synthesize session;
+
+-(NSImage *)image {
+ if (image == nil && !hasRequestedImage)
+ [self beginLoading];
+ return image;
+}
+
+-(void)setImage:(NSImage *)anImage {
+ if (image != anImage) {
+ [anImage retain];
+ [image release];
+ image = anImage;
+ }
+}
+
+#pragma mark -
+
+-(void)beginLoading {
+
+ if (self.spImage != NULL)
+ return;
+
+ [self willChangeValueForKey:@"spImage"];
+ sp_image *newImage = sp_image_create(self.session.session, imageId);
+ spImage = newImage;
+ [self didChangeValueForKey:@"spImage"];
+
+ if (spImage != NULL) {
+ hasRequestedImage = YES;
+ sp_image_add_load_callback(spImage, &image_loaded, self);
+ self.loaded = sp_image_is_loaded(spImage);
+ }
+}
+
+-(const byte *)imageId {
+ return imageId;
+}
+
+-(NSURL *)spotifyURL {
+
+ sp_link *link = sp_link_create_from_image(self.spImage);
+
+ if (link != NULL) {
+ NSURL *url = [NSURL urlWithSpotifyLink:link];
+ sp_link_release(link);
+ return url;
+ }
+ return nil;
+}
+
+-(void)dealloc {
+
+ [self removeObserver:self forKeyPath:@"loaded"];
+ [self setImage:nil];
+
+ sp_image_remove_load_callback(spImage, &image_loaded, self);
+ sp_image_release(spImage);
+
+ [super dealloc];
+}
+
+@end
290 CocoaLibSpotify/SPPlaylist.h
@@ -0,0 +1,290 @@
+//
+// SPPlaylist.h
+// CocoaLibSpotify
+//
+// Created by Daniel Kennett on 2/14/11.
+/*
+Copyright (c) 2011, Spotify AB
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Spotify AB nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL SPOTIFY AB BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/** This class represents a list of tracks, be it a user's starred list, inbox, or a traditional "playlist". */
+
+#import <Cocoa/Cocoa.h>
+#import <libspotify/api.h>
+
+@class SPUser;
+@class SPImage;
+@class SPSession;
+@protocol SPPlaylistDelegate;
+
+@interface SPPlaylist : NSObject {
+@private
+ sp_playlist *playlist;
+ BOOL updating;
+ BOOL loaded;
+ BOOL collaborative;
+ BOOL hasPendingChanges;
+ __weak id <SPPlaylistDelegate> delegate;
+ __weak SPSession *session;
+ NSString *playlistDescription;
+ NSString *name;
+ SPImage *image;
+ SPUser *owner;
+ NSURL *spotifyURL;
+ BOOL trackChangesAreFromLibSpotifyCallback;
+ NSMutableArray *trackWrapper;
+ NSArray *subscribers;
+}
+
+///----------------------------
+/// @name Creating and Initializing Playlists
+///----------------------------
+
+/** Creates an SPPlaylist from the given opaque sp_playlist struct.
+
+ This convenience method creates an SPPlaylist object if one doesn't exist, or
+ returns a cached SPPlaylist if one already exists for the given struct.
+
+ @param pl The sp_playlist struct to create an SPPlaylist for.
+ @param aSession The SPSession the playlist should exist in.