One of the major goals of ATProtoKit
is to create a well-written and well-documented project. Due to this, guidelines will need to be in place to ensure this standard is met. This document will provide the understanding of how to write code to improve the project. This is a living document and as such, things will change. However, they will work the same way as with the ATProtoKit
project itself:
- Minor updates to this project should only apply to
ATProtoKit
’s minor updates. - Major updates to this project should only apply to
ATProtoKit
’s major updates.
Note
This is a living document. Please re-visit these guidelines when a new minor or major version of this project is released.
The Swift API Design Guidelines sets the foundation for these principles. Adherence to these guidelines is strict, and should only deviate from it if it’s 100% necessary and if there’s no other logical way to get around it.
- For code, the maximum length of a line is 170 characters. However, this isn’t a strong goal to have: it can be a little longer than this.
- For documentation, the maximum length of a line is 100 characters. This is a (mostly) strict rule: if necessary, break it down into smaller lines.
- The only exceptions to this rule include the following:
- If a line that contains a oiece of code goes a little over the line, or if the code line is over 100 characters.
- If a link is over 100 characters long.
- If the Unclear Documentation Note is used.
- Documentation lines should use the triple slashes (
///
) rather than the multi-block delimiter (/** */
). - Everything in the project must be written in American English.
- All methods/functions, classes, structs, enums, and properties need to display their access keywords. The only permitted keywords used in this project are
public
,internal
,private
, andfileprivate
:public
should be used for all API user-facing items.internal
should be used for any items that aren’t appropriate for the API user to use alone.fileprivate
is rare. This is only reserved forATProtoKit
extensions. This is so other parts of the project don’t have access to it if it should only be used for the method it’s in.
- Do NOT force-unwrap.
if let
andguard
statements must be used instead. - As said in the Swift API Design Guidelines, don't shorten the words like with the lexicon. All structs, methods, functions, properties, and enums must especially follow this rule.
- Abbreviations are fine, however.
// Always use the full term. public func getRepo() // Incorrect. public func getRepository() // Correct. // Abbreviations are okay. pdsURL // Acceptable. finalPDSURL // Acceptable. repositoryCID // Acceptable.
- File names have some naming conventions:
- They must be in PascalCase and can't have their words separated.
- For Lexicon Models and Lexicon Methods (seen below):
- Files that contain a lexicon model should be based on the lexicon that it's used for. To be more specific, the lexicon's hostname, subdomain, and action name are combined into one.
Examples:
- AppBskyActorDefs.swift
- AppBskyRichTextFacet.swift
- ComAtprotoSyncSubscribeRepos.swift
- ToolsOzoneCommunicationDefs.swift
- With respect to file names, files which contain lexicon models don't use the requirement of expanding the shortened name, as they're designed to be closely aligned with the name of the namespaced identifier.
- Files that contain a lexicon method will be named the same as the method, albeit, in PascalCase.
- For admin and moderator methods, the file name must have an "-AsAdmin" suffix.
- Files that contain a lexicon model should be based on the lexicon that it's used for. To be more specific, the lexicon's hostname, subdomain, and action name are combined into one.
Examples:
- Folder names will be PascalCase, except for the folders inside the
Models/Lexicons
folder, where the namespace identifier's TLD and hostname are all lowercased.- Any folders in the lower level and beyond continue to use PascalCase.
- When writing documentation, make sure it's clear, understandable, and comprehensible enough for users (both experienced and new) to understand it.
- Documentation must link to other parts of
ATProtoKit
if it’s mentioned. You surround the name with two backticks on the left and right side. (Example:ATProtoError
.) - The top line must summarize what the object is doing.
- All paragraphs must be clearly separated by a line that contains
///
. - Paragraphs should give additional context of what's happening with the object or method. This means using examples of how to use the method.
/// This is the summary line
///
/// This is a new paragraph.
- The are a limited number of keywords that can be used in the symbol command syntax are as follows:
Bug
: Use this when there's a known bug inATProtoKit
or in the AT Protocol itself.Important
: Use this if the user requires context for the object they're going to use.Note
: Use this for sharing documentation directly from the AT Protocol specifications, or if the user should keep something in mind when using the object.Parameter
: Use this if there's only one parameter for the method.Parameters
: Use this if there's more than one parameter for the method.'Returns
: Use this to note that the method will return. When doing so, make sure a short description is stated for what the output is. The only exception is if the method is returning a choice of multiple values.
A `Result`, containing either a `[success model]` if successful, or an `ATProtoError`, if not.
SeeAlso
: Use this when referncing the link for the lexicon associated with the object. Reference the link by using[\
[Lexicon NSID]`][github]`, then create another paprgraph and link it.
/// - SeeAlso: This is based on the [`com.atproto.server.activateAccount`][github] lexicon. /// /// [github]: https://github.com/bluesky-social/atproto/blob/main/lexicons/com/atproto/server/activateAccount.json
- In documentation, when referring to the following, you must write them out exactly as shown:
- decentralized identifier (DID)
- content identifer (CID)
- Namespaced Identifier (NSID)
- Personal Data Server (PDS)
Lexicons are relevant to models and methods. Here are some general guidelines:
- Each model will be inside of two other models:
- A model that's within the "Lexicons.swift" file. This model will be named after the NSID's TLD and domain name, followed by the "-Lexicon" suffix. For example, all models that fall under
app.bsky
will go into theAppBskyLexicon
struct
. - A model that's within a Swift file that contains the exact name of the
struct
before it. It will inside the extension of the previousstruct
and will only be named after the subdomain portion of the NSID. For example, any lexicons that fall underapp.bsky.actor
will go inside theActor
struct
, which that one will be inside theAppBskyLexicon
extension
, which is in a file named "AppBskyLexicon.swift".
- An
extension
of the previous subdomainstruct
. All lexicons will have their own file. Each lexicon will insert theirstruct
s into anextension
of the subdomainstruct
. For example, all of theapp.bsky.actor.defs
lexicon models will be inside of anextension
ofActor
, and is contained in a separate file.
- A model that's within the "Lexicons.swift" file. This model will be named after the NSID's TLD and domain name, followed by the "-Lexicon" suffix. For example, all models that fall under
- Models are named after the action portion of the NSID. (Example: for the lexicon
app.bsky.actor.profile
, instead of naming the model as “app.bsky.actor.profile”, you name it as “ActorProfile”.)- There will be suffxes attached to the name, depending on the type of model:
- Any models that are purely a definition (this will usually be located in a
def
lexicon file) will have the "-Definition" suffix. For example, forapp.bsky.actor.defs#profileAssociated
, the model will be namedProfileAssociatedDefinition
. - Any models that are records will have the "-Record" suffix. For example, for the lexicon
app.bsky.actor.profile
, the model will be namedProfileRecord
. - Any portion of the lexicon that has an
procedure
value in thetype
field, and then contains aninput
field, will have the "-RequestBody" suffix in the model. For example, forapp.bsky.feed.sendInteractions
, there will be a model namedSendInteractionsRequestBody
. - Any portion of the leixcon that has an
output
field will have the "-Output" suffix in the model. Continuing withapp.bsky.feed.sendInteractions
, there will be a model namedSendInteractionsOutput
. - Any portion of the lexicon outside of the above exceptions will not contain a suffix. Instead, they will have a model that just contains the name of the action, followed by models within the previous one that contains the name of the definition. These definitions will either be a
struct
orenum
, depending on the appropriate use case:- For definitions where there are known values, an
enum
is required; it must conform to whatever is the most appropriate data type would be (usually it'll be aString
type.) - Anything else will be another
struct
.
- For definitions where there are known values, an
- Any models that are purely a definition (this will usually be located in a
- There will be suffxes attached to the name, depending on the type of model:
- All models are
struct
s, with one exception:- If a lexicon has a
union
type, then the list of values are combined into another model, which will be of typeenum
.
- If a lexicon has a
- All models must conform to
Codable
, but depending on the model, it may only need to conform to eitherEncodable
orDecodable
. - Properties that are of type
Date
must use the@DateFormatting
property wrapper. Likewise, properties that are of typeDate?
must use the@DateFormattingOptional
property wrapper. There are a number of things that need to be done when doing this:- In the initialization method, set the value of an underscored (_) version of the property name to an instance of
@DateFormatting
or@DateFormattingOptional
(which will henceforth be named "@DateFormatting
group”). ThewrappedValue
parameter’s should have the value of the initializer’s version of the non-underscored property:
self._indexedAt = DateFormatting(wrappedValue: indexedAt) self._indexedAt = DateFormattingOptional(wrappedValue: indexedAt)
- In the
Decodable
initializer, set the property normally, but using@DateFormatting
group.self
for thetype
parameter. After that, at the end of the line, add.wrappedValue
. For@DateFormattingOptional
, put?
in between the method and.wrappedValue
.
self.indexedAt = try container.decode(DateFormatting.self, forKey: .indexedAt).wrappedValue self.indexedAt = try container.decodeIfPresent(DateFormattingOptional.self, forKey: .indexedAt)?.wrappedValue ```swift - For the `encode` method, set the underscored version of the property in the `value` parameter’s value for both `encode` and `encodeIfPresent` methods: ```swift try container.encode(self._indexedAt, forKey: .indexedAt) try container.encodeIfPresent(self._indexedAt, forKey: .indexedAt)
- In the initialization method, set the value of an underscored (_) version of the property name to an instance of
Here's the general layout for all Lexicon models:
// Each model starts out with the one-line sentence summary of the model.
// Will be called "one-line documentation summary" for future reference.
/// The record definition for a post record.
///
// If the lexicon associated with this model contains documentation, then it must be inserted as a note.
// Will be called "lexicon spec note" for future reference.
/// - Note: According to the AT Protocol specifications: "Record containing a Bluesky post."
///
// The Namespaced Identifier (NSID) of the lexicon must be displayed. It should also be linked: the link
// will go to the lexicon JSON file associated with this model.
// Will be called "lexicon link" for future reference.
/// - SeeAlso: This is based on the [`app.bsky.feed.post`][github] lexicon.
///
/// [github]: https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/feed/post.json
There are multiple kinds of models: main models, definition models, record models, output models, and requestBody models.
- Documentation for the models are as follows:
- The first line should say “The main model definition for “, followed by a short, one sentence description of what the lexicon is for:
/// The main model for getting a detailed profile view for the user.
- After an empty
///
in the next line, the following line has the description that’s provided by the lexicon. If there’s no description, then this can be skipped. If there is one, it must say "- Note: According to the AT Protocol specifications: “<#Description#>
””, where "<#Description#>
” is the description provided by the lexicon:
/// - Note: According to the AT Protocol specifications: "Get detailed profile view of an actor. Does not require auth, but contains relevant metadata with auth."
- After another empty
///
in the next line, the following line states where the API user can see the name of the lexicon, followed by the link. The structure must look like this:
where: "/// - SeeAlso: This is based on the [`<#Lexicon Type ID#>`][github] lexicon. /// /// [github]: <#Lexicon link#>
<#Lexicon Type ID#>
” is the lexicon’s name, and “<#Lexicon link#>
” is the link to the lexicon itself. Example:/// - SeeAlso: This is based on the [`app.bsky.actor.getProfile`][github] lexicon. /// /// [github]: https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/actor/getProfile.json
- Documentation for the models are as follows:
- The first line must be structured as "A definition model for [...].“ (where "[...]") is a short, one sentence description of what the lexicon is for):
The requirements remain the same for the AT Protocol lexicon descriptions, the lexicon's NSID, and the GitHub link./// A definition model for an actor viewer state.
- For the models themselves, they have the following requirements:
- The name of the struct is exactly the same as the with the Main model name, but the suffix is the property name within the lexicon itself, and
defs
is removed. For example,com.atproto.admin.defs
contains a property namedmodEventView
. Therefore, the name of the struct is calledAdminModEventView
. - After an empty
///
in the next line, the following line has the description that’s provided by the lexicon. If there’s no description, then this can be skipped. If there is one, it must say "- Note: According to the AT Protocol specifications: “<#Description#>
””, where "<#Description#>
” is the description provided by the lexicon:
/// - Note: According to the AT Protocol specifications: "Get detailed profile view of an actor. Does not require auth, but contains relevant metadata with auth."
- After another empty
///
in the next line, the following line states where the API user can see the name of the lexicon, followed by the link. The structure must look like this:
where: "/// - SeeAlso: This is based on the [`<#Lexicon Type ID#>`][github] lexicon. /// /// [github]: <#Lexicon link#>
<#Lexicon Type ID#>
” is the lexicon’s name, and “<#Lexicon link#>
” is the link to the lexicon itself:/// - SeeAlso: This is based on the [`app.bsky.actor.getProfile`][github] lexicon. /// /// [github]: https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/actor/getProfile.json
- The name of the struct is exactly the same as the with the Main model name, but the suffix is the property name within the lexicon itself, and
- Documentation for the models are as follows:
- The first line must be structured as "A record model for [...]." (where "[...]" is a short, one sentence description of what the lexicon is for):
/// A record model of a post.
- After an empty
///
in the next line, the following line has the description that's procided by the lexicon. If there's no description, then this can be skipped. If there is one, it must say "- Note: According to the AT Protocol specifications: "<#Description#>
"", where "<#Description#>
" is the description provided by the lexicon. - After another empty
///
in the next line, the following line states where the API user can see the name of the lexicon, followed by the link. The structure must look like this:
where: "/// - SeeAlso: This is based on the [`<#Lexicon Type ID#>`][github] lexicon. /// /// [github]: <#Lexicon link#>
<#Lexicon Type ID#>
” is the lexicon’s name, and “<#Lexicon link#>
” is the link to the lexicon itself:/// - SeeAlso: This is based on the [`app.bsky.feed.post`][github] lexicon. /// /// [github]: https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/feed/post.json
- Documentation for the model are as follows:
- The first line must be structured as "An output model for [...]." (where "[...]" is a short, one sentence desctiption of what the lexicon is for):
/// An output model for checking the user's account status.
- After an empty
///
in the next line, the following line has the description that’s provided by the lexicon. If there’s no description, then this can be skipped. If there is one, it must say "- Note: According to the AT Protocol specifications: “<#Description#>
””, where "<#Description#>
” is the description provided by the lexicon:
/// - Note: According to the AT Protocol specifications: "Get detailed profile view of an actor. Does not require auth, but contains relevant metadata with auth."
- After another empty
///
in the next line, the following line states where the API user can see the name of the lexicon, followed by the link. The structure must look like this:
where: "/// - SeeAlso: This is based on the [`<#Lexicon Type ID#>`][github] lexicon. /// /// [github]: <#Lexicon link#>
<#Lexicon Type ID#>
” is the lexicon’s name, and “<#Lexicon link#>
” is the link to the lexicon itself:/// - SeeAlso: This is based on the [`app.bsky.actor.getProfile`][github] lexicon. /// /// [github]: https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/actor/getProfile.json
- Documentation for the model are as follows:
- The first line must be structured as "A request body model for [...]." (where "[...]" is a short, one sentence desctiption of what the lexicon is for]):
/// A request body model for checking the user's account status.
- After an empty
///
in the next line, the following line has the description that’s provided by the lexicon. If there’s no description, then this can be skipped. If there is one, it must say "- Note: According to the AT Protocol specifications: “<#Description#>
””, where "<#Description#>
” is the description provided by the lexicon:
/// - Note: According to the AT Protocol specifications: "Get detailed profile view of an actor. Does not require auth, but contains relevant metadata with auth."
- After another empty
///
in the next line, the following line states where the API user can see the name of the lexicon, followed by the link. The structure must look like this:
where: "/// - SeeAlso: This is based on the [`<#Lexicon Type ID#>`][github] lexicon. /// /// [github]: <#Lexicon link#>
<#Lexicon Type ID#>
” is the lexicon’s name, and “<#Lexicon link#>
” is the link to the lexicon itself:/// - SeeAlso: This is based on the [`app.bsky.actor.getProfile`][github] lexicon. /// /// [github]: https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/actor/getProfile.json
There may be times where documentation may be unclear by the lexicon. If this happens, add a note below everything in the documentation. The note must look like this:
/// - Important: The item associated with this property is undocumented in the AT Protocol specifications. The documentation here is based on:\
/// \* **For items with some inferable context from property names or references**: its best interpretation, though not with full certainty.\
/// \* **For items without enough context for even an educated guess**: a direct acknowledgment of their undocumented status.\
/// \
/// Clarifications from Bluesky are needed in order to fully understand this item.
- When creating a model, the following layout must be in the following order:
typealias
es.public
properties.- This includes normal properties and computed properties.
private
properties.- This includes normal properties and computed properties.
internal
properties.- This includes normal properties and computed properties.
- Initalizers.
- They must be in the order of
public
,private
, andinternal
. convenience init()
must be after thepublic
initializers.
- They must be in the order of
- Methods.
- They must follow the order of
public
,private
,internal
, andfileprivate
. static
methods should be before normalpublic
methods.
- They must follow the order of
enum
s.- They must follow the order of
public
,private
, andinternal
.
- They must follow the order of
- All properties, methods,
enum
s,typealias
es, etc. must be fully documented, regardless of visibility.- This ensures that people have a better understanding of what's going on, either for using the package, or contributing to it.
- All models must be
public
struct
s and should conform toCodable
,Decodable
,Encodable
, orATRecordProtocol
.- If the model should only be used to be encoded or decoded, but not both, then the model must only be conforming to
Encodable
orDecodable
respectively; they can't conform toCodable
.
- If the model should only be used to be encoded or decoded, but not both, then the model must only be conforming to
- The name of the model must be the namespaced identifier's subdomain, followed by the name of the lexicon's file name, all in PascalCase. For example:
com.atproto.sync.notifyOfUpdate
becomesSyncNotifyOfUpdate
. Furthermore, the suffix of the name depends on the type of model it is:- For main definition and normal definition models, there is no suffix.
- For output models, they add the
-Output
suffix. - For
requestBody
models, add the-RequestBody
suffix.
- All properties are
public
, unless thetype
property is used (interal
is used then). - Default properties are not allowed.
- If
@DateFormatting
and@DateFormattingOptional
are used in at least one propery, the following must happen:- The property affected must be using
var
instead oflet
. - In the standard
init()
method, set the value ofwrappedValue
to an underscored (_
) version of the name of the property. - In
init(from decoder: Decoder) throws
, attempt to to decode eachDate
property using@DateFormatting
/@DateFormattingOptional
'swrappedValue
: - For
CodingKeys
, only override the case if the value doesn't match the required value in the lexicon.
- The property affected must be using
- The decoding and encoding methods, the line that defines
container
and the decoding/encoding process must be separated by a single line. - For
truncateEncode()
andtruncateEncodeIfPresent()
, make the rest of the code should be separated from the rest of the encoding process of the other properties.
- All union types must be
public
enum
s and should conform toCodable
,Decodable
, orEncodable
. - The name of the union must have the
Union
suffix. - For documentation, It should say "A reference containing a list of " followed by the name of the list.
/// A reference containing the list of preferences.
- The first line should summarize what the method will do in one sentence.
- All paragraphs will be separated by a space.
- Any reference links will be added between the last paragraph of explanation and the group of
Parameter(s)
,Returns
, andThrows
values.
/// - SeeAlso: This is based on the [`com.atproto.repo.createRecord`][github] lexicon.
///
/// [github]: https://github.com/bluesky-social/atproto/blob/main/lexicons/com/atproto/repo/createRecord.json
///
/// - Parameters:
/// [...]
- The documentation parameter associated with the method parameter must only have one sentence. There are exceptions:
- If the method parameter's data type is optional, then the word "Optional." Will be added to the documentation parameter's explanation.
/// - sources: An array of decentralized identifiers (DIDs) for label sources. Optional.
- If the method parameter has a default value, then the the following will be added as an addition sentence: "Defaults to
[value]
", where[value]
is the default value.
- All methods must be inside an
extension
forATProtoKit
, unless it's related one of the following:- Any lexicons that are within the
com.atproto.admin.*
ortools.ozone.*
NSIDs need to be inside theextension
ofATProtoAdmin
. - Any lexicons that are within the
chat.bsky.*
NSIDs need to be inside theextension
ofATBlueskyChat
.
- Any lexicons that are within the
- The method's name must be the name of the namespaced identifier of the lexicon, if the TLD, doman, and subdomain was removed.
- If there's a conflict (there's more than one method with the same name in a class), the sub-domain will be addedin a way that somewhat makes grammatical sense at least. For example:
getRepositoryRecord()
andgetSyncRecord()
.
- If there's a conflict (there's more than one method with the same name in a class), the sub-domain will be addedin a way that somewhat makes grammatical sense at least. For example:
- The list of parameters should be inline with the lexicon's parameter list. Any additional parameters that need to be added should be at the end.
- The only exception to this rule is if there's an order that makes more sense and is in alignment with the Swift API Deisgn Guideline's "Strive for Fluent Usage" guidelines.
- There are three types of lexicon methods: methods where authentication is required (will henceforth be called "AuthRequired"), methods where authentication is optional (will henceforth be called "AuthOptional"), and methods where authentication is not used (will henceforth be called "AuthUnneeded").
- For AuthUnneeded lexicon methods:
- An additional parameter is added at the end: `pdsURL.
pdsURL
is aString?
parameter and defaults tonil
. This is used for if the method call needs to use a Personal Data Server other than the one attached to theUserSession
instance.
- An additional parameter is added at the end: `pdsURL.
- For AuthOptional lexicon methods:
- Two additional parameters are added at the end:
pdsURL
andshouldAuthenticate
.pdsURL
is aString?
parameter and defaults tonil
. This is used for if the method call needs to use a Personal Data Server other than the one attached to theUserSession
instance.shouldAuthenticate
is aBool
parameter and defaults tofalse
. This is used for is the method call wants the access token to be part of the request payload.
- Two additional parameters are added at the end:
- If the method is AuthRequired, use a
guard
statement. In it,session
must not benil
, and a variableaccessToken
will have the valuesession?.accessToken. In the
elseblock, the method will either
returna
.failurecase containing
ATRequestPrepareError.missingActiveSession(if the method returns either a response or error) or
throws
ATRequestPrepareError.missingActiveSession` (if the method only throws errors).
guard session != nil,
let accessToken = session?.accessToken else {
return .failure(ATRequestPrepareError.missingActiveSession)
}
- There must be a
guard
statement.sessionURL
defines the Personal Data Server's hostname.- For AuthOptional, a terniary operator is used:
pdsURL
is set to the method parameter version ofpdsURL
(if it's notnil
), orsession?.pdsURL
ifpdsURL
isnil
).
guard let pdsURL != nil ? pdsURL : session?.pdsURL,
- For AuthOptional, a terniary operator is used:
requestURL
combinessessionURL
and the endpoint, which, aftersessionURL
contains "xrpc/" followed by the namespaced identifier of the lexicon.- Depending on whether the lexicon method can
return
a response/error or justthrow
an error, theelse
block will eitherthrow
ATRequestPrepareError.missingActiveSession
(if the method only throws errors) orreturn
s a.failure()
enum ofATRequestPrepareError.missingActiveSession
(if it canreturn
a response or error).
guard let sessionURL = session?.pdsURL, let requestURL = URL(string: "\(sessionURL)/xrpc/com.atproto.repo.createRecord") else { return .failure(ATRequestPrepareError.invalidRequestURL) }
- If the method uses a request body:
- Call the variable
requestBody
, while using the value of therequestBody
model associated with the method. - All of the parameters of the model must be in separate lines. The closing paranthesis must also have a separate line. Example:
let requestBody = RepoCreateRecord( repositoryDID: repositoryDID, collection: collection, recordKey: recordKey, shouldValidate: shouldValidate, record: record, swapCommit: swapCommit )
- Call the variable
- If the method uses a query:
- Create a variable (named
queryItems
) and set the value of[(String, String)]()
.
var queryItems = [(String, String)]()
- For each query item:
- Append
queryItems
with(String, String)
, where the firstString
contains the real query variable the lexicon specifies, and the secondString
contains the parameter associated with the query item.
queryItems.append(("q", query))
- If the query is optional, use an
if let
statement.
if let cursor { queryItems.append(("cursor", cursor)) }
- If the query is an array, use
Array.map()
to loop through all elements. The element will be denoted by$0
.
queryItems += uriPatterns.map { ("uriPatterns", $0) }
- If the query is related to a date, then an
if let
statement will be used to unwrap the variable UseCustomDateFormatter
to convert theDate
object to an ISO8601 format. After that, the standard rules apply.
if let createdAfterDate = createdAfter, let formattedCreatedAfter = CustomDateFormatter.shared.string(from: createdAfterDate) { queryItems.append(("createdAfter", formattedCreatedAfter)) }
- If the query is
limit
, grab thelimit
parameter and put it inside ofmin(x, y)
, wherex
islimit
andy
is the highest number the lexicon allows for the property. Then putmin()
inside ofmax(x, y)
, wherex
is the lowest number the lexicon allows for the property andy
is themin()
function. This will be the value forfinalLimit
, which will be used for value of the secondString
of the query item.
if let limit { let finalLimit = max(1, min(limit, 100)) queryItems.append(("limit", "\(finalLimit)")) }
- Append
- Create a
queryURL
variable of typeURL
. Don't add a value to it.
let queryURL: URL
- Inside the
do
block, set the value ofqueryURL
toAPIClientService.setQueryItems()
, insertingrequestURL
andqueryItems
respectively. Each parameter, and the closing paranthesis, must have a separate line.
do { queryURL = try APIClientService.setQueryItems( for: requestURL, with: queryItems )
- Create a variable (named
TBD...
- For methods using
APIClientService
:- Each parameter in
createRequest()
andsendRequest()
must be separated in each line, unless there is only one parameter being used. - If
createRequest()
andsendRequest()
are the only ones in thedo-catch
block, separate them with a space;createRequest()
must be the first line in thedo
block, followed by a space.sendRequest()
must be at the last line of thedo
block.
do { let request = APIClientService.createRequest(forRequest: queryURL, andMethod: .post, acceptValue: "application/json", contentTypeValue: nil, authorizationValue: accessToken) try await APIClientService.sendRequest(request, decodeTo: LabelQueryLabelsOutput.self) }
- If there’s a
return
statement in thedo-catch
block, or if the query method is in there, the request and response methods should be beside each other. - If any additional method calls are being made, put them beside
createRequest()
andsendRequest()
if they're strongly related to them.
- Each parameter in