Skip to content

Commit

Permalink
Merge pull request #10 from KarimEbrahemAbdelaziz/feature/add-support…
Browse files Browse the repository at this point in the history
…-to-all-methods

Feature/add support to all methods
  • Loading branch information
KarimEbrahemAbdelaziz authored Jul 19, 2020
2 parents 4735ddb + 2e1b652 commit a2c47fc
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 65 deletions.
34 changes: 22 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<a href="https://github.com/KarimEbrahemAbdelaziz/Zen">
<img src="http://img.shields.io/badge/Swift Package Manager-available-green.svg?style=flat" alt="Swift Package Manager" />
</a>
<img src="http://img.shields.io/badge/version-0.1.5-green.svg?style=flat" alt="Version" />
<img src="http://img.shields.io/badge/version-0.1.6-green.svg?style=flat" alt="Version" />
<br>
<a href="https://www.facebook.com/KarimEbrahemAbdelaziz">
<img src="http://img.shields.io/badge/facebook-%40KarimEbrahemAbdelaziz-70a1fb.svg?style=flat" alt="Facebook: @KarimEbrahemAbdelaziz" />
Expand Down Expand Up @@ -56,15 +56,15 @@ Zen is simple yet powerfull Networking library for iOS. It leverage the powerful
[CocoaPods](https://cocoapods.org) is a dependency manager for Cocoa projects. For usage and installation instructions, visit their website. To integrate Zen into your Xcode project using CocoaPods, specify it in your `Podfile`:

```ruby
pod 'Zen', '~> 0.1.5'
pod 'Zen', '~> 0.1.6'
```

### Carthage

[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. To integrate Zen into your Xcode project using Carthage, specify it in your `Cartfile`:

```ogdl
github "KarimEbrahemAbdelaziz/Zen" ~> 0.1.5
github "KarimEbrahemAbdelaziz/Zen" ~> 0.1.6
```

### SPM
Expand All @@ -75,7 +75,7 @@ Once you have your Swift package set up, adding Zen as a dependency is as easy a

```swift
dependencies: [
.package(url: "https://github.com/KarimEbrahemAbdelaziz/Zen.git", .upToNextMajor(from: "0.1.5"))
.package(url: "https://github.com/KarimEbrahemAbdelaziz/Zen.git", .upToNextMajor(from: "0.1.6"))
]
```

Expand All @@ -96,24 +96,34 @@ struct Todo: Codable {
import Zen

class APIClient {
@GET<[Todo]>(url: "https://jsonplaceholder.typicode.com/todos")
static var fetchTodos: Service<[Todo]>
@ZenRequest<Todo>("https://jsonplaceholder.typicode.com/todos/")
static var fetchTodo: Service<Todo>
}
```

#### Use it 🤓
```swift
APIClient.fetchTodos { result in
print(result)
try? APIClient.$fetchTodo
.set(method: .get)
.set(path: "1")
.build()

APIClient.fetchTodo { result in
switch result {
case .success(let todo):
print(todo.title)
case .failure(let error):
print(error)
}
}
```

## Todo

- [ ] Support all HTTP Methods Requests.
- [ ] Support Body Parameters.
- [ ] Support Query Parameters.
- [ ] Support Headers.
- [x] Support all HTTP Methods Requests.
- [x] Support Body Parameters.
- [x] Support Query Parameters.
- [x] Support Headers.
- [ ] Support Interceptors.

## Author
Expand Down
39 changes: 0 additions & 39 deletions Sources/Zen/GET.swift

This file was deleted.

30 changes: 30 additions & 0 deletions Sources/Zen/RequestParameters.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// RequestParameters.swift
//
//
// Created by KarimEbrahem on 7/19/20.
//

import Foundation
import Alamofire

public enum RequestParameters {
case body([String: String]?)
case url([String: String]?)

var parameters: [String: String]? {
switch self {
case .body(let parameters), .url(let parameters):
return parameters
}
}

var encoder: ParameterEncoder {
switch self {
case .body:
return JSONParameterEncoder.default
case .url:
return URLEncodedFormParameterEncoder.default
}
}
}
13 changes: 13 additions & 0 deletions Sources/Zen/ZenError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// ZenError.swift
//
//
// Created by KarimEbrahem on 7/19/20.
//

import Foundation

public enum ZenError: Error {
case RequestBuilderFailed
case DecodingResponseFailed
}
103 changes: 103 additions & 0 deletions Sources/Zen/ZenRequest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
//
// ZenRequest.swift
//
// Created by Karim Ebrahem on 4/7/20.
//

import Foundation
import Alamofire

public typealias Service<Response> = (_ completionHandler: @escaping (Result<Response, Error>) -> Void) -> Void

@propertyWrapper
public class ZenRequest<T> where T: Codable {

private var url: URL
private var path: String?
private var method: HTTPMethod?
private var headers: [String: Any]?
private var parameters: RequestParameters?
private var request: URLRequest?

public var projectedValue: ZenRequest<T> { return self }

public init(_ url: String) {
self.url = URL(string: url)!
}

@discardableResult
func set(method: HTTPMethod) -> Self {
self.method = method
return self
}

@discardableResult
public func set(path: String) -> Self {
self.path = path
return self
}

@discardableResult
public func set(headers: [String: Any]?) -> Self {
self.headers = headers
return self
}

@discardableResult
public func set(parameters: RequestParameters?) -> Self {
self.parameters = parameters
return self
}

public func build() throws {
do {
var urlRequest = URLRequest(url: url.appendingPathComponent(path ?? ""),
cachePolicy: .reloadIgnoringLocalAndRemoteCacheData,
timeoutInterval: 25)
urlRequest.httpMethod = method?.rawValue

headers?.forEach {
urlRequest.addValue($0.value as! String, forHTTPHeaderField: $0.key)
}

if let params = parameters {
urlRequest = try buildRequestParams(urlRequest, params: params)
}

self.request = urlRequest
} catch {
throw ZenError.RequestBuilderFailed
}
}

fileprivate func buildRequestParams(_ urlRequest: URLRequest, params: RequestParameters) throws -> URLRequest {
var urlRequest = urlRequest
urlRequest = try params.encoder.encode(params.parameters, into: urlRequest)
return urlRequest
}

public var wrappedValue: Service<T> {
get {
return { completion in
guard let request = self.request else { return }

AF.request(request)
.responseData { (response: AFDataResponse<Data>) in

switch response.result {
case .success(let data):
do {
let responseData = try JSONDecoder().decode(T.self, from: data)
completion(.success(responseData))
} catch {
completion(.failure(ZenError.DecodingResponseFailed))
}
case .failure(let error):
completion(.failure(error))
}
}
}
}
}

}
15 changes: 15 additions & 0 deletions Tests/ZenTests/Model/Todo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// Todo.swift
//
//
// Created by KarimEbrahem on 7/19/20.
//

import Foundation

struct Todo: Codable {
var userId: Int
var id: Int
var title: String
var completed: Bool
}
14 changes: 14 additions & 0 deletions Tests/ZenTests/Networking/APIClient.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// APIClient.swift
//
//
// Created by KarimEbrahem on 7/19/20.
//

import Foundation
import Zen

class APIClient {
@ZenRequest<Todo>("https://jsonplaceholder.typicode.com/todos/")
static var fetchTodo: Service<Todo>
}
29 changes: 22 additions & 7 deletions Tests/ZenTests/ZenTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,29 @@ import XCTest
@testable import Zen

final class ZenTests: XCTestCase {
func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct
// results.
// XCTAssertEqual(Zen().text, "Hello, World!")

func testGetRequest() {
let expectation = self.expectation(description: "Get Request")

try? APIClient.$fetchTodo
.set(method: .get)
.set(path: "1")
.build()

APIClient.fetchTodo { result in
switch result {
case .success(let todo):
print(todo.title)
case .failure(let error):
print(error)
}
expectation.fulfill()
}

waitForExpectations(timeout: 5, handler: nil)
}

static var allTests = [
("testExample", testExample),
("testGetRequest", testGetRequest)
]
}
2 changes: 1 addition & 1 deletion Zen.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

Pod::Spec.new do |s|
s.name = 'Zen'
s.version = '0.1.5'
s.version = '0.1.6'
s.summary = 'Zero Effort Networking Library in Swift.'

# This description is used to generate tags and improve search results.
Expand Down
4 changes: 2 additions & 2 deletions Zen/ZenExample/Networking/APIClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ import Foundation
import Zen

class APIClient {
@GET<[Todo]>(url: "https://jsonplaceholder.typicode.com/todos")
static var fetchTodos: Service<[Todo]>
@ZenRequest<Todo>("https://jsonplaceholder.typicode.com/todos/")
static var fetchTodo: Service<Todo>
}
18 changes: 14 additions & 4 deletions Zen/ZenExample/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,22 @@ class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()

fetchTodos()
fetchTodo()
}

private func fetchTodos() {
APIClient.fetchTodos { result in
print(result)
private func fetchTodo() {
try? APIClient.$fetchTodo
.set(method: .get)
.set(path: "1")
.build()

APIClient.fetchTodo { result in
switch result {
case .success(let todo):
print(todo.title)
case .failure(let error):
print(error)
}
}
}

Expand Down

0 comments on commit a2c47fc

Please sign in to comment.