Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved Organisation of APIs #7

Open
KaiTheRedNinja opened this issue Jun 25, 2023 · 2 comments
Open

Improved Organisation of APIs #7

KaiTheRedNinja opened this issue Jun 25, 2023 · 2 comments
Labels
currently not possible Not possible because of Swift / macro limitations enhancement New feature or request

Comments

@KaiTheRedNinja
Copy link

Issue:

When it comes to larger APIs, for example, the Google Classroom API, which I work with very often at glassroom, using Papyrus would result in a lot of messy top-level APIs.

How glassroom currently solves this is to declare all the protocols as enums, and all the functionality goes into extensions of those enums as static functions. This means that the definitions look like this:

// Definitions of all the APIs. They're implemented in other files.
public enum GlassRoomAPI {
    public enum GRCourses: GlassRoomAPIProtocol {
        public enum GRAliases: GlassRoomAPIProtocol {}
        public enum GRAnnouncements: GlassRoomAPIProtocol {}
        public enum GRCourseWork: GlassRoomAPIProtocol {
            public enum GRStudentSubmissions: GlassRoomAPIProtocol {}
        }
/* etc */

and calling functions looks more like this:

GlassRoomAPI.GRCourses.GRCourseWork.list(/* parameters here */) { result in
	/*completion here*/ 
}

However, since Papyrus uses protocols for the definitions and the resultant APIs are autogenerated, such organisation cannot be achieved.

@API
protocol GRCourses {
	/*methods here*/

	@API
	protocol GRAliases { // does not compile, as you cannot define other objects within a protocol
		/*methods here*/
    }
}

Suggested solution:

My suggestion is to have an option in @API and @Mock to extend a struct with the new functionality, instead of generating a whole new struct. This would allow for organisation by defining all the structs in a neat manner.

// empty implementations
struct GRCourses {
	struct GRAliases {}
}

@API(in: GRCourses.self)
protocol GRCoursesProtocol {
	/* methods here */
}

@API(in: GRAliases.self)
protocol GRAliasesProtocol {
	/* methods here */
}

/* 
// autogenerated:

extension GRCourses {
	/* implementations here */
}

extension GRAliases {
	/* implementations here */
}
*/

And you would call them this way:

let result = try await GRCourses.GRAliases().listAliases(/* parameters */)
@joshuawright11
Copy link
Owner

Thanks for the feedback @KaiTheRedNinja!

Great idea! I 100% agree it would be great to be able to better organize large API groups. Unfortunately macros are relatively nascent and so there are a few limitations:

  1. As you mentioned, nested protocols aren't allowed - there is some recent work being done here though.
  2. At the moment, macros aren't allowed to generate extensions. There's some movement towards removing this limitation but until then anything involving extensions on existing types isn't possible. Hopefully that will land soon, but before then I believe generating a new top-level type is the only viable solution.

Ideally, I think it would be great to completely hide the implementor of the protocol. This way there wouldn't be a bunch of concrete API types floating around and calling the API would look something like this:

@API
protocol GRAliases { ... }

let aliases: GRAliases = provider.create(GRAliases.self)
let result = try await aliases.listAliases(...)

Macros generating extensions would also unlock the in parameter as you recommended to further allow organization via nesting inside enums, structs, etc. Once it's available I think that would be a great addition.

@joshuawright11 joshuawright11 added enhancement New feature or request currently not possible Not possible because of Swift / macro limitations labels Dec 21, 2023
@joshuawright11
Copy link
Owner

joshuawright11 commented Jun 2, 2024

With SE-404 organizing APIs inside a type is possible now...

public enum APIs {
    @API
    public protocol Foo {
        @GET("/bar")
        func bar() async throws
    }
}

let foo: APIs.Foo = APIs.FooAPI(provider: provider)

Unfortunately there's still no way for macros to extend types that they aren't annotating - but I think with this answer you should be able to get more organized outputs as long as there's a consistent suffix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
currently not possible Not possible because of Swift / macro limitations enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants