Merge pull request #27 from writefreely/refactor-wfclient-with-templates
Refactor WFClient with Templates
This commit is contained in:
commit
a01bad4a25
|
@ -0,0 +1,18 @@
|
|||
# Types
|
||||
|
||||
- [WFClient](/WFClient)
|
||||
- [WFError](/WFError)
|
||||
- [WFCollection](/WFCollection)
|
||||
- [WFPost](/WFPost)
|
||||
- [WFUser](/WFUser)
|
||||
|
||||
# Protocols
|
||||
|
||||
- [URLSessionProtocol](/URLSessionProtocol):
|
||||
Define requirements for `URLSession`s here for dependency-injection purposes (specifically, for testing).
|
||||
- [URLSessionDataTaskProtocol](/URLSessionDataTaskProtocol):
|
||||
Define requirements for `URLSessionDataTask`s here for dependency-injection purposes (specifically, for testing).
|
||||
|
||||
# Extensions
|
||||
|
||||
- [URLSession](/URLSession)
|
|
@ -0,0 +1,12 @@
|
|||
# Extensions on URLSession
|
||||
|
||||
## Methods
|
||||
|
||||
### `dataTask(with:completionHandler:)`
|
||||
|
||||
``` swift
|
||||
public func dataTask(
|
||||
with request: URLRequest,
|
||||
completionHandler: @escaping DataTaskResult
|
||||
) -> URLSessionDataTaskProtocol
|
||||
```
|
|
@ -0,0 +1,15 @@
|
|||
# URLSessionDataTaskProtocol
|
||||
|
||||
Define requirements for `URLSessionDataTask`s here for dependency-injection purposes (specifically, for testing).
|
||||
|
||||
``` swift
|
||||
public protocol URLSessionDataTaskProtocol
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
### resume()
|
||||
|
||||
``` swift
|
||||
func resume()
|
||||
```
|
|
@ -0,0 +1,21 @@
|
|||
# URLSessionProtocol
|
||||
|
||||
Define requirements for `URLSession`s here for dependency-injection purposes (specifically, for testing).
|
||||
|
||||
``` swift
|
||||
public protocol URLSessionProtocol
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
### DataTaskResult
|
||||
|
||||
``` swift
|
||||
typealias DataTaskResult = (Data?, URLResponse?, Error?) -> Void
|
||||
```
|
||||
|
||||
### dataTask(with:completionHandler:)
|
||||
|
||||
``` swift
|
||||
func dataTask(with request: URLRequest, completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskProtocol
|
||||
```
|
|
@ -0,0 +1,373 @@
|
|||
# WFClient
|
||||
|
||||
``` swift
|
||||
public class WFClient
|
||||
```
|
||||
|
||||
## Initializers
|
||||
|
||||
### `init(for:with:)`
|
||||
|
||||
Initializes the WriteFreely client.
|
||||
|
||||
``` swift
|
||||
public init(for instanceURL: URL, with session: URLSessionProtocol = URLSession.shared)
|
||||
```
|
||||
|
||||
Required for connecting to the API endpoints of a WriteFreely instance.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- instanceURL: The URL for the WriteFreely instance to which we're connecting, including the protocol.
|
||||
- session: The URL session to use for connections; defaults to `URLSession.shared`.
|
||||
|
||||
## Properties
|
||||
|
||||
### `requestURL`
|
||||
|
||||
``` swift
|
||||
public var requestURL: URL
|
||||
```
|
||||
|
||||
### `user`
|
||||
|
||||
``` swift
|
||||
public var user: WFUser?
|
||||
```
|
||||
|
||||
## Methods
|
||||
|
||||
### `createCollection(token:withTitle:alias:completion:)`
|
||||
|
||||
Creates a new collection.
|
||||
|
||||
``` swift
|
||||
public func createCollection(
|
||||
token: String? = nil,
|
||||
withTitle title: String,
|
||||
alias: String? = nil,
|
||||
completion: @escaping (Result<WFCollection, Error>) -> Void
|
||||
)
|
||||
```
|
||||
|
||||
If only a `title` is given, the server will generate and return an alias; in this case, clients should store
|
||||
the returned `alias` for future operations.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: The access token for the user creating the collection.
|
||||
- title: The title of the new collection.
|
||||
- alias: The alias of the collection.
|
||||
- completion: A handler for the returned `WFCollection` on success, or `Error` on failure.
|
||||
|
||||
### `getCollection(token:withAlias:completion:)`
|
||||
|
||||
Retrieves a collection's metadata.
|
||||
|
||||
``` swift
|
||||
public func getCollection(
|
||||
token: String? = nil,
|
||||
withAlias alias: String,
|
||||
completion: @escaping (Result<WFCollection, Error>) -> Void
|
||||
)
|
||||
```
|
||||
|
||||
Collections can be retrieved without authentication. However, authentication is required for retrieving a
|
||||
private collection or one with scheduled posts.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: The access token for the user retrieving the collection.
|
||||
- alias: The alias for the collection to be retrieved.
|
||||
- completion: A handler for the returned `WFCollection` on success, or `Error` on failure.
|
||||
|
||||
### `deleteCollection(token:withAlias:completion:)`
|
||||
|
||||
Permanently deletes a collection.
|
||||
|
||||
``` swift
|
||||
public func deleteCollection(
|
||||
token: String? = nil,
|
||||
withAlias alias: String,
|
||||
completion: @escaping (Result<Bool, Error>) -> Void
|
||||
)
|
||||
```
|
||||
|
||||
Any posts in the collection are not deleted; rather, they are made anonymous.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: The access token for the user deleting the collection.
|
||||
- alias: The alias for the collection to be deleted.
|
||||
- completion: A hander for the returned `Bool` on success, or `Error` on failure.
|
||||
|
||||
### `getPosts(token:in:completion:)`
|
||||
|
||||
Retrieves an array of posts.
|
||||
|
||||
``` swift
|
||||
public func getPosts(
|
||||
token: String? = nil,
|
||||
in collectionAlias: String? = nil,
|
||||
completion: @escaping (Result<[WFPost], Error>) -> Void
|
||||
)
|
||||
```
|
||||
|
||||
If the `collectionAlias` argument is provided, an array of all posts in that collection is retrieved; if
|
||||
omitted, an array of all posts created by the user whose access token is provided is retrieved.
|
||||
|
||||
Collection posts can be retrieved without authentication; however, authentication is required for retrieving a
|
||||
private collection or one with scheduled posts.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: The access token for the user retrieving the posts.
|
||||
- collectionAlias: The alias for the collection whose posts are to be retrieved.
|
||||
- completion: A handler for the returned `[WFPost]` on success, or `Error` on failure.
|
||||
|
||||
### `movePost(token:postId:with:to:completion:)`
|
||||
|
||||
Moves a post to a collection.
|
||||
|
||||
``` swift
|
||||
public func movePost(
|
||||
token: String? = nil,
|
||||
postId: String,
|
||||
with modifyToken: String? = nil,
|
||||
to collectionAlias: String?,
|
||||
completion: @escaping (Result<Bool, Error>) -> Void
|
||||
)
|
||||
```
|
||||
|
||||
>
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: The access token for the user moving the post to a collection.
|
||||
- postId: The ID of the post to add to the collection.
|
||||
- modifyToken: The post's modify token; required if the post doesn't belong to the requesting user. If `collectionAlias` is `nil`, do not include a `modifyToken`.
|
||||
- collectionAlias: The alias of the collection to which the post should be added; if `nil`, this removes the post from any collection.
|
||||
- completion: A handler for the returned `Bool` on success, or `Error` on failure.
|
||||
|
||||
### `pinPost(token:postId:at:in:completion:)`
|
||||
|
||||
Pins a post to a collection.
|
||||
|
||||
``` swift
|
||||
public func pinPost(
|
||||
token: String? = nil,
|
||||
postId: String,
|
||||
at position: Int? = nil,
|
||||
in collectionAlias: String,
|
||||
completion: @escaping (Result<Bool, Error>) -> Void
|
||||
)
|
||||
```
|
||||
|
||||
Pinning a post to a collection adds it as a navigation item in the collection/blog home page header, rather
|
||||
than on the blog itself. While the API endpoint can take an array of posts, this function only accepts a single
|
||||
post.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: The access token of the user pinning the post to the collection.
|
||||
- postId: The ID of the post to be pinned.
|
||||
- position: The numeric position in which to pin the post; if `nil`, will pin at the end of the list.
|
||||
- collectionAlias: The alias of the collection to which the post should be pinned.
|
||||
- completion: A handler for the `Bool` returned on success, or `Error` on failure.
|
||||
|
||||
### `unpinPost(token:postId:from:completion:)`
|
||||
|
||||
Unpins a post from a collection.
|
||||
|
||||
``` swift
|
||||
public func unpinPost(
|
||||
token: String? = nil,
|
||||
postId: String,
|
||||
from collectionAlias: String,
|
||||
completion: @escaping (Result<Bool, Error>) -> Void
|
||||
)
|
||||
```
|
||||
|
||||
Removes the post from a navigation item and puts it back on the blog itself. While the API endpoint can take an
|
||||
array of posts, this function only accepts a single post.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: The access token of the user un-pinning the post from the collection.
|
||||
- postId: The ID of the post to be un-pinned.
|
||||
- collectionAlias: The alias of the collection to which the post should be un-pinned.
|
||||
- completion: A handler for the `Bool` returned on success, or `Error` on failure.
|
||||
|
||||
### `createPost(token:post:in:completion:)`
|
||||
|
||||
Creates a new post.
|
||||
|
||||
``` swift
|
||||
public func createPost(
|
||||
token: String? = nil,
|
||||
post: WFPost,
|
||||
in collectionAlias: String? = nil,
|
||||
completion: @escaping (Result<WFPost, Error>) -> Void
|
||||
)
|
||||
```
|
||||
|
||||
Creates a new post. If a `collectionAlias` is provided, the post is published to that collection; otherwise, it
|
||||
is posted to the user's Drafts.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: The access token of the user creating the post.
|
||||
- post: The `WFPost` object to be published.
|
||||
- collectionAlias: The collection to which the post should be published.
|
||||
- completion: A handler for the `WFPost` object returned on success, or `Error` on failure.
|
||||
|
||||
### `getPost(token:byId:completion:)`
|
||||
|
||||
Retrieves a post.
|
||||
|
||||
``` swift
|
||||
public func getPost(
|
||||
token: String? = nil,
|
||||
byId postId: String,
|
||||
completion: @escaping (Result<WFPost, Error>) -> Void
|
||||
)
|
||||
```
|
||||
|
||||
The `WFPost` object returned may include additional data, including page views and extracted tags.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: The access token of the user retrieving the post.
|
||||
- postId: The ID of the post to be retrieved.
|
||||
- completion: A handler for the `WFPost` object returned on success, or `Error` on failure.
|
||||
|
||||
### `getPost(token:bySlug:from:completion:)`
|
||||
|
||||
Retrieves a post from a collection.
|
||||
|
||||
``` swift
|
||||
public func getPost(
|
||||
token: String? = nil,
|
||||
bySlug slug: String,
|
||||
from collectionAlias: String,
|
||||
completion: @escaping (Result<WFPost, Error>) -> Void
|
||||
)
|
||||
```
|
||||
|
||||
Collection posts can be retrieved without authentication. However, authentication is required for retrieving a
|
||||
post from a private collection.
|
||||
|
||||
The `WFPost` object returned may include additional data, including page views and extracted tags.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: The access token of the user retrieving the post.
|
||||
- slug: The slug of the post to be retrieved.
|
||||
- collectionAlias: The alias of the collection from which the post should be retrieved.
|
||||
- completion: A handler for the `WFPost` object returned on success, or `Error` on failure.
|
||||
|
||||
### `updatePost(token:postId:updatedPost:with:completion:)`
|
||||
|
||||
Updates an existing post.
|
||||
|
||||
``` swift
|
||||
public func updatePost(
|
||||
token: String? = nil,
|
||||
postId: String,
|
||||
updatedPost: WFPost,
|
||||
with modifyToken: String? = nil,
|
||||
completion: @escaping (Result<WFPost, Error>) -> Void
|
||||
)
|
||||
```
|
||||
|
||||
Note that if the `updatedPost` object is provided without a title, the original post's title will be removed.
|
||||
|
||||
>
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: The access token for the user updating the post.
|
||||
- postId: The ID of the post to be updated.
|
||||
- updatedPost: The `WFPost` object with which to update the existing post.
|
||||
- modifyToken: The post's modify token; required if the post doesn't belong to the requesting user.
|
||||
- completion: A handler for the `WFPost` object returned on success, or `Error` on failure.
|
||||
|
||||
### `deletePost(token:postId:with:completion:)`
|
||||
|
||||
Deletes an existing post.
|
||||
|
||||
``` swift
|
||||
public func deletePost(
|
||||
token: String? = nil,
|
||||
postId: String,
|
||||
with modifyToken: String? = nil,
|
||||
completion: @escaping (Result<Bool, Error>) -> Void
|
||||
)
|
||||
```
|
||||
|
||||
>
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: The access token for the user deleting the post.
|
||||
- postId: The ID of the post to be deleted.
|
||||
- modifyToken: The post's modify token; required if the post doesn't belong to the requesting user.
|
||||
- completion: A handler for the `Bool` object returned on success, or `Error` on failure.
|
||||
|
||||
### `login(username:password:completion:)`
|
||||
|
||||
Logs the user in to their account on the WriteFreely instance.
|
||||
|
||||
``` swift
|
||||
public func login(username: String, password: String, completion: @escaping (Result<WFUser, Error>) -> Void)
|
||||
```
|
||||
|
||||
On successful login, the `WFClient`'s `user` property is set to the returned `WFUser` object; this allows
|
||||
authenticated requests to be made without having to provide an access token.
|
||||
|
||||
It is otherwise not necessary to login the user if their access token is provided to the calling function.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- username: The user's username.
|
||||
- password: The user's password.
|
||||
- completion: A handler for the `WFUser` object returned on success, or `Error` on failure.
|
||||
|
||||
### `logout(token:completion:)`
|
||||
|
||||
Invalidates the user's access token.
|
||||
|
||||
``` swift
|
||||
public func logout(token: String? = nil, completion: @escaping (Result<Bool, Error>) -> Void)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: The token to invalidate.
|
||||
- completion: A handler for the `Bool` object returned on success, or `Error` on failure.
|
||||
|
||||
### `getUserData(token:completion:)`
|
||||
|
||||
Retrieves a user's basic data.
|
||||
|
||||
``` swift
|
||||
public func getUserData(token: String? = nil, completion: @escaping (Result<Data, Error>) -> Void)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: The access token for the user to fetch.
|
||||
- completion: A handler for the `Data` object returned on success, or `Error` on failure.
|
||||
|
||||
### `getUserCollections(token:completion:)`
|
||||
|
||||
Retrieves a user's collections.
|
||||
|
||||
``` swift
|
||||
public func getUserCollections(token: String? = nil, completion: @escaping (Result<[WFCollection], Error>) -> Void)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: The access token for the user whose collections are to be retrieved.
|
||||
- completion: A handler for the `[WFCollection]` object returned on success, or `Error` on failure.
|
|
@ -1,7 +1,7 @@
|
|||
# WFCollection
|
||||
|
||||
``` swift
|
||||
public struct WFCollection
|
||||
public struct WFCollection
|
||||
```
|
||||
|
||||
## Inheritance
|
||||
|
@ -15,7 +15,7 @@ public struct WFCollection
|
|||
Creates a basic `WFCollection` object.
|
||||
|
||||
``` swift
|
||||
public init(title: String, alias: String?)
|
||||
public init(title: String, alias: String?)
|
||||
```
|
||||
|
||||
This initializer creates a bare-minimum `WFCollection` object for sending to the server; use the decoder-based
|
||||
|
@ -25,22 +25,22 @@ If no `alias` parameter is provided, one will be generated by the server.
|
|||
|
||||
#### Parameters
|
||||
|
||||
- title: - title: The title to give the Collection.
|
||||
- alias: - alias: The alias for the Collection.
|
||||
- title: The title to give the Collection.
|
||||
- alias: The alias for the Collection.
|
||||
|
||||
### `init(from:)`
|
||||
|
||||
Creates a `WFCollection` object from the server response.
|
||||
|
||||
``` swift
|
||||
public init(from decoder: Decoder) throws
|
||||
public init(from decoder: Decoder) throws
|
||||
```
|
||||
|
||||
Primarily used by the `WFClient` to create a `WFCollection` object from the JSON returned by the server.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- decoder: - decoder: The decoder to use for translating the server response to a Swift object.
|
||||
- decoder: The decoder to use for translating the server response to a Swift object.
|
||||
|
||||
#### Throws
|
||||
|
||||
|
@ -51,47 +51,47 @@ Error thrown by the `try` attempt when decoding any given property.
|
|||
### `alias`
|
||||
|
||||
``` swift
|
||||
var alias: String?
|
||||
public var alias: String?
|
||||
```
|
||||
|
||||
### `title`
|
||||
|
||||
``` swift
|
||||
var title: String
|
||||
public var title: String
|
||||
```
|
||||
|
||||
### `description`
|
||||
|
||||
``` swift
|
||||
var description: String?
|
||||
public var description: String?
|
||||
```
|
||||
|
||||
### `styleSheet`
|
||||
|
||||
``` swift
|
||||
var styleSheet: String?
|
||||
public var styleSheet: String?
|
||||
```
|
||||
|
||||
### `isPublic`
|
||||
|
||||
``` swift
|
||||
var isPublic: Bool?
|
||||
public var isPublic: Bool?
|
||||
```
|
||||
|
||||
### `views`
|
||||
|
||||
``` swift
|
||||
var views: Int?
|
||||
public var views: Int?
|
||||
```
|
||||
|
||||
### `email`
|
||||
|
||||
``` swift
|
||||
var email: String?
|
||||
public var email: String?
|
||||
```
|
||||
|
||||
### `url`
|
||||
|
||||
``` swift
|
||||
var url: String?
|
||||
public var url: String?
|
||||
```
|
|
@ -0,0 +1,101 @@
|
|||
# WFError
|
||||
|
||||
``` swift
|
||||
public enum WFError: Int, Error
|
||||
```
|
||||
|
||||
## Inheritance
|
||||
|
||||
`Error`, `Int`
|
||||
|
||||
## Enumeration Cases
|
||||
|
||||
### `badRequest`
|
||||
|
||||
``` swift
|
||||
case badRequest = 400
|
||||
```
|
||||
|
||||
### `unauthorized`
|
||||
|
||||
``` swift
|
||||
case unauthorized = 401
|
||||
```
|
||||
|
||||
### `forbidden`
|
||||
|
||||
``` swift
|
||||
case forbidden = 403
|
||||
```
|
||||
|
||||
### `notFound`
|
||||
|
||||
``` swift
|
||||
case notFound = 404
|
||||
```
|
||||
|
||||
### `methodNotAllowed`
|
||||
|
||||
``` swift
|
||||
case methodNotAllowed = 405
|
||||
```
|
||||
|
||||
### `gone`
|
||||
|
||||
``` swift
|
||||
case gone = 410
|
||||
```
|
||||
|
||||
### `preconditionFailed`
|
||||
|
||||
``` swift
|
||||
case preconditionFailed = 412
|
||||
```
|
||||
|
||||
### `tooManyRequests`
|
||||
|
||||
``` swift
|
||||
case tooManyRequests = 429
|
||||
```
|
||||
|
||||
### `internalServerError`
|
||||
|
||||
``` swift
|
||||
case internalServerError = 500
|
||||
```
|
||||
|
||||
### `badGateway`
|
||||
|
||||
``` swift
|
||||
case badGateway = 502
|
||||
```
|
||||
|
||||
### `serviceUnavailable`
|
||||
|
||||
``` swift
|
||||
case serviceUnavailable = 503
|
||||
```
|
||||
|
||||
### `unknownError`
|
||||
|
||||
``` swift
|
||||
case unknownError = -1
|
||||
```
|
||||
|
||||
### `couldNotComplete`
|
||||
|
||||
``` swift
|
||||
case couldNotComplete = -2
|
||||
```
|
||||
|
||||
### `invalidResponse`
|
||||
|
||||
``` swift
|
||||
case invalidResponse = -3
|
||||
```
|
||||
|
||||
### `invalidData`
|
||||
|
||||
``` swift
|
||||
case invalidData = -4
|
||||
```
|
|
@ -1,7 +1,7 @@
|
|||
# WFPost
|
||||
|
||||
``` swift
|
||||
public struct WFPost
|
||||
public struct WFPost
|
||||
```
|
||||
|
||||
## Inheritance
|
||||
|
@ -15,7 +15,14 @@ public struct WFPost
|
|||
Creates a basic `WFPost` object.
|
||||
|
||||
``` swift
|
||||
public init(body: String, title: String? = nil, appearance: String? = nil, language: String? = nil, rtl: Bool? = nil, createdDate: Date? = nil)
|
||||
public init(
|
||||
body: String,
|
||||
title: String? = nil,
|
||||
appearance: String? = nil,
|
||||
language: String? = nil,
|
||||
rtl: Bool? = nil,
|
||||
createdDate: Date? = nil
|
||||
)
|
||||
```
|
||||
|
||||
This initializer creates a bare-minimum `WFPost` object for sending to the server; use the decoder-based
|
||||
|
@ -26,26 +33,26 @@ the server.
|
|||
|
||||
#### Parameters
|
||||
|
||||
- body: - body: The body text for the post.
|
||||
- title: - title: The title for the post.
|
||||
- appearance: - appearance: The appearance for the post; one of `sans`, `serif`/`norm`, `wrap`, `mono`, or `code`. Defaults to `serif`.
|
||||
- language: - language: An ISO 639-1 language code.
|
||||
- rtl: - rtl: Set to `true` to show content right-to-left.
|
||||
- createdDate: - createdDate: The published date for the post.
|
||||
- body: The body text for the post.
|
||||
- title: The title for the post.
|
||||
- appearance: The appearance for the post; one of `sans`, `serif`/`norm`, `wrap`, `mono`, or `code`. Defaults to `serif`.
|
||||
- language: An ISO 639-1 language code.
|
||||
- rtl: Set to `true` to show content right-to-left.
|
||||
- createdDate: The published date for the post.
|
||||
|
||||
### `init(from:)`
|
||||
|
||||
Creates a `WFPost` object from the server response.
|
||||
|
||||
``` swift
|
||||
public init(from decoder: Decoder) throws
|
||||
public init(from decoder: Decoder) throws
|
||||
```
|
||||
|
||||
Primarily used by the `WFClient` to create a `WFPost` object from the JSON returned by the server.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- decoder: - decoder: The decoder to use for translating the server response to a Swift object.
|
||||
- decoder: The decoder to use for translating the server response to a Swift object.
|
||||
|
||||
#### Throws
|
||||
|
||||
|
@ -56,71 +63,71 @@ Error thrown by the `try` attempt when decoding any given property.
|
|||
### `postId`
|
||||
|
||||
``` swift
|
||||
var postId: String?
|
||||
public var postId: String?
|
||||
```
|
||||
|
||||
### `slug`
|
||||
|
||||
``` swift
|
||||
var slug: String?
|
||||
public var slug: String?
|
||||
```
|
||||
|
||||
### `appearance`
|
||||
|
||||
``` swift
|
||||
var appearance: String?
|
||||
public var appearance: String?
|
||||
```
|
||||
|
||||
### `language`
|
||||
|
||||
``` swift
|
||||
var language: String?
|
||||
public var language: String?
|
||||
```
|
||||
|
||||
### `rtl`
|
||||
|
||||
``` swift
|
||||
var rtl: Bool?
|
||||
public var rtl: Bool?
|
||||
```
|
||||
|
||||
### `createdDate`
|
||||
|
||||
``` swift
|
||||
var createdDate: Date?
|
||||
public var createdDate: Date?
|
||||
```
|
||||
|
||||
### `updatedDate`
|
||||
|
||||
``` swift
|
||||
var updatedDate: Date?
|
||||
public var updatedDate: Date?
|
||||
```
|
||||
|
||||
### `title`
|
||||
|
||||
``` swift
|
||||
var title: String?
|
||||
public var title: String?
|
||||
```
|
||||
|
||||
### `body`
|
||||
|
||||
``` swift
|
||||
var body: String
|
||||
public var body: String
|
||||
```
|
||||
|
||||
### `tags`
|
||||
|
||||
``` swift
|
||||
var tags: [String]?
|
||||
public var tags: [String]?
|
||||
```
|
||||
|
||||
### `views`
|
||||
|
||||
``` swift
|
||||
var views: Int?
|
||||
public var views: Int?
|
||||
```
|
||||
|
||||
### `collectionAlias`
|
||||
|
||||
``` swift
|
||||
var collectionAlias: String?
|
||||
public var collectionAlias: String?
|
||||
```
|
|
@ -1,7 +1,7 @@
|
|||
# WFUser
|
||||
|
||||
``` swift
|
||||
public struct WFUser
|
||||
public struct WFUser
|
||||
```
|
||||
|
||||
## Inheritance
|
||||
|
@ -15,29 +15,29 @@ public struct WFUser
|
|||
Creates a minimum `WFUser` object from a stored token.
|
||||
|
||||
``` swift
|
||||
public init(token: String, username: String?)
|
||||
public init(token: String, username: String?)
|
||||
```
|
||||
|
||||
Use this when the client has already logged in a user and only needs to reconstruct the type from saved properties.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: - token: The user's access token
|
||||
- username: - username: The user's username (optional)
|
||||
- token: The user's access token
|
||||
- username: The user's username (optional)
|
||||
|
||||
### `init(from:)`
|
||||
|
||||
Creates a `WFUser` object from the server response.
|
||||
|
||||
``` swift
|
||||
public init(from decoder: Decoder) throws
|
||||
public init(from decoder: Decoder) throws
|
||||
```
|
||||
|
||||
Primarily used by the `WFClient` to create a `WFUser` object from the JSON returned by the server.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- decoder: - decoder: The decoder to use for translating the server response to a Swift object.
|
||||
- decoder: The decoder to use for translating the server response to a Swift object.
|
||||
|
||||
#### Throws
|
||||
|
||||
|
@ -48,23 +48,23 @@ Error thrown by the `try` attempt when decoding any given property.
|
|||
### `token`
|
||||
|
||||
``` swift
|
||||
var token: String
|
||||
public var token: String
|
||||
```
|
||||
|
||||
### `username`
|
||||
|
||||
``` swift
|
||||
var username: String?
|
||||
public var username: String?
|
||||
```
|
||||
|
||||
### `email`
|
||||
|
||||
``` swift
|
||||
var email: String?
|
||||
public var email: String?
|
||||
```
|
||||
|
||||
### `createdDate`
|
||||
|
||||
``` swift
|
||||
var createdDate: Date?
|
||||
public var createdDate: Date?
|
||||
```
|
|
@ -0,0 +1 @@
|
|||
Generated at 2021-05-20T15:56:24-0400 using [swift-doc](https://github.com/SwiftDocOrg/swift-doc) 1.0.0-beta.6.
|
|
@ -0,0 +1,25 @@
|
|||
<details>
|
||||
<summary>Types</summary>
|
||||
|
||||
- [WFClient](/WFClient)
|
||||
- [WFCollection](/WFCollection)
|
||||
- [WFError](/WFError)
|
||||
- [WFPost](/WFPost)
|
||||
- [WFUser](/WFUser)
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Protocols</summary>
|
||||
|
||||
- [URLSessionDataTaskProtocol](/URLSessionDataTaskProtocol)
|
||||
- [URLSessionProtocol](/URLSessionProtocol)
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Extensions</summary>
|
||||
|
||||
- [URLSession](/URLSession)
|
||||
|
||||
</details>
|
|
@ -1,5 +1,7 @@
|
|||
.DS_Store
|
||||
/.build
|
||||
/Packages
|
||||
/*.xcodeproj
|
||||
xcuserdata/
|
||||
!/.build/
|
||||
/.build/*
|
||||
!/.build/documentation/
|
||||
|
|
|
@ -13,8 +13,8 @@ You'll need Xcode 11.5 / Swift 5.2 installed along with the command line tools t
|
|||
Additionally, documentation is generated by [SwiftDoc](https://github.com/SwiftDocOrg/swift-doc). After making any changes to the package's public API, you'll need to regenerate the docs; to do so, run the following commands in the terminal from the root directory of the package:
|
||||
|
||||
```bash
|
||||
$ rm -rf docs/
|
||||
$ swift doc generate Sources --module-name WriteFreely --output docs
|
||||
$ rm -rf .build/documentation
|
||||
$ swift doc generate Sources --module-name WriteFreely
|
||||
```
|
||||
|
||||
### Installing
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
import Foundation
|
||||
|
||||
struct ServerData<T: Decodable>: Decodable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case code
|
||||
case data
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
code = try container.decode(Int.self, forKey: .code)
|
||||
data = try container.decode(T.self, forKey: .data)
|
||||
}
|
||||
|
||||
let code: Int
|
||||
let data: T
|
||||
}
|
||||
|
||||
struct NestedPostsJson: Decodable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case code
|
||||
case data
|
||||
|
||||
enum PostKeys: String, CodingKey {
|
||||
case posts
|
||||
}
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let postsContainer = try container.nestedContainer(keyedBy: CodingKeys.PostKeys.self, forKey: .data)
|
||||
data = try postsContainer.decode([WFPost].self, forKey: .posts)
|
||||
}
|
||||
|
||||
let data: [WFPost]
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
import Foundation
|
||||
|
||||
extension WFClient {
|
||||
|
||||
/// Sends a `GET` request.
|
||||
/// - Parameters:
|
||||
/// - request: The `URLRequest` for the `GET` request
|
||||
/// - completion: A closure that captures a `Result` with a `Data` object on success, or a `WFError` on failure.
|
||||
func get(with request: URLRequest, completion: @escaping (Result<Data, WFError>) -> Void) {
|
||||
if request.httpMethod != "GET" {
|
||||
preconditionFailure("Expected GET request, but got \(request.httpMethod ?? "nil")")
|
||||
}
|
||||
|
||||
let dataTask = session.dataTask(with: request) { (data, response, error) in
|
||||
if error != nil {
|
||||
completion(.failure(.couldNotComplete))
|
||||
return
|
||||
}
|
||||
|
||||
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
|
||||
if let response = response as? HTTPURLResponse {
|
||||
completion(.failure(WFError(rawValue: response.statusCode) ?? .invalidResponse))
|
||||
} else {
|
||||
completion(.failure(.invalidResponse))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
guard let data = data else {
|
||||
completion(.failure(.invalidData))
|
||||
return
|
||||
}
|
||||
|
||||
completion(.success(data))
|
||||
}
|
||||
|
||||
dataTask.resume()
|
||||
}
|
||||
|
||||
/// Sends a `POST` request.
|
||||
/// - Parameters:
|
||||
/// - request: The `URLRequest` for the `POST` request
|
||||
/// - expecting: The status code expected to be returned by the server
|
||||
/// - completion: A closure that captures a `Result` with a `Data` object on success, or a `WFError` on failure.
|
||||
func post(
|
||||
with request: URLRequest,
|
||||
expecting statusCode: Int,
|
||||
completion: @escaping (Result<Data, WFError>) -> Void
|
||||
) {
|
||||
if request.httpMethod != "POST" {
|
||||
preconditionFailure("Expected POST request, but got \(request.httpMethod ?? "nil")")
|
||||
}
|
||||
|
||||
let dataTask = session.dataTask(with: request) { (data, response, error) in
|
||||
if error != nil {
|
||||
completion(.failure(.couldNotComplete))
|
||||
return
|
||||
}
|
||||
|
||||
guard let response = response as? HTTPURLResponse, response.statusCode == statusCode else {
|
||||
if let response = response as? HTTPURLResponse {
|
||||
completion(.failure(WFError(rawValue: response.statusCode) ?? .invalidResponse))
|
||||
} else {
|
||||
completion(.failure(.invalidResponse))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
guard let data = data else {
|
||||
completion(.failure(.invalidData))
|
||||
return
|
||||
}
|
||||
|
||||
completion(.success(data))
|
||||
}
|
||||
|
||||
dataTask.resume()
|
||||
}
|
||||
|
||||
/// Sends a `DELETE` request.
|
||||
/// - Parameters:
|
||||
/// - request: The `URLRequest` for the `DELETE` request
|
||||
/// - completion: A closure that captures a `Result` with a `Data` object on success, or a `WFError` on failure.
|
||||
func delete(with request: URLRequest, completion: @escaping (Result<Data, WFError>) -> Void) {
|
||||
if request.httpMethod != "DELETE" {
|
||||
preconditionFailure("Expected DELETE request, but got \(request.httpMethod ?? "nil")")
|
||||
}
|
||||
|
||||
let dataTask = session.dataTask(with: request) { (data, response, error) in
|
||||
if error != nil {
|
||||
completion(.failure(.couldNotComplete))
|
||||
return
|
||||
}
|
||||
|
||||
guard let response = response as? HTTPURLResponse, response.statusCode == 204 else {
|
||||
if let response = response as? HTTPURLResponse {
|
||||
completion(.failure(WFError(rawValue: response.statusCode) ?? .invalidResponse))
|
||||
} else {
|
||||
completion(.failure(.invalidResponse))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
guard let data = data else {
|
||||
completion(.failure(.invalidData))
|
||||
return
|
||||
}
|
||||
|
||||
completion(.success(data))
|
||||
}
|
||||
|
||||
dataTask.resume()
|
||||
}
|
||||
}
|
|
@ -1,42 +1,23 @@
|
|||
import Foundation
|
||||
|
||||
struct ServerData<T: Decodable>: Decodable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case code
|
||||
case data
|
||||
}
|
||||
// MARK: - URLSession-related protocols
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
code = try container.decode(Int.self, forKey: .code)
|
||||
data = try container.decode(T.self, forKey: .data)
|
||||
}
|
||||
|
||||
let code: Int
|
||||
let data: T
|
||||
/// Define requirements for `URLSession`s here for dependency-injection purposes (specifically, for testing).
|
||||
public protocol URLSessionProtocol {
|
||||
typealias DataTaskResult = (Data?, URLResponse?, Error?) -> Void
|
||||
func dataTask(with request: URLRequest, completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskProtocol
|
||||
}
|
||||
|
||||
struct NestedPostsJson: Decodable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case code
|
||||
case data
|
||||
|
||||
enum PostKeys: String, CodingKey {
|
||||
case posts
|
||||
}
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
let postsContainer = try container.nestedContainer(keyedBy: CodingKeys.PostKeys.self, forKey: .data)
|
||||
data = try postsContainer.decode([WFPost].self, forKey: .posts)
|
||||
}
|
||||
|
||||
let data: [WFPost]
|
||||
/// Define requirements for `URLSessionDataTask`s here for dependency-injection purposes (specifically, for testing).
|
||||
public protocol URLSessionDataTaskProtocol {
|
||||
func resume()
|
||||
}
|
||||
|
||||
// MARK: - Class definition
|
||||
|
||||
public class WFClient {
|
||||
let decoder = JSONDecoder()
|
||||
let decoder: JSONDecoder
|
||||
let session: URLSessionProtocol
|
||||
|
||||
public var requestURL: URL
|
||||
public var user: WFUser?
|
||||
|
@ -45,11 +26,17 @@ public class WFClient {
|
|||
///
|
||||
/// Required for connecting to the API endpoints of a WriteFreely instance.
|
||||
///
|
||||
/// - Parameter instanceURL: The URL for the WriteFreely instance to which we're connecting, including the protocol.
|
||||
public init(for instanceURL: URL) {
|
||||
/// - Parameters:
|
||||
/// - instanceURL: The URL for the WriteFreely instance to which we're connecting, including the protocol.
|
||||
/// - session: The URL session to use for connections; defaults to `URLSession.shared`.
|
||||
public init(for instanceURL: URL, with session: URLSessionProtocol = URLSession.shared) {
|
||||
decoder = JSONDecoder()
|
||||
decoder.dateDecodingStrategy = .iso8601
|
||||
|
||||
self.session = session
|
||||
|
||||
// TODO: - Check that the protocol for instanceURL is HTTPS
|
||||
requestURL = URL(string: "api/", relativeTo: instanceURL) ?? instanceURL
|
||||
decoder.dateDecodingStrategy = .iso8601
|
||||
}
|
||||
|
||||
// MARK: - Collection-related methods
|
||||
|
@ -98,33 +85,19 @@ public class WFClient {
|
|||
completion(.failure(error))
|
||||
}
|
||||
|
||||
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
|
||||
// Something went wrong; return the error message.
|
||||
if let error = error {
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
if let response = response as? HTTPURLResponse {
|
||||
guard let data = data else { return }
|
||||
|
||||
// If we get a 201 CREATED, return the WFCollection as success; if not, return a WFError as failure.
|
||||
if response.statusCode == 201 {
|
||||
do {
|
||||
let collection = try self.decoder.decode(ServerData<WFCollection>.self, from: data)
|
||||
|
||||
completion(.success(collection.data))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
} else {
|
||||
// We didn't get a 200 OK, so return a WFError
|
||||
guard let error = self.translateWFError(fromServerResponse: data) else { return }
|
||||
post(with: request, expecting: 201) { result in
|
||||
switch result {
|
||||
case .success(let data):
|
||||
do {
|
||||
let collection = try self.decoder.decode(ServerData<WFCollection>.self, from: data)
|
||||
completion(.success(collection.data))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
dataTask.resume()
|
||||
}
|
||||
|
||||
/// Retrieves a collection's metadata.
|
||||
|
@ -150,33 +123,19 @@ public class WFClient {
|
|||
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
|
||||
request.addValue(tokenToVerify, forHTTPHeaderField: "Authorization")
|
||||
|
||||
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
|
||||
// Something went wrong; return the error message.
|
||||
if let error = error {
|
||||
get(with: request) { result in
|
||||
switch result {
|
||||
case .success(let data):
|
||||
do {
|
||||
let collection = try self.decoder.decode(ServerData<WFCollection>.self, from: data)
|
||||
completion(.success(collection.data))
|
||||
} catch {
|
||||
completion(.failure(WFError.invalidData))
|
||||
}
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
if let response = response as? HTTPURLResponse {
|
||||
guard let data = data else { return }
|
||||
|
||||
// If we get a 200 OK, return the WFUser as success; if not, return a WFError as failure.
|
||||
if response.statusCode == 200 {
|
||||
do {
|
||||
let collection = try self.decoder.decode(ServerData<WFCollection>.self, from: data)
|
||||
|
||||
completion(.success(collection.data))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
} else {
|
||||
// We didn't get a 200 OK, so return a WFError
|
||||
guard let error = self.translateWFError(fromServerResponse: data) else { return }
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dataTask.resume()
|
||||
}
|
||||
|
||||
/// Permanently deletes a collection.
|
||||
|
@ -202,48 +161,14 @@ public class WFClient {
|
|||
request.addValue(tokenToVerify, forHTTPHeaderField: "Authorization")
|
||||
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
|
||||
|
||||
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
|
||||
// Something went wrong; return the error message.
|
||||
if let error = error {
|
||||
// ⚠️ HACK: There's something that URLSession doesn't like about 204 NO CONTENT response that the API
|
||||
// server is returning. If we get back a "protocol error", the operation probably succeeded,
|
||||
// but URLSession is being pedantic/cranky and throwing an NSPOSIXErrorDomain error code 100.
|
||||
// Here, we check for that error, make sure the token was invalidated, and only then fire the
|
||||
// success case in the completion block.
|
||||
let nsError = error as NSError
|
||||
if nsError.code == 100 && nsError.domain == NSPOSIXErrorDomain {
|
||||
// Confirm that the operation succeeded by testing for a 404 on the same token.
|
||||
self.deleteCollection(withAlias: alias) { result in
|
||||
do {
|
||||
_ = try result.get()
|
||||
completion(.failure(error))
|
||||
} catch WFError.notFound {
|
||||
completion(.success(true))
|
||||
} catch WFError.unauthorized {
|
||||
completion(.success(true))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
if let response = response as? HTTPURLResponse {
|
||||
// We got a response. If it's a 204 NO CONTENT, return true as success;
|
||||
// if not, return a WFError as failure.
|
||||
if response.statusCode != 204 {
|
||||
guard let data = data else { return }
|
||||
guard let error = self.translateWFError(fromServerResponse: data) else { return }
|
||||
completion(.failure(error))
|
||||
} else {
|
||||
completion(.success(true))
|
||||
}
|
||||
delete(with: request) { result in
|
||||
switch result {
|
||||
case .success(_):
|
||||
completion(.success(true))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
dataTask.resume()
|
||||
}
|
||||
|
||||
// MARK: - Post-related methods
|
||||
|
@ -282,40 +207,27 @@ public class WFClient {
|
|||
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
|
||||
request.addValue(tokenToVerify, forHTTPHeaderField: "Authorization")
|
||||
|
||||
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
|
||||
// Something went wrong; return the error message.
|
||||
if let error = error {
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
if let response = response as? HTTPURLResponse {
|
||||
guard let data = data else { return }
|
||||
|
||||
// If we get a 200 OK, return the WFUser as success; if not, return a WFError as failure.
|
||||
if response.statusCode == 200 {
|
||||
do {
|
||||
// The response is formatted differently depending on if we're getting user posts or collection
|
||||
// posts,so we need to determine what kind of structure we're decoding based on the
|
||||
// collectionAlias argument.
|
||||
if collectionAlias != nil {
|
||||
let post = try self.decoder.decode(NestedPostsJson.self, from: data)
|
||||
completion(.success(post.data))
|
||||
} else {
|
||||
let post = try self.decoder.decode(ServerData<[WFPost]>.self, from: data)
|
||||
completion(.success(post.data))
|
||||
}
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
get(with: request) { result in
|
||||
switch result {
|
||||
case .success(let data):
|
||||
do {
|
||||
// The response is formatted differently depending on if we're getting user posts or collection
|
||||
// posts,so we need to determine what kind of structure we're decoding based on the
|
||||
// collectionAlias argument.
|
||||
if collectionAlias != nil {
|
||||
let post = try self.decoder.decode(NestedPostsJson.self, from: data)
|
||||
completion(.success(post.data))
|
||||
} else {
|
||||
let post = try self.decoder.decode(ServerData<[WFPost]>.self, from: data)
|
||||
completion(.success(post.data))
|
||||
}
|
||||
} else {
|
||||
// We didn't get a 200 OK, so return a WFError.
|
||||
guard let error = self.translateWFError(fromServerResponse: data) else { return }
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
dataTask.resume()
|
||||
}
|
||||
|
||||
/// Moves a post to a collection.
|
||||
|
@ -368,27 +280,14 @@ public class WFClient {
|
|||
completion(.failure(error))
|
||||
}
|
||||
|
||||
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
|
||||
// Something went wrong; return the error message.
|
||||
if let error = error {
|
||||
post(with: request, expecting: 200) { result in
|
||||
switch result {
|
||||
case .success(_):
|
||||
completion(.success(true))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
if let response = response as? HTTPURLResponse {
|
||||
guard let data = data else { return }
|
||||
|
||||
// If we get a 200 OK, return true as success; if not, return a WFError as failure.
|
||||
if response.statusCode == 200 {
|
||||
completion(.success(true))
|
||||
} else {
|
||||
// We didn't get a 200 OK, so return a WFError
|
||||
guard let error = self.translateWFError(fromServerResponse: data) else { return }
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dataTask.resume()
|
||||
}
|
||||
|
||||
/// Pins a post to a collection.
|
||||
|
@ -442,27 +341,14 @@ public class WFClient {
|
|||
completion(.failure(error))
|
||||
}
|
||||
|
||||
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
|
||||
// Something went wrong; return the error message.
|
||||
if let error = error {
|
||||
post(with: request, expecting: 200) { result in
|
||||
switch result {
|
||||
case .success(_):
|
||||
completion(.success(true))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
if let response = response as? HTTPURLResponse {
|
||||
guard let data = data else { return }
|
||||
|
||||
// If we get a 200 OK, return the WFUser as success; if not, return a WFError as failure.
|
||||
if response.statusCode == 200 {
|
||||
completion(.success(true))
|
||||
} else {
|
||||
// We didn't get a 200 OK, so return a WFError
|
||||
guard let error = self.translateWFError(fromServerResponse: data) else { return }
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dataTask.resume()
|
||||
}
|
||||
|
||||
/// Unpins a post from a collection.
|
||||
|
@ -503,27 +389,14 @@ public class WFClient {
|
|||
completion(.failure(error))
|
||||
}
|
||||
|
||||
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
|
||||
// Something went wrong; return the error message.
|
||||
if let error = error {
|
||||
post(with: request, expecting: 200) { result in
|
||||
switch result {
|
||||
case .success(_):
|
||||
completion(.success(true))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
if let response = response as? HTTPURLResponse {
|
||||
guard let data = data else { return }
|
||||
|
||||
// If we get a 200 OK, return the WFUser as success; if not, return a WFError as failure.
|
||||
if response.statusCode == 200 {
|
||||
completion(.success(true))
|
||||
} else {
|
||||
// We didn't get a 200 OK, so return a WFError
|
||||
guard let error = self.translateWFError(fromServerResponse: data) else { return }
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dataTask.resume()
|
||||
}
|
||||
|
||||
/// Creates a new post.
|
||||
|
@ -581,33 +454,19 @@ public class WFClient {
|
|||
completion(.failure(error))
|
||||
}
|
||||
|
||||
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
|
||||
// Something went wrong; return the error message.
|
||||
if let error = error {
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
if let response = response as? HTTPURLResponse {
|
||||
guard let data = data else { return }
|
||||
|
||||
// If we get a 200 OK, return the WFPost as success; if not, return a WFError as failure.
|
||||
if response.statusCode == 201 {
|
||||
do {
|
||||
let post = try self.decoder.decode(ServerData<WFPost>.self, from: data)
|
||||
|
||||
completion(.success(post.data))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
} else {
|
||||
// We didn't get a 200 OK, so return a WFError
|
||||
guard let error = self.translateWFError(fromServerResponse: data) else { return }
|
||||
self.post(with: request, expecting: 201) { result in
|
||||
switch result {
|
||||
case .success(let data):
|
||||
do {
|
||||
let post = try self.decoder.decode(ServerData<WFPost>.self, from: data)
|
||||
completion(.success(post.data))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
dataTask.resume()
|
||||
}
|
||||
|
||||
/// Retrieves a post.
|
||||
|
@ -632,33 +491,19 @@ public class WFClient {
|
|||
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
|
||||
request.addValue(tokenToVerify, forHTTPHeaderField: "Authorization")
|
||||
|
||||
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
|
||||
// Something went wrong; return the error message.
|
||||
if let error = error {
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
if let response = response as? HTTPURLResponse {
|
||||
guard let data = data else { return }
|
||||
|
||||
// If we get a 200 OK, return the WFUser as success; if not, return a WFError as failure.
|
||||
if response.statusCode == 200 {
|
||||
do {
|
||||
let post = try self.decoder.decode(ServerData<WFPost>.self, from: data)
|
||||
|
||||
completion(.success(post.data))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
} else {
|
||||
// We didn't get a 200 OK, so return a WFError
|
||||
guard let error = self.translateWFError(fromServerResponse: data) else { return }
|
||||
get(with: request) { result in
|
||||
switch result {
|
||||
case .success(let data):
|
||||
do {
|
||||
let post = try self.decoder.decode(ServerData<WFPost>.self, from: data)
|
||||
completion(.success(post.data))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
dataTask.resume()
|
||||
}
|
||||
|
||||
/// Retrieves a post from a collection.
|
||||
|
@ -689,33 +534,19 @@ public class WFClient {
|
|||
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
|
||||
request.addValue(tokenToVerify, forHTTPHeaderField: "Authorization")
|
||||
|
||||
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
|
||||
// Something went wrong; return the error message.
|
||||
if let error = error {
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
if let response = response as? HTTPURLResponse {
|
||||
guard let data = data else { return }
|
||||
|
||||
// If we get a 200 OK, return the WFUser as success; if not, return a WFError as failure.
|
||||
if response.statusCode == 200 {
|
||||
do {
|
||||
let post = try self.decoder.decode(ServerData<WFPost>.self, from: data)
|
||||
|
||||
completion(.success(post.data))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
} else {
|
||||
// We didn't get a 200 OK, so return a WFError
|
||||
guard let error = self.translateWFError(fromServerResponse: data) else { return }
|
||||
get(with: request) { result in
|
||||
switch result {
|
||||
case .success(let data):
|
||||
do {
|
||||
let post = try self.decoder.decode(ServerData<WFPost>.self, from: data)
|
||||
completion(.success(post.data))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
dataTask.resume()
|
||||
}
|
||||
|
||||
/// Updates an existing post.
|
||||
|
@ -762,33 +593,19 @@ public class WFClient {
|
|||
completion(.failure(error))
|
||||
}
|
||||
|
||||
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
|
||||
// Something went wrong; return the error message.
|
||||
if let error = error {
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
if let response = response as? HTTPURLResponse {
|
||||
guard let data = data else { return }
|
||||
|
||||
// If we get a 200 OK, return the WFPost as success; if not, return a WFError as failure.
|
||||
if response.statusCode == 200 {
|
||||
do {
|
||||
let post = try self.decoder.decode(ServerData<WFPost>.self, from: data)
|
||||
|
||||
completion(.success(post.data))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
} else {
|
||||
// We didn't get a 200 OK, so return a WFError
|
||||
guard let error = self.translateWFError(fromServerResponse: data) else { return }
|
||||
post(with: request, expecting: 200) { result in
|
||||
switch result {
|
||||
case .success(let data):
|
||||
do {
|
||||
let post = try self.decoder.decode(ServerData<WFPost>.self, from: data)
|
||||
completion(.success(post.data))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
dataTask.resume()
|
||||
}
|
||||
|
||||
/// Deletes an existing post.
|
||||
|
@ -817,51 +634,14 @@ public class WFClient {
|
|||
request.addValue(tokenToVerify, forHTTPHeaderField: "Authorization")
|
||||
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
|
||||
|
||||
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
|
||||
// Something went wrong; return the error message.
|
||||
if let error = error {
|
||||
// ⚠️ HACK: There's something that URLSession doesn't like about 204 NO CONTENT response that the API
|
||||
// server is returning. If we get back a "protocol error", the operation probably succeeded,
|
||||
// but URLSession is being pedantic/cranky and throwing an NSPOSIXErrorDomain error code 100.
|
||||
// Here, we check for that error, make sure the token was invalidated, and only then fire the
|
||||
// success case in the completion block.
|
||||
let nsError = error as NSError
|
||||
if nsError.code == 100 && nsError.domain == NSPOSIXErrorDomain {
|
||||
// Confirm that the operation succeeded by testing for a 404 on the same token.
|
||||
self.deletePost(postId: postId) { result in
|
||||
do {
|
||||
_ = try result.get()
|
||||
completion(.failure(error))
|
||||
} catch WFError.notFound {
|
||||
completion(.success(true))
|
||||
} catch WFError.unauthorized {
|
||||
completion(.success(true))
|
||||
} catch WFError.internalServerError {
|
||||
// If you try to delete a non-existent post, the API returns a 500 Internal Server Error.
|
||||
completion(.success(true))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
if let response = response as? HTTPURLResponse {
|
||||
// We got a response. If it's a 204 NO CONTENT, return true as success;
|
||||
// if not, return a WFError as failure.
|
||||
if response.statusCode != 204 {
|
||||
guard let data = data else { return }
|
||||
guard let error = self.translateWFError(fromServerResponse: data) else { return }
|
||||
completion(.failure(error))
|
||||
} else {
|
||||
completion(.success(true))
|
||||
}
|
||||
delete(with: request) { result in
|
||||
switch result {
|
||||
case .success(_):
|
||||
completion(.success(true))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
dataTask.resume()
|
||||
}
|
||||
|
||||
/* Placeholder method stub: API design for this feature is not yet finalized.
|
||||
|
@ -903,37 +683,20 @@ public class WFClient {
|
|||
completion(.failure(error))
|
||||
}
|
||||
|
||||
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
|
||||
// Something went wrong; return the error message.
|
||||
if let error = error {
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
if let response = response as? HTTPURLResponse {
|
||||
guard let data = data else { return }
|
||||
|
||||
// If we get a 200 OK, return the WFUser as success; if not, return a WFError as failure.
|
||||
if response.statusCode == 200 {
|
||||
do {
|
||||
let user = try self.decoder.decode(WFUser.self, from: data)
|
||||
self.user = user
|
||||
completion(.success(user))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
} else {
|
||||
// We didn't get a 200 OK, so return a WFError
|
||||
guard let error = self.translateWFError(fromServerResponse: data) else {
|
||||
// We couldn't generate a WFError from the server response data, so return an unknown error.
|
||||
completion(.failure(WFError.unknownError))
|
||||
return
|
||||
}
|
||||
post(with: request, expecting: 200) { result in
|
||||
switch result {
|
||||
case .success(let data):
|
||||
do {
|
||||
let user = try self.decoder.decode(WFUser.self, from: data)
|
||||
self.user = user
|
||||
completion(.success(user))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
dataTask.resume()
|
||||
}
|
||||
|
||||
/// Invalidates the user's access token.
|
||||
|
@ -953,51 +716,15 @@ public class WFClient {
|
|||
request.addValue(tokenToDelete, forHTTPHeaderField: "Authorization")
|
||||
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
|
||||
|
||||
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
|
||||
// Something went wrong; return the error message.
|
||||
if let error = error {
|
||||
// ⚠️ HACK: There's something that URLSession doesn't like about 204 NO CONTENT response that the API
|
||||
// server is returning. If we get back a "protocol error", the operation probably succeeded,
|
||||
// but URLSession is being pedantic/cranky and throwing an NSPOSIXErrorDomain error code 100.
|
||||
// Here, we check for that error, make sure the token was invalidated, and only then fire the
|
||||
// success case in the completion block.
|
||||
let nsError = error as NSError
|
||||
if nsError.code == 100 && nsError.domain == NSPOSIXErrorDomain {
|
||||
// Confirm that the operation succeeded by testing for a 404 on the same token.
|
||||
self.logout(token: tokenToDelete) { result in
|
||||
do {
|
||||
_ = try result.get()
|
||||
completion(.failure(error))
|
||||
} catch WFError.notFound {
|
||||
self.user = nil
|
||||
completion(.success(true))
|
||||
} catch WFError.unauthorized {
|
||||
self.user = nil
|
||||
completion(.success(true))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
if let response = response as? HTTPURLResponse {
|
||||
// We got a response. If it's a 204 NO CONTENT, return true as success;
|
||||
// if not, return a WFError as failure.
|
||||
if response.statusCode != 204 {
|
||||
guard let data = data else { return }
|
||||
guard let error = self.translateWFError(fromServerResponse: data) else { return }
|
||||
completion(.failure(error))
|
||||
} else {
|
||||
self.user = nil
|
||||
completion(.success(true))
|
||||
}
|
||||
delete(with: request) { result in
|
||||
switch result {
|
||||
case .success(_):
|
||||
self.user = nil
|
||||
completion(.success(true))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
dataTask.resume()
|
||||
}
|
||||
|
||||
/// Retrieves a user's basic data.
|
||||
|
@ -1016,27 +743,14 @@ public class WFClient {
|
|||
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
|
||||
request.addValue(tokenToVerify, forHTTPHeaderField: "Authorization")
|
||||
|
||||
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
|
||||
// Something went wrong; return the error message.
|
||||
if let error = error {
|
||||
get(with: request) { result in
|
||||
switch result {
|
||||
case .success(let data):
|
||||
completion(.success(data))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
if let response = response as? HTTPURLResponse {
|
||||
guard let data = data else { return }
|
||||
|
||||
// If we get a 200 OK, return the WFUser as success; if not, return a WFError as failure.
|
||||
if response.statusCode == 200 {
|
||||
completion(.success(data))
|
||||
} else {
|
||||
// We didn't get a 200 OK, so return a WFError.
|
||||
guard let error = self.translateWFError(fromServerResponse: data) else { return }
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dataTask.resume()
|
||||
}
|
||||
|
||||
/// Retrieves a user's collections.
|
||||
|
@ -1055,32 +769,19 @@ public class WFClient {
|
|||
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
|
||||
request.addValue(tokenToVerify, forHTTPHeaderField: "Authorization")
|
||||
|
||||
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
|
||||
// Something went wrong; return the error message.
|
||||
if let error = error {
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
if let response = response as? HTTPURLResponse {
|
||||
guard let data = data else { return }
|
||||
|
||||
// If we get a 200 OK, return the WFUser as success; if not, return a WFError as failure.
|
||||
if response.statusCode == 200 {
|
||||
do {
|
||||
let collection = try self.decoder.decode(ServerData<[WFCollection]>.self, from: data)
|
||||
completion(.success(collection.data))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
} else {
|
||||
// We didn't get a 200 OK, so return a WFError.
|
||||
guard let error = self.translateWFError(fromServerResponse: data) else { return }
|
||||
get(with: request) { result in
|
||||
switch result {
|
||||
case .success(let data):
|
||||
do {
|
||||
let collection = try self.decoder.decode(ServerData<[WFCollection]>.self, from: data)
|
||||
completion(.success(collection.data))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
dataTask.resume()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1096,3 +797,16 @@ private extension WFClient {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Protocol conformance
|
||||
|
||||
extension URLSession: URLSessionProtocol {
|
||||
public func dataTask(
|
||||
with request: URLRequest,
|
||||
completionHandler: @escaping DataTaskResult
|
||||
) -> URLSessionDataTaskProtocol {
|
||||
return dataTask(with: request, completionHandler: completionHandler) as URLSessionDataTask
|
||||
}
|
||||
}
|
||||
|
||||
extension URLSessionDataTask: URLSessionDataTaskProtocol {}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import Foundation
|
||||
|
||||
public enum WFError: Int, Error {
|
||||
// Errors returned by the server
|
||||
case badRequest = 400
|
||||
case unauthorized = 401
|
||||
case forbidden = 403
|
||||
|
@ -12,7 +13,12 @@ public enum WFError: Int, Error {
|
|||
case internalServerError = 500
|
||||
case badGateway = 502
|
||||
case serviceUnavailable = 503
|
||||
|
||||
// Other errors
|
||||
case unknownError = -1
|
||||
case couldNotComplete = -2
|
||||
case invalidResponse = -3
|
||||
case invalidData = -4
|
||||
}
|
||||
|
||||
struct ErrorMessage: Codable {
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
# Types
|
||||
|
||||
- [WFClient](/WFClient)
|
||||
- [WFCollection](/WFCollection)
|
||||
- [WFError](/WFError)
|
||||
- [WFPost](/WFPost)
|
||||
- [WFUser](/WFUser)
|
329
docs/WFClient.md
329
docs/WFClient.md
|
@ -1,329 +0,0 @@
|
|||
# WFClient
|
||||
|
||||
``` swift
|
||||
public class WFClient
|
||||
```
|
||||
|
||||
## Initializers
|
||||
|
||||
### `init(for:)`
|
||||
|
||||
Initializes the WriteFreely client.
|
||||
|
||||
``` swift
|
||||
public init(for instanceURL: URL)
|
||||
```
|
||||
|
||||
Required for connecting to the API endpoints of a WriteFreely instance.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- instanceURL: - instanceURL: The URL for the WriteFreely instance to which we're connecting, including the protocol.
|
||||
|
||||
## Properties
|
||||
|
||||
### `decoder`
|
||||
|
||||
``` swift
|
||||
let decoder
|
||||
```
|
||||
|
||||
### `requestURL`
|
||||
|
||||
``` swift
|
||||
var requestURL: URL
|
||||
```
|
||||
|
||||
### `user`
|
||||
|
||||
``` swift
|
||||
var user: WFUser?
|
||||
```
|
||||
|
||||
## Methods
|
||||
|
||||
### `createCollection(token:withTitle:alias:completion:)`
|
||||
|
||||
Creates a new collection.
|
||||
|
||||
``` swift
|
||||
public func createCollection(token: String? = nil, withTitle title: String, alias: String? = nil, completion: @escaping (Result<WFCollection, Error>) -> Void)
|
||||
```
|
||||
|
||||
If only a `title` is given, the server will generate and return an alias; in this case, clients should store
|
||||
the returned `alias` for future operations.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: - token: The access token for the user creating the collection.
|
||||
- title: - title: The title of the new collection.
|
||||
- alias: - alias: The alias of the collection.
|
||||
- completion: - completion: A handler for the returned `WFCollection` on success, or `Error` on failure.
|
||||
|
||||
### `getCollection(token:withAlias:completion:)`
|
||||
|
||||
Retrieves a collection's metadata.
|
||||
|
||||
``` swift
|
||||
public func getCollection(token: String? = nil, withAlias alias: String, completion: @escaping (Result<WFCollection, Error>) -> Void)
|
||||
```
|
||||
|
||||
Collections can be retrieved without authentication. However, authentication is required for retrieving a
|
||||
private collection or one with scheduled posts.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: - token: The access token for the user retrieving the collection.
|
||||
- alias: - alias: The alias for the collection to be retrieved.
|
||||
- completion: - completion: A handler for the returned `WFCollection` on success, or `Error` on failure.
|
||||
|
||||
### `deleteCollection(token:withAlias:completion:)`
|
||||
|
||||
Permanently deletes a collection.
|
||||
|
||||
``` swift
|
||||
public func deleteCollection(token: String? = nil, withAlias alias: String, completion: @escaping (Result<Bool, Error>) -> Void)
|
||||
```
|
||||
|
||||
Any posts in the collection are not deleted; rather, they are made anonymous.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: - token: The access token for the user deleting the collection.
|
||||
- alias: - alias: The alias for the collection to be deleted.
|
||||
- completion: - completion: A hander for the returned `Bool` on success, or `Error` on failure.
|
||||
|
||||
### `getPosts(token:in:completion:)`
|
||||
|
||||
Retrieves an array of posts.
|
||||
|
||||
``` swift
|
||||
public func getPosts(token: String? = nil, in collectionAlias: String? = nil, completion: @escaping (Result<[WFPost], Error>) -> Void)
|
||||
```
|
||||
|
||||
If the `collectionAlias` argument is provided, an array of all posts in that collection is retrieved; if
|
||||
omitted, an array of all posts created by the user whose access token is provided is retrieved.
|
||||
|
||||
Collection posts can be retrieved without authentication; however, authentication is required for retrieving a
|
||||
private collection or one with scheduled posts.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: - token: The access token for the user retrieving the posts.
|
||||
- collectionAlias: - collectionAlias: The alias for the collection whose posts are to be retrieved.
|
||||
- completion: - completion: A handler for the returned `[WFPost]` on success, or `Error` on failure.
|
||||
|
||||
### `movePost(token:postId:with:to:completion:)`
|
||||
|
||||
Moves a post to a collection.
|
||||
|
||||
``` swift
|
||||
public func movePost(token: String? = nil, postId: String, with modifyToken: String? = nil, to collectionAlias: String, completion: @escaping (Result<Bool, Error>) -> Void)
|
||||
```
|
||||
|
||||
> Attention: - The closure should return a result type of \`\<\[WFPost\], Error\>\`.
|
||||
> - The modifyToken for the post is currently ignored.
|
||||
>
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: - token: The access token for the user moving the post to a collection.
|
||||
- postId: - postId: The ID of the post to add to the collection.
|
||||
- modifyToken: - modifyToken: The post's modify token; required if the post doesn't belong to the requesting user.
|
||||
- collectionAlias: - collectionAlias: The alias of the collection to which the post should be added.
|
||||
- completion: - completion: A handler for the returned `Bool` on success, or `Error` on failure.
|
||||
|
||||
### `pinPost(token:postId:at:in:completion:)`
|
||||
|
||||
Pins a post to a collection.
|
||||
|
||||
``` swift
|
||||
public func pinPost(token: String? = nil, postId: String, at position: Int? = nil, in collectionAlias: String, completion: @escaping (Result<Bool, Error>) -> Void)
|
||||
```
|
||||
|
||||
Pinning a post to a collection adds it as a navigation item in the collection/blog home page header, rather
|
||||
than on the blog itself. While the API endpoint can take an array of posts, this function only accepts a single
|
||||
post.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: - token: The access token of the user pinning the post to the collection.
|
||||
- postId: - postId: The ID of the post to be pinned.
|
||||
- position: - position: The numeric position in which to pin the post; if `nil`, will pin at the end of the list.
|
||||
- collectionAlias: - collectionAlias: The alias of the collection to which the post should be pinned.
|
||||
- completion: - completion: A handler for the `Bool` returned on success, or `Error` on failure.
|
||||
|
||||
### `unpinPost(token:postId:from:completion:)`
|
||||
|
||||
Unpins a post from a collection.
|
||||
|
||||
``` swift
|
||||
public func unpinPost(token: String? = nil, postId: String, from collectionAlias: String, completion: @escaping (Result<Bool, Error>) -> Void)
|
||||
```
|
||||
|
||||
Removes the post from a navigation item and puts it back on the blog itself. While the API endpoint can take an
|
||||
array of posts, this function only accepts a single post.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: - token: The access token of the user un-pinning the post from the collection.
|
||||
- postId: - postId: The ID of the post to be un-pinned.
|
||||
- collectionAlias: - collectionAlias: The alias of the collection to which the post should be un-pinned.
|
||||
- completion: - completion: A handler for the `Bool` returned on success, or `Error` on failure.
|
||||
|
||||
### `createPost(token:post:in:completion:)`
|
||||
|
||||
Creates a new post.
|
||||
|
||||
``` swift
|
||||
public func createPost(token: String? = nil, post: WFPost, in collectionAlias: String? = nil, completion: @escaping (Result<WFPost, Error>) -> Void)
|
||||
```
|
||||
|
||||
Creates a new post. If a `collectionAlias` is provided, the post is published to that collection; otherwise, it
|
||||
is posted to the user's Drafts.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: - token: The access token of the user creating the post.
|
||||
- post: - post: The `WFPost` object to be published.
|
||||
- collectionAlias: - collectionAlias: The collection to which the post should be published.
|
||||
- completion: - completion: A handler for the `WFPost` object returned on success, or `Error` on failure.
|
||||
|
||||
### `getPost(token:byId:completion:)`
|
||||
|
||||
Retrieves a post.
|
||||
|
||||
``` swift
|
||||
public func getPost(token: String? = nil, byId postId: String, completion: @escaping (Result<WFPost, Error>) -> Void)
|
||||
```
|
||||
|
||||
The `WFPost` object returned may include additional data, including page views and extracted tags.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: - token: The access token of the user retrieving the post.
|
||||
- postId: - postId: The ID of the post to be retrieved.
|
||||
- completion: - completion: A handler for the `WFPost` object returned on success, or `Error` on failure.
|
||||
|
||||
### `getPost(token:bySlug:from:completion:)`
|
||||
|
||||
Retrieves a post from a collection.
|
||||
|
||||
``` swift
|
||||
public func getPost(token: String? = nil, bySlug slug: String, from collectionAlias: String, completion: @escaping (Result<WFPost, Error>) -> Void)
|
||||
```
|
||||
|
||||
Collection posts can be retrieved without authentication. However, authentication is required for retrieving a
|
||||
post from a private collection.
|
||||
|
||||
The `WFPost` object returned may include additional data, including page views and extracted tags.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: - token: The access token of the user retrieving the post.
|
||||
- slug: - slug: The slug of the post to be retrieved.
|
||||
- collectionAlias: - collectionAlias: The alias of the collection from which the post should be retrieved.
|
||||
- completion: - completion: A handler for the `WFPost` object returned on success, or `Error` on failure.
|
||||
|
||||
### `updatePost(token:postId:updatedPost:with:completion:)`
|
||||
|
||||
Updates an existing post.
|
||||
|
||||
``` swift
|
||||
public func updatePost(token: String? = nil, postId: String, updatedPost: WFPost, with modifyToken: String? = nil, completion: @escaping (Result<WFPost, Error>) -> Void)
|
||||
```
|
||||
|
||||
Note that if the `updatedPost` object is provided without a title, the original post's title will be removed.
|
||||
|
||||
> Attention: - The modifyToken for the post is currently ignored.
|
||||
>
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: - token: The access token for the user updating the post.
|
||||
- postId: - postId: The ID of the post to be updated.
|
||||
- updatedPost: - updatedPost: The `WFPost` object with which to update the existing post.
|
||||
- modifyToken: - modifyToken: The post's modify token; required if the post doesn't belong to the requesting user.
|
||||
- completion: - completion: A handler for the `WFPost` object returned on success, or `Error` on failure.
|
||||
|
||||
### `deletePost(token:postId:with:completion:)`
|
||||
|
||||
Deletes an existing post.
|
||||
|
||||
``` swift
|
||||
public func deletePost(token: String? = nil, postId: String, with modifyToken: String? = nil, completion: @escaping (Result<Bool, Error>) -> Void)
|
||||
```
|
||||
|
||||
> Attention: - The modifyToken for the post is currently ignored.
|
||||
>
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: - token: The access token for the user deleting the post.
|
||||
- postId: - postId: The ID of the post to be deleted.
|
||||
- modifyToken: - modifyToken: The post's modify token; required if the post doesn't belong to the requesting user.
|
||||
- completion: - completion: A handler for the `Bool` object returned on success, or `Error` on failure.
|
||||
|
||||
### `login(username:password:completion:)`
|
||||
|
||||
Logs the user in to their account on the WriteFreely instance.
|
||||
|
||||
``` swift
|
||||
public func login(username: String, password: String, completion: @escaping (Result<WFUser, Error>) -> Void)
|
||||
```
|
||||
|
||||
On successful login, the `WFClient`'s `user` property is set to the returned `WFUser` object; this allows
|
||||
authenticated requests to be made without having to provide an access token.
|
||||
|
||||
It is otherwise not necessary to login the user if their access token is provided to the calling function.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- username: - username: The user's username.
|
||||
- password: - password: The user's password.
|
||||
- completion: - completion: A handler for the `WFUser` object returned on success, or `Error` on failure.
|
||||
|
||||
### `logout(token:completion:)`
|
||||
|
||||
Invalidates the user's access token.
|
||||
|
||||
``` swift
|
||||
public func logout(token: String? = nil, completion: @escaping (Result<Bool, Error>) -> Void)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: - token: The token to invalidate.
|
||||
- completion: - completion: A handler for the `Bool` object returned on success, or `Error` on failure.
|
||||
|
||||
### `getUserData(token:completion:)`
|
||||
|
||||
Retrieves a user's basic data.
|
||||
|
||||
``` swift
|
||||
public func getUserData(token: String? = nil, completion: @escaping (Result<Data, Error>) -> Void)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: - token: The access token for the user to fetch.
|
||||
- completion: - completion: A handler for the `Data` object returned on success, or `Error` on failure.
|
||||
|
||||
### `getUserCollections(token:completion:)`
|
||||
|
||||
Retrieves a user's collections.
|
||||
|
||||
``` swift
|
||||
public func getUserCollections(token: String? = nil, completion: @escaping (Result<[WFCollection], Error>) -> Void)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
|
||||
- token: - token: The access token for the user whose collections are to be retrieved.
|
||||
- completion: - completion: A handler for the `[WFCollection]` object returned on success, or `Error` on failure.
|
||||
|
||||
### `translateWFError(fromServerResponse:)`
|
||||
|
||||
``` swift
|
||||
func translateWFError(fromServerResponse response: Data) -> WFError?
|
||||
```
|
|
@ -1,77 +0,0 @@
|
|||
# WFError
|
||||
|
||||
``` swift
|
||||
public enum WFError
|
||||
```
|
||||
|
||||
## Inheritance
|
||||
|
||||
`Error`, `Int`
|
||||
|
||||
## Enumeration Cases
|
||||
|
||||
### `badRequest`
|
||||
|
||||
``` swift
|
||||
case badRequest
|
||||
```
|
||||
|
||||
### `unauthorized`
|
||||
|
||||
``` swift
|
||||
case unauthorized
|
||||
```
|
||||
|
||||
### `forbidden`
|
||||
|
||||
``` swift
|
||||
case forbidden
|
||||
```
|
||||
|
||||
### `notFound`
|
||||
|
||||
``` swift
|
||||
case notFound
|
||||
```
|
||||
|
||||
### `methodNotAllowed`
|
||||
|
||||
``` swift
|
||||
case methodNotAllowed
|
||||
```
|
||||
|
||||
### `gone`
|
||||
|
||||
``` swift
|
||||
case gone
|
||||
```
|
||||
|
||||
### `preconditionFailed`
|
||||
|
||||
``` swift
|
||||
case preconditionFailed
|
||||
```
|
||||
|
||||
### `tooManyRequests`
|
||||
|
||||
``` swift
|
||||
case tooManyRequests
|
||||
```
|
||||
|
||||
### `internalServerError`
|
||||
|
||||
``` swift
|
||||
case internalServerError
|
||||
```
|
||||
|
||||
### `badGateway`
|
||||
|
||||
``` swift
|
||||
case badGateway
|
||||
```
|
||||
|
||||
### `serviceUnavailable`
|
||||
|
||||
``` swift
|
||||
case serviceUnavailable
|
||||
```
|
|
@ -1 +0,0 @@
|
|||
Generated at 2020-08-31T10:27:33-0400 using [swift-doc](https://github.com/SwiftDocOrg/swift-doc) 1.0.0-beta.3.
|
|
@ -1,10 +0,0 @@
|
|||
<details>
|
||||
<summary>Types</summary>
|
||||
|
||||
- [WFClient](/WFClient)
|
||||
- [WFCollection](/WFCollection)
|
||||
- [WFError](/WFError)
|
||||
- [WFPost](/WFPost)
|
||||
- [WFUser](/WFUser)
|
||||
|
||||
</details>
|
Loading…
Reference in New Issue