Skip to content

Latest commit

 

History

History
457 lines (431 loc) · 34.2 KB

API_GUIDELINES.md

File metadata and controls

457 lines (431 loc) · 34.2 KB

ATProtoKit API Design Guidelines

Introduction

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.

Swift API Design Guidelines

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.

Fundamentals

  • 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, and fileprivate:
    • 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 for ATProtoKit 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 and guard 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.
  • 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.

Documentation

General

  • 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 in ATProtoKit 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
    • Throws: Use this if a method can throw an error. When using this, ensure that all of the ways the method can throw are listed in bullet point form.
    • Warning: Use this if the user must know something, and failure to do so will cause something major to break.
  • 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)

Lexicon Models

Lexicons are relevant to models and methods. Here are some general guidelines:

  • Each model will be inside of two other models:
    1. 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 the AppBskyLexicon struct.
    2. 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 previous struct and will only be named after the subdomain portion of the NSID. For example, any lexicons that fall under app.bsky.actor will go inside the Actor struct, which that one will be inside the AppBskyLexicon extension, which is in a file named "AppBskyLexicon.swift".
    • An extension of the previous subdomain struct. All lexicons will have their own file. Each lexicon will insert their structs into an extension of the subdomain struct. For example, all of the app.bsky.actor.defs lexicon models will be inside of an extension of Actor, and is contained in a separate file.
  • 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, for app.bsky.actor.defs#profileAssociated, the model will be named ProfileAssociatedDefinition.
      • Any models that are records will have the "-Record" suffix. For example, for the lexicon app.bsky.actor.profile, the model will be named ProfileRecord.
      • Any portion of the lexicon that has an procedure value in the type field, and then contains an input field, will have the "-RequestBody" suffix in the model. For example, for app.bsky.feed.sendInteractions, there will be a model named SendInteractionsRequestBody.
      • Any portion of the leixcon that has an output field will have the "-Output" suffix in the model. Continuing with app.bsky.feed.sendInteractions, there will be a model named SendInteractionsOutput.
      • 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 or enum, 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 a String type.)
        • Anything else will be another struct.
  • All models are structs, with one exception:
    • If a lexicon has a union type, then the list of values are combined into another model, which will be of type enum.
  • All models must conform to Codable, but depending on the model, it may only need to conform to either Encodable or Decodable.
  • Properties that are of type Date must use the @DateFormatting property wrapper. Likewise, properties that are of type Date? 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”). The wrappedValue 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 the type 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)

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.

Main 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:
    /// - SeeAlso: This is based on the [`<#Lexicon Type ID#>`][github] lexicon.
    ///
    /// [github]: <#Lexicon link#>
    where: "<#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

Definition Models

  • 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):
     /// A definition model for an actor viewer state.
    The requirements remain the same for the AT Protocol lexicon descriptions, the lexicon's NSID, and the GitHub link.
  • 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 named modEventView. Therefore, the name of the struct is called AdminModEventView.
    • 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:
    /// - SeeAlso: This is based on the [`<#Lexicon Type ID#>`][github] lexicon.
    ///
    /// [github]: <#Lexicon link#>
    where: "<#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

Record Models

  • 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:
    /// - SeeAlso: This is based on the [`<#Lexicon Type ID#>`][github] lexicon.
    ///
    /// [github]: <#Lexicon link#>
    where: "<#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

Output Models

  • 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:
    /// - SeeAlso: This is based on the [`<#Lexicon Type ID#>`][github] lexicon.
    ///
    /// [github]: <#Lexicon link#>
    where: "<#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

requestBody Models

  • 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:
    /// - SeeAlso: This is based on the [`<#Lexicon Type ID#>`][github] lexicon.
    ///
    /// [github]: <#Lexicon link#>
    where: "<#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

Unclear Documentation Note

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.

Model Designs

General

  • When creating a model, the following layout must be in the following order:
    1. typealiases.
    2. public properties.
      • This includes normal properties and computed properties.
    3. private properties.
      • This includes normal properties and computed properties.
    4. internal properties.
      • This includes normal properties and computed properties.
    5. Initalizers.
      • They must be in the order of public, private, and internal.
      • convenience init() must be after the public initializers.
    6. Methods.
      • They must follow the order of public, private, internal, and fileprivate.
      • static methods should be before normal public methods.
    7. enums.
      • They must follow the order of public, private, and internal.
  • All properties, methods, enums, typealiases, 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.

Lexicon models

  • All models must be public structs and should conform to Codable, Decodable, Encodable, or ATRecordProtocol.
    • If the model should only be used to be encoded or decoded, but not both, then the model must only be conforming to Encodable or Decodable respectively; they can't conform to Codable.
  • 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 becomes SyncNotifyOfUpdate. 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 the type 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 of let.
    • In the standard init() method, set the value of wrappedValue to an underscored (_) version of the name of the property.
    • In init(from decoder: Decoder) throws, attempt to to decode each Date property using @DateFormatting/@DateFormattingOptional's wrappedValue:
    • For CodingKeys, only override the case if the value doesn't match the required value in the lexicon.
  • The decoding and encoding methods, the line that defines container and the decoding/encoding process must be separated by a single line.
  • For truncateEncode() and truncateEncodeIfPresent(), make the rest of the code should be separated from the rest of the encoding process of the other properties.

Lexicon Union Designs

  • All union types must be public enums and should conform to Codable, Decodable, or Encodable.
  • 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.

Lexicon Method Designs

Documentation

  • 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, and Throws 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.

Lexicon Methods

  • All methods must be inside an extension for ATProtoKit, unless it's related one of the following:
    • Any lexicons that are within the com.atproto.admin.* or tools.ozone.* NSIDs need to be inside the extension of ATProtoAdmin.
    • Any lexicons that are within the chat.bsky.* NSIDs need to be inside the extension of ATBlueskyChat.
  • 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() and getSyncRecord().
  • 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 a String? parameter and defaults to nil. This is used for if the method call needs to use a Personal Data Server other than the one attached to the UserSession instance.
  • For AuthOptional lexicon methods:
    • Two additional parameters are added at the end: pdsURL and shouldAuthenticate.
      • pdsURL is a String? parameter and defaults to nil. This is used for if the method call needs to use a Personal Data Server other than the one attached to the UserSession instance.
      • shouldAuthenticate is a Bool parameter and defaults to false. This is used for is the method call wants the access token to be part of the request payload.
  • If the method is AuthRequired, use a guard statement. In it, session must not be nil, and a variable accessToken will have the value session?.accessToken. In the elseblock, the method will eitherreturna.failurecase containingATRequestPrepareError.missingActiveSession(if the method returns either a response or error) orthrows 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 of pdsURL (if it's not nil), or session?.pdsURL if pdsURL is nil).
      guard let pdsURL != nil ? pdsURL : session?.pdsURL,
    • requestURL combines sessionURL and the endpoint, which, after sessionURL contains "xrpc/" followed by the namespaced identifier of the lexicon.
    • Depending on whether the lexicon method can return a response/error or just throw an error, the else block will either throw ATRequestPrepareError.missingActiveSession (if the method only throws errors) or returns a .failure() enum of ATRequestPrepareError.missingActiveSession (if it can return 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 the requestBody 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
    )
  • 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 first String contains the real query variable the lexicon specifies, and the second String 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 Use CustomDateFormatter to convert the Date 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 the limit parameter and put it inside of min(x, y), where x is limit and y is the highest number the lexicon allows for the property. Then put min() inside of max(x, y), where x is the lowest number the lexicon allows for the property and y is the min() function. This will be the value for finalLimit, which will be used for value of the second String of the query item.
      if let limit {
          let finalLimit = max(1, min(limit, 100))
          queryItems.append(("limit", "\(finalLimit)"))
      }
    • Create a queryURL variable of type URL. Don't add a value to it.
    let queryURL: URL
    • Inside the do block, set the value of queryURL to APIClientService.setQueryItems(), inserting requestURL and queryItems respectively. Each parameter, and the closing paranthesis, must have a separate line.
    do {
    queryURL = try APIClientService.setQueryItems(
                for: requestURL,
                with: queryItems
    )

Logging

TBD...

Uncategorized

  • For methods using APIClientService:
    • Each parameter in createRequest() and sendRequest() must be separated in each line, unless there is only one parameter being used.
    • If createRequest() and sendRequest() are the only ones in the do-catch block, separate them with a space; createRequest() must be the first line in the do block, followed by a space. sendRequest() must be at the last line of the do 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 the do-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() and sendRequest() if they're strongly related to them.