Git Product home page Git Product logo

netreconlab / parse-server-swift Goto Github PK

View Code? Open in Web Editor NEW
11.0 2.0 2.0 18.69 MB

Write Server-Side Parse Cloud Code in Swift!

Home Page: https://swiftpackageindex.com/netreconlab/parse-server-swift/documentation

License: Apache License 2.0

Dockerfile 1.55% Swift 67.48% Shell 4.59% JavaScript 26.38%
parse parse-server swift vapor hacktoberfest cloud-code server-side-swift trie-node macos swift-linux

parse-server-swift's Introduction

ParseServerSwift

Documentation Tutorial Build Status CI release codecov License


Write Parse Cloud Code in Swift!

What is Cloud Code? For complex apps, sometimes you just need logic that isn’t running on a mobile device. Cloud Code makes this possible. Cloud Code in ParseServerSwift is easy to use because it’s built using Parse-SwiftOG and Vapor. ParseServerSwift provides many additional benefits over the traditional JS based Cloud Code that runs on the Node.js parse-server:

Technically, complete apps can be written with ParseServerSwift, the only difference is that this code runs in your ParseServerSwift rather than running on the user’s mobile device. When you update your Cloud Code, it becomes available to all mobile environments instantly. You don’t have to wait for a new release of your application. This lets you change app behavior on the fly and add new features faster.

Creating Your Cloud Code App with ParseServerSwift

Setup a Vapor project by following the directions for installing and setting up your project on macOS or linux.

Then add ParseServerSwift to dependencies in your Package.swift file:

// swift-tools-version:5.6
import PackageDescription

let package = Package(
    name: "YOUR_PROJECT_NAME",
    platforms: [
        .iOS(.v13),
        .macCatalyst(.v13),
        .macOS(.v10_15),
        .tvOS(.v13),
        .watchOS(.v6)
    ],
    products: [
            .library(name: "YOUR_PROJECT_NAME", targets: ["YOUR_PROJECT_NAME"])
    ],
    dependencies: [
        .package(url: "https://github.com/netreconlab/ParseServerSwift", .upToNextMajor(from: "0.8.4")),
        .package(url: "https://github.com/vapor/vapor.git", .upToNextMajor(from: "4.76.2")),
        .package(url: "https://github.com/netreconlab/Parse-Swift.git", .upToNextMajor(from: "5.7.0"))
    ]
    ...
    targets: [
        .target(
            name: "YOUR_PROJECT_NAME",
            dependencies: [
                .product(name: "Vapor", package: "vapor"),
                .product(name: "ParseSwift", package: "Parse-Swift"),
                .product(name: "ParseServerSwift", package: "ParseServerSwift"),
            ]
        ),
        .executableTarget(name: "App",
                          dependencies: [.target(name: "YOUR_PROJECT_NAME")],
                          swiftSettings: [
                              // Enable better optimizations when building in Release configuration. Despite the use of
                              // the `.unsafeFlags` construct required by SwiftPM, this flag is recommended for Release
                              // builds. See <https://github.com/swift-server/guides/blob/main/docs/building.md#building-for-production> for details.
                              .unsafeFlags(["-cross-module-optimization"], .when(configuration: .release))
                          ]),
        .testTarget(name: "YOUR_PROJECT_NAMETests", dependencies: [
            .target(name: "YOUR_PROJECT_NAME"),
            .product(name: "XCTVapor", package: "vapor"),
        ])
    ]
)

Adding ParseServerSwift will allow you to quickly add routes for Parse Cloud Hook Functions and Triggers.

Configure ParseServerSwift to Connect to Your Parse Servers

Environment Variables

The following enviroment variables are available and can be configured directly or through .env, .env.production, etc. See the Vapor Docs for more details.

PARSE_SERVER_SWIFT_HOST_NAME: cloud-code # The name of your host. If you are running in Docker it should be same name as the docker service
PARSE_SERVER_SWIFT_PORT: # This is the default port on the docker image
PARSE_SERVER_SWIFT_DEFAULT_MAX_BODY_SIZE: 500kb # Set the default size for bodies that are collected into memory before calling your handlers (See Vapor docs for more details)
PARSE_SERVER_SWIFT_URLS: http://parse:1337/parse # (Required) Specify one of your Parse Servers to connect to. Can connect to multiple by seperating URLs with commas
PARSE_SERVER_SWIFT_APPLICATION_ID: appId # (Required) The application id of your Parse Server
PARSE_SERVER_SWIFT_PRIMARY_KEY: primaryKey # (Required) The master key of your Parse Server 
PARSE_SERVER_SWIFT_WEBHOOK_KEY: webookKey # The webhookKey of your Parse Server

WebhookKey

The webhookKey should match the webhookKey on the Parse Server.

Parse-SwiftOG SDK

The aforementioned environment variables automatically configure Parse-SwiftOG SDK. If you need a more custom configuration, see the documentation.

Initializing ParseSwiftServer

To levergage the aforementioned environment variables, you should modify configure.swift in your project to look similar to below:

public func configure(_ app: Application) throws {
    // Initialize ParseServerSwift
    let configuration = try ParseServerConfiguration(app: app)
    try ParseServerSwift.initialize(configuration, app: app)
    
    // Add any additional code to configure your server here...
    
    // register routes
    try routes(app)
}

If you want to pass the configuration parameters programitically, your configure method should look similar to below:

public func configure(_ app: Application) throws {
    // Initialize ParseServerSwift
    let configuration = try ParseServerConfiguration(app: app,
                                                     hostName: "hostName",
                                                     port: 8081,
                                                     applicationId: "applicationId",
                                                     primaryKey: "primaryKey",
                                                     webhookKey: hookKey,
                                                     parseServerURLString: "primaryKey")
    try ParseServerSwift.initialize(configuration, app: app)
    
    // Add any additional code to configure your server here...
    
    // register routes
    try routes(app)
}

Starting the Server

ParseServerSwift is optimized to run in Docker containers. A sample docker-compose.yml demonstrates how to quickly spin up one (1) ParseServerSwift server with one (1) parse-hipaa servers and (1) hipaa-postgres database.

In Docker

ParseSwift depends on FoundationNetworking when it is not built on Apple Platforms. Be sure to add the following lines to your Dockerfile release stage when building your own projects with ParseServerSwift.

  1. Fork this repo
  2. In your terminal, change directories into ParseServerSwift folder
  3. Type docker-compose up
  4. Accessing your containers:

image

On macOS

To start your server type, swift run in the terminal of the project root directory.

Writing Cloud Code

Sharing Server-Client Code

Apple's WWDC User Xcode for server-side development recommends creating Swift packages (15:26 mark) to house your models and share them between server and clients apps to reduce code duplication. To maximize Parse-Swift, it is recommended to not only add your models to your shared package, but to also add all of your queries (server and client). The reasons for this are:

  1. Parse-Swift queries on the client are cached by default; allowing Parse-Swift based apps to leverage cache to build zippier experiences
  2. When leveraging your shared queries in ParseServerSwift; they will never access local server cache as they always request the latest data from the Node.js Parse Server
  3. Calling Cloud-Code functions from clients do not ever access local cache as these are POST calls to the Node.js Parse Server

Learn more about sharing models by reading the SwiftLee Blog.

Creating ParseObject's

If you have not created a shared package for your models, it is recommended to add all of your ParseObject's in a folder called Models similar to ParseServerSwift/Sources/ParseServerSwift/Models.

The ParseUser Model

Be mindful that the ParseUser in ParseServerSwift should conform to ParseCloudUser. This is because the ParseCloudUser contains some additional properties on the server-side. On the client, you should always use ParseUser instead of ParseCloudUser. In addition, make sure to add all of the additional properties you have in your _User class to the User model. An example User model is below:

/**
 An example `ParseUser`. You will want to add custom
 properties to reflect the `ParseUser` on your Parse Server.
 */
struct User: ParseCloudUser {

    var authData: [String: [String: String]?]?
    var username: String?
    var email: String?
    var emailVerified: Bool?
    var password: String?
    var objectId: String?
    var createdAt: Date?
    var updatedAt: Date?
    var ACL: ParseACL?
    var originalData: Data?
    var sessionToken: String?
    var _failed_login_count: Int?
    var _account_lockout_expires_at: Date?
}

An example ParseObject Model

The GameScore model is below:

import Foundation
import ParseSwift

/**
 An example `ParseObject`. This is for testing. You can
 remove when creating your application.
 */
struct GameScore: ParseObject {
    // These are required by ParseObject.
    var objectId: String?
    var createdAt: Date?
    var updatedAt: Date?
    var ACL: ParseACL?
    var originalData: Data?

    // Your own properties.
    var points: Int?

    // Implement your own version of merge.
    func merge(with object: Self) throws -> Self {
        var updated = try mergeParse(with: object)
        if updated.shouldRestoreKey(\.points,
                                     original: object) {
            updated.points = object.points
        }
        return updated
    }
}

Creating New Cloud Code Routes

Adding routes for ParseHooks are as simple as adding routes in Vapor. ParseServerSwift provides some additional methods to routes to easily create and register Hook Functions and Hook Triggers. All routes should be added to the routes.swift file in your project. Example ParseServerSwift routes can be found in ParseServerSwift/Sources/ParseServerSwift/routes.swift.

Router Groups and Collections

Since ParseServerSwift is a Vapor server, it can be configured a number of different ways to suite your needs. Be sure to read through the vapor documentation. Some important features you may want to take advantage of are highlighed below:

  • Route groups allows you to create a set of routes with a path prefix or specific middleware
  • Route collections allow multiple routes and route groups to be organized in different files or modules

To learn more about creating groups and collections, checkout this blog.

Be sure to add import ParseSwift and import ParseServerSwift to the top of routes.swift

Sending Errors From Cloud Code Routes

There will be times you will need to respond by sending an error to the Node.js Parse Server to propagate to the client. Sending errors can be accomplished by sending a ParseHookResponse. Below are two examples of sending an error:

// Note: `T` is the type to be returned if there is no error thrown.

// Standard Parse error with your own unique message
let standardError = ParseError(code: .missingObjectId, message: "This object requires an objectId")
return ParseHookResponse<T>(error: standardError) // Be sure to "return" the ParseHookResponse in your route, DO NOT "throw" the error.

// Custom error with your own unique code and message
let customError = ParseError(otherCode: 1001, message: "My custom error")
return ParseHookResponse<T>(error: customError) // Be sure to "return" ParseHookResponse in your route, DO NOT "throw" the error.

Cloud Code Examples

Parse-Swift has a number of Swift Playgrounds to demonstrate how to use the SDK. Below are some notable Playgrounds specifically for Cloud Code that can be used directly in ParseServerSwift:

Cloud Code Functions

Cloud Code Functions can also take parameters. It's recommended to place all parameters in ParseServerSwift/Sources/ParseServerSwift/Models/Parameters

// A simple Parse Hook Function route that returns "Hello World".
app.post("hello",
         name: "hello") { req async throws -> ParseHookResponse<String> in
    // Note that `ParseHookResponse<String>` means a "successful"
    // response will return a "String" type.
    if let error: ParseHookResponse<String> = checkHeaders(req) {
        return error
    }
    var parseRequest = try req.content
        .decode(ParseHookFunctionRequest<User, FooParameters>.self)
    
    // If a User made the request, fetch the complete user.
    if parseRequest.user != nil {
        parseRequest = try await parseRequest.hydrateUser(request: req)
    }
    
    // To query using the User's credentials who called this function,
    // use the options() method from the parseRequest
    let options = try parseRequest.options(req)
    let scores = try await GameScore.query.findAll(options: options)
    req.logger.info("Scores this user can access: \(scores)")
    return ParseHookResponse(success: "Hello world!")
}

Cloud Code Triggers

// A Parse Hook Trigger route.
app.post("score", "save", "before",
         object: GameScore.self,
         trigger: .beforeSave) { req async throws -> ParseHookResponse<GameScore> in
    // Note that `ParseHookResponse<GameScore>` means a "successful"
    // response will return a "GameScore" type.
    if let error: ParseHookResponse<GameScore> = checkHeaders(req) {
        return error
    }
    var parseRequest = try req.content
        .decode(ParseHookTriggerObjectRequest<User, GameScore>.self)

    // If a User made the request, fetch the complete user.
    if parseRequest.user != nil {
        parseRequest = try await parseRequest.hydrateUser(request: req)
    }

    guard let object = parseRequest.object else {
        return ParseHookResponse(error: .init(code: .missingObjectId,
                                              message: "Object not sent in request."))
    }
    // To query using the primaryKey pass the `usePrimaryKey` option
    // to ther query.
    let scores = try await GameScore.query.findAll(options: [.usePrimaryKey])
    req.logger.info("Before save is being made. Showing all scores before saving new ones: \(scores)")
    return ParseHookResponse(success: object)
}

// Another Parse Hook Trigger route.
app.post("score", "find", "before",
         object: GameScore.self,
         trigger: .beforeFind) { req async throws -> ParseHookResponse<[GameScore]> in
    // Note that `ParseHookResponse<[GameScore]>` means a "successful"
    // response will return a "[GameScore]" type.
    if let error: ParseHookResponse<[GameScore]> = checkHeaders(req) {
        return error
    }
    let parseRequest = try req.content
        .decode(ParseHookTriggerObjectRequest<User, GameScore>.self)
    req.logger.info("A query is being made: \(parseRequest)")

    // Return two custom scores instead.
    let score1 = GameScore(objectId: "yolo",
                           createdAt: Date(),
                           points: 50)
    let score2 = GameScore(objectId: "nolo",
                           createdAt: Date(),
                           points: 60)
    req.logger.info("""
        Returning custom objects to the user from Cloud Code instead of querying:
        \(score1); \(score2)
    """)
    return ParseHookResponse(success: [score1, score2])
}

// Another Parse Hook Trigger route.
app.post("user", "login", "after",
         object: User.self,
         trigger: .afterLogin) { req async throws -> ParseHookResponse<Bool> in
    // Note that `ParseHookResponse<Bool>` means a "successful"
    // response will return a "Bool" type. Bool is the standard response with
    // a "true" response meaning everything is okay or continue.
    if let error: ParseHookResponse<Bool> = checkHeaders(req) {
        return error
    }
    let parseRequest = try req.content
        .decode(ParseHookTriggerObjectRequest<User, GameScore>.self)

    req.logger.info("A user has logged in: \(parseRequest)")
    return ParseHookResponse(success: true)
}

// A Parse Hook Trigger route for `ParseFile`.
app.on("file", "save", "before",
       trigger: .beforeSave) { req async throws -> ParseHookResponse<Bool> in
    // Note that `ParseHookResponse<Bool>` means a "successful"
    // response will return a "Bool" type. Bool is the standard response with
    // a "true" response meaning everything is okay or continue. Sending "false"
    // in this case will reject saving the file.
    if let error: ParseHookResponse<Bool> = checkHeaders(req) {
        return error
    }
    let parseRequest = try req.content
        .decode(ParseHookTriggerRequest<User>.self)

    req.logger.info("A ParseFile is being saved: \(parseRequest)")
    return ParseHookResponse(success: true)
}

// Another Parse Hook Trigger route for `ParseFile`.
app.post("file", "delete", "before",
         trigger: .beforeDelete) { req async throws -> ParseHookResponse<Bool> in
    // Note that `ParseHookResponse<Bool>` means a "successful"
    // response will return a "Bool" type. Bool is the standard response with
    // a "true" response meaning everything is okay or continue.
    if let error: ParseHookResponse<Bool> = checkHeaders(req) {
        return error
    }
    let parseRequest = try req.content
        .decode(ParseHookTriggerRequest<User>.self)

    req.logger.info("A ParseFile is being deleted: \(parseRequest)")
    return ParseHookResponse(success: true)
}

// A Parse Hook Trigger route for `ParseLiveQuery`.
app.post("connect", "before",
         trigger: .beforeConnect) { req async throws -> ParseHookResponse<Bool> in
    // Note that `ParseHookResponse<Bool>` means a "successful"
    // response will return a "Bool" type. Bool is the standard response with
    // a "true" response meaning everything is okay or continue.
    if let error: ParseHookResponse<Bool> = checkHeaders(req) {
        return error
    }
    let parseRequest = try req.content
        .decode(ParseHookTriggerRequest<User>.self)

    req.logger.info("A LiveQuery connection is being made: \(parseRequest)")
    return ParseHookResponse(success: true)
}

// Another Parse Hook Trigger route for `ParseLiveQuery`.
app.post("score", "subscribe", "before",
         object: GameScore.self,
         trigger: .beforeSubscribe) { req async throws -> ParseHookResponse<Bool> in
    // Note that `ParseHookResponse<Bool>` means a "successful"
    // response will return a "Bool" type. Bool is the standard response with
    // a "true" response meaning everything is okay or continue.
    if let error: ParseHookResponse<Bool> = checkHeaders(req) {
        return error
    }
    let parseRequest = try req.content
        .decode(ParseHookTriggerObjectRequest<User, GameScore>.self)

    req.logger.info("A LiveQuery subscription is being made: \(parseRequest)")
    return ParseHookResponse(success: true)
}

// Another Parse Hook Trigger route for `ParseLiveQuery`.
app.post("score", "event", "after",
         object: GameScore.self,
         trigger: .afterEvent) { req async throws -> ParseHookResponse<Bool> in
    // Note that `ParseHookResponse<Bool>` means a "successful"
    // response will return a "Bool" type. Bool is the standard response with
    // a "true" response meaning everything is okay or continue.
    if let error: ParseHookResponse<Bool> = checkHeaders(req) {
        return error
    }
    let parseRequest = try req.content
        .decode(ParseHookTriggerObjectRequest<User, GameScore>.self)

    req.logger.info("A LiveQuery event occured: \(parseRequest)")
    return ParseHookResponse(success: true)
}

parse-server-swift's People

Contributors

cbaker6 avatar dependabot[bot] avatar jaysonng avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

jaysonng

parse-server-swift's Issues

Routes Grouping with .post HookTrigger methods doesn't take into account group prefix

When grouping methods together with

func post<Response, V>(_ path: PathComponent...,
object: V.Type,
trigger: ParseHookTriggerType,
use closure: @escaping (Request) async throws -> Response) -> Route
where Response: AsyncResponseEncodable, V: ParseObject {
self.on(path,
object: object,
trigger: trigger,
use: closure)
}

the prefix of the group, ie "user"

    func boot(routes: RoutesBuilder) throws {
        
        let usersGroup = routes.grouped("user")

        usersGroup.post("login", "after",
                        object: User.self,
                        trigger: .afterLogin,
                        use: afterLogin
        )
    
    }

Screenshot 2024-01-10 at 10 31 15 AM

which produces a ParseError when called

  Error: {"message":{"status":404,"headers":{"content-length":"35","content-type":"application/json; charset=utf-8","connection":"keep-alive","date":"Wed, 10 Jan 2024 02:20:30 GMT"},"buffer":{"type":"Buffer","data":[123,34,114,101,97,115,111,110,34,58,34,78,111,116,32,70,111,117,110,100,34,44,34,101,114,114,111,114,34,58,116,114,117,101,125]},"text":"{\"reason\":\"Not Found\",\"error\":true}","data":{"reason":"Not Found","error":true}},"code":141} {"className":"_User","error":{"code":141,"message":{"buffer":{"data":[123,34,114,101,97,115,111,110,34,58,34,78,111,116,32,70,111,117,110,100,34,44,34,101,114,114,111,114,34,58,116,114,117,101,125],"type":"Buffer"},"data":{"error":true,"reason":"Not Found"},"headers":{"connection":"keep-alive","content-length":"35","content-type":"application/json; charset=utf-8","date":"Wed, 10 Jan 2024 02:20:30 GMT"},"status":404,"text":"{\"reason\":\"Not Found\",\"error\":true}"}},"triggerType":"afterLogin","user":"WnjQEA6FsE"}
/Users/jayson/Projects/heroku/node_modules/parse-server/lib/ParseServer.js:261
          throw err;
          ^

ParseError: [object Object]
    at resolveError (/Users/jayson/Projects/heroku/node_modules/parse-server/lib/triggers.js:657:17)
    at error (/Users/jayson/Projects/heroku/node_modules/parse-server/lib/triggers.js:431:17)
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  code: 141
}

the afterLogin method is the same from the routes.swift but modified to work with Collections

func afterLogin(req: Request) async throws -> ParseHookResponse<Bool> {

        // Note that `ParseHookResponse<Bool>` means a "successful"
        // response will return a "Bool" type. Bool is the standard response with
        // a "true" response meaning everything is okay or continue.
        if let error: ParseHookResponse<Bool> = checkHeaders(req) {
            return error
        }
        let parseRequest = try req.content
            .decode(ParseHookTriggerObjectRequest<User, User>.self)
        
        req.logger.info("A user has logged in: \(parseRequest)")
        
        return ParseHookResponse(success: true)
    }

I tried pinpointing where it all goes wrong in ParseHookTrigger%2BVapor.swift but couldn't.

to make it work, I had to make the code as such:

    func boot(routes: RoutesBuilder) throws {
        
        //let usersGroup = routes.grouped("user")

        routes.post("user", "login", "after",
                        object: User.self,
                        trigger: .afterLogin,
                        use: afterLogin
        )
                
    }
    

thanks,

Forced port 8081 even when not wanted for Webhooks

Today I'm trying to get my Parse-Server-Swift setup on Heroku.

Regarding webhooks where on restart of the server, we delete and recreate the webhooks.

However, right now, the Port is always added. By default, if there's no port in the Environments, port 8081 is setup by default and added to the webhook URL.

Is there a way we can have 8081 not show up? As of now, after restart, my webooks revert back to

http://sampledomain.com:8081/xxx/get

when I just need

http://sampledomain.com/xxx/get

thanks,

When using beforeSave on object with a Pointer column - "error: ParseError code=111 error=schema mismatch for Test.test1; expected Pointer<Test1> but got Object"

When using beforeSave trigger on an object that has a pointer to another object like so:

struct Test: ParseObject {

    //: These are required for `ParseObject`.
    var originalData: Data?
    var objectId: String?
    var createdAt: Date?
    var updatedAt: Date?
    var ACL: ParseACL?

    var name: String?
    var test1: Test1?
}

struct Test1: ParseObject {

    //: These are required for `ParseObject`.
    var originalData: Data?
    var objectId: String?
    var createdAt: Date?
    var updatedAt: Date?
    var ACL: ParseACL?

    var description: String?
    var test2: Test2?
    var test3: Test3?
}

Doing nothing but just having the beforeSave trigger on produces a schema mismatch error where it expects a Pointer to the sub-object.

func beforeSaveTest(req: Request) async throws -> HookResponse<Test> {

        if let error: HookResponse<Test> = checkHeaders(req) { return error }
        
        var parseRequest = try req.content.decode(HookTrigObjReq<User, Test>.self)
        let options      = try parseRequest.options(req)
        
        if parseRequest.user != nil {
            parseRequest = try await parseRequest.hydrateUser(options: options, request: req)
        }
        
        guard let object = parseRequest.object else {
            return ParseHookResponse(error: .init(code: .missingObjectId,
                                                  message: "Object not sent in request."))
        }
        
        return HookResponse(success: object)
        
    }

I encountered this as I was trying to save a complex object with multiple pointers and tested with these Test objects getting the same result.

error: ParseError code=111 error=schema mismatch for Test.test1; expected Pointer<Test1> but got Object

Container restarts on Linux with swift:5.8-jammy

There's an issue when running ParseServerSwift on Linux using swift:5.8-jammy. See information in #27; error:

2023-05-28 11:40:28 Received signal 11. Backtrace:
2023-05-28 11:40:28 0x55e176770802, Backtrace.(printBacktrace in _B82A8C0ED7C904841114FDF244F9E58E)(signal: Swift.Int32) -> () at /build/.build/checkouts/swift-backtrace/Sources/Backtrace/Backtrace.swift:66
2023-05-28 11:40:28 0x55e176770a77, closure #1 (Swift.Int32) -> () in static Backtrace.Backtrace.install(signals: Swift.Array<Swift.Int32>) -> () at /build/.build/checkouts/swift-backtrace/Sources/Backtrace/Backtrace.swift:80
2023-05-28 11:40:28 0x55e176770a77, @objc closure #1 (Swift.Int32) -> () in static Backtrace.Backtrace.install(signals: Swift.Array<Swift.Int32>) -> () at /build/<compiler-generated>:79
2023-05-28 11:40:28 0x7f984567c51f
2023-05-28 11:40:28 0x55e177706312
2023-05-28 11:40:28 0x55e17772cd7d
2023-05-28 11:40:28 0x55e17772d45b
2023-05-28 11:40:28 0x55e17712e043
2023-05-28 11:40:28 0x55e17712ddf1
2023-05-28 11:40:28 0x55e1771398d1
2023-05-28 11:40:28 0x7f98456ceb42
2023-05-28 11:40:28 0x7f984575fbb3
2023-05-28 11:40:28 0xffffffffffffffff

The workaround is to use swift:5.7-jammy for now until the issue is resolved. If you followed the directions in https://github.com/netreconlab/parse-server-swift#creating-your-cloud-code-app-with-parseserverswift to setup a project that uses ParseServerSwift, be sure to update your Dockerfile to the following:

FROM swift:5.7-jammy as build

Everything in routes.swift?

Discussed in #38

Originally posted by jaysonng June 21, 2023
Hi,

Currently all cloudcode needs to be in routes.swift?

There's no way to separate code into their own files for maintainability?

thanks,
Jayson

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.