Is is possible to use Vapor 3 Postgres Fluent in a standalone script?
You will need to create an event loop group to be able to make database requests. SwiftNIO's MultiThreadedEventLoopGroup
is good for this:
let worker = MultiThreadedEventLoopGroup(numberOfThreads: 2)
You can change the number of threads used as you need.
Now you can create a connection to the database with that worker:
let conn = try database.newConnection(on: worker)
The connection is in a future, so you can map
it and pass the connection in your query:
conn.flatMap { connection in
return Product.query(on: connection)...
}
Make sure you shutdown your worker when you are done with it using shutdownGracefully(queue:_:)
Vapor 3 PostgreSQL CRUD without requests http
Based on this question and answers (Is is possible to use Vapor 3 Postgres Fluent in a standalone script?) I realized CRUD like this:
import Vapor
import FluentPostgreSQL
final class Device: PostgreSQLModel {
var id: Int?
var isWorking: Bool
var serial: Int
init(isWorking: Bool, serial: Int) {
self.isWorking = isWorking
self.serial = serial
}
}
extension Device: Content {}
extension Device: Migration {}
extension Device: Parameter {}
final class WorkWithPostgres {
let databaseConfig = PostgreSQLDatabaseConfig(hostname: "localhost", port: 5432, username: "username", database: "testestest", password: nil)
let database: PostgreSQLDatabase
static let shared = WorkWithPostgres()
private init() {
database = PostgreSQLDatabase(config: databaseConfig)
}
func readAll<T: PostgreSQLModel>(postgreSQLModel: T.Type, completion: (([T]) -> Void)?) {
let worker = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let conn = database.newConnection(on: worker)
let _ = conn.map { connection in
postgreSQLModel.query(on: connection).all().map { databaseData in
worker.shutdownGracefully { _ in
}
completion?(databaseData)
}
}
}
func create<T: PostgreSQLModel>(postgreSQLModel: T) {
let worker = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let conn = database.newConnection(on: worker)
let _ = conn.map { connection in
let _ = postgreSQLModel.save(on: connection).whenComplete {
worker.shutdownGracefully { _ in
}
}
}
}
}
final class DeviceController {
func readAll(completion: (([Device]) -> Void)?) {
WorkWithPostgres.shared.readAll(postgreSQLModel: Device.self) { devices in
completion?(devices)
}
}
func create(isWorking: Bool, serial: Int) {
let device = Device(isWorking: isWorking, serial: serial)
WorkWithPostgres.shared.create(postgreSQLModel: device)
}
}
It is working, but I am not sure is it good way to do this. Does somebody know?
Vapor 4 PostgreSQL CRUD without http requests
So, correct idea not to work with database like I did with Vapor 3. Now, in Vapor 4, you should use Database
instance. Where to get it? I can suggest 2 options for you. First is boot(_ app: Application)
in boot.swift
or in configure(_ app: Application)
in configure.swift
. So app
will have options to work with database like this: <Model>.query(on: app.db)
. Second option is req
in any request. req
should be of type Request
, like this: <Model>.query(on: req.db)
. By Model
, you can take any class inherited of class Model
, for example: final class User: Model, Content {
You should organise your code to be called from boot.swift
or configure.swift
or from any request, for example, in routes.swift
Where do I get the database object using Vapor?
I'm not sure how I missed it before, but it is accessible with
app.db
How to get remote ip through Vapor
You should be able to get that from the request object.
print(req.peerAddress)
How to get client IP address in Vapor 3.0?
Thanks to the Vapor community, I have got an answer.
You can use req.http.remotePeer.hostname
for Vapor 3.0 project.
How to upload files for a model using vapor 3 for an iOS app
Server-side
Model
final class AppObject: Codable {
var id: Int?
var ipaFile: String // relative path to file in Public dir
var plistFile: String // relative path to file in Public dir
var imageFile: String // relative path to file in Public dir
var notes: String
var name: String
init(ipaFile: String, plistFile: String, imageFile: String, notes: String, name: String) {
self.ipaFile = ipaFile
self.plistFile = plistFile
self.imageFile = imageFile
self.notes = notes
self.name = name
}
}
extension AppObject: MySQLModel {}
extension AppObject: Content {}
extension AppObject: Migration {}
extension AppObject: Parameter {}
Controller
struct AppObjectsController: RouteCollection {
func boot(router: Router) throws {
let appObjectsRoute = router.grouped("api", "apps")
appObjectsRoute.get(use: getAllHandler)
appObjectsRoute.post(PostData.self, use: createHandler)
}
func getAllHandler(_ req: Request) throws -> Future<[AppObject]> {
return AppObject.query(on: req).all()
}
}
extension AppObjectsController {
struct PostData: Content {
let ipaFile, plistFile, imageFile: File
let name, notes: String
}
func createHandler(_ req: Request, payload: PostData) throws -> Future<AppObject> {
let ipaFile = ServerFile(ext: "ipa", folder: .ipa)
let plistFile = ServerFile(ext: "plist", folder: .plist)
let imageFile = ServerFile(ext: "jpg", folder: .image)
let appObject = AppObject(ipaFile: ipaFile.relativePath, plistFile: plistFile.relativePath, imageFile: imageFile.relativePath, notes: payload.notes, name: payload.name)
/// we have to wrap it in transaction
/// to rollback object creating
/// in case if file saving fails
return req.transaction(on: .mysql) { conn in
return appObject.create(on: conn).map { appObject in
try ipaFile.save(with: payload.ipaFile.data)
try plistFile.save(with: payload.plistFile.data)
try imageFile.save(with: payload.imageFile.data)
}
}
}
}
ServerFile struct
struct ServerFile {
enum Folder: String {
case ipa = "ipa"
case plist = "plists"
case image = "images"
case root = ""
}
let file, ext: String
let folder: Folder
init (file: String? = UUID().uuidString, ext: String, folder: Folder? = .root) {
self.file = file
self.ext = ext
self.folder = folder
}
var relativePath: String {
guard folder != .root else { return fileWithExt }
return folder.rawValue + "/" + fileWithExt
}
var fileWithExt: String { return file + "." + ext }
func save(with data: Data) throws {
/// Get path to project's dir
let workDir = DirectoryConfig.detect().workDir
/// Build path to Public folder
let publicDir = workDir.appending("Public")
/// Build path to file folder
let fileFolder = publicDir + "/" + folder.rawValue
/// Create file folder if needed
var isDir : ObjCBool = true
if !FileManager.default.fileExists(atPath: fileFolder, isDirectory: &isDir) {
try FileManager.default.createDirectory(atPath: fileFolder, withIntermediateDirectories: true)
}
let filePath = publicDir + "/" + relativePath
/// Save data into file
try data.write(to: URL(fileURLWithPath: filePath))
}
}
iOS
Declare AppObject
model
struct AppObject: Codable {
var id: Int
var ipaFile, plistFile, imageFile: String
var name, notes: String
}
With CodyFire library multipart requests are really easy
Declare you endpoint
import CodyFire
struct AppController: EndpointController {
static var server: ServerURL? = nil
static var endpoint: String = "apps"
}
/// Usually separate file like App+Create.swift
extension AppController {
struct CreateAppRequest: MultipartPayload {
var ipaFile, plistFile, imageFile: Attachment
var name, note: String
public init (ipaFile: Attachment, plistFile: Attachment, imageFile: Attachment, name: String, note: String) {
self.ipaFile = ipaFile
self.plistFile = plistFile
self.imageFile = imageFile
self.name = name
self.note = note
}
}
static func create(_ payload: CreateAppRequest) -> APIRequest<AppObject> {
return request(payload: payload).method(.post)
}
}
Then in some view controller try to create an app on the server
/// Replace _ with file data
let ipaFile = Attachment(data: _, fileName: "", mimeType: "ipa")
let plistFile = Attachment(data: _, fileName: "", mimeType: "plist")
let imageFile = Attachment(data: _, fileName: "", mimeType: .jpg)
let payload = AppController.CreateAppRequest(ipaFile: ipaFile,
plistFile: plistFile,
imageFile: imageFile,
name: "something",
note: "something")
AppController.create(payload).onRequestStarted {
/// it calls only if request started properly
/// start showing loading bar
}.onError { error in
let alert = UIAlertController(title: nil, message: error.description, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .cancel))
self.present(alert, animated: true)
}.onProgress { progress in
/// show progress
}.onSuccess { appObject in
/// show success
/// here you received just created `appObject`
}
And that's it, it just works :)
Next example for getting list of AppObject
s
/// Separate file like App+List.swift
extension AppController {
static func list() -> APIRequest<[AppObject]> {
return request()
}
}
then somewhere in view controller
AppController.list().onSuccess { appObjects in
/// `appObjects` is `[AppObject]`
}
Hope it helps.
Argument labels '(content:)' do not match any available overloads, swift ,vapor,rest in vapor ,using postgresql
It looks like you are trying to call this init?
init(node:Node ,content: String?, displayName:String, name:String)
If that is the case, you need to pass in the rest of the arguments because there are no default values available for them:
convenience init(json: JSON) throws {
try self.init(
node: <VALUE>,
content: json.get(dataPointStorage.contentKey),
displayName: <VALUE>,
name: <VALULE>
)
}
That will fix your problem.
Related Topics
How to Handle Menu Button Action in Tvos Remote
How to Await X Seconds with Async Await Swift 5.5
How to Pass Binding to Child View in the New Navigationstack.Navigationdestination
Swift Realm: After Writing Transaction Reference Set to Nil
Instance Member Cannot Be Used on Type | Closures
Swift: Cast Any Object to Int64 = Nil
Uiimagepickercontroller Navigation Bar Tint Color Not Working with iOS 13
Changing the Color of the Icons in a Uitextfield Inside a Uisearchbar
Swift: Can Not Use Array Filter in If Let Statement Condition
Why Does My @Lazy Property Crash, But If I Make It Non Lazy It Works
Code Highlighting in Latex for Swift
Loading Views into Nscontainerview with Swift
Swift 4: Validating Credit Card Expiration Date
Swift iOS 9: Section Header Change Position After Reload Data