├── .gitignore ├── Cartfile ├── Cartfile.resolved ├── HypeMachineAPI.podspec ├── HypeMachineAPI.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── HypeMachineAPI.xcscheme ├── HypeMachineAPI.xcworkspace └── contents.xcworkspacedata ├── LICENSE ├── README.md ├── Source ├── Artist.swift ├── ArtistsRequests.swift ├── ArtistsRouter.swift ├── Blog.swift ├── BlogsRequests.swift ├── BlogsRouter.swift ├── Dictionary.swift ├── Errors.swift ├── HypeMachineAPI.h ├── HypeMachineAPI.swift ├── Info.plist ├── MeRequests.swift ├── MeRouter.swift ├── MiscRequests.swift ├── MiscRouter.swift ├── Requests.swift ├── ResponseSerializers.swift ├── Router.swift ├── String.swift ├── Tag.swift ├── TagsRequests.swift ├── TagsRouter.swift ├── Track.swift ├── TracksRequests.swift ├── TracksRouter.swift ├── User.swift ├── UsersRequests.swift ├── UsersRouter.swift └── Validations.swift └── Tests ├── Artist.json ├── ArtistTests.swift ├── Artists.json ├── Blog.json ├── BlogTests.swift ├── Blogs.json ├── Fixtures.swift ├── Info.plist ├── Tag.json ├── TagTests.swift ├── Tags.json ├── Track.json ├── TrackTests.swift ├── Tracks.json ├── User.json ├── UserTests.swift └── Users.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata 15 | *.xccheckout 16 | *.moved-aside 17 | DerivedData 18 | *.hmap 19 | *.ipa 20 | *.xcuserstate 21 | 22 | # CocoaPods 23 | # 24 | # We recommend against adding the Pods directory to your .gitignore. However 25 | # you should judge for yourself, the pros and cons are mentioned at: 26 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 27 | # 28 | # Pods/ 29 | 30 | # Carthage 31 | # 32 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 33 | Carthage/Checkouts 34 | Carthage/Build 35 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "Alamofire/Alamofire" ~> 4.0.1 2 | github "krzyzanowskim/CryptoSwift" ~> 0.10.0 3 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "Alamofire/Alamofire" "4.7.3" 2 | github "krzyzanowskim/CryptoSwift" "0.10.0" 3 | -------------------------------------------------------------------------------- /HypeMachineAPI.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "HypeMachineAPI" 3 | s.version = "1.1.0" 4 | s.summary = "This is a partial implementation of the Hype Machine API in Swift." 5 | s.homepage = "https://github.com/PlugForMac/HypeMachineAPI" 6 | s.license = { :type => "MIT", :file => "LICENSE" } 7 | s.author = { "Alex Marchant" => "alexjmarchant@gmail.com" } 8 | s.platform = :osx, "10.11" 9 | s.source = { :git => "https://github.com/PlugForMac/HypeMachineAPI.git", :tag => s.version } 10 | s.source_files = 'Source/*.swift' 11 | s.requires_arc = true 12 | s.dependency "Alamofire", "~> 4.0.1" 13 | s.dependency "CryptoSwift", "~> 0.10.0" 14 | end 15 | -------------------------------------------------------------------------------- /HypeMachineAPI.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | FA0E22151DC0FE2E00C9515B /* Validations.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA0E22141DC0FE2E00C9515B /* Validations.swift */; }; 11 | FA107A7F1B03E618006FA015 /* Requests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA107A7E1B03E618006FA015 /* Requests.swift */; }; 12 | FA107A811B03E7C8006FA015 /* BlogsRequests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA107A801B03E7C8006FA015 /* BlogsRequests.swift */; }; 13 | FA107A901B042811006FA015 /* TracksRequests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA107A8F1B042811006FA015 /* TracksRequests.swift */; }; 14 | FA107A921B042938006FA015 /* ArtistsRequests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA107A911B042938006FA015 /* ArtistsRequests.swift */; }; 15 | FA107A941B042A5D006FA015 /* TagsRequests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA107A931B042A5D006FA015 /* TagsRequests.swift */; }; 16 | FA107A961B042BAD006FA015 /* UsersRequests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA107A951B042BAD006FA015 /* UsersRequests.swift */; }; 17 | FA107A981B04494A006FA015 /* MeRequests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA107A971B04494A006FA015 /* MeRequests.swift */; }; 18 | FA107A9A1B044ED9006FA015 /* MiscRequests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA107A991B044ED9006FA015 /* MiscRequests.swift */; }; 19 | FA107AA31B0459D2006FA015 /* Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA107AA21B0459D2006FA015 /* Dictionary.swift */; }; 20 | FA21C3C31B01078900C1EAA8 /* MiscRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA21C3C21B01078900C1EAA8 /* MiscRouter.swift */; }; 21 | FA277E481B31C941009254A0 /* Cartfile in Resources */ = {isa = PBXBuildFile; fileRef = FA277E471B31C941009254A0 /* Cartfile */; }; 22 | FA277E4A1B31C966009254A0 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = FA277E491B31C966009254A0 /* LICENSE */; }; 23 | FA277E4C1B31CE84009254A0 /* HypeMachineAPI.podspec in Resources */ = {isa = PBXBuildFile; fileRef = FA277E4B1B31CE84009254A0 /* HypeMachineAPI.podspec */; }; 24 | FA4D18ED1B0112C10043A040 /* Tags.json in Resources */ = {isa = PBXBuildFile; fileRef = FA4D18EB1B0112BA0043A040 /* Tags.json */; }; 25 | FA4D18EF1B0112F80043A040 /* TagTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA4D18EE1B0112F80043A040 /* TagTests.swift */; }; 26 | FA4D18F11B01132E0043A040 /* Tag.json in Resources */ = {isa = PBXBuildFile; fileRef = FA4D18F01B01132E0043A040 /* Tag.json */; }; 27 | FA4D18F31B0114730043A040 /* TagsRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA4D18F21B0114730043A040 /* TagsRouter.swift */; }; 28 | FA5B33DC1AFF4FF6009F2F95 /* HypeMachineAPI.h in Headers */ = {isa = PBXBuildFile; fileRef = FA5B33DB1AFF4FF6009F2F95 /* HypeMachineAPI.h */; settings = {ATTRIBUTES = (Public, ); }; }; 29 | FA5B33E21AFF4FF6009F2F95 /* HypeMachineAPI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA5B33D61AFF4FF6009F2F95 /* HypeMachineAPI.framework */; }; 30 | FA5B33F51AFF5286009F2F95 /* HypeMachineAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5B33F41AFF5286009F2F95 /* HypeMachineAPI.swift */; }; 31 | FA5B33F81AFF540F009F2F95 /* Blog.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5B33F71AFF540F009F2F95 /* Blog.swift */; }; 32 | FA5B33FA1AFF5414009F2F95 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5B33F91AFF5414009F2F95 /* User.swift */; }; 33 | FA5B33FC1AFF5419009F2F95 /* Tag.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5B33FB1AFF5419009F2F95 /* Tag.swift */; }; 34 | FA5B340B1AFF5447009F2F95 /* Track.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5B340A1AFF5447009F2F95 /* Track.swift */; }; 35 | FA5B340F1AFF54D5009F2F95 /* ResponseSerializers.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5B340E1AFF54D5009F2F95 /* ResponseSerializers.swift */; }; 36 | FA5B34121B002013009F2F95 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5B34111B002013009F2F95 /* Router.swift */; }; 37 | FA5B34141B002061009F2F95 /* TracksRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5B34131B002061009F2F95 /* TracksRouter.swift */; }; 38 | FA5B341A1B008DC4009F2F95 /* Track.json in Resources */ = {isa = PBXBuildFile; fileRef = FA5B34181B008BA2009F2F95 /* Track.json */; }; 39 | FA5B341D1B009052009F2F95 /* Tracks.json in Resources */ = {isa = PBXBuildFile; fileRef = FA5B341B1B008F6A009F2F95 /* Tracks.json */; }; 40 | FA5B341F1B00F23B009F2F95 /* BlogsRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5B341E1B00F23B009F2F95 /* BlogsRouter.swift */; }; 41 | FA5B34211B00F345009F2F95 /* TrackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5B34201B00F345009F2F95 /* TrackTests.swift */; }; 42 | FA5B34231B00F3AA009F2F95 /* BlogTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5B34221B00F3AA009F2F95 /* BlogTests.swift */; }; 43 | FA5B34271B00F558009F2F95 /* Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5B34251B00F42C009F2F95 /* Fixtures.swift */; }; 44 | FA5B34291B00F699009F2F95 /* Blogs.json in Resources */ = {isa = PBXBuildFile; fileRef = FA5B34281B00F699009F2F95 /* Blogs.json */; }; 45 | FA5B342B1B00F6C8009F2F95 /* Blog.json in Resources */ = {isa = PBXBuildFile; fileRef = FA5B342A1B00F6C8009F2F95 /* Blog.json */; }; 46 | FA5B342E1B00F86D009F2F95 /* ArtistsRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5B342C1B00F869009F2F95 /* ArtistsRouter.swift */; }; 47 | FA5B34301B00F934009F2F95 /* Artist.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5B342F1B00F934009F2F95 /* Artist.swift */; }; 48 | FA5B34321B00FAC4009F2F95 /* ArtistTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5B34311B00FAC4009F2F95 /* ArtistTests.swift */; }; 49 | FA5B34341B00FB25009F2F95 /* Artist.json in Resources */ = {isa = PBXBuildFile; fileRef = FA5B34331B00FB25009F2F95 /* Artist.json */; }; 50 | FA5B34361B00FB30009F2F95 /* Artists.json in Resources */ = {isa = PBXBuildFile; fileRef = FA5B34351B00FB30009F2F95 /* Artists.json */; }; 51 | FA5B34381B00FF11009F2F95 /* User.json in Resources */ = {isa = PBXBuildFile; fileRef = FA5B34371B00FF11009F2F95 /* User.json */; }; 52 | FA5B343A1B00FF2B009F2F95 /* Users.json in Resources */ = {isa = PBXBuildFile; fileRef = FA5B34391B00FF2B009F2F95 /* Users.json */; }; 53 | FA5B343C1B00FFAB009F2F95 /* UserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5B343B1B00FFAB009F2F95 /* UserTests.swift */; }; 54 | FA5B34401B01024C009F2F95 /* MeRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5B343F1B01024C009F2F95 /* MeRouter.swift */; }; 55 | FA5B34411B010251009F2F95 /* UsersRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA5B343D1B010075009F2F95 /* UsersRouter.swift */; }; 56 | FA75E8231B563C180070FB1D /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA75E8221B563C180070FB1D /* String.swift */; }; 57 | FA848AAC1D3E7D6C00AC1262 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA849A461BABA776004EACD8 /* Alamofire.framework */; }; 58 | FA849A481BABC646004EACD8 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA849A461BABA776004EACD8 /* Alamofire.framework */; }; 59 | FA8AD7461DC0429800518B0B /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA8AD7451DC0429800518B0B /* Errors.swift */; }; 60 | FABB1EBF1BABC7680068C03F /* Alamofire.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = FA849A461BABA776004EACD8 /* Alamofire.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 61 | /* End PBXBuildFile section */ 62 | 63 | /* Begin PBXContainerItemProxy section */ 64 | FA5B33E31AFF4FF6009F2F95 /* PBXContainerItemProxy */ = { 65 | isa = PBXContainerItemProxy; 66 | containerPortal = FA5B33CD1AFF4FF6009F2F95 /* Project object */; 67 | proxyType = 1; 68 | remoteGlobalIDString = FA5B33D51AFF4FF6009F2F95; 69 | remoteInfo = HypeMachineAPI; 70 | }; 71 | /* End PBXContainerItemProxy section */ 72 | 73 | /* Begin PBXCopyFilesBuildPhase section */ 74 | FA4D19021B012B440043A040 /* CopyFiles */ = { 75 | isa = PBXCopyFilesBuildPhase; 76 | buildActionMask = 2147483647; 77 | dstPath = ""; 78 | dstSubfolderSpec = 10; 79 | files = ( 80 | FABB1EBF1BABC7680068C03F /* Alamofire.framework in CopyFiles */, 81 | ); 82 | runOnlyForDeploymentPostprocessing = 0; 83 | }; 84 | /* End PBXCopyFilesBuildPhase section */ 85 | 86 | /* Begin PBXFileReference section */ 87 | FA0E22141DC0FE2E00C9515B /* Validations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Validations.swift; sourceTree = ""; }; 88 | FA107A7E1B03E618006FA015 /* Requests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Requests.swift; sourceTree = ""; }; 89 | FA107A801B03E7C8006FA015 /* BlogsRequests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlogsRequests.swift; sourceTree = ""; }; 90 | FA107A8F1B042811006FA015 /* TracksRequests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TracksRequests.swift; sourceTree = ""; }; 91 | FA107A911B042938006FA015 /* ArtistsRequests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ArtistsRequests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 92 | FA107A931B042A5D006FA015 /* TagsRequests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = TagsRequests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 93 | FA107A951B042BAD006FA015 /* UsersRequests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UsersRequests.swift; sourceTree = ""; }; 94 | FA107A971B04494A006FA015 /* MeRequests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = MeRequests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 95 | FA107A991B044ED9006FA015 /* MiscRequests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = MiscRequests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 96 | FA107AA21B0459D2006FA015 /* Dictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Dictionary.swift; sourceTree = ""; }; 97 | FA21C3C21B01078900C1EAA8 /* MiscRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MiscRouter.swift; sourceTree = ""; }; 98 | FA277E451B31C937009254A0 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 99 | FA277E471B31C941009254A0 /* Cartfile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Cartfile; sourceTree = ""; }; 100 | FA277E491B31C966009254A0 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 101 | FA277E4B1B31CE84009254A0 /* HypeMachineAPI.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HypeMachineAPI.podspec; sourceTree = ""; }; 102 | FA4D18EB1B0112BA0043A040 /* Tags.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Tags.json; sourceTree = ""; }; 103 | FA4D18EE1B0112F80043A040 /* TagTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = TagTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 104 | FA4D18F01B01132E0043A040 /* Tag.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Tag.json; sourceTree = ""; }; 105 | FA4D18F21B0114730043A040 /* TagsRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TagsRouter.swift; sourceTree = ""; }; 106 | FA5B33D61AFF4FF6009F2F95 /* HypeMachineAPI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = HypeMachineAPI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 107 | FA5B33DA1AFF4FF6009F2F95 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 108 | FA5B33DB1AFF4FF6009F2F95 /* HypeMachineAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HypeMachineAPI.h; sourceTree = ""; }; 109 | FA5B33E11AFF4FF6009F2F95 /* HypeMachineAPITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HypeMachineAPITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 110 | FA5B33E71AFF4FF6009F2F95 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 111 | FA5B33F41AFF5286009F2F95 /* HypeMachineAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HypeMachineAPI.swift; sourceTree = ""; }; 112 | FA5B33F71AFF540F009F2F95 /* Blog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Blog.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 113 | FA5B33F91AFF5414009F2F95 /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; 114 | FA5B33FB1AFF5419009F2F95 /* Tag.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Tag.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 115 | FA5B340A1AFF5447009F2F95 /* Track.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Track.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 116 | FA5B340E1AFF54D5009F2F95 /* ResponseSerializers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ResponseSerializers.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 117 | FA5B34111B002013009F2F95 /* Router.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = ""; }; 118 | FA5B34131B002061009F2F95 /* TracksRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TracksRouter.swift; sourceTree = ""; }; 119 | FA5B34181B008BA2009F2F95 /* Track.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Track.json; sourceTree = ""; }; 120 | FA5B341B1B008F6A009F2F95 /* Tracks.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Tracks.json; sourceTree = ""; }; 121 | FA5B341E1B00F23B009F2F95 /* BlogsRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlogsRouter.swift; sourceTree = ""; }; 122 | FA5B34201B00F345009F2F95 /* TrackTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackTests.swift; sourceTree = ""; }; 123 | FA5B34221B00F3AA009F2F95 /* BlogTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = BlogTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 124 | FA5B34251B00F42C009F2F95 /* Fixtures.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Fixtures.swift; sourceTree = ""; }; 125 | FA5B34281B00F699009F2F95 /* Blogs.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Blogs.json; sourceTree = ""; }; 126 | FA5B342A1B00F6C8009F2F95 /* Blog.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Blog.json; sourceTree = ""; }; 127 | FA5B342C1B00F869009F2F95 /* ArtistsRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArtistsRouter.swift; sourceTree = ""; }; 128 | FA5B342F1B00F934009F2F95 /* Artist.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Artist.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 129 | FA5B34311B00FAC4009F2F95 /* ArtistTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ArtistTests.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 130 | FA5B34331B00FB25009F2F95 /* Artist.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Artist.json; sourceTree = ""; }; 131 | FA5B34351B00FB30009F2F95 /* Artists.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Artists.json; sourceTree = ""; }; 132 | FA5B34371B00FF11009F2F95 /* User.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = User.json; sourceTree = ""; }; 133 | FA5B34391B00FF2B009F2F95 /* Users.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Users.json; sourceTree = ""; }; 134 | FA5B343B1B00FFAB009F2F95 /* UserTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserTests.swift; sourceTree = ""; }; 135 | FA5B343D1B010075009F2F95 /* UsersRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UsersRouter.swift; sourceTree = ""; }; 136 | FA5B343F1B01024C009F2F95 /* MeRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MeRouter.swift; sourceTree = ""; }; 137 | FA75E8221B563C180070FB1D /* String.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; 138 | FA849A461BABA776004EACD8 /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = Carthage/Build/Mac/Alamofire.framework; sourceTree = ""; }; 139 | FA8AD7451DC0429800518B0B /* Errors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = ""; }; 140 | /* End PBXFileReference section */ 141 | 142 | /* Begin PBXFrameworksBuildPhase section */ 143 | FA5B33D21AFF4FF6009F2F95 /* Frameworks */ = { 144 | isa = PBXFrameworksBuildPhase; 145 | buildActionMask = 2147483647; 146 | files = ( 147 | FA848AAC1D3E7D6C00AC1262 /* Alamofire.framework in Frameworks */, 148 | ); 149 | runOnlyForDeploymentPostprocessing = 0; 150 | }; 151 | FA5B33DE1AFF4FF6009F2F95 /* Frameworks */ = { 152 | isa = PBXFrameworksBuildPhase; 153 | buildActionMask = 2147483647; 154 | files = ( 155 | FA5B33E21AFF4FF6009F2F95 /* HypeMachineAPI.framework in Frameworks */, 156 | FA849A481BABC646004EACD8 /* Alamofire.framework in Frameworks */, 157 | ); 158 | runOnlyForDeploymentPostprocessing = 0; 159 | }; 160 | /* End PBXFrameworksBuildPhase section */ 161 | 162 | /* Begin PBXGroup section */ 163 | FA107A7D1B03E5EA006FA015 /* Requests */ = { 164 | isa = PBXGroup; 165 | children = ( 166 | FA107A7E1B03E618006FA015 /* Requests.swift */, 167 | FA0E22141DC0FE2E00C9515B /* Validations.swift */, 168 | FA8AD7451DC0429800518B0B /* Errors.swift */, 169 | FA5B340E1AFF54D5009F2F95 /* ResponseSerializers.swift */, 170 | FA107A801B03E7C8006FA015 /* BlogsRequests.swift */, 171 | FA107A8F1B042811006FA015 /* TracksRequests.swift */, 172 | FA107A911B042938006FA015 /* ArtistsRequests.swift */, 173 | FA107A931B042A5D006FA015 /* TagsRequests.swift */, 174 | FA107A951B042BAD006FA015 /* UsersRequests.swift */, 175 | FA107A971B04494A006FA015 /* MeRequests.swift */, 176 | FA107A991B044ED9006FA015 /* MiscRequests.swift */, 177 | ); 178 | name = Requests; 179 | sourceTree = ""; 180 | }; 181 | FA4D18F41B0115830043A040 /* Models */ = { 182 | isa = PBXGroup; 183 | children = ( 184 | FA5B34221B00F3AA009F2F95 /* BlogTests.swift */, 185 | FA5B34201B00F345009F2F95 /* TrackTests.swift */, 186 | FA5B34311B00FAC4009F2F95 /* ArtistTests.swift */, 187 | FA4D18EE1B0112F80043A040 /* TagTests.swift */, 188 | FA5B343B1B00FFAB009F2F95 /* UserTests.swift */, 189 | ); 190 | name = Models; 191 | sourceTree = ""; 192 | }; 193 | FA4D18FF1B0129C20043A040 /* Frameworks */ = { 194 | isa = PBXGroup; 195 | children = ( 196 | FA849A461BABA776004EACD8 /* Alamofire.framework */, 197 | ); 198 | name = Frameworks; 199 | sourceTree = ""; 200 | }; 201 | FA5B33CC1AFF4FF6009F2F95 = { 202 | isa = PBXGroup; 203 | children = ( 204 | FA5B33D81AFF4FF6009F2F95 /* Source */, 205 | FA5B33E51AFF4FF6009F2F95 /* Tests */, 206 | FA5B33D71AFF4FF6009F2F95 /* Products */, 207 | FA4D18FF1B0129C20043A040 /* Frameworks */, 208 | FA277E451B31C937009254A0 /* README.md */, 209 | FA277E491B31C966009254A0 /* LICENSE */, 210 | FA277E471B31C941009254A0 /* Cartfile */, 211 | FA277E4B1B31CE84009254A0 /* HypeMachineAPI.podspec */, 212 | ); 213 | sourceTree = ""; 214 | }; 215 | FA5B33D71AFF4FF6009F2F95 /* Products */ = { 216 | isa = PBXGroup; 217 | children = ( 218 | FA5B33D61AFF4FF6009F2F95 /* HypeMachineAPI.framework */, 219 | FA5B33E11AFF4FF6009F2F95 /* HypeMachineAPITests.xctest */, 220 | ); 221 | name = Products; 222 | sourceTree = ""; 223 | }; 224 | FA5B33D81AFF4FF6009F2F95 /* Source */ = { 225 | isa = PBXGroup; 226 | children = ( 227 | FA5B33DB1AFF4FF6009F2F95 /* HypeMachineAPI.h */, 228 | FA5B33F41AFF5286009F2F95 /* HypeMachineAPI.swift */, 229 | FA5B33F61AFF53E2009F2F95 /* Models */, 230 | FA5B34101B001FBD009F2F95 /* Routers */, 231 | FA107A7D1B03E5EA006FA015 /* Requests */, 232 | FA5B340C1AFF54B8009F2F95 /* Extensions */, 233 | FA5B33D91AFF4FF6009F2F95 /* Supporting Files */, 234 | ); 235 | path = Source; 236 | sourceTree = ""; 237 | }; 238 | FA5B33D91AFF4FF6009F2F95 /* Supporting Files */ = { 239 | isa = PBXGroup; 240 | children = ( 241 | FA5B33DA1AFF4FF6009F2F95 /* Info.plist */, 242 | ); 243 | name = "Supporting Files"; 244 | sourceTree = ""; 245 | }; 246 | FA5B33E51AFF4FF6009F2F95 /* Tests */ = { 247 | isa = PBXGroup; 248 | children = ( 249 | FA4D18F41B0115830043A040 /* Models */, 250 | FA5B34241B00F404009F2F95 /* Helpers */, 251 | FA5B34171B008B7E009F2F95 /* Response Fixtures */, 252 | FA5B33E61AFF4FF6009F2F95 /* Supporting Files */, 253 | ); 254 | path = Tests; 255 | sourceTree = ""; 256 | }; 257 | FA5B33E61AFF4FF6009F2F95 /* Supporting Files */ = { 258 | isa = PBXGroup; 259 | children = ( 260 | FA5B33E71AFF4FF6009F2F95 /* Info.plist */, 261 | ); 262 | name = "Supporting Files"; 263 | sourceTree = ""; 264 | }; 265 | FA5B33F61AFF53E2009F2F95 /* Models */ = { 266 | isa = PBXGroup; 267 | children = ( 268 | FA5B33F71AFF540F009F2F95 /* Blog.swift */, 269 | FA5B340A1AFF5447009F2F95 /* Track.swift */, 270 | FA5B342F1B00F934009F2F95 /* Artist.swift */, 271 | FA5B33FB1AFF5419009F2F95 /* Tag.swift */, 272 | FA5B33F91AFF5414009F2F95 /* User.swift */, 273 | ); 274 | name = Models; 275 | sourceTree = ""; 276 | }; 277 | FA5B340C1AFF54B8009F2F95 /* Extensions */ = { 278 | isa = PBXGroup; 279 | children = ( 280 | FA107AA21B0459D2006FA015 /* Dictionary.swift */, 281 | FA75E8221B563C180070FB1D /* String.swift */, 282 | ); 283 | name = Extensions; 284 | sourceTree = ""; 285 | }; 286 | FA5B34101B001FBD009F2F95 /* Routers */ = { 287 | isa = PBXGroup; 288 | children = ( 289 | FA5B34111B002013009F2F95 /* Router.swift */, 290 | FA5B341E1B00F23B009F2F95 /* BlogsRouter.swift */, 291 | FA5B34131B002061009F2F95 /* TracksRouter.swift */, 292 | FA5B342C1B00F869009F2F95 /* ArtistsRouter.swift */, 293 | FA4D18F21B0114730043A040 /* TagsRouter.swift */, 294 | FA5B343D1B010075009F2F95 /* UsersRouter.swift */, 295 | FA5B343F1B01024C009F2F95 /* MeRouter.swift */, 296 | FA21C3C21B01078900C1EAA8 /* MiscRouter.swift */, 297 | ); 298 | name = Routers; 299 | sourceTree = ""; 300 | }; 301 | FA5B34171B008B7E009F2F95 /* Response Fixtures */ = { 302 | isa = PBXGroup; 303 | children = ( 304 | FA5B342A1B00F6C8009F2F95 /* Blog.json */, 305 | FA5B34281B00F699009F2F95 /* Blogs.json */, 306 | FA5B34181B008BA2009F2F95 /* Track.json */, 307 | FA5B341B1B008F6A009F2F95 /* Tracks.json */, 308 | FA5B34331B00FB25009F2F95 /* Artist.json */, 309 | FA5B34351B00FB30009F2F95 /* Artists.json */, 310 | FA4D18F01B01132E0043A040 /* Tag.json */, 311 | FA4D18EB1B0112BA0043A040 /* Tags.json */, 312 | FA5B34371B00FF11009F2F95 /* User.json */, 313 | FA5B34391B00FF2B009F2F95 /* Users.json */, 314 | ); 315 | name = "Response Fixtures"; 316 | sourceTree = ""; 317 | }; 318 | FA5B34241B00F404009F2F95 /* Helpers */ = { 319 | isa = PBXGroup; 320 | children = ( 321 | FA5B34251B00F42C009F2F95 /* Fixtures.swift */, 322 | ); 323 | name = Helpers; 324 | sourceTree = ""; 325 | }; 326 | /* End PBXGroup section */ 327 | 328 | /* Begin PBXHeadersBuildPhase section */ 329 | FA5B33D31AFF4FF6009F2F95 /* Headers */ = { 330 | isa = PBXHeadersBuildPhase; 331 | buildActionMask = 2147483647; 332 | files = ( 333 | FA5B33DC1AFF4FF6009F2F95 /* HypeMachineAPI.h in Headers */, 334 | ); 335 | runOnlyForDeploymentPostprocessing = 0; 336 | }; 337 | /* End PBXHeadersBuildPhase section */ 338 | 339 | /* Begin PBXNativeTarget section */ 340 | FA5B33D51AFF4FF6009F2F95 /* HypeMachineAPI */ = { 341 | isa = PBXNativeTarget; 342 | buildConfigurationList = FA5B33EC1AFF4FF6009F2F95 /* Build configuration list for PBXNativeTarget "HypeMachineAPI" */; 343 | buildPhases = ( 344 | FA5B33D11AFF4FF6009F2F95 /* Sources */, 345 | FA5B33D21AFF4FF6009F2F95 /* Frameworks */, 346 | FA5B33D31AFF4FF6009F2F95 /* Headers */, 347 | FA5B33D41AFF4FF6009F2F95 /* Resources */, 348 | ); 349 | buildRules = ( 350 | ); 351 | dependencies = ( 352 | ); 353 | name = HypeMachineAPI; 354 | productName = HypeMachineAPI; 355 | productReference = FA5B33D61AFF4FF6009F2F95 /* HypeMachineAPI.framework */; 356 | productType = "com.apple.product-type.framework"; 357 | }; 358 | FA5B33E01AFF4FF6009F2F95 /* HypeMachineAPITests */ = { 359 | isa = PBXNativeTarget; 360 | buildConfigurationList = FA5B33EF1AFF4FF6009F2F95 /* Build configuration list for PBXNativeTarget "HypeMachineAPITests" */; 361 | buildPhases = ( 362 | FA5B33DD1AFF4FF6009F2F95 /* Sources */, 363 | FA5B33DE1AFF4FF6009F2F95 /* Frameworks */, 364 | FA5B33DF1AFF4FF6009F2F95 /* Resources */, 365 | FA4D19021B012B440043A040 /* CopyFiles */, 366 | ); 367 | buildRules = ( 368 | ); 369 | dependencies = ( 370 | FA5B33E41AFF4FF6009F2F95 /* PBXTargetDependency */, 371 | ); 372 | name = HypeMachineAPITests; 373 | productName = HypeMachineAPITests; 374 | productReference = FA5B33E11AFF4FF6009F2F95 /* HypeMachineAPITests.xctest */; 375 | productType = "com.apple.product-type.bundle.unit-test"; 376 | }; 377 | /* End PBXNativeTarget section */ 378 | 379 | /* Begin PBXProject section */ 380 | FA5B33CD1AFF4FF6009F2F95 /* Project object */ = { 381 | isa = PBXProject; 382 | attributes = { 383 | LastSwiftMigration = 0700; 384 | LastSwiftUpdateCheck = 0700; 385 | LastUpgradeCheck = 0800; 386 | ORGANIZATIONNAME = Plug; 387 | TargetAttributes = { 388 | FA5B33D51AFF4FF6009F2F95 = { 389 | CreatedOnToolsVersion = 6.3.1; 390 | DevelopmentTeam = XM22JRGFT9; 391 | LastSwiftMigration = 0800; 392 | }; 393 | FA5B33E01AFF4FF6009F2F95 = { 394 | CreatedOnToolsVersion = 6.3.1; 395 | LastSwiftMigration = 0800; 396 | }; 397 | }; 398 | }; 399 | buildConfigurationList = FA5B33D01AFF4FF6009F2F95 /* Build configuration list for PBXProject "HypeMachineAPI" */; 400 | compatibilityVersion = "Xcode 3.2"; 401 | developmentRegion = English; 402 | hasScannedForEncodings = 0; 403 | knownRegions = ( 404 | en, 405 | ); 406 | mainGroup = FA5B33CC1AFF4FF6009F2F95; 407 | productRefGroup = FA5B33D71AFF4FF6009F2F95 /* Products */; 408 | projectDirPath = ""; 409 | projectRoot = ""; 410 | targets = ( 411 | FA5B33D51AFF4FF6009F2F95 /* HypeMachineAPI */, 412 | FA5B33E01AFF4FF6009F2F95 /* HypeMachineAPITests */, 413 | ); 414 | }; 415 | /* End PBXProject section */ 416 | 417 | /* Begin PBXResourcesBuildPhase section */ 418 | FA5B33D41AFF4FF6009F2F95 /* Resources */ = { 419 | isa = PBXResourcesBuildPhase; 420 | buildActionMask = 2147483647; 421 | files = ( 422 | FA277E4C1B31CE84009254A0 /* HypeMachineAPI.podspec in Resources */, 423 | FA277E4A1B31C966009254A0 /* LICENSE in Resources */, 424 | FA277E481B31C941009254A0 /* Cartfile in Resources */, 425 | ); 426 | runOnlyForDeploymentPostprocessing = 0; 427 | }; 428 | FA5B33DF1AFF4FF6009F2F95 /* Resources */ = { 429 | isa = PBXResourcesBuildPhase; 430 | buildActionMask = 2147483647; 431 | files = ( 432 | FA5B34341B00FB25009F2F95 /* Artist.json in Resources */, 433 | FA5B341A1B008DC4009F2F95 /* Track.json in Resources */, 434 | FA5B342B1B00F6C8009F2F95 /* Blog.json in Resources */, 435 | FA5B34381B00FF11009F2F95 /* User.json in Resources */, 436 | FA4D18F11B01132E0043A040 /* Tag.json in Resources */, 437 | FA5B343A1B00FF2B009F2F95 /* Users.json in Resources */, 438 | FA5B34291B00F699009F2F95 /* Blogs.json in Resources */, 439 | FA4D18ED1B0112C10043A040 /* Tags.json in Resources */, 440 | FA5B34361B00FB30009F2F95 /* Artists.json in Resources */, 441 | FA5B341D1B009052009F2F95 /* Tracks.json in Resources */, 442 | ); 443 | runOnlyForDeploymentPostprocessing = 0; 444 | }; 445 | /* End PBXResourcesBuildPhase section */ 446 | 447 | /* Begin PBXSourcesBuildPhase section */ 448 | FA5B33D11AFF4FF6009F2F95 /* Sources */ = { 449 | isa = PBXSourcesBuildPhase; 450 | buildActionMask = 2147483647; 451 | files = ( 452 | FA5B33FA1AFF5414009F2F95 /* User.swift in Sources */, 453 | FA5B340B1AFF5447009F2F95 /* Track.swift in Sources */, 454 | FA107AA31B0459D2006FA015 /* Dictionary.swift in Sources */, 455 | FA75E8231B563C180070FB1D /* String.swift in Sources */, 456 | FA107A9A1B044ED9006FA015 /* MiscRequests.swift in Sources */, 457 | FA107A941B042A5D006FA015 /* TagsRequests.swift in Sources */, 458 | FA107A961B042BAD006FA015 /* UsersRequests.swift in Sources */, 459 | FA5B34411B010251009F2F95 /* UsersRouter.swift in Sources */, 460 | FA5B341F1B00F23B009F2F95 /* BlogsRouter.swift in Sources */, 461 | FA107A7F1B03E618006FA015 /* Requests.swift in Sources */, 462 | FA5B33F51AFF5286009F2F95 /* HypeMachineAPI.swift in Sources */, 463 | FA5B342E1B00F86D009F2F95 /* ArtistsRouter.swift in Sources */, 464 | FA5B33F81AFF540F009F2F95 /* Blog.swift in Sources */, 465 | FA0E22151DC0FE2E00C9515B /* Validations.swift in Sources */, 466 | FA5B33FC1AFF5419009F2F95 /* Tag.swift in Sources */, 467 | FA5B340F1AFF54D5009F2F95 /* ResponseSerializers.swift in Sources */, 468 | FA107A811B03E7C8006FA015 /* BlogsRequests.swift in Sources */, 469 | FA21C3C31B01078900C1EAA8 /* MiscRouter.swift in Sources */, 470 | FA5B34401B01024C009F2F95 /* MeRouter.swift in Sources */, 471 | FA5B34301B00F934009F2F95 /* Artist.swift in Sources */, 472 | FA107A901B042811006FA015 /* TracksRequests.swift in Sources */, 473 | FA8AD7461DC0429800518B0B /* Errors.swift in Sources */, 474 | FA4D18F31B0114730043A040 /* TagsRouter.swift in Sources */, 475 | FA107A981B04494A006FA015 /* MeRequests.swift in Sources */, 476 | FA5B34121B002013009F2F95 /* Router.swift in Sources */, 477 | FA107A921B042938006FA015 /* ArtistsRequests.swift in Sources */, 478 | FA5B34141B002061009F2F95 /* TracksRouter.swift in Sources */, 479 | ); 480 | runOnlyForDeploymentPostprocessing = 0; 481 | }; 482 | FA5B33DD1AFF4FF6009F2F95 /* Sources */ = { 483 | isa = PBXSourcesBuildPhase; 484 | buildActionMask = 2147483647; 485 | files = ( 486 | FA5B34211B00F345009F2F95 /* TrackTests.swift in Sources */, 487 | FA5B34231B00F3AA009F2F95 /* BlogTests.swift in Sources */, 488 | FA4D18EF1B0112F80043A040 /* TagTests.swift in Sources */, 489 | FA5B343C1B00FFAB009F2F95 /* UserTests.swift in Sources */, 490 | FA5B34321B00FAC4009F2F95 /* ArtistTests.swift in Sources */, 491 | FA5B34271B00F558009F2F95 /* Fixtures.swift in Sources */, 492 | ); 493 | runOnlyForDeploymentPostprocessing = 0; 494 | }; 495 | /* End PBXSourcesBuildPhase section */ 496 | 497 | /* Begin PBXTargetDependency section */ 498 | FA5B33E41AFF4FF6009F2F95 /* PBXTargetDependency */ = { 499 | isa = PBXTargetDependency; 500 | target = FA5B33D51AFF4FF6009F2F95 /* HypeMachineAPI */; 501 | targetProxy = FA5B33E31AFF4FF6009F2F95 /* PBXContainerItemProxy */; 502 | }; 503 | /* End PBXTargetDependency section */ 504 | 505 | /* Begin XCBuildConfiguration section */ 506 | FA5B33EA1AFF4FF6009F2F95 /* Debug */ = { 507 | isa = XCBuildConfiguration; 508 | buildSettings = { 509 | ALWAYS_SEARCH_USER_PATHS = NO; 510 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 511 | CLANG_CXX_LIBRARY = "libc++"; 512 | CLANG_ENABLE_MODULES = YES; 513 | CLANG_ENABLE_OBJC_ARC = YES; 514 | CLANG_WARN_BOOL_CONVERSION = YES; 515 | CLANG_WARN_CONSTANT_CONVERSION = YES; 516 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 517 | CLANG_WARN_EMPTY_BODY = YES; 518 | CLANG_WARN_ENUM_CONVERSION = YES; 519 | CLANG_WARN_INFINITE_RECURSION = YES; 520 | CLANG_WARN_INT_CONVERSION = YES; 521 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 522 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 523 | CLANG_WARN_UNREACHABLE_CODE = YES; 524 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 525 | COPY_PHASE_STRIP = NO; 526 | CURRENT_PROJECT_VERSION = 1; 527 | DEBUG_INFORMATION_FORMAT = dwarf; 528 | ENABLE_STRICT_OBJC_MSGSEND = YES; 529 | ENABLE_TESTABILITY = YES; 530 | GCC_C_LANGUAGE_STANDARD = gnu99; 531 | GCC_DYNAMIC_NO_PIC = NO; 532 | GCC_NO_COMMON_BLOCKS = YES; 533 | GCC_OPTIMIZATION_LEVEL = 0; 534 | GCC_PREPROCESSOR_DEFINITIONS = ( 535 | "DEBUG=1", 536 | "$(inherited)", 537 | ); 538 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 539 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 540 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 541 | GCC_WARN_UNDECLARED_SELECTOR = YES; 542 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 543 | GCC_WARN_UNUSED_FUNCTION = YES; 544 | GCC_WARN_UNUSED_VARIABLE = YES; 545 | MACOSX_DEPLOYMENT_TARGET = 10.11; 546 | MTL_ENABLE_DEBUG_INFO = YES; 547 | ONLY_ACTIVE_ARCH = YES; 548 | SDKROOT = macosx; 549 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 550 | VERSIONING_SYSTEM = "apple-generic"; 551 | VERSION_INFO_PREFIX = ""; 552 | }; 553 | name = Debug; 554 | }; 555 | FA5B33EB1AFF4FF6009F2F95 /* Release */ = { 556 | isa = XCBuildConfiguration; 557 | buildSettings = { 558 | ALWAYS_SEARCH_USER_PATHS = NO; 559 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 560 | CLANG_CXX_LIBRARY = "libc++"; 561 | CLANG_ENABLE_MODULES = YES; 562 | CLANG_ENABLE_OBJC_ARC = YES; 563 | CLANG_WARN_BOOL_CONVERSION = YES; 564 | CLANG_WARN_CONSTANT_CONVERSION = YES; 565 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 566 | CLANG_WARN_EMPTY_BODY = YES; 567 | CLANG_WARN_ENUM_CONVERSION = YES; 568 | CLANG_WARN_INFINITE_RECURSION = YES; 569 | CLANG_WARN_INT_CONVERSION = YES; 570 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 571 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 572 | CLANG_WARN_UNREACHABLE_CODE = YES; 573 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 574 | COPY_PHASE_STRIP = NO; 575 | CURRENT_PROJECT_VERSION = 1; 576 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 577 | ENABLE_NS_ASSERTIONS = NO; 578 | ENABLE_STRICT_OBJC_MSGSEND = YES; 579 | GCC_C_LANGUAGE_STANDARD = gnu99; 580 | GCC_NO_COMMON_BLOCKS = YES; 581 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 582 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 583 | GCC_WARN_UNDECLARED_SELECTOR = YES; 584 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 585 | GCC_WARN_UNUSED_FUNCTION = YES; 586 | GCC_WARN_UNUSED_VARIABLE = YES; 587 | MACOSX_DEPLOYMENT_TARGET = 10.11; 588 | MTL_ENABLE_DEBUG_INFO = NO; 589 | SDKROOT = macosx; 590 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 591 | VERSIONING_SYSTEM = "apple-generic"; 592 | VERSION_INFO_PREFIX = ""; 593 | }; 594 | name = Release; 595 | }; 596 | FA5B33ED1AFF4FF6009F2F95 /* Debug */ = { 597 | isa = XCBuildConfiguration; 598 | buildSettings = { 599 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; 600 | APPLICATION_EXTENSION_API_ONLY = YES; 601 | CLANG_ENABLE_MODULES = YES; 602 | COMBINE_HIDPI_IMAGES = YES; 603 | DEFINES_MODULE = YES; 604 | DYLIB_COMPATIBILITY_VERSION = 1; 605 | DYLIB_CURRENT_VERSION = 1; 606 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 607 | FRAMEWORK_SEARCH_PATHS = ( 608 | "$(inherited)", 609 | "$(PROJECT_DIR)/Carthage/Build/Mac", 610 | "$(PROJECT_DIR)/Carthage/Build/iOS", 611 | ); 612 | FRAMEWORK_VERSION = A; 613 | INFOPLIST_FILE = Source/Info.plist; 614 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 615 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 616 | MACOSX_DEPLOYMENT_TARGET = 10.11; 617 | PRODUCT_BUNDLE_IDENTIFIER = "com.Plug.$(PRODUCT_NAME:rfc1034identifier)"; 618 | PRODUCT_NAME = "$(TARGET_NAME)"; 619 | SKIP_INSTALL = YES; 620 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 621 | SWIFT_VERSION = 3.0; 622 | }; 623 | name = Debug; 624 | }; 625 | FA5B33EE1AFF4FF6009F2F95 /* Release */ = { 626 | isa = XCBuildConfiguration; 627 | buildSettings = { 628 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; 629 | APPLICATION_EXTENSION_API_ONLY = YES; 630 | CLANG_ENABLE_MODULES = YES; 631 | COMBINE_HIDPI_IMAGES = YES; 632 | DEFINES_MODULE = YES; 633 | DYLIB_COMPATIBILITY_VERSION = 1; 634 | DYLIB_CURRENT_VERSION = 1; 635 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 636 | FRAMEWORK_SEARCH_PATHS = ( 637 | "$(inherited)", 638 | "$(PROJECT_DIR)/Carthage/Build/Mac", 639 | "$(PROJECT_DIR)/Carthage/Build/iOS", 640 | ); 641 | FRAMEWORK_VERSION = A; 642 | INFOPLIST_FILE = Source/Info.plist; 643 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 644 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 645 | MACOSX_DEPLOYMENT_TARGET = 10.11; 646 | PRODUCT_BUNDLE_IDENTIFIER = "com.Plug.$(PRODUCT_NAME:rfc1034identifier)"; 647 | PRODUCT_NAME = "$(TARGET_NAME)"; 648 | SKIP_INSTALL = YES; 649 | SWIFT_VERSION = 3.0; 650 | }; 651 | name = Release; 652 | }; 653 | FA5B33F01AFF4FF6009F2F95 /* Debug */ = { 654 | isa = XCBuildConfiguration; 655 | buildSettings = { 656 | COMBINE_HIDPI_IMAGES = YES; 657 | FRAMEWORK_SEARCH_PATHS = ( 658 | "$(DEVELOPER_FRAMEWORKS_DIR)", 659 | "$(inherited)", 660 | "$(PROJECT_DIR)/Carthage/Build/Mac", 661 | ); 662 | GCC_PREPROCESSOR_DEFINITIONS = ( 663 | "DEBUG=1", 664 | "$(inherited)", 665 | ); 666 | INFOPLIST_FILE = Tests/Info.plist; 667 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 668 | ONLY_ACTIVE_ARCH = YES; 669 | PRODUCT_BUNDLE_IDENTIFIER = "com.Plug.$(PRODUCT_NAME:rfc1034identifier)"; 670 | PRODUCT_NAME = "$(TARGET_NAME)"; 671 | SWIFT_VERSION = 3.0; 672 | }; 673 | name = Debug; 674 | }; 675 | FA5B33F11AFF4FF6009F2F95 /* Release */ = { 676 | isa = XCBuildConfiguration; 677 | buildSettings = { 678 | COMBINE_HIDPI_IMAGES = YES; 679 | FRAMEWORK_SEARCH_PATHS = ( 680 | "$(DEVELOPER_FRAMEWORKS_DIR)", 681 | "$(inherited)", 682 | "$(PROJECT_DIR)/Carthage/Build/Mac", 683 | ); 684 | INFOPLIST_FILE = Tests/Info.plist; 685 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 686 | PRODUCT_BUNDLE_IDENTIFIER = "com.Plug.$(PRODUCT_NAME:rfc1034identifier)"; 687 | PRODUCT_NAME = "$(TARGET_NAME)"; 688 | SWIFT_VERSION = 3.0; 689 | }; 690 | name = Release; 691 | }; 692 | /* End XCBuildConfiguration section */ 693 | 694 | /* Begin XCConfigurationList section */ 695 | FA5B33D01AFF4FF6009F2F95 /* Build configuration list for PBXProject "HypeMachineAPI" */ = { 696 | isa = XCConfigurationList; 697 | buildConfigurations = ( 698 | FA5B33EA1AFF4FF6009F2F95 /* Debug */, 699 | FA5B33EB1AFF4FF6009F2F95 /* Release */, 700 | ); 701 | defaultConfigurationIsVisible = 0; 702 | defaultConfigurationName = Release; 703 | }; 704 | FA5B33EC1AFF4FF6009F2F95 /* Build configuration list for PBXNativeTarget "HypeMachineAPI" */ = { 705 | isa = XCConfigurationList; 706 | buildConfigurations = ( 707 | FA5B33ED1AFF4FF6009F2F95 /* Debug */, 708 | FA5B33EE1AFF4FF6009F2F95 /* Release */, 709 | ); 710 | defaultConfigurationIsVisible = 0; 711 | defaultConfigurationName = Release; 712 | }; 713 | FA5B33EF1AFF4FF6009F2F95 /* Build configuration list for PBXNativeTarget "HypeMachineAPITests" */ = { 714 | isa = XCConfigurationList; 715 | buildConfigurations = ( 716 | FA5B33F01AFF4FF6009F2F95 /* Debug */, 717 | FA5B33F11AFF4FF6009F2F95 /* Release */, 718 | ); 719 | defaultConfigurationIsVisible = 0; 720 | defaultConfigurationName = Release; 721 | }; 722 | /* End XCConfigurationList section */ 723 | }; 724 | rootObject = FA5B33CD1AFF4FF6009F2F95 /* Project object */; 725 | } 726 | -------------------------------------------------------------------------------- /HypeMachineAPI.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /HypeMachineAPI.xcodeproj/xcshareddata/xcschemes/HypeMachineAPI.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 79 | 85 | 86 | 87 | 88 | 89 | 90 | 96 | 97 | 103 | 104 | 105 | 106 | 108 | 109 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /HypeMachineAPI.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Alex Marchant 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HypeMachineAPI 2 | 3 | This is a partial implementation of the Hype Machine API in Swift. It is currently targetted at OSX but could easily be modified to work for iOS as well. Please create a pull request if you'd like iOS support. 4 | 5 | ## Swift Compatability 6 | 7 | Version 1.0 and beyond have been updated to work with Swift 3. Use the previous version, 0.5.4, for Swift 2.2. 8 | 9 | ## Installation 10 | 11 | ### Carthage 12 | 13 | ```ogdl 14 | github "PlugForMac/HypeMachineAPI" ~> VERSION_NUMBER 15 | ``` 16 | 17 | ### CocoaPods 18 | 19 | ```ruby 20 | platform :osx, '10.11' 21 | use_frameworks! 22 | 23 | pod 'HypeMachineAPI', '~> VERSION_NUMBER' 24 | ``` 25 | 26 | ## Use 27 | 28 | ```swift 29 | HypeMachineAPI.Requests.Blogs.index { response in 30 | switch response.result { 31 | case .success(let blogs): 32 | print(blogs) 33 | case .failure(let error): 34 | print(error) 35 | } 36 | } 37 | ``` 38 | 39 | This API is build on top of Alamofire and uses it's data types extensively. We use their DataResponse as the response for all requests, we also use their Result type to determine success or failure. Please check out their documentation for more info. 40 | 41 | Note: requests return an instance of Alamofire DataRequest so that you can chain additional validations or make other modifications to the request if you need to. 42 | 43 | You can find the source for all requests in the `Source/*Request.swift` files. 44 | -------------------------------------------------------------------------------- /Source/Artist.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Artist.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/11/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | public struct Artist { 12 | public let name: String 13 | public let thumbURL: URL? 14 | public let cnt: Int? 15 | public let rank: Int? 16 | 17 | public init(name: String, thumbURL: URL?, cnt: Int?, rank: Int?) { 18 | self.name = name 19 | self.thumbURL = thumbURL 20 | self.cnt = cnt 21 | self.rank = rank 22 | } 23 | } 24 | 25 | extension Artist: CustomStringConvertible { 26 | public var description: String { 27 | return "Artist: { name: \(name) }" 28 | } 29 | } 30 | 31 | extension Artist: ResponseObjectSerializable, ResponseCollectionSerializable { 32 | public init?(response: HTTPURLResponse, representation: Any) { 33 | guard 34 | let representation = representation as? [String: Any], 35 | let name = representation["artist"] as? String 36 | else { return nil } 37 | 38 | func urlForJSONKey(_ key: String) -> URL? { 39 | guard let urlString = representation[key] as? String else { 40 | return nil 41 | } 42 | return URL(string: urlString) 43 | } 44 | 45 | self.name = name 46 | self.thumbURL = urlForJSONKey("thumb_url_artist") 47 | self.cnt = representation["cnt"] as? Int 48 | self.rank = representation["rank"] as? Int 49 | } 50 | } 51 | 52 | extension Artist: Equatable { 53 | public static func == (lhs: Artist, rhs: Artist) -> Bool { 54 | return lhs.name == rhs.name 55 | } 56 | } 57 | 58 | extension Artist: Hashable { 59 | public var hashValue: Int { 60 | return name.hashValue 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Source/ArtistsRequests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ArtistsRequests.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/13/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | extension Requests { 13 | public struct Artists { 14 | public static func index( 15 | params: Parameters? = nil, 16 | completionHandler: @escaping (DataResponse<[Artist]>)->Void 17 | ) -> DataRequest 18 | { 19 | return Requests 20 | .defaultRequest(Router.Artists.index(params: params)) 21 | .responseCollection(completionHandler: completionHandler) 22 | } 23 | 24 | public static func show( 25 | name: String, 26 | completionHandler: @escaping (DataResponse)->Void 27 | ) -> DataRequest 28 | { 29 | return Requests 30 | .defaultRequest(Router.Artists.show(id: name)) 31 | .responseObject(completionHandler: completionHandler) 32 | } 33 | 34 | public static func showTracks( 35 | name: String, 36 | params: Parameters? = nil, 37 | completionHandler: @escaping (DataResponse<[Track]>)->Void 38 | ) -> DataRequest 39 | { 40 | return Requests 41 | .defaultRequest(Router.Artists.showTracks(name: name, params: params)) 42 | .responseCollection(completionHandler: completionHandler) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Source/ArtistsRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ArtistsRouter.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/11/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | extension Router { 13 | public enum Artists: URLRequestConvertible { 14 | 15 | case index(params: Parameters?) 16 | case show(id: String) 17 | case showTracks(name: String, params: Parameters?) 18 | 19 | var method: HTTPMethod { 20 | switch self { 21 | case .index: 22 | return .get 23 | case .show: 24 | return .get 25 | case .showTracks: 26 | return .get 27 | } 28 | } 29 | 30 | var path: String { 31 | switch self { 32 | case .index: 33 | return "/artists" 34 | case .show(let name): 35 | let escapedName = name.stringByAddingPercentEncodingForURLQueryValue()! 36 | return "/artists/\(escapedName)" 37 | case .showTracks(let name, _): 38 | let escapedName = name.stringByAddingPercentEncodingForURLQueryValue()! 39 | return "/artists/\(escapedName)/tracks" 40 | } 41 | } 42 | 43 | var params: Parameters? { 44 | switch self { 45 | case .index(let optionalParams): 46 | return optionalParams 47 | case .show: 48 | return nil 49 | case .showTracks(_, let optionalParams): 50 | return optionalParams 51 | } 52 | } 53 | 54 | public func asURLRequest() throws -> URLRequest { 55 | return try Router.GenerateURLRequest(method: method, path: path, params: params) 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Source/Blog.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Blog.swift 3 | // Plug 4 | // 5 | // Created by Alex Marchant on 7/25/14. 6 | // Copyright (c) 2014 Plug. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | public struct Blog { 12 | public let id: Int 13 | public let name: String 14 | public let url: URL 15 | public let followerCount: Int 16 | public let trackCount: Int 17 | public let imageURL: URL 18 | public let imageURLSmall: URL 19 | public let featured: Bool 20 | public let following: Bool 21 | 22 | public init( 23 | id: Int, 24 | name: String, 25 | url: URL, 26 | followerCount: Int, 27 | trackCount: Int, 28 | imageURL: URL, 29 | imageURLSmall: URL, 30 | featured: Bool, 31 | following: Bool 32 | ) { 33 | self.id = id 34 | self.name = name 35 | self.url = url 36 | self.followerCount = followerCount 37 | self.trackCount = trackCount 38 | self.imageURL = imageURL 39 | self.imageURLSmall = imageURLSmall 40 | self.featured = featured 41 | self.following = following 42 | } 43 | 44 | public var followerCountNum: NSNumber { 45 | return NSNumber(value: followerCount) 46 | } 47 | 48 | public var trackCountNum: NSNumber { 49 | return NSNumber(value: trackCount) 50 | } 51 | 52 | public func imageURL(size: ImageSize) -> URL { 53 | switch size { 54 | case .normal: 55 | return imageURL 56 | case .small: 57 | return imageURLSmall 58 | } 59 | } 60 | 61 | public enum ImageSize { 62 | case normal 63 | case small 64 | } 65 | } 66 | 67 | extension Blog: CustomStringConvertible { 68 | public var description: String { 69 | return "Blog: { name: \(name) }" 70 | } 71 | } 72 | 73 | extension Blog: ResponseObjectSerializable, ResponseCollectionSerializable { 74 | public init?(response: HTTPURLResponse, representation: Any) { 75 | guard 76 | let representation = representation as? [String: Any], 77 | let id = representation["siteid"] as? Int, 78 | let name = representation["sitename"] as? String, 79 | let urlString = representation["siteurl"] as? String, 80 | let url = URL(string: urlString.replacingOccurrences(of: " ", with: "")), 81 | let followerCount = representation["followers"] as? Int, 82 | let trackCount = representation["total_tracks"] as? Int, 83 | let imageURLString = representation["blog_image"] as? String, 84 | let imageURL = URL(string: imageURLString), 85 | let imageURLSmallString = representation["blog_image_small"] as? String, 86 | let imageURLSmall = URL(string: imageURLSmallString) 87 | else { return nil } 88 | 89 | self.id = id 90 | self.name = name 91 | self.url = url 92 | self.followerCount = followerCount 93 | self.trackCount = trackCount 94 | self.imageURL = imageURL 95 | self.imageURLSmall = imageURLSmall 96 | self.featured = representation["ts_featured"] is Int 97 | self.following = representation["ts_loved_me"] is Int 98 | } 99 | } 100 | 101 | extension Blog: Equatable { 102 | public static func == (lhs: Blog, rhs: Blog) -> Bool { 103 | return lhs.id == rhs.id 104 | } 105 | } 106 | 107 | extension Blog: Hashable { 108 | public var hashValue: Int { 109 | return name.hashValue 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Source/BlogsRequests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BlogsRequests.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/13/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | extension Requests { 13 | public struct Blogs { 14 | public static func index( 15 | params: Parameters? = nil, 16 | completionHandler: @escaping (DataResponse<[Blog]>)->Void 17 | ) -> DataRequest 18 | { 19 | return Requests 20 | .defaultRequest(Router.Blogs.index(params: params)) 21 | .responseCollection(completionHandler: completionHandler) 22 | } 23 | 24 | public static func show( 25 | id: Int, 26 | completionHandler: @escaping (DataResponse)->Void 27 | ) -> DataRequest 28 | { 29 | return Requests 30 | .defaultRequest(Router.Blogs.show(id: id)) 31 | .responseObject(completionHandler: completionHandler) 32 | } 33 | 34 | public static func showTracks( 35 | id: Int, 36 | params: Parameters? = nil, 37 | completionHandler: @escaping (DataResponse<[Track]>)->Void 38 | ) -> DataRequest 39 | { 40 | return Requests 41 | .defaultRequest(Router.Blogs.showTracks(id: id, params: params)) 42 | .responseCollection(completionHandler: completionHandler) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Source/BlogsRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BlogsRouter.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/11/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | extension Router { 13 | public enum Blogs: URLRequestConvertible { 14 | 15 | case index(params: Parameters?) 16 | case show(id: Int) 17 | case showTracks(id: Int, params: Parameters?) 18 | 19 | var method: HTTPMethod { 20 | switch self { 21 | case .index: 22 | return .get 23 | case .show: 24 | return .get 25 | case .showTracks: 26 | return .get 27 | } 28 | } 29 | 30 | var path: String { 31 | switch self { 32 | case .index: 33 | return "/blogs" 34 | case .show(let id): 35 | return "/blogs/\(id)" 36 | case .showTracks(let id, _): 37 | return "/blogs/\(id)/tracks" 38 | } 39 | } 40 | 41 | var params: Parameters? { 42 | switch self { 43 | case .index(let optionalParams): 44 | return optionalParams 45 | case .show: 46 | return nil 47 | case .showTracks(_, let optionalParams): 48 | return optionalParams 49 | } 50 | } 51 | 52 | public func asURLRequest() throws -> URLRequest { 53 | return try Router.GenerateURLRequest(method: method, path: path, params: params) 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Source/Dictionary.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Dictionary+merge.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/14/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Dictionary /* */ { 12 | func merge(_ dictionary: Dictionary?) -> Dictionary { 13 | if dictionary == nil { return self } 14 | 15 | var newDictionary = self 16 | for key in dictionary!.keys { 17 | if newDictionary[key] != nil { continue } 18 | newDictionary[key] = dictionary![key] 19 | } 20 | 21 | return newDictionary 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Source/Errors.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Errors.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 10/25/16. 6 | // Copyright © 2016 Plug. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum APIError: Error { 12 | case network(error: Error) // Capture any underlying Error from the URLSession API 13 | case dataSerialization(error: Error) 14 | case stringSerialization(error: Error) 15 | case jsonSerialization(error: Error) 16 | case objectSerialization(reason: String) 17 | case invalidHMToken 18 | case incorrectPassword 19 | case incorrectUsername 20 | case unknownError(message: String) 21 | } 22 | -------------------------------------------------------------------------------- /Source/HypeMachineAPI.h: -------------------------------------------------------------------------------- 1 | // 2 | // HypeMachineAPI.h 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/10/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for HypeMachineAPI. 12 | FOUNDATION_EXPORT double HypeMachineAPIVersionNumber; 13 | 14 | //! Project version string for HypeMachineAPI. 15 | FOUNDATION_EXPORT const unsigned char HypeMachineAPIVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Source/HypeMachineAPI.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HypeMachineAPI.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/10/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | public var apiKey: String? 13 | public var hmToken: String? 14 | public var userAgent: String? 15 | 16 | public typealias Result = Alamofire.Result -------------------------------------------------------------------------------- /Source/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2015 Plug. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Source/MeRequests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MeRequests.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/13/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | extension Requests { 13 | public struct Me { 14 | public static func favorites( 15 | params: Parameters? = nil, 16 | completionHandler: @escaping (DataResponse<[Track]>)->Void 17 | ) -> DataRequest 18 | { 19 | return Requests 20 | .defaultRequest(Router.Me.favorites(params: params)) 21 | .responseCollection(completionHandler: completionHandler) 22 | } 23 | 24 | public static func toggleTrackFavorite( 25 | id: String, 26 | params: Parameters? = nil, 27 | completionHandler: @escaping (DataResponse)->Void 28 | ) -> DataRequest 29 | { 30 | return Requests 31 | .defaultRequest(Router.Me.toggleTrackFavorite(id: id, params: params)) 32 | .responseBool(completionHandler: completionHandler) 33 | } 34 | 35 | public static func toggleBlogFavorite( 36 | id: Int, 37 | params: Parameters? = nil, 38 | completionHandler: @escaping (DataResponse)->Void 39 | ) -> DataRequest 40 | { 41 | return Requests 42 | .defaultRequest(Router.Me.toggleBlogFavorite(id: id, params: params)) 43 | .responseBool(completionHandler: completionHandler) 44 | } 45 | 46 | public static func toggleUserFavorite( 47 | id: String, 48 | params: Parameters? = nil, 49 | completionHandler: @escaping (DataResponse)->Void 50 | ) -> DataRequest 51 | { 52 | return Requests 53 | .defaultRequest(Router.Me.toggleUserFavorite(id: id, params: params)) 54 | .responseBool(completionHandler: completionHandler) 55 | } 56 | 57 | public static func playlistNames( 58 | _ completionHandler: @escaping (DataResponse<[String]>)->Void 59 | ) -> DataRequest 60 | { 61 | return Requests 62 | .defaultRequest(Router.Me.playlistNames) 63 | .responseStringArray(completionHandler: completionHandler) 64 | } 65 | 66 | // Playlist id's are 1...3 67 | public static func showPlaylist( 68 | id: Int, 69 | params: Parameters? = nil, 70 | completionHandler: @escaping (DataResponse<[Track]>)->Void 71 | ) -> DataRequest 72 | { 73 | return Requests 74 | .defaultRequest(Router.Me.showPlaylist(id: id, params: params)) 75 | .responseCollection(completionHandler: completionHandler) 76 | } 77 | 78 | public static func postHistory( 79 | id: String, 80 | position: Int, 81 | params: Parameters? = nil, 82 | completionHandler: @escaping (DataResponse)->Void 83 | ) -> DataRequest 84 | { 85 | return Requests 86 | .defaultRequest(Router.Me.postHistory(id: id, position: position, params: params)) 87 | .responseString(completionHandler: completionHandler) 88 | } 89 | 90 | public static func friends( 91 | params: Parameters? = nil, 92 | completionHandler: @escaping (DataResponse<[User]>)->Void 93 | ) -> DataRequest 94 | { 95 | return Requests 96 | .defaultRequest(Router.Me.friends(params: params)) 97 | .responseCollection(completionHandler: completionHandler) 98 | } 99 | 100 | public static func feed( 101 | params: Parameters? = nil, 102 | completionHandler: @escaping (DataResponse<[Track]>)->Void 103 | ) -> DataRequest 104 | { 105 | return Requests 106 | .defaultRequest(Router.Me.feed(params: params)) 107 | .responseCollection(completionHandler: completionHandler) 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Source/MeRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MeRouter.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/11/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | extension Router { 13 | public enum Me: URLRequestConvertible { 14 | 15 | case favorites(params: Parameters?) 16 | case toggleTrackFavorite(id: String, params: Parameters?) 17 | case toggleBlogFavorite(id: Int, params: Parameters?) 18 | case toggleUserFavorite(id: String, params: Parameters?) 19 | case playlistNames 20 | case showPlaylist(id: Int, params: Parameters?) 21 | case postHistory(id: String, position: Int, params: Parameters?) 22 | case friends(params: Parameters?) 23 | case feed(params: Parameters?) 24 | 25 | var method: HTTPMethod { 26 | switch self { 27 | case .favorites: 28 | return .get 29 | case .toggleTrackFavorite: 30 | return .post 31 | case .toggleBlogFavorite: 32 | return .post 33 | case .toggleUserFavorite: 34 | return .post 35 | case .playlistNames: 36 | return .get 37 | case .showPlaylist: 38 | return .get 39 | case .postHistory: 40 | return .post 41 | case .friends: 42 | return .get 43 | case .feed: 44 | return .get 45 | } 46 | } 47 | 48 | var path: String { 49 | switch self { 50 | case .favorites: 51 | return "/me/favorites" 52 | case .toggleTrackFavorite: 53 | return "/me/favorites" 54 | case .toggleBlogFavorite: 55 | return "/me/favorites" 56 | case .toggleUserFavorite: 57 | return "/me/favorites" 58 | case .playlistNames: 59 | return "/me/playlist_names" 60 | case .showPlaylist(let id, _): 61 | return "/me/playlists/\(id)" 62 | case .postHistory: 63 | return "/me/history" 64 | case .friends: 65 | return "/me/friends" 66 | case .feed: 67 | return "/me/feed" 68 | } 69 | } 70 | 71 | var params: Parameters? { 72 | switch self { 73 | case .favorites(let optionalParams): 74 | return optionalParams 75 | case .toggleTrackFavorite(let id, let optionalParams): 76 | return ["val": id, "type": "item"].merge(optionalParams) 77 | case .toggleBlogFavorite(let id, let optionalParams): 78 | return ["val": id, "type": "site"].merge(optionalParams) 79 | case .toggleUserFavorite(let id, let optionalParams): 80 | return ["val": id, "type": "user"].merge(optionalParams) 81 | case .playlistNames: 82 | return nil 83 | case .showPlaylist(_, let optionalParams): 84 | return optionalParams 85 | case .postHistory(let id, let position, let optionalParams): 86 | return ["type": "listen", "itemid": id, "pos": position].merge(optionalParams) 87 | case .friends(let optionalParams): 88 | return optionalParams 89 | case .feed(let optionalParams): 90 | return optionalParams 91 | } 92 | } 93 | 94 | public func asURLRequest() throws -> URLRequest { 95 | return try Router.GenerateURLRequest(method: method, path: path, params: params) 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Source/MiscRequests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MiscRequests.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/13/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | extension Requests { 13 | public struct Misc { 14 | public static func getToken( 15 | usernameOrEmail: String, 16 | password: String, 17 | completionHandler: @escaping (DataResponse)->Void 18 | ) -> DataRequest 19 | { 20 | return Requests 21 | .defaultRequest(Router.Misc.getToken(usernameOrEmail: usernameOrEmail, password: password)) 22 | .responseUsernameAndToken(completionHandler: completionHandler) 23 | } 24 | } 25 | } 26 | 27 | public struct UsernameAndToken { 28 | public let username: String 29 | public let token: String 30 | 31 | init(username: String, token: String) { 32 | self.username = username 33 | self.token = token 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Source/MiscRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MiscRouter.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/11/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | import CryptoSwift 12 | 13 | extension Router { 14 | public enum Misc: URLRequestConvertible { 15 | 16 | case getToken(usernameOrEmail: String, password: String) 17 | 18 | var method: HTTPMethod { 19 | switch self { 20 | case .getToken: 21 | return .post 22 | } 23 | } 24 | 25 | var path: String { 26 | switch self { 27 | case .getToken: 28 | return "/get_token" 29 | } 30 | } 31 | 32 | var params: Parameters? { 33 | switch self { 34 | case .getToken(let usernameOrEmail, let password): 35 | return [ 36 | "username": usernameOrEmail as AnyObject, 37 | "password": password as AnyObject, 38 | "device_id": DeviceID() as AnyObject, 39 | ] 40 | } 41 | } 42 | 43 | public func asURLRequest() throws -> URLRequest { 44 | return try Router.GenerateURLRequest(method: method, path: path, params: params) 45 | } 46 | 47 | 48 | func DeviceID() -> String { 49 | let serialNumber = GetSerialNumber()! 50 | let hashedSerialNumber = serialNumber.md5() 51 | print(hashedSerialNumber) 52 | return hashedSerialNumber 53 | } 54 | 55 | func GetSerialNumber() -> String? { 56 | var serial: String? = nil 57 | var platformExpert: io_service_t? 58 | 59 | platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice")) 60 | 61 | if platformExpert != nil { 62 | serial = IORegistryEntryCreateCFProperty(platformExpert!, kIOPlatformSerialNumberKey as CFString!, kCFAllocatorDefault, 0).takeRetainedValue() as? String 63 | IOObjectRelease(platformExpert!) 64 | } 65 | 66 | return serial 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Source/Requests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Requests.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/13/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | public struct Requests { 13 | public static func defaultRequest(_ urlRequest: URLRequestConvertible) -> Alamofire.DataRequest { 14 | return Alamofire 15 | .request(urlRequest) 16 | .validate() 17 | .validate(Validations.apiErrorValidation) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Source/ResponseSerializers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResponseObjectSerializable.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/10/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | // MARK: ResponseObject 13 | 14 | protocol ResponseObjectSerializable { 15 | init?(response: HTTPURLResponse, representation: Any) 16 | } 17 | 18 | extension DataRequest { 19 | func responseObject ( 20 | queue: DispatchQueue? = nil, 21 | completionHandler: @escaping (DataResponse) -> Void) 22 | -> Self 23 | { 24 | let responseSerializer = DataResponseSerializer { request, response, data, error in 25 | guard error == nil else { return .failure(APIError.network(error: error!)) } 26 | 27 | let jsonResponseSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments) 28 | let result = jsonResponseSerializer.serializeResponse(request, response, data, nil) 29 | 30 | guard case let .success(jsonObject) = result else { 31 | return .failure(APIError.jsonSerialization(error: result.error!)) 32 | } 33 | 34 | guard let response = response, let responseObject = T(response: response, representation: jsonObject) else { 35 | return .failure(APIError.objectSerialization(reason: "JSON could not be serialized: \(jsonObject)")) 36 | } 37 | 38 | return .success(responseObject) 39 | } 40 | 41 | return response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler) 42 | } 43 | } 44 | 45 | // MARK: ResponseCollection 46 | 47 | protocol ResponseCollectionSerializable { 48 | static func collection(from response: HTTPURLResponse, withRepresentation representation: Any) -> [Self] 49 | } 50 | 51 | extension ResponseCollectionSerializable where Self: ResponseObjectSerializable { 52 | static func collection(from response: HTTPURLResponse, withRepresentation representation: Any) -> [Self] { 53 | var collection: [Self] = [] 54 | 55 | if let representation = representation as? [[String: Any]] { 56 | for itemRepresentation in representation { 57 | if let item = Self(response: response, representation: itemRepresentation) { 58 | collection.append(item) 59 | } 60 | } 61 | } 62 | 63 | return collection 64 | } 65 | } 66 | 67 | extension DataRequest { 68 | @discardableResult 69 | func responseCollection ( 70 | queue: DispatchQueue? = nil, 71 | completionHandler: @escaping (DataResponse<[T]>) -> Void) -> Self 72 | { 73 | let responseSerializer = DataResponseSerializer<[T]> { request, response, data, error in 74 | guard error == nil else { return .failure(APIError.network(error: error!)) } 75 | 76 | let jsonSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments) 77 | let result = jsonSerializer.serializeResponse(request, response, data, nil) 78 | 79 | guard case let .success(jsonObject) = result else { 80 | return .failure(APIError.jsonSerialization(error: result.error!)) 81 | } 82 | 83 | guard let response = response else { 84 | let reason = "Response collection could not be serialized due to nil response." 85 | return .failure(APIError.objectSerialization(reason: reason)) 86 | } 87 | 88 | return .success(T.collection(from: response, withRepresentation: jsonObject)) 89 | } 90 | 91 | return response(responseSerializer: responseSerializer, completionHandler: completionHandler) 92 | } 93 | } 94 | 95 | // MARK: ResponseBool 96 | 97 | extension DataRequest { 98 | @discardableResult 99 | func responseBool ( 100 | queue: DispatchQueue? = nil, 101 | completionHandler: @escaping (DataResponse) -> Void) -> Self 102 | { 103 | let responseSerializer = DataResponseSerializer { request, response, data, error in 104 | guard error == nil else { return .failure(APIError.network(error: error!)) } 105 | 106 | let stringSerializer = DataRequest.stringResponseSerializer() 107 | let result = stringSerializer.serializeResponse(request, response, data, nil) 108 | 109 | guard case let .success(string) = result else { 110 | return .failure(APIError.stringSerialization(error: result.error!)) 111 | } 112 | 113 | guard string == "0" || string == "1" else { 114 | let reason = "Expected a 0 or 1 in response but got something else." 115 | return .failure(APIError.objectSerialization(reason: reason)) 116 | } 117 | 118 | let bool = string == "1" 119 | 120 | return .success(bool) 121 | } 122 | 123 | return response(responseSerializer: responseSerializer, completionHandler: completionHandler) 124 | } 125 | } 126 | 127 | // MARK: ResponseStringArray 128 | 129 | extension DataRequest { 130 | @discardableResult 131 | func responseStringArray ( 132 | queue: DispatchQueue? = nil, 133 | completionHandler: @escaping (DataResponse<[String]>) -> Void) -> Self 134 | { 135 | let responseSerializer = DataResponseSerializer<[String]> { request, response, data, error in 136 | guard error == nil else { return .failure(APIError.network(error: error!)) } 137 | 138 | let jsonSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments) 139 | let result = jsonSerializer.serializeResponse(request, response, data, nil) 140 | 141 | guard case let .success(jsonObject) = result else { 142 | return .failure(APIError.jsonSerialization(error: result.error!)) 143 | } 144 | 145 | guard let responseArray = jsonObject as? [String] else { 146 | let reason = "Expected an array of strings but got something else." 147 | return .failure(APIError.objectSerialization(reason: reason)) 148 | } 149 | 150 | return .success(responseArray) 151 | } 152 | 153 | return response(responseSerializer: responseSerializer, completionHandler: completionHandler) 154 | } 155 | } 156 | 157 | // MARK: ResponseUsernameAndToken 158 | 159 | extension DataRequest { 160 | @discardableResult 161 | func responseUsernameAndToken ( 162 | queue: DispatchQueue? = nil, 163 | completionHandler: @escaping (DataResponse) -> Void) -> Self 164 | { 165 | let responseSerializer = DataResponseSerializer { request, response, data, error in 166 | guard error == nil else { return .failure(APIError.network(error: error!)) } 167 | 168 | let jsonSerializer = DataRequest.jsonResponseSerializer(options: .allowFragments) 169 | let result = jsonSerializer.serializeResponse(request, response, data, nil) 170 | 171 | guard case let .success(jsonObject) = result else { 172 | return .failure(APIError.jsonSerialization(error: result.error!)) 173 | } 174 | 175 | guard 176 | let representation = jsonObject as? [String: Any], 177 | let username = representation["username"] as? String, 178 | let token = representation["hm_token"] as? String 179 | else { 180 | let reason = "No username or token found in response." 181 | return .failure(APIError.objectSerialization(reason: reason)) 182 | } 183 | 184 | return .success(UsernameAndToken(username: username, token: token)) 185 | } 186 | 187 | return response(responseSerializer: responseSerializer, completionHandler: completionHandler) 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /Source/Router.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Router.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/10/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | public struct Router { 13 | static let baseURLString = "https://api.hypem.com/v2" 14 | 15 | static func GenerateURLRequest(method: HTTPMethod, path: String, params: Parameters?) throws -> URLRequest { 16 | 17 | // Probably a bug in the hype machine API, but hm_token must be part of the path, can't be form encoded 18 | var urlString = baseURLString + path 19 | if hmToken != nil { 20 | urlString += "?hm_token=\(hmToken!)" 21 | } 22 | let URL = try urlString.asURL() 23 | var urlRequest = URLRequest(url: URL) 24 | urlRequest.httpMethod = method.rawValue 25 | 26 | if userAgent != nil { 27 | urlRequest.addValue(userAgent!, forHTTPHeaderField: "User-Agent") 28 | } 29 | 30 | let mergedParams = addApiKeyParam(params ?? [:]) 31 | 32 | return try URLEncoding.default.encode(urlRequest, with: mergedParams) 33 | } 34 | 35 | static func addApiKeyParam(_ params: Parameters) -> Parameters { 36 | if apiKey == nil { return params } 37 | return ["key": apiKey!].merge(params) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Source/String.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 7/15/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | extension String { 12 | func stringByAddingPercentEncodingForURLQueryValue() -> String? { 13 | let characterSet = NSMutableCharacterSet.alphanumeric() 14 | characterSet.addCharacters(in: "-._~:/") 15 | 16 | return addingPercentEncoding(withAllowedCharacters: characterSet as CharacterSet) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Source/Tag.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Tag.swift 3 | // Plug 4 | // 5 | // Created by Alex Marchant on 8/1/14. 6 | // Copyright (c) 2014 Plug. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | public struct Tag { 12 | public let name: String 13 | public let priority: Bool 14 | 15 | public init(name: String, priority: Bool) { 16 | self.name = name; 17 | self.priority = priority; 18 | } 19 | } 20 | 21 | extension Tag: CustomStringConvertible { 22 | public var description: String { 23 | return "Tag: { name: \(name), priority: \(priority) }" 24 | } 25 | } 26 | 27 | extension Tag: ResponseObjectSerializable, ResponseCollectionSerializable { 28 | public init?(response: HTTPURLResponse, representation: Any) { 29 | guard 30 | let representation = representation as? [String: Any], 31 | let name = representation["tag_name"] as? String 32 | else { return nil } 33 | 34 | self.name = name 35 | self.priority = representation["priority"] as? Bool ?? false 36 | } 37 | } 38 | 39 | extension Tag: Equatable { 40 | public static func == (lhs: Tag, rhs: Tag) -> Bool { 41 | return lhs.name == rhs.name 42 | } 43 | } 44 | 45 | extension Tag: Hashable { 46 | public var hashValue: Int { 47 | return name.hashValue 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Source/TagsRequests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TagsRequests.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/13/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | extension Requests { 13 | public struct Tags { 14 | public static func index( 15 | _ completionHandler: @escaping (DataResponse<[Tag]>)->Void 16 | ) -> DataRequest 17 | { 18 | return Requests 19 | .defaultRequest(Router.Tags.index) 20 | .responseCollection(completionHandler: completionHandler) 21 | } 22 | 23 | public static func showTracks( 24 | name: String, 25 | params: Parameters? = nil, 26 | completionHandler: @escaping (DataResponse<[Track]>)->Void 27 | ) -> DataRequest 28 | { 29 | return Requests 30 | .defaultRequest(Router.Tags.showTracks(name: name, params: params)) 31 | .responseCollection(completionHandler: completionHandler) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Source/TagsRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TagsRouter.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/11/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | extension Router { 13 | public enum Tags: URLRequestConvertible { 14 | 15 | case index 16 | case showTracks(name: String, params: Parameters?) 17 | 18 | var method: HTTPMethod { 19 | switch self { 20 | case .index: 21 | return .get 22 | case .showTracks: 23 | return .get 24 | } 25 | } 26 | 27 | var path: String { 28 | switch self { 29 | case .index: 30 | return "/tags" 31 | case .showTracks(let name, _): 32 | let escapedName = name.stringByAddingPercentEncodingForURLQueryValue()! 33 | return "/tags/\(escapedName)/tracks" 34 | } 35 | } 36 | 37 | var params: Parameters? { 38 | switch self { 39 | case .index: 40 | return nil 41 | case .showTracks(_, let optionalParams): 42 | return optionalParams 43 | } 44 | } 45 | 46 | public func asURLRequest() throws -> URLRequest { 47 | return try Router.GenerateURLRequest(method: method, path: path, params: params) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Source/Track.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Track.swift 3 | // Plug 4 | // 5 | // Created by Alexander Marchant on 7/16/14. 6 | // Copyright (c) 2014 Plug. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | public struct Track { 12 | public let id: String 13 | public let artist: String 14 | public let title: String 15 | public let lovedCount: Int 16 | public let thumbURLSmall: URL? 17 | public let thumbURLMedium: URL? 18 | public let thumbURLLarge: URL? 19 | public let rank: Int? 20 | public let viaUser: String? 21 | public let viaQuery: String? 22 | public let postedBy: String 23 | public let postedById: Int 24 | public let postedCount: Int 25 | public let postedByDescription: String 26 | public let datePosted: Date 27 | public let audioUnavailable: Bool 28 | public let postURL: URL 29 | public let iTunesURL: URL 30 | public let mediaType: String? 31 | 32 | public var loved: Bool 33 | 34 | public init( 35 | id: String, 36 | artist: String, 37 | title: String, 38 | lovedCount: Int, 39 | thumbURLSmall: URL?, 40 | thumbURLMedium: URL?, 41 | thumbURLLarge: URL?, 42 | rank: Int?, 43 | viaUser: String?, 44 | viaQuery: String?, 45 | postedBy: String, 46 | postedById: Int, 47 | postedCount: Int, 48 | postedByDescription: String, 49 | datePosted: Date, 50 | audioUnavailable: Bool, 51 | postURL: URL, 52 | iTunesURL: URL, 53 | mediaType: String?, 54 | loved: Bool 55 | ) { 56 | self.id = id 57 | self.artist = artist 58 | self.title = title 59 | self.lovedCount = lovedCount 60 | self.thumbURLSmall = thumbURLSmall 61 | self.thumbURLMedium = thumbURLMedium 62 | self.thumbURLLarge = thumbURLLarge 63 | self.rank = rank 64 | self.viaUser = viaUser 65 | self.viaQuery = viaQuery 66 | self.postedBy = postedBy 67 | self.postedById = postedById 68 | self.postedCount = postedCount 69 | self.postedByDescription = postedByDescription 70 | self.datePosted = datePosted 71 | self.audioUnavailable = audioUnavailable 72 | self.postURL = postURL 73 | self.iTunesURL = iTunesURL 74 | self.mediaType = mediaType 75 | self.loved = loved 76 | } 77 | 78 | public var lovedCountNum: NSNumber { 79 | return NSNumber(value: lovedCount) 80 | } 81 | 82 | public func mediaURL() -> URL { 83 | var mediaLinkString = "https://hypem.com/serve/public/\(id)" 84 | if apiKey != nil { 85 | mediaLinkString += "?key=\(apiKey!)" 86 | } 87 | return URL(string: mediaLinkString)! 88 | } 89 | 90 | public func thumbURL(preferedSize: ImageSize) -> URL { 91 | switch preferedSize { 92 | case .large: 93 | if thumbURLLarge != nil { 94 | return thumbURLLarge! 95 | } else if thumbURLMedium != nil { 96 | return thumbURLMedium! 97 | } else { 98 | return thumbURLSmall! 99 | } 100 | case .medium: 101 | if thumbURLMedium != nil { 102 | return thumbURLMedium! 103 | } else if thumbURLLarge != nil { 104 | return thumbURLLarge! 105 | } else { 106 | return thumbURLSmall! 107 | } 108 | case .small: 109 | if thumbURLSmall != nil { 110 | return thumbURLSmall! 111 | } else if thumbURLMedium != nil { 112 | return thumbURLMedium! 113 | } else { 114 | return thumbURLLarge! 115 | } 116 | } 117 | } 118 | 119 | public func hypeMachineURL() -> URL { 120 | return URL(string: "http://hypem.com/track/\(id)")! 121 | } 122 | 123 | public enum ImageSize { 124 | case small 125 | case medium 126 | case large 127 | } 128 | } 129 | 130 | extension Track: CustomStringConvertible { 131 | public var description: String { 132 | return "Track: { artist: \(artist), title: \(title) }" 133 | } 134 | } 135 | 136 | extension Track: ResponseObjectSerializable, ResponseCollectionSerializable { 137 | public init?(response: HTTPURLResponse, representation: Any) { 138 | guard 139 | let representation = representation as? [String: Any], 140 | let id = representation["itemid"] as? String, 141 | let artist = representation["artist"] as? String, 142 | let title = representation["title"] as? String, 143 | let lovedCount = representation["loved_count"] as? Int, 144 | let postedBy = representation["sitename"] as? String, 145 | let postedById = representation["siteid"] as? Int, 146 | let postedCount = representation["posted_count"] as? Int, 147 | let datePostedInterval = representation["dateposted"] as? TimeInterval, 148 | let postURLString = representation["posturl"] as? String, 149 | let postURLStringEscaped = postURLString.stringByAddingPercentEncodingForURLQueryValue(), 150 | let postURL = URL(string: postURLStringEscaped), 151 | let iTunesURLString = representation["itunes_link"] as? String, 152 | let iTunesURLStringEscaped = iTunesURLString.stringByAddingPercentEncodingForURLQueryValue(), 153 | let iTunesURL = URL(string: iTunesURLStringEscaped) 154 | else { return nil } 155 | 156 | func urlForJSONKey(_ key: String) -> URL? { 157 | guard let urlString = representation[key] as? String else { 158 | return nil 159 | } 160 | return URL(string: urlString) 161 | } 162 | 163 | self.id = id 164 | self.artist = artist == "" ? "Unknown artist" : artist 165 | self.title = title == "" ? "Unknown track" : title 166 | self.loved = representation["ts_loved_me"] is Int 167 | self.lovedCount = lovedCount 168 | self.thumbURLSmall = urlForJSONKey("thumb_url") 169 | self.thumbURLMedium = urlForJSONKey("thumb_url_medium") 170 | self.thumbURLLarge = urlForJSONKey("thumb_url_large") 171 | self.rank = representation["rank"] as? Int 172 | self.viaUser = representation["via_user"] as? String 173 | self.viaQuery = representation["via_query"] as? String 174 | self.postedBy = postedBy 175 | self.postedById = postedById 176 | self.postedCount = postedCount 177 | self.postedByDescription = (representation["description"] as? String) ?? "No description available" 178 | self.datePosted = Date(timeIntervalSince1970: datePostedInterval) 179 | self.audioUnavailable = representation["pub_audio_unavail"] as? Bool == true 180 | self.postURL = postURL 181 | self.iTunesURL = iTunesURL 182 | self.mediaType = representation["media_type"] as? String 183 | } 184 | } 185 | 186 | 187 | extension Track: Equatable { 188 | public static func == (lhs: Track, rhs: Track) -> Bool { 189 | return lhs.id == rhs.id 190 | } 191 | } 192 | 193 | extension Track: Hashable { 194 | public var hashValue: Int { 195 | return id.hashValue 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /Source/TracksRequests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TracksRequests.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/13/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | extension Requests { 13 | public struct Tracks { 14 | public static func index( 15 | params: Parameters? = nil, 16 | completionHandler: @escaping (DataResponse<[Track]>)->Void 17 | ) -> DataRequest 18 | { 19 | return Requests 20 | .defaultRequest(Router.Tracks.index(params: params)) 21 | .responseCollection(completionHandler: completionHandler) 22 | } 23 | 24 | public static func show( 25 | id: String, 26 | completionHandler: @escaping (DataResponse)->Void 27 | ) -> DataRequest 28 | { 29 | return Requests 30 | .defaultRequest(Router.Tracks.show(id: id)) 31 | .responseObject(completionHandler: completionHandler) 32 | } 33 | 34 | public static func popular( 35 | params: Parameters? = nil, 36 | completionHandler: @escaping (DataResponse<[Track]>)->Void 37 | ) -> DataRequest 38 | { 39 | return Requests 40 | .defaultRequest(Router.Tracks.popular(params: params)) 41 | .responseCollection(completionHandler: completionHandler) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Source/TracksRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TracksRouter.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/10/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | extension Router { 13 | public enum Tracks: URLRequestConvertible { 14 | 15 | case index(params: Parameters?) 16 | case show(id: String) 17 | case popular(params: Parameters?) 18 | 19 | var method: HTTPMethod { 20 | switch self { 21 | case .index: 22 | return .get 23 | case .show: 24 | return .get 25 | case .popular: 26 | return .get 27 | } 28 | } 29 | 30 | var path: String { 31 | switch self { 32 | case .index: 33 | return "/tracks" 34 | case .show(let id): 35 | return "/tracks/\(id)" 36 | case .popular: 37 | return "/popular" 38 | } 39 | } 40 | 41 | var params: Parameters? { 42 | switch self { 43 | case .index(let optionalParams): 44 | return optionalParams 45 | case .show: 46 | return nil 47 | case .popular(let optionalParams): 48 | return optionalParams 49 | } 50 | } 51 | 52 | public func asURLRequest() throws -> URLRequest { 53 | return try Router.GenerateURLRequest(method: method, path: path, params: params) 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Source/User.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Friend.swift 3 | // Plug 4 | // 5 | // Created by Alex Marchant on 8/1/14. 6 | // Copyright (c) 2014 Plug. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | public struct User { 12 | public let username: String 13 | public let fullName: String? 14 | public let avatarURL: URL? 15 | public let favoritesCount: Int 16 | public let followersCount: Int 17 | public let followingCount: Int 18 | public var friend: Bool? 19 | public var follower: Bool? 20 | 21 | public init( 22 | username: String, 23 | fullName: String, 24 | avatarURL: URL?, 25 | favoritesCount: Int, 26 | followersCount: Int, 27 | followingCount: Int, 28 | friend: Bool?, 29 | follower: Bool? 30 | ) { 31 | self.username = username 32 | self.fullName = fullName 33 | self.avatarURL = avatarURL 34 | self.favoritesCount = favoritesCount 35 | self.followersCount = followersCount 36 | self.followingCount = followingCount 37 | self.friend = friend 38 | self.follower = follower 39 | } 40 | 41 | public var favoritesCountNum: NSNumber { 42 | return NSNumber(value: favoritesCount) 43 | } 44 | 45 | public var followersCountNum: NSNumber { 46 | return NSNumber(value: followersCount) 47 | } 48 | 49 | public var followingCountNum: NSNumber { 50 | return NSNumber(value: followingCount) 51 | } 52 | } 53 | 54 | extension User: CustomStringConvertible { 55 | public var description: String { 56 | return "User: { username: \(username), fullName: \(fullName) }" 57 | } 58 | } 59 | 60 | extension User: ResponseObjectSerializable, ResponseCollectionSerializable { 61 | public init?(response: HTTPURLResponse, representation: Any) { 62 | guard 63 | let representation = representation as? [String: Any], 64 | let username = representation["username"] as? String, 65 | let favoritesCountInfo = representation["favorites_count"] as? NSDictionary 66 | else { return nil } 67 | 68 | func nonEmptyStringForJSONKey(_ key: String) -> String? { 69 | guard let string = representation["key"] as? String else { 70 | return nil 71 | } 72 | return string == "" ? nil : string 73 | } 74 | 75 | func urlForJSONKey(_ key: String) -> URL? { 76 | guard let urlString = representation[key] as? String else { 77 | return nil 78 | } 79 | return URL(string: urlString) 80 | } 81 | 82 | self.username = username 83 | self.fullName = nonEmptyStringForJSONKey("fullname") 84 | self.avatarURL = urlForJSONKey("userpic") 85 | self.favoritesCount = favoritesCountInfo["item"] as? Int ?? 0 86 | self.followersCount = favoritesCountInfo["followers"] as? Int ?? 0 87 | self.followingCount = favoritesCountInfo["user"] as? Int ?? 0 88 | self.friend = representation["is_friend"] as? Bool 89 | self.follower = representation["is_follower"] as? Bool 90 | } 91 | } 92 | 93 | 94 | extension User: Equatable { 95 | public static func == (lhs: User, rhs: User) -> Bool { 96 | return lhs.username == rhs.username 97 | } 98 | } 99 | 100 | extension User: Hashable { 101 | public var hashValue: Int { 102 | return username.hashValue 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Source/UsersRequests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UsersRequests.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/13/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | extension Requests { 13 | public struct Users { 14 | public static func show( 15 | username: String, 16 | completionHandler: @escaping (DataResponse)->Void 17 | ) -> DataRequest 18 | { 19 | return Requests 20 | .defaultRequest(Router.Users.show(username: username)) 21 | .responseObject(completionHandler: completionHandler) 22 | } 23 | 24 | public static func showFavorites( 25 | username: String, 26 | params: Parameters? = nil, 27 | completionHandler: @escaping (DataResponse<[Track]>)->Void 28 | ) -> DataRequest 29 | { 30 | return Requests 31 | .defaultRequest(Router.Users.showFavorites(username: username, params: params)) 32 | .responseCollection(completionHandler: completionHandler) 33 | } 34 | 35 | public static func showFriends( 36 | username: String, 37 | params: Parameters? = nil, 38 | completionHandler: @escaping (DataResponse<[User]>)->Void 39 | ) -> DataRequest 40 | { 41 | return Requests 42 | .defaultRequest(Router.Users.showFriends(username: username, params: params)) 43 | .responseCollection(completionHandler: completionHandler) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Source/UsersRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UsersRouter.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/11/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | extension Router { 13 | public enum Users: URLRequestConvertible { 14 | 15 | case show(username: String) 16 | case showFavorites(username: String, params: Parameters?) 17 | case showFriends(username: String, params: Parameters?) 18 | 19 | var method: HTTPMethod { 20 | switch self { 21 | case .show: 22 | return .get 23 | case .showFavorites: 24 | return .get 25 | case .showFriends: 26 | return .get 27 | } 28 | } 29 | 30 | var path: String { 31 | switch self { 32 | case .show(let username): 33 | let escapedUsername = username.stringByAddingPercentEncodingForURLQueryValue()! 34 | return "/users/\(escapedUsername)" 35 | case .showFavorites(let username, _): 36 | let escapedUsername = username.stringByAddingPercentEncodingForURLQueryValue()! 37 | return "/users/\(escapedUsername)/favorites" 38 | case .showFriends(let username, _): 39 | let escapedUsername = username.stringByAddingPercentEncodingForURLQueryValue()! 40 | return "/users/\(escapedUsername)/friends" 41 | } 42 | } 43 | 44 | var params: Parameters? { 45 | switch self { 46 | case .show: 47 | return nil 48 | case .showFavorites(_, let optionalParams): 49 | return optionalParams 50 | case .showFriends(_, let optionalParams): 51 | return optionalParams 52 | } 53 | } 54 | 55 | public func asURLRequest() throws -> URLRequest { 56 | return try Router.GenerateURLRequest(method: method, path: path, params: params) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Source/Validations.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Validations.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 10/26/16. 6 | // Copyright © 2016 Plug. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Alamofire 11 | 12 | public struct Validations { 13 | public static let apiErrorValidation: Alamofire.DataRequest.Validation = { (request, response, data) in 14 | guard 15 | let data = data, 16 | let object = try? JSONSerialization.jsonObject(with: data, options: .allowFragments), 17 | let representation = object as? [String: Any], 18 | let errorMessage = representation["error_msg"] as? String 19 | else { return .success } 20 | 21 | switch errorMessage { 22 | case "Must provide valid hm_token": 23 | return .failure(APIError.invalidHMToken) 24 | case "Wrong password": 25 | return .failure(APIError.incorrectPassword) 26 | case "Wrong username": 27 | return .failure(APIError.incorrectUsername) 28 | default: 29 | return .failure(APIError.unknownError(message: errorMessage)) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Tests/Artist.json: -------------------------------------------------------------------------------- 1 | { 2 | "artist": "ducktails", 3 | "thumb_url_artist": "http://static-ak.hypem.net/resizer/thumbs_orig/a3/2028707.jpg" 4 | } -------------------------------------------------------------------------------- /Tests/ArtistTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ArtistTests.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/11/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import XCTest 11 | @testable import HypeMachineAPI 12 | 13 | class ArtistTests: XCTestCase { 14 | override func setUp() { 15 | super.setUp() 16 | } 17 | override func tearDown() { 18 | super.tearDown() 19 | } 20 | 21 | func testBuildArtist() { 22 | let json: AnyObject = Helpers.loadJSONFixtureFile("Artist", ofType: "json") 23 | let artist = HypeMachineAPI.Artist(response: HTTPURLResponse(), representation: json) 24 | 25 | XCTAssertNotNil(artist) 26 | XCTAssert(artist!.name == "ducktails") 27 | } 28 | 29 | func testBuildArtistCollection() { 30 | let json: AnyObject = Helpers.loadJSONFixtureFile("Artists", ofType: "json") 31 | let artists = HypeMachineAPI.Artist.collection(from: HTTPURLResponse(), withRepresentation: json) 32 | 33 | XCTAssertNotNil(artists) 34 | XCTAssert(artists.count == 3) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Tests/Artists.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "artist": "ducktails", 4 | "cnt": 26, 5 | "thumb_url_artist": "http://static-ak.hypem.net/resizer/thumbs_orig/a3/2028707.jpg", 6 | "rank": 1 7 | }, { 8 | "artist": "made in heights", 9 | "cnt": 25, 10 | "thumb_url_artist": "http://static-ak.hypem.net/resizer/thumbs_orig/1a/2387994.jpg", 11 | "rank": 2 12 | }, { 13 | "artist": "little boots", 14 | "cnt": 21, 15 | "thumb_url_artist": "http://static-ak.hypem.net/resizer/thumbs_orig/45/2207045.jpg", 16 | "rank": 3 17 | } 18 | ] -------------------------------------------------------------------------------- /Tests/Blog.json: -------------------------------------------------------------------------------- 1 | { 2 | "siteid": 16684, 3 | "sitename": "Okayplayer", 4 | "siteurl": "http://www.okayplayer.com", 5 | "followers": 524, 6 | "region_name": false, 7 | "total_tracks": 485, 8 | "last_posted": 1431284090, 9 | "first_posted": 1412609699, 10 | "blog_image": "http://static.hypem.net/rev_1412718041/images/blog_images/16684.png", 11 | "blog_image_small": "http://static.hypem.net/rev_1412718042/images/blog_images/100/16684.png", 12 | "ts_featured": 1421167359 13 | } -------------------------------------------------------------------------------- /Tests/BlogTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BlogTests.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/11/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import XCTest 11 | @testable import HypeMachineAPI 12 | 13 | extension HypeMachineAPI.Blog { 14 | 15 | } 16 | 17 | class BlogTests: XCTestCase { 18 | override func setUp() { 19 | super.setUp() 20 | } 21 | override func tearDown() { 22 | super.tearDown() 23 | } 24 | 25 | func testBuildBlog() { 26 | let json: AnyObject = Helpers.loadJSONFixtureFile("Blog", ofType: "json") 27 | let blog = HypeMachineAPI.Blog(response: HTTPURLResponse(), representation: json) 28 | 29 | XCTAssertNotNil(blog) 30 | XCTAssert(blog!.id == 16684) 31 | } 32 | 33 | func testBuildBlogCollection() { 34 | let json: AnyObject = Helpers.loadJSONFixtureFile("Blogs", ofType: "json") 35 | let blogs = HypeMachineAPI.Blog.collection(from: HTTPURLResponse(), withRepresentation: json) 36 | 37 | XCTAssertNotNil(blogs) 38 | XCTAssert(blogs.count == 3) 39 | 40 | XCTAssert(blogs[0].featured == true) 41 | XCTAssert(blogs[0].following == true) 42 | 43 | XCTAssert(blogs[1].featured == false) 44 | XCTAssert(blogs[1].following == false) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Tests/Blogs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "siteid": 16684, 4 | "sitename": "Okayplayer", 5 | "siteurl": "http://www.okayplayer.com", 6 | "followers": 524, 7 | "region_name": false, 8 | "total_tracks": 485, 9 | "last_posted": 1431284090, 10 | "first_posted": 1412609699, 11 | "blog_image": "http://static.hypem.net/rev_1412718041/images/blog_images/16684.png", 12 | "blog_image_small": "http://static.hypem.net/rev_1412718042/images/blog_images/100/16684.png", 13 | "ts_featured": 1421167359, 14 | "ts_loved_me": 1421167359 15 | }, { 16 | "siteid": 21614, 17 | "sitename": "The Quietus", 18 | "siteurl": "http://thequietus.com/", 19 | "followers": 346, 20 | "region_name": "United Kingdom", 21 | "total_tracks": 412, 22 | "last_posted": 1431341720, 23 | "first_posted": 1400510540, 24 | "blog_image": "http://static.hypem.net/rev_1400706694/images/blog_images/21614.png", 25 | "blog_image_small": "http://static.hypem.net/rev_1400706694/images/blog_images/100/21614.png" 26 | }, { 27 | "siteid": 4779, 28 | "sitename": "MOARRR", 29 | "siteurl": "http://moarrr.com", 30 | "followers": 3630, 31 | "region_name": "Hungary", 32 | "total_tracks": 6901, 33 | "last_posted": 1431334967, 34 | "first_posted": 1232277186, 35 | "blog_image": "http://static.hypem.net/rev_1380834884/images/blog_images/4779.png", 36 | "blog_image_small": "http://static.hypem.net/rev_1372782824/images/blog_images/100/4779.png", 37 | "ts_featured": 1414606327 38 | } 39 | ] -------------------------------------------------------------------------------- /Tests/Fixtures.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Fixtures.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/11/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Helpers { 12 | class func loadJSONFixtureFile(_ name: String, ofType ext: String) -> AnyObject { 13 | let path = Bundle(for: type(of: Helpers())).path(forResource: name, ofType: ext)! 14 | let jsonData = try! Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) 15 | return try! JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Tests/Tag.json: -------------------------------------------------------------------------------- 1 | { 2 | "tag_name": "electronic", 3 | "priority": true 4 | } -------------------------------------------------------------------------------- /Tests/TagTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TagsTests.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/11/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import XCTest 11 | @testable import HypeMachineAPI 12 | 13 | class TagTests: XCTestCase { 14 | override func setUp() { 15 | super.setUp() 16 | } 17 | override func tearDown() { 18 | super.tearDown() 19 | } 20 | 21 | func testBuildTag() { 22 | let json: AnyObject = Helpers.loadJSONFixtureFile("Tag", ofType: "json") 23 | let tag = HypeMachineAPI.Tag(response: HTTPURLResponse(), representation: json) 24 | 25 | XCTAssertNotNil(tag) 26 | XCTAssert(tag!.name == "electronic") 27 | } 28 | 29 | func testBuildTagCollection() { 30 | let json: AnyObject = Helpers.loadJSONFixtureFile("Tags", ofType: "json") 31 | let tags = HypeMachineAPI.Tag.collection(from: HTTPURLResponse(), withRepresentation: json) 32 | 33 | XCTAssertNotNil(tags) 34 | XCTAssert(tags.count == 3) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Tests/Tags.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "tag_name": "electronic", 4 | "priority": true 5 | }, { 6 | "tag_name": "indie" 7 | }, { 8 | "tag_name": "hip hop", 9 | "priority": true 10 | } 11 | ] -------------------------------------------------------------------------------- /Tests/Track.json: -------------------------------------------------------------------------------- 1 | { 2 | "itemid": "2b8fz", 3 | "artist": "The Bad Years", 4 | "title": "Pieces", 5 | "dateposted": 1431325685, 6 | "siteid": 6356, 7 | "sitename": "Rollo & Grady", 8 | "posturl": "http://www.rollogrady.com/the-bad-years-pieces/", 9 | "postid": 2679501, 10 | "loved_count": 1, 11 | "posted_count": 1, 12 | "thumb_url": "http://static.hypem.net/thumbs_new/cd/2679501.jpg", 13 | "thumb_url_medium": "http://static.hypem.net/thumbs_new/cd/2679501_120.jpg", 14 | "thumb_url_large": "http://static.hypem.net/thumbs_new/cd/2679501_320.jpg", 15 | "time": 205, 16 | "description": "The Bad Years Album: Beautiful Liar EP (Buy) Label: Kaleidoscopic Wondersound The Bad Years – Pieces Tweet", 17 | "itunes_link": "http://hypem.com/go/itunes_search/The%20Bad%20Years" 18 | } -------------------------------------------------------------------------------- /Tests/TrackTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TrackTests.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/11/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import XCTest 11 | @testable import HypeMachineAPI 12 | 13 | class TrackTests: XCTestCase { 14 | override func setUp() { 15 | super.setUp() 16 | } 17 | override func tearDown() { 18 | super.tearDown() 19 | } 20 | 21 | func testBuildTrack() { 22 | let json: AnyObject = Helpers.loadJSONFixtureFile("Track", ofType: "json") 23 | let track = HypeMachineAPI.Track(response: HTTPURLResponse(), representation: json) 24 | 25 | XCTAssertNotNil(track) 26 | XCTAssert(track!.id == "2b8fz") 27 | } 28 | 29 | func testBuildTrackCollection() { 30 | let json: AnyObject = Helpers.loadJSONFixtureFile("Tracks", ofType: "json") 31 | let tracks = HypeMachineAPI.Track.collection(from: HTTPURLResponse(), withRepresentation: json) 32 | 33 | XCTAssertNotNil(tracks) 34 | XCTAssert(tracks.count == 3) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Tests/Tracks.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "itemid": "2aq57", 4 | "artist": "Black Stone from the Sun", 5 | "title": "Pastel Roses", 6 | "dateposted": 1431327613, 7 | "siteid": 19958, 8 | "sitename": "Happy", 9 | "posturl": "http://hhhhappy.com/2015/05/11/black-stone-from-the-sun-eat-edgy-rock-n-roll-for-breakfast/", 10 | "postid": 2679508, 11 | "loved_count": 4, 12 | "posted_count": 2, 13 | "thumb_url": "http://static.hypem.net/thumbs_new/d4/2679508.jpg", 14 | "thumb_url_medium": "http://static.hypem.net/thumbs_new/d4/2679508_120.jpg", 15 | "thumb_url_large": "http://static.hypem.net/thumbs_new/f6/2662134_320.jpg", 16 | "time": 232, 17 | "description": "Perth seems to be the place of the Aussie grunge revival at the moment with acts such as Foam, Puck,...", 18 | "itunes_link": "http://hypem.com/go/itunes_search/Black%20Stone%20from%20the%20Sun" 19 | }, { 20 | "itemid": "29ahf", 21 | "artist": "Death", 22 | "title": "Look At Your Life", 23 | "dateposted": 1431327365, 24 | "siteid": 10441, 25 | "sitename": "The 405", 26 | "posturl": "http://www.thefourohfive.com/review/article/death-n-e-w-143", 27 | "postid": 2679504, 28 | "loved_count": 21, 29 | "posted_count": 14, 30 | "thumb_url": "http://static.hypem.net/thumbs_new/d0/2679504.jpg", 31 | "thumb_url_medium": "http://static.hypem.net/thumbs_new/d0/2679504_120.jpg", 32 | "thumb_url_large": "http://static.hypem.net/thumbs_new/eb/2618603_320.jpg", 33 | "time": 280, 34 | "description": "Ain't no hyperbole, ain't no cliché… DEATH know how to rock!", 35 | "itunes_link": "http://hypem.com/go/itunes_search/Death" 36 | }, { 37 | "itemid": "28m6g", 38 | "artist": "Sons Of Maria", 39 | "title": "With You (Radio Mix)", 40 | "dateposted": 1431326625, 41 | "siteid": 13958, 42 | "sitename": "ACIDTED", 43 | "posturl": "https://acidted.wordpress.com/2015/05/11/what/", 44 | "postid": 2679503, 45 | "loved_count": 35, 46 | "posted_count": 2, 47 | "thumb_url": "http://static.hypem.net/thumbs_new/cf/2679503.jpg", 48 | "thumb_url_medium": "http://static.hypem.net/thumbs_new/cf/2679503_120.jpg", 49 | "thumb_url_large": "http://static.hypem.net/thumbs_new/cf/2679503_320.jpg", 50 | "time": 212, 51 | "description": "Here’s a new/old Ferdinand Weber track. What appeared on this blog about two years ago and is getting a proper release it’s a lovely Balearic deep house track, which samples the guitar at the start of Maynard Ferguson’s Fantasy. But that guitar line is al", 52 | "itunes_link": "http://hypem.com/go/itunes_search/Sons%20Of%20Maria" 53 | } 54 | ] -------------------------------------------------------------------------------- /Tests/User.json: -------------------------------------------------------------------------------- 1 | { 2 | "username": "alex_marchant", 3 | "profile_url": "http://hypem.com/alex_marchant", 4 | "fullname": "", 5 | "twitter_username": "alex_marchant", 6 | "userpic": "https://s3.amazonaws.com/faces-s3.hypem.com/8857931860581139_75.png", 7 | "joined_ts": 1298496344, 8 | "location": "", 9 | "uid": 885793, 10 | "favorites_count": { 11 | "user": 20, 12 | "item": 601, 13 | "site": 0, 14 | "query": 0, 15 | "followers": 17 16 | } 17 | } -------------------------------------------------------------------------------- /Tests/UserTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserTests.swift 3 | // HypeMachineAPI 4 | // 5 | // Created by Alex Marchant on 5/11/15. 6 | // Copyright (c) 2015 Plug. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import XCTest 11 | @testable import HypeMachineAPI 12 | 13 | class UserTests: XCTestCase { 14 | override func setUp() { 15 | super.setUp() 16 | } 17 | override func tearDown() { 18 | super.tearDown() 19 | } 20 | 21 | func testBuildArtist() { 22 | let json: AnyObject = Helpers.loadJSONFixtureFile("User", ofType: "json") 23 | let user = HypeMachineAPI.User(response: HTTPURLResponse(), representation: json) 24 | 25 | XCTAssertNotNil(user) 26 | XCTAssert(user!.username == "alex_marchant") 27 | } 28 | 29 | func testBuildArtistCollection() { 30 | let json: AnyObject = Helpers.loadJSONFixtureFile("Users", ofType: "json") 31 | let users = HypeMachineAPI.User.collection(from: HTTPURLResponse(), withRepresentation: json) 32 | 33 | XCTAssertNotNil(users) 34 | XCTAssert(users.count == 3) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Tests/Users.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "username": "robgcarroll", 4 | "profile_url": "http://hypem.com/robgcarroll", 5 | "fullname": "Robert Carroll", 6 | "twitter_username": "", 7 | "userpic": "https://s3.amazonaws.com/faces-s3.hypem.com/9097532046788884_75.png", 8 | "joined_ts": 1301433433, 9 | "location": "Newport Beach, CA", 10 | "uid": 909753, 11 | "favorites_count": { 12 | "user": 2, 13 | "item": 127, 14 | "site": 1, 15 | "query": 1, 16 | "followers": 13 17 | }, 18 | "ts_loved": null 19 | }, { 20 | "username": "thoyt", 21 | "profile_url": "http://hypem.com/thoyt", 22 | "fullname": "", 23 | "twitter_username": "thoyt", 24 | "userpic": "https://s3.amazonaws.com/faces-s3.hypem.com/4238781839794447_75.png", 25 | "joined_ts": 1263760224, 26 | "location": "in the stars", 27 | "uid": 423878, 28 | "favorites_count": { 29 | "user": 38, 30 | "item": 2112, 31 | "site": 19, 32 | "query": 0, 33 | "followers": 38 34 | }, 35 | "ts_loved": null 36 | }, { 37 | "username": "davidjohnson", 38 | "profile_url": "http://hypem.com/davidjohnson", 39 | "fullname": "david johnson", 40 | "twitter_username": "davidjohnsonn", 41 | "userpic": "https://s3.amazonaws.com/faces-s3.hypem.com/8349261560640634_75.png", 42 | "joined_ts": 1292773886, 43 | "location": null, 44 | "uid": 834926, 45 | "favorites_count": { 46 | "user": 20, 47 | "item": 411, 48 | "site": 5, 49 | "query": 21, 50 | "followers": 31 51 | }, 52 | "ts_loved": null 53 | } 54 | ] --------------------------------------------------------------------------------