Giter VIP home page Giter VIP logo

malibu's People

Contributors

daneov avatar guilhermearaujo avatar jobinsjohn avatar lfarah avatar onmyway133 avatar oscarapeland avatar ramongilabert avatar vadymmarkov avatar zenangst avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

malibu's Issues

Q: Problems with enumation based Network Intance

I've got response from the following code

    let request = Request.get("https://jsonplaceholder.typicode.com/posts/1")

        // Make a call
        Malibu.request(request)
            .validate()
            .toJsonDictionary()
            .then({ dictionary -> Post in
                print(dictionary)
                return try Post.init(object: dictionary)
            })
            .done({ posts in
                // Handle response data
                print(post)
            })
            .fail({ error in
                // Handle errors
                print(error)
            })
            .always({ _ in
                // Hide progress bar
            })

but when I define the API like network instance which named jsonPlaceholder. I don't any response, error, log. Nothing printed. No breakpoint entered to done, fail, or then.

enum JSONPlaceholderAPI: RequestConvertible {
    
    case getPost(id: String)
    
    static var baseUrl: URLStringConvertible? = "https://jsonplaceholder.typicode.com/"
    
    static var headers: [String: String] = [:]
    
    public var request: Request {
        switch self {
        case .getPost(let id):
            return Request.get("posts/\(id)")
        }
    }
}

  let jsonPlaceholder = Networking<JSONPlaceholderAPI>()
        
        jsonPlaceholder.request(.getPost(id: "1"))
            .validate()
            .toJsonDictionary()
            .then({ dictionary -> Post in
                print(dictionary)
                return try Post.init(object: dictionary)
            })
            .done({ posts in
                // Handle response data
                print(posts)
            })
            .fail({ error in
                // Handle errors
                print(error)
            })
            .always({ _ in
                // Hide progress bar
            })

Can't get the response body if validation fails

If the validation fails (let's say the server returns 400> status code) there is no way to get the data that the server returns.

Would be very helpful if there is a way to get the data so you can display server sided exceptions.

ETag still present even after calling Malibu.clearStorage()

I noticed that my requests are being dispatched with the header If-None-Match: "Some ETag hash" even after calling Malibu.clearStorage()

I created a sample project demonstrating it: https://github.com/guilhermearaujo/malibu-etag
Its README contains more details.

The weird thing is that at this line:
https://github.com/hyperoslo/Malibu/blob/26657c5e46339749aa6cc178ed2a4111fe6841ce/Sources/Networking.swift#L200-L212
before the return, I added the code:

print(urlRequest.allHTTPHeaderFields!)

And the If-None-Match header is not present, but somehow it ends being sent anyway.

Looks like a bug to me.
Tested on versions 6.2.0 and 6.4.1

Unable to change response code when mocking with JSON

In our backend we employ JSON error responses to inform the client of what went wrong exactly.
This can be a missing field (400), which would then contain a body like:

{
    "error_code" : "missing_field_y"
}

In order to verify that the app correctly passes this information, I'm writing a test to verify that the parsing goes correctly.

However, when creating a Mock object the following way, it seems to default to a successful response.

let provider = MockProvider<HTTPMethod> { _ in
            return Mock(json: ["error_code": errorCode])
        }

Do you have any suggestions on how to work around this?

(P.S. It's also inconvenient that passing data encoded with Swift 4's JSONEncoder is not working but results in an invalid_api_response)

Networking class doesn't work

Hi, I just wanted to try the library , but could not manage to make it work, it seems like request is not fired at all.

import UIKit
import Malibu

enum ParseError: Error {
case InvalidJson
}

struct User: CustomStringConvertible {
let id: Int
let name: String
let username: String
let email: String

var description: String {
    return "Id: \(id)\nName: \(name)\nUsername:\(username)\nEmail: \(email)\n"
}

init(dictionary: [String: Any]) throws {
    guard
        let id = dictionary["id"] as? Int,
        let name = dictionary["name"] as? String,
        let username = dictionary["username"] as? String,
        let email = dictionary["email"] as? String
        else {
            throw ParseError.InvalidJson
    }
    
    self.id = id
    self.name = name
    self.username = username
    self.email = email
}

}

enum Endpoint: RequestConvertible {
case fetchUsers
case createUser(id: Int, name: String, username: String, email: String)

static let baseUrl: URLStringConvertible? = "http://jsonplaceholder.typicode.com/"
static let sessionConfiguration: SessionConfiguration = .default

// Additional headers will be used in the each request.
static var headers: [String : String] {
    return ["Accept" : "application/json"]
}

var request: Request {
    switch self {
    case .fetchUsers:
        return Request.get("users", etagPolicy: .disabled)
    case .createUser(let id, let name, let username, let email):
        return Request.post("users", parameters: [
            "id": id,
            "name": name,
            "username": username,
            "email": email])
    }
}

}

class ViewController: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()
    
    // Create and configure Networking.
    let networking = Networking<Endpoint>()
    
    // Make GET request
    networking.request(.fetchUsers)
        .validate()
        .toJsonArray()
        .then({ data -> [User] in
            return try data.map({ try User(dictionary: $0) })
        })
        .done({ users in
            print("A list of users:\n")
            users.forEach { print($0) }
        })
        .fail({ error in
            print(error)
        })
        .always({ _ in
            /// ...
        })
}

}

Malibu including 'When' in `Malibu.framework/Frameworks` folder

Because of how Malibu is including When, using the copy-frameworks, it's included in the build result, you end up with the following:

Malibu.framework
| Malibu (executable)
| Info.plist
| _CodeSignature
| Frameworks
    | When.framework
        |   <contents of When>

This, in turn, results in the following rejections in the App Store:

Invalid Bundle. The bundle at '<AppName>.app/Frameworks/Malibu.framework' contains disallowed file 'Frameworks.
CFBundleIdentifier Collision. There is more than one bundle with the CFBundleIdentifier value 'com.vadymmarkov.When-iOS' under the iOS application '<AppName>.app'

I also include the 'When.framework' in my own project.

Any reason you're doing it that way? Am I the first to encounter this? @vadymmarkov

MockProvider does not call promise's callback

The following snippet shows a reproducible case in which neither success nor failure on the promise's part is called.

This seems to be in part due to the Networking instance being deallocated (the weak self check) returns since it is nil.

How can I work around this?

final class MockProviderSpec: QuickSpec {
    override func spec() {
        describe("Test") {
            it("Passes") {
                let fetcher = APIFetcher()
                waitUntil { done in
                    fetcher.request(request: CustomEndpoint.test).then({ _ in
                        done()
                    }).fail({ error in
                        done()
                    })
                }
            }
        }
    }
}

class APIFetcher {
    var mockClosure: ((RequestConvertible) -> Mock?)?

    func defaultClosure(_ request: RequestConvertible) -> Mock?{
        return Mock(json: [:])
    }

    func request(request: CustomEndpoint) -> NetworkPromise {

        let networking = Networking<CustomEndpoint>(mode: .sync, 
                                                    mockProvider: MockProvider<CustomEndpoint>(defaultClosure)
        )
        return networking.request(request)
    }
}

enum CustomEndpoint: RequestConvertible {
    static var baseUrl: URLStringConvertible?

    static var headers: [String : String] = [:]

    var request: Request {
        return Request.post("mock://derp")
    }

    case test
}

Re: Multipartform upload file using RequestConvertible

Hi there,

Thanks for the great library. However, I could not seem to figure out how to upload multipart form data (with files attached) using Malibu and RequestConvertible enum.

Could you please give me an example of how to do it?

Many thanks.

Documentation Error for Pre-processing

Pre-processing

// Use this closure to modify your Request value before URLRequest
// is created on base of it
networking.beforeEach = { request in
var request = request
request.message.parameters["userId"] = "12345" // <=== line does not compile, due to both no message on request, and parameters not mutable

return request
}

Retry with backoff

A retry function (with backoff) would be neat. Something like -

retry(request: Promise<Request>, retryCap: Int = 5, retryInterval: TimeInterval = 0.5)

Or some other syntax if anyone else has any better ideas.

I'd be happy to implement this; @vadymmarkov is this within what you'd like Malibu to be?

Idea/suggestion: improve and extend Codable support

I've been playing around with the CodyFire network library recently and while it has some flaws (and doesn't use promises), it uses Codable throughout in nifty ways. For example, it uses strongly typed structs for both request parameters and the response, like this:

class API {
  func register(_ request: RegisterData) -> APIRequest<LoginResponse> {
    return APIRequest("user/register/", payload: request).method(.post).desiredStatusCode(.created)
  }

  func login(_ request: LoginRequest) -> APIRequest<LoginResponse> {
    return APIRequest("user/login/", payload: request).method(.post)
  }

  func logout() -> APIRequest<Nothing> {
    return APIRequest("user/logout/").method(.post).desiredStatusCode(.noContent)
  }
}

struct RegisterData: JSONPayload {
  let username: String
  let name: String
  let password: String
  let email: String
  let gender: String
}

struct LoginRequest: JSONPayload {
  let username: String
  let password: String
}

struct LoginResponse: Codable {
  var token: String
}

As you can see, the parameters are strongly typed, this works for query params, form params, multi-part upload, etc etc. And the response is also automatically decoded. I know that you can do that in Malibu 8.1.0 as well, but there you have to do it when you make the request, instead of declaring it upfront on the Request itself.

Another thing that would be great is to have Decodable support for errors as well, something that CodyFire also lacks sadly.

Improve mocks

I just got an idea for an improvements to mocks. Right now you can mock using static JSON files, which is awesome and super easy to setup. But what if you could mock it by passing a closure so that you could give back computed mocks. I think this could be pretty neat if you combine it with components like Fakery (https://github.com/vadymmarkov/Fakery)

What do you guys think? @hyperoslo/ios

Increased number of errors after updating to 6.3.0+

Recently I released two updates of my app with no updates (concerning networking or the requests the app makes, or how and when they are made), but I did update Malibu from 6.2.0 to 6.3.0, and 6.4.0 in the second update.

Since 6.3.0, several customers have been complaining that some contents cannot be load in the app, and send screenshots displaying the error messages I programmed to be displayed when a network request fails for any reason other than 2 cases: Reachability says the device is offline; or the request returns with a 401 status code.

In my tests, I could never reproduce an error, and the worst case I could recreate was to enable the network conditioner with a very poor condition to force a request to time out.

.fail { error in
  switch error {
  case is UnreachableError: Alert.warn(R.string.alert.unreachable()) // Reachability
  case is Unauthorized: AppNotification.loggedOut.post() // 401
  default:
    Alert.warn("some 'could not load' message")
    if !(error is ApiError) { // ApiError is base for UnreachableError, Unauthorized and some other erros like 404, 500, 503, etc.
      Crashlytics.sharedInstance().recordError(error)
    }
  }
}

So I started logging the errors to Crashlytics, but the only data I get from them is:

NSERROR-DOMAIN: Malibu.NetworkError
NSERROR-CODE: 8

Is there any important change since 6.2.0? What case of NetworkError is this one whose code is 8??
How can I debug it better and get more data to understand what's really happening?

This is how the UnreachableError is thrown:

networking.middleware = { promise in
  if let connection = Reachability()?.connection, connection != .none {
    promise.resolve(Void())
  } else {
    promise.reject(UnreachableError())
  }
}

Download progress

Is it possible to track the progress/percentage of a file download? If yes, how?

The operation couldn’t be completed. (Malibu.NetworkError error 4.)

I am trying to run the first example:

let request = Request.get("http://sharkywaters.com/api/boards", parameters: ["type": 1])

// Make a call
Malibu.request(request)
  .validate()
  .toJsonDictionary()
  .done({ boards in
    // Handle response data
    print("done")
  })
  .fail({ error in
    // Handle errors
    print("fail \(error.localizedDescription)")
  })
  .always({ _ in
    // Hide progress bar
    print("always")
  })

and it always fails with the message The operation couldn’t be completed. (Malibu.NetworkError error 4.). What am I doing wrong?

Make `preProcessRequest` return a promise (or give middleware access to the Request)

Hi Vadym!

We have an Auth SDK at work that exposes a simple function:

public func addAuthentication(to request: Request, completion: (TypedResult<Request, TokenStorageError>) -> Void) 

(since we don't want clients to have access to the access tokens directly)

Since this API works asynchronously, we can't add this to preProcessRequest (which is synchronous) or to middleware (since that doesn't have access to the request). Is there a "right" way to do this?

Can't run playground

Playground complains: "No such module 'Malibu'". After I've updated Carthage dependencies I'm getting:

error: Couldn't lookup symbols:
_T06Malibu10NetworkingCACyxGAA0B4ModeO4mode_AA12MockProviderCyxGSg04mockF0AA20SessionConfigurationO07sessionI0So18URLSessionDelegate_pSg0jL0tcfcfA
_T06Malibu10NetworkingCACyxGAA0B4ModeO4mode_AA12MockProviderCyxGSg04mockF0AA20SessionConfigurationO07sessionI0So18URLSessionDelegate_pSg0jL0tcfcfA0
__T06Malibu7RequestV3getAcA20URLStringConvertible_p_s10DictionaryVySSypG10parametersAGyS2SG7headersAA10EtagPolicyO04etagJ0AA05StoreJ0O05storeJ0So12NSURLRequestC05CacheJ0O05cacheJ0tFZ
__T04When7PromiseC6MalibuAD8ResponseCRbzlE8validateACyAFGyF
.....

cancelAllRequests not working?

Super simpel test.

import Malibu

enum Endpoint: RequestConvertible {
  case fetchUsers

  static let baseUrl: URLStringConvertible? = "https://jsonplaceholder.typicode.com/"

  static var headers: [String : String] {
    return ["Accept" : "application/json"]
  }

  var request: Request {
    switch self {
    case .fetchUsers:
      return Request.get("users")
    }
  }
}

class ViewController: UIViewController {
  let networking = Networking<Endpoint>()

  override func viewDidLoad() {
    super.viewDidLoad()

    let promise = networking.request(.fetchUsers)

    promise.validate().done({ data in
      print("Success")
    }).fail({ error in
      print(error)
    })

    promise.cancel()
  }
}

With the method above, where I cancel a single network promise, the request is not made and neither the success or an error is printed. Great!

However, when I change that last line to networking.cancelAllRequests(), I do get "Success" printed out, and I can confirm that the request is indeed made.

SSL Pinning

What's the right way of setting URLCredentials for "session.configuration.urlCredentialStorage?.defaultCredential" in ssl pinning code? Who should be responsible for this part?

Completion handler blocks are not supported in background sessions.

Using SessionConfiguration.Background and a custom delegate with a POST request ends up with the error "Completion handler blocks are not supported in background sessions." I was thinking about working around that by creating a SessionUploadTask class based on SessionDataTask, using session.uploadTaskWithRequest (http://stackoverflow.com/a/23738200), to support background uploads. Doing so seems like it will be fairly straight forward, as long as the upload body tmp file is dealt with correctly (and probably will need to store the task identifier).

Is this kind of usage (background upload using uploadTaskWithRequest and a body file) something that Malibu might support eventually?

Update Dependencies

Morning All,
Tried to install the library through cocoapods yesterday and the library won't compile. Seems there are references to updates in the When library which aren't there. Installing through cocoa-odds installs When version 2.1.1 when the changes are at least in the latest version 2.3.3. I updated the pod spec locally and installed When 2.3.3 which fixed the references, but the RequestConvertible isn't running the promise. Using the default promise works, i.e. "let promise = Malibu.request(Request.get("https://api.myjson.com/bins/1bgj27"))", but using the RequestConvertible method doesn't. Didn't want to troubleshoot much without you having a chance to update the dependencies. Hope this makes sense. Let me know if I'm just doing something stupid. Bruce

Testing multi-request flows and MockProvider

Hi!

I'm trying to test a flow where access tokens are refreshed(similar to #42 ), and I'm using a MockProvider instead of making actual network requests (imagine making a MockProvider where if a request is made with the old token, it'll return a "failed" json and if the request is made with a new token, you get the "success" json). However, since the request in the MockProvider closure doesn't contain any information about the middleware, authentication etc, I can't check what token the request is made with.

Any suggestions on how I can write a test for this? 🤔

Manual Installation

CocoaPods and Carthage are awesome tools and make our life really easier, but there are some devs who still don't know how to use them.

It would be cool to add the Manual installation guide in your README.md. You can take a look at my iOS Readme Template to see how you can do it.

JSONSerialization cannot parse Date objects

When I try to perform a requests with a JSON body, and passing a parameter dictionary such as

params = ["date": Date()]

the serialization performed here will fail.

From a user point-of-view, I could (and probably will, for a while), pre-convert Date objects to String, but maybe it would be interesting if Malibu could handle this automatically. Perhaps with a configuration method that accepts a format for the conversion.

Reject promise in middleware

I'm doing authentication in middleware and when authentication fails I want to reject the promise with a custom error object. Where do I catch this error object? I have added validate(), done(), fail() and always handlers but none of them seem to be called. Have I misunderstood how reject works?

Authorization with refresh tokens

So the API i'm currently working against have tokens that does not last very long (30 mins). I'm also provided with a refreshtoken which I'm using to generate a new Accesstoken.

So when creating a request i want to check if the token has expired. IF the token has expired i want to request a new one and then continue with the previous request.

I have the flow to check if the token have expired or not working. But when requesting a new one i cant figure out how to "pause" the previous one and the continue it after i've received the new token.

Could someone point me into the correct direction here?

Proposal: Adding `RequestBehaviour`s to Malibu

Hey!

I've been reading about RequestBehaviour recently and I think it'd be a great addition to Malibu, especially for things like Logging, NetworkActivityIndicators, authentication, caching etc.

Here's what the protocol looks like

protocol RequestBehavior {

	func beforeSend()

	func afterSuccess(result: Any)

	func afterFailure(error: Error)

	func adapt(_ request: URLRequest) -> URLRequest

}

// default implementations
extension RequestBehavior {
	func beforeSend() {

	}

	func afterSuccess(result: Any) {

	}

	func afterFailure(error: Error) {

	}

	func adapt(_ request: URLRequest) -> URLRequest {
		return request
	}
}

And Auth can be implemented as follows:

struct TokenAuthBehaviour: RequestBehavior {
	let token: String

	func adapt(_ request: URLRequest) -> URLRequest {
		var copy = request
		var headers = copy.allHTTPHeaderFields ?? [:]
		headers["Authorization"] = "Bearer \(token)"
		copy.allHTTPHeaderFields = headers
		return copy
	}
}

struct CustomAuthBehaviour: RequestBehavior {

	let key: String
	let value: String

	func adapt(_ request: URLRequest) -> URLRequest {
		var copy = request
		var headers = copy.allHTTPHeaderFields ?? [:]
		headers[key] = value
		copy.allHTTPHeaderFields = headers
		return copy
	}
}

struct CustomAuthBehaviour: RequestBehavior {

	let key: String
	let value: String

	func adapt(_ request: URLRequest) -> URLRequest {
		var copy = request
		var headers = copy.allHTTPHeaderFields ?? [:]
		headers[key] = value
		copy.allHTTPHeaderFields = headers
		return copy
	}
}

There's other examples in the blog post, but this would be an excellent starting point (Logging could be in both before and after function calls, for example)

PS: Discussed this with @vadymmarkov on Twitter recently: https://twitter.com/vadymmarkov/status/956075029736894465

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.