GithubHelp home page GithubHelp logo

nerdsupremacist / graphaello Goto Github PK

View Code? Open in Web Editor NEW
494.0 4.0 18.0 23.68 MB

A Tool for Writing Declarative, Type-Safe and Data-Driven Applications in SwiftUI using GraphQL

Home Page: https://graphaello.dev

License: MIT License

Makefile 0.25% Swift 99.75%
swiftui graphql command-line-tool type-safety codegen swift declarative data-driven

graphaello's Issues

Caching

On larger projects, Code Generation takes a very long time.

Evaluate caching:

  • Fragments and Queries for structs
  • Code generated for each struct

How production ready is this?

I'm wondering how production ready this library is? And if not, what other features would be required to be added in until it becomes production ready? Also is there a specific timeframe for this?

The initializer is not created.

Hi! Thank you for creating this great library.
As I saw int the documentation, with this code there should be autogenerated initializer similar to this CourseCell(course: PandaEntry.Course), but the initializer is missing. Can you please help me to understand what is the problem?

struct CourseCell: View {
    
    @GraphQL(PandaEntry.Course.title)
    var title: String?
    
    var body: some View {
        Text(title ?? "unknown")
    }
}

Make promotional Website

make a nice promotional website for Graphaello...

Include some nice examples (with animations) and documentation on how it all can be used

Xcode 11.4 will not build.

SwiftSyntax parser library isn't compatible
Command PhaseScriptExecution failed with a nonzero exit code

Issue with macro definition

In beta 3 there is a mistake in the #if-macro-statement. The name looks like GRAPHAELLO_SWIFT_GRAPH_QL__TARGET. There is on _ too much.

Polling

I was looking at this, and was wondering if there currently is a way to poll a query every N seconds? (E.g. if I display a set of comments, I might want to refresh the view every few seconds such that people are always up-to-date.)

Explore dynamicMemberLookup and dynamicCallable to make code simpler

Something like this:

@dynamicMemberLookup
protocol Type: Target { }

extension Type {

    // make member key paths static for the type

    static subscript<T>(dynamicMember keyPath: KeyPath<GraphQLFragmentPath<Self, Self>, GraphQLFragmentPath<Self, T>>) -> GraphQLFragmentPath<Self, T> {
        return .init()
    }

    static subscript<T>(dynamicMember keyPath: KeyPath<GraphQLFragmentPath<Self, Self>, GraphQLPath<Self, T>>) -> GraphQLPath<Self, T> {
        return .init()
    }

}

@dynamicMemberLookup
struct GraphQLFragmentPath<TargetType: Target, UnderlyingType> {
    fileprivate init() {}

    // nested values inside arrays

    subscript<Value, Output>(dynamicMember _: KeyPath<GraphQLFragmentPath<TargetType, Value>, GraphQLPath<TargetType, Output>>) -> GraphQLPath<TargetType, [Output]> where UnderlyingType == [Value] {
        return .init()
    }

    subscript<Value, Output>(dynamicMember _: KeyPath<GraphQLFragmentPath<TargetType, Value>, GraphQLPath<TargetType, Output>>) -> GraphQLPath<TargetType, [Output]?> where UnderlyingType == [Value]? {
        return .init()
    }

    subscript<Value, Output>(dynamicMember _: KeyPath<GraphQLFragmentPath<TargetType, Value>, GraphQLFragmentPath<TargetType, Output>>) -> GraphQLPath<TargetType, [Output]> where UnderlyingType == [Value] {
        return .init()
    }

    subscript<Value, Output>(dynamicMember _: KeyPath<GraphQLFragmentPath<TargetType, Value>, GraphQLFragmentPath<TargetType, Output>>) -> GraphQLPath<TargetType, [Output]?> where UnderlyingType == [Value]? {
        return .init()
    }
}

and similar strategies for dealing with optionals...

The main reason this is not being used right now is the fact that autocomplete is broken. We should evaluate if there's a way to have autocomplete working and still bringing the amount of code waaaay down

Instantiate structs with only one fragment directly

This example should be valid:

struct CountryMapPin {
    @GraphQL(Covid.Country.cases)
    var cases: Int

    @GraphQL(Covid.Country.info.latitude)
    var latitude: Double?

    @GraphQL(Covid.Country.info.longitude)
    var longitude: Double?
}

struct ContentView: View {
    @GraphQL(Covid.countries)
    var pins: [CountryMapPin]

    var body: some View {
         MapView(pins: pins).frame(height: 400)
    }
}

Proposal

We solve this by adding the following extension to every struct that has a single fragmentl:

extension CountryMapPin: Fragment {
    typealias UnderlyingType = Country.UnderlyingType
}

extension CountryMapPin.Country { 
    func countryMapPin() -> CountryMapPin { 
        return CountryMapPin(country: self) 
    }
}

In code generation add an extra operation to implicitly convert. Create it during resolution.

Mutations are not properly propagated to parents

Currently you can change values of the GraphQL property wrapper after a mutation.

However, when the mutated value is in a Fragment, the change isn't propagated to the original dictionary from where the query originated from.

This is an issue in the following scenarios:

  • The Fragment is actually in a List and the view gets recycled: then it will change back to the original value at some point.
  • The Fragment and the Parent both use the same variables. Then they run out of sync with each other

This will most likely require a rewrite of a lot of the GraphQL Property Wrapper and the generated initializer code

Generated view initializers don't camelize default values for enums

My GraphQL API has an enum which is used as a default value in a field, and when my Grapheaello.swift is generated it does not camelize the enum (API.Lang.enUs) when building out the query renderer function parameters:

extension API {
  // lang should be:
  // lang: API.Lang? = API.Lang.enUs
  func contentView<Loading: View>(lang: API.Lang? = API.Lang.en_us,
                                  @ViewBuilder loading: () -> Loading) -> some View {
      return QueryRenderer(client: client,
                            query: ApolloAPIContentViewQuery(lang: lang),
                            loading: loading(),
                            error: { BasicErrorView(error: $0) }) { (data: ApolloAPI.ContentViewQuery.Data) -> ContentView in

          ContentView(data: data)
      }
  }
}

For my code to compile I have to modify the Swift to be:

extension API {
  func contentView<Loading: View>(lang: API.Lang? = API.Lang.enUs,
                                  @ViewBuilder loading: () -> Loading) -> some View {
      return QueryRenderer(client: client,
                            query: ApolloAPIContentViewQuery(lang: lang),
                            loading: loading(),
                            error: { BasicErrorView(error: $0) }) { (data: ApolloAPI.ContentViewQuery.Data) -> ContentView in

          ContentView(data: data)
      }
  }
}

Here is the enum that is generated from the GraphQL schema:

# Lang
enum Lang {
  # en-GB
  EN_GB

  # en-US
  EN_US
}
public enum ApolloAPI {
  /// Lang
  public enum Lang: RawRepresentable, Equatable, Hashable, CaseIterable, Apollo.JSONDecodable, Apollo.JSONEncodable {
    public typealias RawValue = String
    /// en-US
    case enUs
    /// en-GB
    case enGb
    /// Auto generated constant for unknown enum values
    case __unknown(RawValue)

    public init?(rawValue: RawValue) {
      switch rawValue {
        case "EN_US": self = .enUs
        case "EN_GB": self = .enGb
        default: self = .__unknown(rawValue)
      }
    }

    public var rawValue: RawValue {
      switch self {
        case .enUs: return "EN_US"
        case .enGb: return "EN_GB"
        case .__unknown(let value): return value
      }
    }

    public static func == (lhs: Lang, rhs: Lang) -> Bool {
      switch (lhs, rhs) {
        case (.enUs, .enUs): return true
        case (.enGb, .enGb): return true
        case (.__unknown(let lhsValue), .__unknown(let rhsValue)): return lhsValue == rhsValue
        default: return false
      }
    }

    public static var allCases: [Lang] {
      return [
        .enUs,
        .enGb,
      ]
    }
  }
}

I did some debugging and found that the expression passed to QueryRendererArgument isn't camelizing the enum properly when rendering the Struct.swift.stencil:

Graphaello.QueryRendererArgument(name: "lang", type: "API.Lang?", expression: Optional(SwiftSyntax.ExprSyntax))

I'm not exactly sure how to change what is returned from SwiftSyntax.ExprSyntax otherwise I'd submit a PR. Any help is appreciated!

graphaello add throws Fatal error

Can anybody explain to me why my API generation throws an error? I am using graphcms.com.

Screen Shot 2021-08-10 at 8 35 39 pm

graphaello add --apiName GAPI https://api-eu-central-1.graphcms.com/v2/ckqxy4gw1m2gb01z6gj25c7nf/master

It does generate the json, but nothing in Graphaello.swift.

Thanks,
Michal

M1 Mac "graphaello is not installed on your machine"

getting M1 Mac "graphaello is not installed on your machine" even if it is

$ type "graphaello"
graphaello is /opt/homebrew/bin/graphaello

adding PATH=/opt/homebrew/bin:$PATH to run phase works so I assume it's bash/zsh difference - clean install of Mac I'm sure more people will hit this

Fix warnings from json files in target

while working on creating a sample application, using the apollographl tutorial endpoint, https://apollo-fullstack-tutorial.herokuapp.com/

I am getting errors and warnings.

Showing Recent Issues
no rule to process file '/Users/stunjiturner/Downloads/CodeReviewRepos/OutOfThisWorld-Graphaello/ApolloFullstackTutorialHerokuappCom.graphql.json' of type 'text.json' for architecture 'x86_64'

Showing Recent Issues
/Users/stunjiturner/Downloads/CodeReviewRepos/OutOfThisWorld-Graphaello/Graphaello.swift:564:23: 'ApolloFullstackTutorialHerokuappCom' is ambiguous for type lookup in this context

Graphaello Buildtime error: Keyword 'repeat' cannot be used as an identifier

The API I'm using has "repeat" as a field, which is a swift reserved keyword.

Graphaello.swift:

...
case repeat = "REPEAT"   // ".../AL/Graphaello.swift:4222:18: Keyword 'repeat' cannot be used as an identifier here"
...

Theres will always be a swift compiler error on Buildtime using that API.

A fix would be to use backticks to escape the keyword.

Handle Updating Apollo

Since our generated code accesses Apollo API it is important that before we generate the code, we make sure that the correct version of Apollo is installed.

We could either:

  1. Automatically update the version of the dependency to Apollo if out of date
  2. or warn the user that they should update Apollo

I think that 1 would lead to less frustration, but 2 is more "correct" I guess?

RegexTokenGenerator(word: "false") wrong value

Shouldn't the value for false be this way?

RegexTokenGenerator(word: "false").map(to: .value(.bool(true))),

should be:

RegexTokenGenerator(word: "false").map(to: .value(.bool(false))),

???

Input Objects are not Properly translated to Apollo Types

Apollo can only run the query with their types.
Despite the fact that our types are identical to theirs, we still need to convert.

Problematic areas:

We can't just run .init() as in the case with enums right now, because the Apollo type is only generated when it's being used by a query. So we can't always ship the init code

Possible Ideas:

  • Only generate the init code when the type is being used by a query
  • Maybe unsafeBitCast would do the job ¯\_(ツ)_/¯

Relevant areas:

Documentation of Client API

The Generated Client API is remains largely undocumented...

It would also be awesome if we could include the documentation of the API in the Generated DSL

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.