Convert several methods to async await.
This commit is contained in:
parent
d2ae1d3120
commit
f75f67a42c
@ -300,78 +300,32 @@ final class FeedlyAccountDelegate: AccountDelegate {
|
|||||||
|
|
||||||
func renameFolder(for account: Account, with folder: Folder, to name: String) async throws {
|
func renameFolder(for account: Account, with folder: Folder, to name: String) async throws {
|
||||||
|
|
||||||
try await withCheckedThrowingContinuation { continuation in
|
|
||||||
|
|
||||||
self.renameFolder(for: account, with: folder, to: name) { result in
|
|
||||||
switch result {
|
|
||||||
case .success:
|
|
||||||
continuation.resume()
|
|
||||||
case .failure(let error):
|
|
||||||
continuation.resume(throwing: error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
|
|
||||||
guard let id = folder.externalID else {
|
guard let id = folder.externalID else {
|
||||||
return DispatchQueue.main.async {
|
throw FeedlyAccountDelegateError.unableToRenameFolder(folder.nameForDisplay, name)
|
||||||
completion(.failure(FeedlyAccountDelegateError.unableToRenameFolder(folder.nameForDisplay, name)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let nameBefore = folder.name
|
let nameBefore = folder.name
|
||||||
|
|
||||||
caller.renameCollection(with: id, to: name) { result in
|
do {
|
||||||
switch result {
|
let collection = try await caller.renameCollection(with: id, to: name)
|
||||||
case .success(let collection):
|
folder.name = collection.label
|
||||||
folder.name = collection.label
|
} catch {
|
||||||
completion(.success(()))
|
folder.name = nameBefore
|
||||||
case .failure(let error):
|
throw error
|
||||||
folder.name = nameBefore
|
|
||||||
completion(.failure(error))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
folder.name = name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeFolder(for account: Account, with folder: Folder) async throws {
|
func removeFolder(for account: Account, with folder: Folder) async throws {
|
||||||
|
|
||||||
try await withCheckedThrowingContinuation { continuation in
|
|
||||||
|
|
||||||
self.removeFolder(for: account, with: folder) { result in
|
|
||||||
switch result {
|
|
||||||
case .success:
|
|
||||||
continuation.resume()
|
|
||||||
case .failure(let error):
|
|
||||||
continuation.resume(throwing: error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func removeFolder(for account: Account, with folder: Folder, completion: @escaping (Result<Void, Error>) -> Void) {
|
|
||||||
guard let id = folder.externalID else {
|
guard let id = folder.externalID else {
|
||||||
return DispatchQueue.main.async {
|
throw FeedlyAccountDelegateError.unableToRemoveFolder(folder.nameForDisplay)
|
||||||
completion(.failure(FeedlyAccountDelegateError.unableToRemoveFolder(folder.nameForDisplay)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let progress = refreshProgress
|
refreshProgress.addTask()
|
||||||
progress.addToNumberOfTasksAndRemaining(1)
|
defer { refreshProgress.completeTask() }
|
||||||
|
|
||||||
caller.deleteCollection(with: id) { result in
|
try await caller.deleteCollection(with: id)
|
||||||
progress.completeTask()
|
account.removeFolder(folder: folder)
|
||||||
|
|
||||||
switch result {
|
|
||||||
case .success:
|
|
||||||
account.removeFolder(folder: folder)
|
|
||||||
completion(.success(()))
|
|
||||||
case .failure(let error):
|
|
||||||
completion(.failure(error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createFeed(for account: Account, url: String, name: String?, container: Container, validateFeed: Bool) async throws -> Feed {
|
func createFeed(for account: Account, url: String, name: String?, container: Container, validateFeed: Bool) async throws -> Feed {
|
||||||
@ -511,84 +465,38 @@ final class FeedlyAccountDelegate: AccountDelegate {
|
|||||||
|
|
||||||
func removeFeed(for account: Account, with feed: Feed, from container: any Container) async throws {
|
func removeFeed(for account: Account, with feed: Feed, from container: any Container) async throws {
|
||||||
|
|
||||||
try await withCheckedThrowingContinuation { continuation in
|
|
||||||
|
|
||||||
self.removeFeed(for: account, with: feed, from: container) { result in
|
|
||||||
switch result {
|
|
||||||
case .success:
|
|
||||||
continuation.resume()
|
|
||||||
case .failure(let error):
|
|
||||||
continuation.resume(throwing: error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func removeFeed(for account: Account, with feed: Feed, from container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
|
||||||
guard let folder = container as? Folder, let collectionID = folder.externalID else {
|
guard let folder = container as? Folder, let collectionID = folder.externalID else {
|
||||||
return DispatchQueue.main.async {
|
throw FeedlyAccountDelegateError.unableToRemoveFeed(feed.nameForDisplay)
|
||||||
completion(.failure(FeedlyAccountDelegateError.unableToRemoveFeed(feed.nameForDisplay)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
caller.removeFeed(feed.feedID, fromCollectionWith: collectionID) { result in
|
|
||||||
switch result {
|
|
||||||
case .success:
|
|
||||||
completion(.success(()))
|
|
||||||
case .failure(let error):
|
|
||||||
folder.addFeed(feed)
|
|
||||||
completion(.failure(error))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try await caller.removeFeed(feed.feedID, fromCollectionWith: collectionID)
|
||||||
folder.removeFeed(feed)
|
folder.removeFeed(feed)
|
||||||
}
|
}
|
||||||
|
|
||||||
func moveFeed(for account: Account, with feed: Feed, from: Container, to: Container) async throws {
|
func moveFeed(for account: Account, with feed: Feed, from: Container, to: Container) async throws {
|
||||||
|
|
||||||
try await withCheckedThrowingContinuation { continuation in
|
guard let sourceFolder = from as? Folder, let destinationFolder = to as? Folder else {
|
||||||
self.moveFeed(for: account, with: feed, from: from, to: to) { result in
|
throw FeedlyAccountDelegateError.addFeedChooseFolder
|
||||||
switch result {
|
|
||||||
case .success:
|
|
||||||
continuation.resume()
|
|
||||||
case .failure(let error):
|
|
||||||
continuation.resume(throwing: error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// Optimistically move the feed, undoing as appropriate to the failure
|
||||||
|
sourceFolder.removeFeed(feed)
|
||||||
|
destinationFolder.addFeed(feed)
|
||||||
|
|
||||||
@MainActor func moveFeed(for account: Account, with feed: Feed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
do {
|
||||||
guard let from = from as? Folder, let to = to as? Folder else {
|
try await addFeed(for: account, with: feed, to: destinationFolder)
|
||||||
return DispatchQueue.main.async {
|
} catch {
|
||||||
completion(.failure(FeedlyAccountDelegateError.addFeedChooseFolder))
|
destinationFolder.removeFeed(feed)
|
||||||
}
|
throw FeedlyAccountDelegateError.unableToMoveFeedBetweenFolders(feed.nameForDisplay, sourceFolder.nameForDisplay, destinationFolder.nameForDisplay)
|
||||||
}
|
}
|
||||||
|
|
||||||
addFeed(for: account, with: feed, to: to) { [weak self] addResult in
|
// Now that we have added the feed, remove it from the source folder
|
||||||
switch addResult {
|
do {
|
||||||
// now that we have added the feed, remove it from the other collection
|
try await removeFeed(for: account, with: feed, from: sourceFolder)
|
||||||
case .success:
|
} catch {
|
||||||
self?.removeFeed(for: account, with: feed, from: from) { removeResult in
|
sourceFolder.addFeed(feed)
|
||||||
switch removeResult {
|
throw FeedlyAccountDelegateError.unableToMoveFeedBetweenFolders(feed.nameForDisplay, sourceFolder.nameForDisplay, destinationFolder.nameForDisplay)
|
||||||
case .success:
|
|
||||||
completion(.success(()))
|
|
||||||
case .failure:
|
|
||||||
from.addFeed(feed)
|
|
||||||
completion(.failure(FeedlyAccountDelegateError.unableToMoveFeedBetweenFolders(feed.nameForDisplay, from.nameForDisplay, to.nameForDisplay)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case .failure(let error):
|
|
||||||
from.addFeed(feed)
|
|
||||||
to.removeFeed(feed)
|
|
||||||
completion(.failure(error))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// optimistically move the feed, undoing as appropriate to the failure
|
|
||||||
from.removeFeed(feed)
|
|
||||||
to.addFeed(feed)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func restoreFeed(for account: Account, feed: Feed, container: any Container) async throws {
|
func restoreFeed(for account: Account, feed: Feed, container: any Container) async throws {
|
||||||
|
@ -153,25 +153,11 @@ protocol FeedlyAPICallerDelegate: AnyObject {
|
|||||||
|
|
||||||
func importOPML(_ opmlData: Data) async throws {
|
func importOPML(_ opmlData: Data) async throws {
|
||||||
|
|
||||||
guard !isSuspended else {
|
guard !isSuspended else { throw TransportError.suspended }
|
||||||
throw TransportError.suspended
|
|
||||||
}
|
|
||||||
guard let accessToken = credentials?.secret else {
|
|
||||||
throw CredentialsError.incompleteCredentials
|
|
||||||
}
|
|
||||||
|
|
||||||
var components = baseURLComponents
|
var request = try urlRequest(path: "/v3/opml", method: HTTPMethod.post, addJSONHeaders: false, addOauthToken: true)
|
||||||
components.path = "/v3/opml"
|
|
||||||
|
|
||||||
guard let url = components.url else {
|
|
||||||
fatalError("\(components) does not produce a valid URL.")
|
|
||||||
}
|
|
||||||
|
|
||||||
var request = URLRequest(url: url)
|
|
||||||
request.httpMethod = "POST"
|
|
||||||
request.addValue("text/xml", forHTTPHeaderField: HTTPRequestHeader.contentType)
|
request.addValue("text/xml", forHTTPHeaderField: HTTPRequestHeader.contentType)
|
||||||
request.addValue("application/json", forHTTPHeaderField: "Accept-Type")
|
request.addValue("application/json", forHTTPHeaderField: HTTPRequestHeader.acceptType)
|
||||||
request.addValue("OAuth \(accessToken)", forHTTPHeaderField: HTTPRequestHeader.authorization)
|
|
||||||
request.httpBody = opmlData
|
request.httpBody = opmlData
|
||||||
|
|
||||||
let (httpResponse, _) = try await send(request: request, resultType: String.self)
|
let (httpResponse, _) = try await send(request: request, resultType: String.self)
|
||||||
@ -182,25 +168,9 @@ protocol FeedlyAPICallerDelegate: AnyObject {
|
|||||||
|
|
||||||
func createCollection(named label: String) async throws -> FeedlyCollection {
|
func createCollection(named label: String) async throws -> FeedlyCollection {
|
||||||
|
|
||||||
guard !isSuspended else {
|
guard !isSuspended else { throw TransportError.suspended }
|
||||||
throw TransportError.suspended
|
|
||||||
}
|
|
||||||
guard let accessToken = credentials?.secret else {
|
|
||||||
throw CredentialsError.incompleteCredentials
|
|
||||||
}
|
|
||||||
|
|
||||||
var components = baseURLComponents
|
var request = try urlRequest(path: "/v3/collections", method: HTTPMethod.post, addJSONHeaders: true, addOauthToken: true)
|
||||||
components.path = "/v3/collections"
|
|
||||||
|
|
||||||
guard let url = components.url else {
|
|
||||||
fatalError("\(components) does not produce a valid URL.")
|
|
||||||
}
|
|
||||||
|
|
||||||
var request = URLRequest(url: url)
|
|
||||||
request.httpMethod = "POST"
|
|
||||||
request.addValue("application/json", forHTTPHeaderField: HTTPRequestHeader.contentType)
|
|
||||||
request.addValue("application/json", forHTTPHeaderField: "Accept-Type")
|
|
||||||
request.addValue("OAuth \(accessToken)", forHTTPHeaderField: HTTPRequestHeader.authorization)
|
|
||||||
|
|
||||||
struct CreateCollectionBody: Encodable {
|
struct CreateCollectionBody: Encodable {
|
||||||
var label: String
|
var label: String
|
||||||
@ -216,166 +186,81 @@ protocol FeedlyAPICallerDelegate: AnyObject {
|
|||||||
}
|
}
|
||||||
return collection
|
return collection
|
||||||
}
|
}
|
||||||
|
|
||||||
func renameCollection(with id: String, to name: String, completion: @escaping (Result<FeedlyCollection, Error>) -> ()) {
|
func renameCollection(with id: String, to name: String) async throws -> FeedlyCollection {
|
||||||
guard !isSuspended else {
|
|
||||||
return DispatchQueue.main.async {
|
guard !isSuspended else { throw TransportError.suspended }
|
||||||
completion(.failure(TransportError.suspended))
|
|
||||||
}
|
var request = try urlRequest(path: "/v3/collections", method: HTTPMethod.post, addJSONHeaders: true, addOauthToken: true)
|
||||||
|
|
||||||
|
struct RenameCollectionBody: Encodable {
|
||||||
|
var id: String
|
||||||
|
var label: String
|
||||||
}
|
}
|
||||||
|
let encoder = JSONEncoder()
|
||||||
guard let accessToken = credentials?.secret else {
|
let data = try encoder.encode(RenameCollectionBody(id: id, label: name))
|
||||||
return DispatchQueue.main.async {
|
request.httpBody = data
|
||||||
completion(.failure(CredentialsError.incompleteCredentials))
|
|
||||||
}
|
let (httpResponse, collections) = try await send(request: request, resultType: [FeedlyCollection].self)
|
||||||
}
|
|
||||||
var components = baseURLComponents
|
guard let collection = collections?.first, httpResponse.statusCode == HTTPResponseCode.OK else {
|
||||||
components.path = "/v3/collections"
|
throw URLError(.cannotDecodeContentData)
|
||||||
|
|
||||||
guard let url = components.url else {
|
|
||||||
fatalError("\(components) does not produce a valid URL.")
|
|
||||||
}
|
|
||||||
|
|
||||||
var request = URLRequest(url: url)
|
|
||||||
request.httpMethod = "POST"
|
|
||||||
request.addValue("application/json", forHTTPHeaderField: HTTPRequestHeader.contentType)
|
|
||||||
request.addValue("application/json", forHTTPHeaderField: "Accept-Type")
|
|
||||||
request.addValue("OAuth \(accessToken)", forHTTPHeaderField: HTTPRequestHeader.authorization)
|
|
||||||
|
|
||||||
do {
|
|
||||||
struct RenameCollectionBody: Encodable {
|
|
||||||
var id: String
|
|
||||||
var label: String
|
|
||||||
}
|
|
||||||
let encoder = JSONEncoder()
|
|
||||||
let data = try encoder.encode(RenameCollectionBody(id: id, label: name))
|
|
||||||
request.httpBody = data
|
|
||||||
} catch {
|
|
||||||
return DispatchQueue.main.async {
|
|
||||||
completion(.failure(error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
send(request: request, resultType: [FeedlyCollection].self, dateDecoding: .millisecondsSince1970, keyDecoding: .convertFromSnakeCase) { result in
|
|
||||||
switch result {
|
|
||||||
case .success(let (httpResponse, collections)):
|
|
||||||
if httpResponse.statusCode == 200, let collection = collections?.first {
|
|
||||||
completion(.success(collection))
|
|
||||||
} else {
|
|
||||||
completion(.failure(URLError(.cannotDecodeContentData)))
|
|
||||||
}
|
|
||||||
case .failure(let error):
|
|
||||||
completion(.failure(error))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return collection
|
||||||
}
|
}
|
||||||
|
|
||||||
private func encodeForURLPath(_ pathComponent: String) -> String? {
|
private func encodeForURLPath(_ pathComponent: String) -> String? {
|
||||||
return pathComponent.addingPercentEncoding(withAllowedCharacters: uriComponentAllowed)
|
return pathComponent.addingPercentEncoding(withAllowedCharacters: uriComponentAllowed)
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteCollection(with id: String, completion: @escaping (Result<Void, Error>) -> ()) {
|
func deleteCollection(with id: String) async throws {
|
||||||
guard !isSuspended else {
|
|
||||||
return DispatchQueue.main.async {
|
guard !isSuspended else { throw TransportError.suspended }
|
||||||
completion(.failure(TransportError.suspended))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let accessToken = credentials?.secret else {
|
|
||||||
return DispatchQueue.main.async {
|
|
||||||
completion(.failure(CredentialsError.incompleteCredentials))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
guard let encodedID = encodeForURLPath(id) else {
|
guard let encodedID = encodeForURLPath(id) else {
|
||||||
return DispatchQueue.main.async {
|
throw FeedlyAccountDelegateError.unexpectedResourceID(id)
|
||||||
completion(.failure(FeedlyAccountDelegateError.unexpectedResourceID(id)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
var components = baseURLComponents
|
let request = try urlRequest(path: "/v3/collections/\(encodedID)", method: HTTPMethod.delete, addJSONHeaders: true, addOauthToken: true)
|
||||||
components.percentEncodedPath = "/v3/collections/\(encodedID)"
|
|
||||||
|
let (httpResponse, _) = try await send(request: request, resultType: Optional<FeedlyCollection>.self)
|
||||||
guard let url = components.url else {
|
|
||||||
fatalError("\(components) does not produce a valid URL.")
|
guard httpResponse.statusCode == HTTPResponseCode.OK else {
|
||||||
}
|
throw URLError(.cannotDecodeContentData)
|
||||||
|
|
||||||
var request = URLRequest(url: url)
|
|
||||||
request.httpMethod = "DELETE"
|
|
||||||
request.addValue("application/json", forHTTPHeaderField: HTTPRequestHeader.contentType)
|
|
||||||
request.addValue("application/json", forHTTPHeaderField: "Accept-Type")
|
|
||||||
request.addValue("OAuth \(accessToken)", forHTTPHeaderField: HTTPRequestHeader.authorization)
|
|
||||||
|
|
||||||
send(request: request, resultType: Optional<FeedlyCollection>.self, dateDecoding: .millisecondsSince1970, keyDecoding: .convertFromSnakeCase) { result in
|
|
||||||
switch result {
|
|
||||||
case .success(let (httpResponse, _)):
|
|
||||||
if httpResponse.statusCode == 200 {
|
|
||||||
completion(.success(()))
|
|
||||||
} else {
|
|
||||||
completion(.failure(URLError(.cannotDecodeContentData)))
|
|
||||||
}
|
|
||||||
case .failure(let error):
|
|
||||||
completion(.failure(error))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeFeed(_ feedId: String, fromCollectionWith collectionID: String, completion: @escaping (Result<Void, Error>) -> ()) {
|
func removeFeed(_ feedId: String, fromCollectionWith collectionID: String) async throws {
|
||||||
guard !isSuspended else {
|
|
||||||
return DispatchQueue.main.async {
|
guard !isSuspended else { throw TransportError.suspended }
|
||||||
completion(.failure(TransportError.suspended))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let accessToken = credentials?.secret else {
|
|
||||||
return DispatchQueue.main.async {
|
|
||||||
completion(.failure(CredentialsError.incompleteCredentials))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let encodedCollectionID = encodeForURLPath(collectionID) else {
|
guard let encodedCollectionID = encodeForURLPath(collectionID) else {
|
||||||
return DispatchQueue.main.async {
|
throw FeedlyAccountDelegateError.unexpectedResourceID(collectionID)
|
||||||
completion(.failure(FeedlyAccountDelegateError.unexpectedResourceID(collectionID)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var components = baseURLComponents
|
var components = baseURLComponents
|
||||||
components.percentEncodedPath = "/v3/collections/\(encodedCollectionID)/feeds/.mdelete"
|
components.percentEncodedPath = "/v3/collections/\(encodedCollectionID)/feeds/.mdelete"
|
||||||
|
|
||||||
guard let url = components.url else {
|
guard let url = components.url else {
|
||||||
fatalError("\(components) does not produce a valid URL.")
|
fatalError("\(components) does not produce a valid URL.")
|
||||||
}
|
}
|
||||||
|
|
||||||
var request = URLRequest(url: url)
|
var request = URLRequest(url: url)
|
||||||
request.httpMethod = "DELETE"
|
request.httpMethod = HTTPMethod.delete
|
||||||
request.addValue("application/json", forHTTPHeaderField: HTTPRequestHeader.contentType)
|
_addJSONHeaders(&request)
|
||||||
request.addValue("application/json", forHTTPHeaderField: "Accept-Type")
|
try addOauthAccessToken(&request)
|
||||||
request.addValue("OAuth \(accessToken)", forHTTPHeaderField: HTTPRequestHeader.authorization)
|
|
||||||
|
struct RemovableFeed: Encodable {
|
||||||
do {
|
let id: String
|
||||||
struct RemovableFeed: Encodable {
|
|
||||||
let id: String
|
|
||||||
}
|
|
||||||
let encoder = JSONEncoder()
|
|
||||||
let data = try encoder.encode([RemovableFeed(id: feedId)])
|
|
||||||
request.httpBody = data
|
|
||||||
} catch {
|
|
||||||
return DispatchQueue.main.async {
|
|
||||||
completion(.failure(error))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
let encoder = JSONEncoder()
|
||||||
|
let data = try encoder.encode([RemovableFeed(id: feedId)])
|
||||||
|
request.httpBody = data
|
||||||
|
|
||||||
// `resultType` is optional because the Feedly API has gone from returning an array of removed feeds to returning `null`.
|
// `resultType` is optional because the Feedly API has gone from returning an array of removed feeds to returning `null`.
|
||||||
// https://developer.feedly.com/v3/collections/#remove-multiple-feeds-from-a-personal-collection
|
// https://developer.feedly.com/v3/collections/#remove-multiple-feeds-from-a-personal-collection
|
||||||
send(request: request, resultType: Optional<[FeedlyFeed]>.self, dateDecoding: .millisecondsSince1970, keyDecoding: .convertFromSnakeCase) { result in
|
let (httpResponse, _) = try await send(request: request, resultType: Optional<[FeedlyFeed]>.self)
|
||||||
switch result {
|
|
||||||
case .success((let httpResponse, _)):
|
guard httpResponse.statusCode == HTTPResponseCode.OK else {
|
||||||
if httpResponse.statusCode == 200 {
|
throw URLError(.cannotDecodeContentData)
|
||||||
completion(.success(()))
|
|
||||||
} else {
|
|
||||||
completion(.failure(URLError(.cannotDecodeContentData)))
|
|
||||||
}
|
|
||||||
case .failure(let error):
|
|
||||||
completion(.failure(error))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -946,3 +831,50 @@ extension FeedlyAPICaller: FeedlyLogoutService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private extension FeedlyAPICaller {
|
||||||
|
|
||||||
|
func urlRequest(path: String, method: String, addJSONHeaders: Bool, addOauthToken: Bool) throws -> URLRequest {
|
||||||
|
|
||||||
|
let url = apiURL(path)
|
||||||
|
var request = URLRequest(url: url)
|
||||||
|
|
||||||
|
request.httpMethod = method
|
||||||
|
|
||||||
|
if addJSONHeaders {
|
||||||
|
_addJSONHeaders(&request)
|
||||||
|
}
|
||||||
|
if addOauthToken {
|
||||||
|
try addOauthAccessToken(&request)
|
||||||
|
}
|
||||||
|
|
||||||
|
return request
|
||||||
|
}
|
||||||
|
|
||||||
|
func _addJSONHeaders(_ request: inout URLRequest) {
|
||||||
|
|
||||||
|
request.addValue("application/json", forHTTPHeaderField: HTTPRequestHeader.contentType)
|
||||||
|
request.addValue("application/json", forHTTPHeaderField: "Accept-Type")
|
||||||
|
}
|
||||||
|
|
||||||
|
func addOauthAccessToken(_ request: inout URLRequest) throws {
|
||||||
|
|
||||||
|
guard let accessToken = credentials?.secret else {
|
||||||
|
throw CredentialsError.incompleteCredentials
|
||||||
|
}
|
||||||
|
|
||||||
|
request.addValue("OAuth \(accessToken)", forHTTPHeaderField: HTTPRequestHeader.authorization)
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiURL(_ path: String) -> URL {
|
||||||
|
|
||||||
|
var components = baseURLComponents
|
||||||
|
components.path = path
|
||||||
|
|
||||||
|
guard let url = components.url else {
|
||||||
|
fatalError("\(components) does not produce a valid URL.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user