SkillAgentSearch skills...

SociableWeaver

Build declarative GraphQL queries in Swift.

Install / Use

/learn @NicholasBellucci/SociableWeaver
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

SociableWeaver

Swift meets GraphQL in this lightweight, easy to use framework. SociableWeaver uses a declarative style of programming and makes GraphQL queries look natural in Swift code. Through the use of Swift 5.1 function builders and CodingKeys, SociableWeaver removes all of the need for unsafe strings and Dictionaries when creating objects and fields.

Requirements

Xcode 11.x or a Swift 5.1x toolchain with Swift Package Manager.

Installation

Swift Package Manager

For projects using a .xcodeproj the best method is to navigate to File > Swift Packages > Add Package Dependency.... From there just simply enter https://github.com/NicholasBellucci/SociableWeaver as the package repository url and use the master branch or the most recent version. Master will always be inline with the newest release. The other method is to simply add .package(url: "https://github.com/NicholasBellucci/SociableWeaver.git", from: "0.1.0") to your Package.swift file's dependencies.

Carthage

Add the following entry to your Cartfile and run $ carthage update SociableWeaver

github "NicholasBellucci/SociableWeaver"

Table of Contents

Usage

SociableWeaver supports all that GraphQL has to offer. In order to get everything out of this framework, just make sure that any Codable models used contain CodingKeys. For example:

public struct Post: Codable {
    public enum CodingKeys: String, CodingKey, CaseIterable {
        case id, title, content
    }

    public let id: String
    public let title: String
    public let content: String

    public init(id: String, title: String, content: String) {
        self.id = id
        self.title = title
        self.content = content
    }
}

If CodingKeys aren't possible, SociableWeaver does support strings. It is highly recommended this be used as a last resort as it will make queries more difficult to manage.

Objects and Fields

GraphQL Fields

GraphQL is all about querying specific fields on objects and returning only what is needed. With SociableWeaver constructing objects with fields is a breeze.

Swift
Weave(.query) {
    Object(Post.self) {
        Field(Post.CodingKeys.id)
        Field(Post.CodingKeys.title)
        Field(Post.CodingKeys.content)
    }
}
GraphQL Query
query {
    post {
        id
        title
        content
    }
}

Arguments

GraphQL Arguments

Arguments are a key part of GraphQL and allow for much more refined queries. SociableWeaver supports arguments on both objects and fields.

The only requirement is that the value for the argument conforms to ArgumentValueRepresentable. Core types such as String, Int, Bool etc. will already conform. Enumerations will need to conform to the EnumValueRepresentable protocol.

Swift
Weave(.query) {
    Object(Post.self) {
        Field(Post.CodingKeys.title)

        Object(Post.CodingKeys.author) {
            Field(Author.CodingKeys.id)
            Field(Author.CodingKeys.name)
                .argument(key: "lastName", value: "Doe")
        }

        Object(Post.CodingKeys.comments) {
            Field(Comment.CodingKeys.id)
            Field(Comment.CodingKeys.content)
        }
        .argument(key: "filter", value: CommentFilter.recent)
    }
}
GraphQL Query
query {
    post {
        title
        author {
            id
            name(lastName: "Doe")
        }
        comments(filter: RECENT) {
            id
            content
        }
    }
}

Optionals

Optionals are supported and can be included in the query. In the instance where an optional should be included and the value is nil, the resulting GraphQL value will be null.

In order to include an optional make sure to get the argument value of the property without including a ?. This will result in a query param of age: null.

public struct Author: Codable {
    public enum CodingKeys: String, CodingKey, CaseIterable {
        case id, name, age, birthplace
    }

    ...
    public let age: Int?
    ...
}

extension Author: ArgumentValueRepresentable {
    public var argumentValue: String {
        var params: [String: String?] = [:]

        ...
        params["age"] = age.argumentValue
        ...

        let paramStrings: [String] = params.compactMap { argument in
            guard let value = argument.value else {
                return nil
            }

            return "\(argument.key): \(value)"
        }

        return "{ \(paramStrings.joined(separator: ",")) }"
    }
}'

Alias

GraphQL Alias

Aliases are key when querying a single object multiple times in the same request.

Swift
Weave(.query) {
    Object(Post.self) {
        Object(Post.CodingKeys.comments) {
            Field(Comment.CodingKeys.id)
            Field(Comment.CodingKeys.content)
        }
        .argument(key: "filter", value: CommentFilter.recent)
        .alias("newComments")
        
        Object(Post.CodingKeys.comments) {
            Field(Comment.CodingKeys.id)
            Field(Comment.CodingKeys.content)
        }
        .argument(key: "filter", value: CommentFilter.old)
        .alias("oldComments")
    }
}
GraphQL Query
query {
    post {
        newComments: comments(filter: RECENT) {
            id
            content
        }
        oldComments: comments(filter: OLD) {
            id
            content
        }
    }
}

Fragments

GraphQL Fragments

GraphQL fragments can help when building complicated queries. SociableWeaver makes them extremely simple and allows the proper references to be placed exactly where they would be in the query. With the help of a FragmentBuilder the FragmentReference can be added to the objects that require the fields and the Fragment can be added to the operation itself.

Swift
let authorFragment = FragmentBuilder(name: "authorFields", type: Author.self)
let query = Weave(.query) {
    Object(Post.self) {
        Object(Post.CodingKeys.author) {
            FragmentReference(for: authorFragment)
        }

        Object(Post.CodingKeys.comments) {
            Field(Comment.CodingKeys.content)
            
            Object(Comment.CodingKeys.author) {
                FragmentReference(for: authorFragment)
            }
        }
    }

    Fragment(authorFragment) {
        Field(Author.CodingKeys.id)
        Field(Author.CodingKeys.name)
    }
}
GraphQL Query
query {
  post {
    author {
      ...authorFields
    }
    comments {
      content
      author {
        ...authorFields
      }
    }
  }
}

fragment authorFields on Author {
  id
  name
}

Operation Name

GraphQL Operation Name

Operation names aren't required but can make the queries more unique.

Weave(.query) {
    Object(Post.self) {
        Field(Post.CodingKeys.id)
        Field(Post.CodingKeys.title)
        Field(Post.CodingKeys.content)
    }
}
.name("GetPostAndContent")
GraphQL Query
query GetPost {
  post {
    id
    title
    content
  }
}

Variables

GraphQL Variables

Since direct JSON is not needed when making queries in SociableWeaver, variables can and should be define in a method and passed into the query as arguments.

Swift
queryPost(id: 1)

func queryPost(id: Int) {
    Weave(.query) {
        Object(Post.self) {
            Field(Post.CodingKeys.title)
            Field(Post.CodingKeys.content)
            
            Object(Post.CodingKeys.author) {
                Field(Author.CodingKeys.id)
                Field(Author.CodingKeys.name)
            }
        }
        .argument(key: "id", value: id)
    }
}
GraphQL Query
query {
  post(id: 1) {
    title
    content
    author {
      id
      name
    }
  }
}

Directives

GraphQL Directives

Directives in GraphQL allows the server to affect execution of the query. The two directives are @include and @skip both of which can be added to fields or included fragments. The example defines true or false but in an actual query these values would be boolean variables.

Just to note, Skip will always take precedent over include. Also any objects/fragments that end up not having fields will be removed from the query.

let query = Weave(.query) {
    Object(Post.self) {
        Field(Post.CodingKeys.title)
        Field(Post.CodingKeys.content)
            .include(if: true)

        Object(Post.CodingKeys.author) {
            Field(Author.CodingKeys.name)
        }
        .include(if: false)

        Object(Post.CodingKeys.comments) {
            Field(Comment.CodingKeys.content)
                .include(if: true)
                .skip(if: true)
                
            Object(Comment.CodingKeys.author) {
                Field(Author.CodingKeys.name)
                    .skip(if: true)
       
View on GitHub
GitHub Stars78
CategoryProduct
Updated19d ago
Forks7

Languages

Swift

Security Score

100/100

Audited on Mar 12, 2026

No findings