Skip to content

Feature/js network formdata type#71

Open
jsanderson44 wants to merge 8 commits intomainfrom
feature/js-network-formdata-type
Open

Feature/js network formdata type#71
jsanderson44 wants to merge 8 commits intomainfrom
feature/js-network-formdata-type

Conversation

@jsanderson44
Copy link

Added NetworkFormDataResourceType and NetworkJSONResourceType.
Refactored NetworkResourceType to take more generic 'body' and moved encoding of data to the conforming types.
Updated tests

John Sanderson added 2 commits January 30, 2018 12:59
Refactored NetworkResourceType to take more generic 'body' and moved encoding of data to the conforming types.
Updated tests
@codecov-io
Copy link

codecov-io commented Jan 30, 2018

Codecov Report

Merging #71 into master will increase coverage by 0.43%.
The diff coverage is 79.16%.

@@            Coverage Diff             @@
##           master      #71      +/-   ##
==========================================
+ Coverage   86.12%   86.56%   +0.43%     
==========================================
  Files          22       22              
  Lines         346      268      -78     
  Branches       30        0      -30     
==========================================
- Hits          298      232      -66     
+ Misses         48       36      -12

self.forEach { (key, value) in
data.append(key + "=\(value)")
}
return data.map { String($0) }.joined(separator: "&")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could use the syntax return data.map(String.init).joined(separator: "&")

/// Returns dictionary as string used to POST Form Data
public var formDataPostString: String {
var data = [String]()
self.forEach { (key, value) in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

superfluous self


import Foundation

public extension Dictionary where Key == String, Value == Any {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

presumably it's just left to the consumer to ensure their value type can be converted to a string? only works for primitive types.

/// The HTTP body as JSON used to fetch this resource
var jsonBody: Any? { get }
/// The HTTP body used to fetch this resource
var body: Any? { get }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this isn't needed by this protocol. it's only used by the protocols that inherit from this protocol. i suggest declaring it specifically in each of the other protocols, which also means you can name it more appropriately for each of the protocols that actually uses it.

it could lead to confusion. e.g. if you create a concrete conformer of NetworkResourceType (that does not conform to either NetworkJSONResourceType or NetworkPropertyListDecodableResourceType), then setting the body property does nothing.

var httpHeaderFields: [String: String]? {
return ["Content-Type": "application/x-plist"]
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i suggest adding var jsonBody: Any? { get } to the protocol definition. this demands that your conforming concrete type provides this property, and the property name is more descriptive of how it will be used.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, this plist one is a bit of a mess. it's intended to parse a plist from the response. the type being sent in the request is JSON, as you've created below. but the Content-Type header field is "application/x-plist" - that's wrong. we should:

  • either change the Content-Type to "application/json"
  • change the transformation from body to bodyData to use PropertyListSerialization instead of JSONSerialization - although this is probably ludicrous
  • change the entire protocol conformance pattern to distinguish between incoming and outgoing data formats (e.g. what if you want to send form data, but get a JSON response?)

import Foundation

/// Defines a resource that can be fetched from a network where the root type is a JSON array
public protocol NetworkJSONResourceType: NetworkResourceType {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i suggest adding var jsonBody: Any? { get } to the protocol definition. (see my previous comment above removing the body property from the NetworkResourceType definition.


import Foundation

/// Defines a resource that can be fetched from a network where the root type is a JSON array
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure this comment is true john, what do you mean by root type? i don't believe this resource specifies what type it is expecting in the JSON body response, only the request, and that's not specified as an array anyway

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. Now in line with NetworkFormDataResourceType docs

// TABResourceLoader
//
// Created by John Sanderson on 30/01/2018.
// Copyright © 2018 Luciano Marisi. All rights reserved.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use the MIT license and the TAB copyright here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

//
// The MIT License (MIT)
//
// Copyright (c) 2016 Luciano Marisi
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe change the copyright ot TAB here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

// TABResourceLoader
//
// Created by John Sanderson on 30/01/2018.
// Copyright © 2018 Luciano Marisi. All rights reserved.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use the MIT license and the TAB copyright here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

return ["Content-Type": "application/x-www-form-urlencoded"]
}

var bodyData: Data? {
Copy link
Contributor

@NikolaKirev NikolaKirev Jul 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to name this httpBody or just body?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah i like that idea @NikolaKirev , people will automatically start typing http if you want to add data to the body of your request so it'll help in xcode

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm there is also formDataBody, that could be quite confusing in the resource which one you should be using

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have updated to httpBody


/// Returns dictionary as string used to POST Form Data
public var formDataPostString: String {
var data = [String]()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming this data might be confusing. How about formEntries or entries?

let stringValue = String(describing: value)
data.append(key + "=" + stringValue)
}
return data.map(String.init(_:)).joined(separator: "&")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the map(String.init(_:)) needed? Could the whole thing be done with a single reduce call on the dictionary?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah a single reduce would work better. but anyway data is of type [String] so data.map(String.init) will just return the same as data. won't it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to use reduce

return ["Content-Type": "application/json"]
}
}
public protocol NetworkJSONDictionaryResourceType: NetworkJSONResourceType, JSONDictionaryResourceType {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm not so sure about this, i feel like we need a bit of a beat up around it. it's not clear from the name that the request body needs to be JSON (if you need to send data) and also that the response type is JSON. seems like this was an existing issue in TRL and why we needed this PR in the first place. maybe we need to look at removing some of these types and leave it to the consumer to compose the resource types they want (that would obviously be a breaking change 😢 ). i also think it might be nice to make a clearer distinction between the resource request information (NetworkJSONResourceType) and the resource response information (JSONDictionaryResourceType). i find the namings all a bit confusing at the moment... but i could just be out of touch as i didn't build this to start with. @Neil3079 @lucianomarisi any thoughts?

as a side note i think the change in NetworkResourceType.swift is spot on and where we should be heading for

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i was thinking and commenting exactly the same @deanbrindley87. see my comments 😄

var jsonBody: Any? { return nil }

var httpHeaderFields: [String: String]? {
return ["Content-Type": "application/json"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i've never actually liked the fact that we had this in there in the first place. i'm sure most users of this library are sending GET requests without sending content.

it would be better to distinguish between the request content type and the response content type.

e.g. would you ever need to send FORM data and get a JSON response? perhaps. the current implementation restricts this by confusing the two. this isn't anything to do with your change, just an observation about the restriction of the library in the first place. we've definitely gone with the simplest approach, which serve 90% of use cases. but something to bear in mind.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on second thoughts, i think it would be good to give this more thought now.

looking at your NetworkFormDataResourceType, it conforms to NetworkResourceType and DataResourceType. i assume you just use this to send form data and get an HTTP response code without any content in the response body?

what if you did get some content in the response body? what about error messages? do you get any JSON to give you more details on the error?

i think it would be good to think about this in terms of DataRequestType and DataResponseType protocols, which we could extend with FormDataRequestType, JSONDataRequestType, FormDataResponseType and FormDataResponseType. do you ever get form data in a response?

the "Request" type protocols would implement in protocol extensions the var body: Data? { get } requirement of the DataRequestType protocol, just as your NetworkJSONResourceType extension does here. these extensions would also implement the relevant header fields' content type.

the "Response" type protocols would implement in protocol extensions the func model(from data: Data) throws -> Model requirement of the DataResponseType protocol, just as the JSONDictionaryResourceType currently does.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@samdods did you read my comment above? looks like we're on a similar page...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah just read your comment after writing mine 👍

return ["Content-Type": "application/x-www-form-urlencoded"]
}

var httpBody: Data? {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'll check the code out and have a look @jsanderson44 , not following enough on here!!

Copy link
Contributor

@samdods samdods left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as per comments. let's discuss

@MarcoGuerrieriTAB MarcoGuerrieriTAB changed the base branch from master to main September 15, 2020 09:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants