Skip to content

Commit

Permalink
first version
Browse files Browse the repository at this point in the history
  • Loading branch information
Carlos Pérez committed Apr 5, 2021
1 parent f9cb23f commit 2d77e29
Show file tree
Hide file tree
Showing 18 changed files with 701 additions and 15 deletions.
66 changes: 66 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore

## Build generated
build/
DerivedData/

## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/

## Other
*.moved-aside
*.xcuserstate

## Obj-C/Swift specific
*.hmap
*.ipa
*.dSYM.zip
*.dSYM

## Playgrounds
timeline.xctimeline
playground.xcworkspace

# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
.build/

# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
Pods/
Podfile.lock

# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts

Carthage/Build

# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md

fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
258 changes: 253 additions & 5 deletions MusicTAM.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<key>MusicTAM.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
<integer>4</integer>
</dict>
</dict>
</dict>
Expand Down
14 changes: 14 additions & 0 deletions MusicTAM/Models/Album.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Foundation

struct AlbumResponse: Codable {
let resultCount: Int?
let results: [Album]?
}

struct Album: Codable {
let wrapperType: String?
let collectionId: Int32?
let collectionName: String?
let artworkUrl60: String?
let releaseDate: String?
}
13 changes: 13 additions & 0 deletions MusicTAM/Models/Artist.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Foundation

struct ArtistResponse: Codable {
let resultCount: Int?
let results: [Artist]?
}

struct Artist: Codable {
let artistId: Int32?
let amgArtistId: Int32?
let artistName: String?
let primaryGenreName: String?
}
14 changes: 14 additions & 0 deletions MusicTAM/Models/Song.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Foundation

struct SongResponse: Codable {
let resultCount: Int?
let results: [Song]?
}

struct Song: Codable {
let wrapperType: String?
let trackId: Int32?
let trackName: String?
let previewUrl: String?
let artworkUrl100: String?
}
82 changes: 82 additions & 0 deletions MusicTAM/Network/DataSources/ArtistDataSource.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//BY USING DATASOURCE EXTENSIONS, WE GET TO ISOLATE WHAT INTERACTORS CAN ACCESS AND ALSO
//TO GET ESCALABILITY BY NOT CREATING MASSIVE DATA SOURCES WITH THE WHOLE CALLS IN A SINGLE FILE

import Foundation

protocol ArtistDataSource {
func getArtists(searchTerm: String,
page: String?,
limit: String?,
needsLoader: ((Bool) -> Void)?,
completion: @escaping (([ArtistResponse]?) -> Void),
failure: ((String?) -> Void)?)

func getAlbums(amgArtistId: Int32,
needsLoader: ((Bool) -> Void)?,
completion: @escaping (([AlbumResponse]?) -> Void),
failure: ((String?) -> Void)?)

func getSongs(collectionId: Int32,
needsLoader: ((Bool) -> Void)?,
completion: @escaping (([SongResponse]?) -> Void),
failure: ((String?) -> Void)?)
}

extension DataSource: ArtistDataSource {

func getArtists(searchTerm: String,
page: String? = nil,
limit: String? = nil,
needsLoader: ((Bool) -> Void)?,
completion: @escaping (([ArtistResponse]?) -> Void),
failure: ((String?) -> Void)?) {

let serviceUrl = webServiceURLProvider.artistSearchURL
.replacingOccurrences(of: "{{searchTerm}}", with: searchTerm)

let request = Request(serviceUrl: serviceUrl,
contentType: .queryString,
method: .get)

performRequest(requestParams: request,
needsLoader: needsLoader,
completion: completion,
failure: failure)
}

func getAlbums(amgArtistId: Int32,
needsLoader: ((Bool) -> Void)?,
completion: @escaping (([AlbumResponse]?) -> Void),
failure: ((String?) -> Void)?) {

let serviceUrl = webServiceURLProvider.albumsURL
.replacingOccurrences(of: "{{amgArtistId}}", with: "\(amgArtistId)")

let request = Request(serviceUrl: serviceUrl,
contentType: .queryString,
method: .get)

performRequest(requestParams: request,
needsLoader: needsLoader,
completion: completion,
failure: failure)
}

func getSongs(collectionId: Int32,
needsLoader: ((Bool) -> Void)?,
completion: @escaping (([SongResponse]?) -> Void),
failure: ((String?) -> Void)?) {

let serviceUrl = webServiceURLProvider.songsURL
.replacingOccurrences(of: "{{collectionId}}", with: "\(collectionId)")

let request = Request(serviceUrl: serviceUrl,
contentType: .queryString,
method: .get)

performRequest(requestParams: request,
needsLoader: needsLoader,
completion: completion,
failure: failure)
}
}
28 changes: 28 additions & 0 deletions MusicTAM/Network/DataSources/DataSource.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Foundation

protocol DataSourceProtocol {
}

class DataSource: DataSourceProtocol {

internal let repository: Repository
internal let webServiceURLProvider: WebServiceURLProvider
typealias T = Codable

init(repository: Repository, webServiceURLProvider: WebServiceURLProvider) {
self.repository = repository
self.webServiceURLProvider = webServiceURLProvider
}

// MARK: Internal Methods

internal func performRequest<T>(requestParams: Request,
needsLoader: ((Bool) -> Void)?,
completion: @escaping (([T]?) -> Void),
failure: ((String?) -> Void)?) where T: Codable {

repository.request(requestParams: requestParams,
completion: completion,
failure: failure)
}
}
50 changes: 50 additions & 0 deletions MusicTAM/Network/Request.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import Foundation

struct Request{
public var serviceUrl: String?
public var params: [String: Any]?
public var contentType: RequestType
public var method: RequestMethod

public init(serviceUrl: String? = nil,
params: [String: Any]? = nil,
contentType: RequestType,
method: RequestMethod) {

self.serviceUrl = serviceUrl
self.params = params
self.contentType = contentType
self.method = method
}

public func getId() -> String {
var output = serviceUrl

if let params = params, params.count > 0 {
for (key, value) in params {
output?.append(contentsOf: "_\(key)_\(value)")
}
}

return output ?? ""
}

public func description() -> String {
return "\(serviceUrl != nil ? "ServiceURL: \(serviceUrl!)\n" : "")" +
"\(params != nil ? "Params: \(params!)\n" : "")"
}
}

public enum RequestType: String {
case urlEncoded
case json
case queryString
}

public enum RequestMethod: String {
case get
case post
case delete
case put
case patch
}
47 changes: 47 additions & 0 deletions MusicTAM/Network/RestRepository.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//REPOSITORY IS A FEATURE WE USE FOR BOTH READ OR WRITE FROM OR TO WHEREVER, GIVEN
//THAT MOST OF THE TIME, WE WILL BE READING DATA FROM REMOTE SERVERS, DATABASES, ETC
//AND SAVING THEM TO LOCAL STORAGES OR SIMPLY USING DATA TO BE SHOWN IN APP.

//IN THIS CASE, IT IS PROVIDED A RESTREPOSITORY THAT WILL BE USING THE WEBSERVICE
//LAYER, IN CHARGE OF THE NETWORK CONNECTIONS

import Foundation

protocol Repository {
func request<T>(requestParams: Request,
completion: @escaping (_ result: [T]) -> Void,
failure: ((_ error: String?) -> Void)?) where T: Codable
}

class RestRepository: Repository {
private let webService: WebServiceProtocol

init(webService: WebService) {
self.webService = webService
}

func request<T>(requestParams: Request,
completion: @escaping (_ result: [T]) -> Void,
failure: ((_ error: String?) -> Void)?) where T: Codable {

webService.get(request: requestParams, completion: {(result) in

print("GET FROM REMOTE 👍: \n\(requestParams.description())")

if let parsed: T = try? JSONDecoder().decode(T.self, from: result) {
completion([parsed])
return

} else if let parsed: [T] = try? JSONDecoder().decode([T].self, from: result) {
completion(parsed)
return
}

completion([])

}, failure: {(error) in
failure?(error.localizedDescription)
})
}

}
41 changes: 41 additions & 0 deletions MusicTAM/Network/WebService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import Alamofire

protocol WebServiceProtocol: class {
func get(request: Request,
completion : @escaping (_ result : Data) -> Void,
failure : @escaping (_ result : AnyObject) -> Void)
}

class WebService: WebServiceProtocol {

func get(request: Request,
completion: @escaping (Data) -> Void,
failure: @escaping (AnyObject) -> Void) {

guard let serviceUrl = request.serviceUrl,
let trimmed = serviceUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
let finalUrl = URL(string: trimmed) else {

failure("NO_REQUEST" as AnyObject)
return
}

Alamofire.request(finalUrl,
method: request.alamofireMethod,
parameters: request.params,
encoding: request.alamofireEncondingType,
headers: nil)

.responseData(completionHandler: { response in
if response.result.isSuccess,
let data = response.result.value {
completion(data)

} else {
failure((response.result.error?.localizedDescription ?? "NO_DATA") as AnyObject)
}
}
)
}

}
Loading

0 comments on commit 2d77e29

Please sign in to comment.