As a continuation from this question I'm not receiving any text message after following this guide. I am also not getting any errors.
This is my Package.swift file:
import PackageDescription
let package = Package(
name: "MyProject",
dependencies: [
.package(url: "https://github.com/Alamofire/Alamofire.git", from: "4.0.0")
],
targets: [
.target(
name: "MyProject",
dependencies: ["Alamofire"]),
.testTarget(
name: "MyProjectTests",
dependencies: ["Alamofire"]),
]
)
This is my main.swift´file:
import Foundation
import Alamofire
if let accountSID = ProcessInfo.processInfo.environment["MY_PERSONAL_SID"],
let authToken = ProcessInfo.processInfo.environment["MY_PERSONAL_AUTHTOKEN"] {
let url = "https://api.twilio.com/2010-04-01/Accounts/\(accountSID)/Messages"
let parameters = ["From": "MY_TWILIONUMBER", "To": "MY_PERSONAL_NUMBER", "Body": "Hello from Swift!"]
Alamofire.request(url, method: .post, parameters: parameters)
.authenticate(user: accountSID, password: authToken)
.responseJSON { response in
debugPrint(response)
}
RunLoop.main.run()
}
When I run swift build && ./.build/debug/MyProject it seemingly works fine, but I'm not receiving any message.
I´m not sure what I have done wrong, but I have some questions:
These two commands:
export TWILIO_ACCOUNT_SID='YOUR_ACCOUNT_SID'
export TWILIO_AUTH_TOKEN='YOUR_AUTH_TOKEN'
Can I run them in terminal from where ever? Or should they be ran in a specific folder? (I changed the SID and TOKEN to my personal ones when I ran the commands)
The Package.swift should be located in the base folder of my Xcode project?
The main.swiftshould be located in .../MyProject/Sources/MyProject/main.swift?
The command swift build && ./.build/debug/MyProject
In the guide under the explanation of ´main.swift´ it says:
Run it with this command, and you should receive a text message!
Do they mean something special with run it with this command? Or can I just run the command from terminal from where ever?
Twilio developer evangelist here.
When you run
export TWILIO_ACCOUNT_SID='YOUR_ACCOUNT_SID'
export TWILIO_AUTH_TOKEN='YOUR_AUTH_TOKEN'
you need to do so in the terminal in the same window that you eventually run your application from. You can read more about setting environment variables in this blog post.
In your main.swift file you have the line:
if let accountSID = ProcessInfo.processInfo.environment["MY_PERSONAL_SID"],
let authToken = ProcessInfo.processInfo.environment["MY_PERSONAL_AUTHTOKEN"] {
I believe that your program is running but not sending anything because you are trying to assign variables from the environment called MY_PERSONAL_SID and MY_PERSONAL_AUTHTOKEN. If you are exporting environment variables as you describe, then this line should be:
if let accountSID = ProcessInfo.processInfo.environment["TWILIO_ACCOUNT_SID"],
let authToken = ProcessInfo.processInfo.environment["TWILIO_AUTH_TOKEN"] {
Let me know if that helps at all.
Related
I will build an CLI Tool in Swift. I have created the project with this command swift package init --type executable
When I build my project and parse the read-aliases parameter in Xcode and click the "Play" Button everything worked fine.
I defined the read-aliases parameter
Then I receive the right output it is the following, the aliases from my .zsh file
These are your Aliases
zshconfig="mate ~/.zshrc"
ohmyzsh="mate ~/.oh-my-zsh"
python="/usr/local/bin/python3.7"
python2="/usr/bin/python2"
Program ended with exit code: 0
But when I run the following command swift run inside my project in the command line
Then I receive
Which seems to work so far these are the messages from my tool.
An when I parse the same parameter like this
$ swift run read-aliases
I get this error
error: no executable product named 'read-aliases'
Here is my code
import Foundation
import ArgumentParser
struct Alias: ParsableCommand {
static let configuration = CommandConfiguration(
abstract: "Make Editing Your .zshrc Much Easier",
subcommands: [readAliases.self])
}
extension Alias {
struct readAliases: ParsableCommand {
static let configuration = CommandConfiguration(
abstract: "Reads All The Aliases In Your .zshrc File")
func run() {
print("These are your Aliases");
readFile(path: "/Users/alexanderhess/.zshrc")
}
func readFile(path: String) -> Int {
errno = 0
if freopen(path, "r", stdin) == nil {
perror(path)
return 1
}
while let line = readLine() {
if(line.starts(with: "# alias")){
print(line.dropFirst(8));
}
}
return 0
}
}
}
Alias.main();
My Package.swift
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "easy-aliaser",
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/apple/swift-argument-parser", from: "0.2.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "easy-aliaser",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser")
]),
.testTarget(
name: "easy-aliaserTests",
dependencies: ["easy-aliaser"]),
]
)
Here is my github repository in case you would like to reproduce it.
So why I receive this Error in the Command Line but not in Xcode?
Thanks in advance.
the problem
the command command line specify sub-command but miss the executable product
$ swift run read-aliases
the solution
you must use the executable product before sub-command
swift run easy-aliaser read-aliases
steps to reproduce
[so-test]$ git clone https://github.com/CreaTorAleXander/easy-aliaser
Cloning into 'easy-aliaser'...
remote: Enumerating objects: 41, done.
remote: Counting objects: 100% (41/41), done.
remote: Compressing objects: 100% (32/32), done.
remote: Total 41 (delta 2), reused 38 (delta 1), pack-reused 0
Unpacking objects: 100% (41/41), done.
[so-test]$ cd easy-aliaser
#
# [easy-aliaser (main)]$ swift package generate-xcodeproj
# edited the wired filename in your code just to refer to an existing file
# run the project in XCode without problems
# back to the command line
#
[easy-aliaser (main)]$ swift run easy-aliaser read-aliases
Fetching https://github.com/apple/swift-argument-parser
Cloning https://github.com/apple/swift-argument-parser
Resolving https://github.com/apple/swift-argument-parser at 0.3.1
/Users/me/projects/so-test/easy-aliaser/Sources/easy-aliaser/main.swift:23:13: warning: result of call to 'readFile(path:)' is unused
readFile(path: "/Users/me/projects/so-test/65203567/myzshrc")
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[3/3] Linking easy-aliaser
These are your Aliases
who_listening='sudo lsof -nP -iTCP -sTCP:LISTEN'
du-docker="du -h ~/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/Docker.qcow2 && docker images"
du-openshift=" /Users/ronda/.docker/machine/machines/openshift/disk.vmdk && du -h ~/.docker/machine/machines/openshift/boot2docker.iso"
I want to write some integration tests for Vapor 3 server and I need to have clean Postgre database each time I run my tests. How can I achieve this? It seems migrations isn't the right way to go as they've been running once if database doesn't exist yet.
Have a look at https://github.com/raywenderlich/vapor-til/tree/master/Tests
This requires a DB to be running before you run the tests, but it reverts all migrations at the start of each test run, which gives you a clean DB each time. (Specifically here)
There's also a docker-compose.yml in the root directory for spinning up a completely isolated test environment on Linux
I've found a solution that is less resource-intensive, then reverting all migrations every time.
RSpec has a configuration (use_transactional_fixtures) that allows wrapping every test in an SQL transaction. When testing is over it will rollback the transaction and in consequence revert all the changes that happened during testing. Relevant documentation is here.
We can implement a similar solution in Vapor. My example test looks like this.
final class VaporTests: XCTestCase {
var app: Application!
override func setUp() {
super.setUp()
app = try! Application.buildForTesting()
let conn = try! app.requestPooledConnection(to: .psql).wait()
try! conn.simpleQuery("BEGIN TRANSACTION").wait()
try! app.releasePooledConnection(conn, to: .psql)
}
override func tearDown() {
let conn = try! app.requestPooledConnection(to: .psql).wait()
try! conn.simpleQuery("ROLLBACK").wait()
try! app.releasePooledConnection(conn, to: .psql)
super.tearDown()
}
func testExample() throws {
let request = HTTPRequest(method: .GET, url: "my/endpoint/example")
let wrapper = Request(http: request, using: app)
let response = try ExampleController().example(wrapper).wait()
XCTAssertEqual(response, .ok)
}
}
To make sure that I don't encounter issues with concurrency I'm limiting database pool to 1 connection in the test application.
func configure(_ config: inout Config, _ env: inout Environment, _ services: inout Services) throws {
// ... other configurations
let poolConfig = DatabaseConnectionPoolConfig(maxConnections: 1)
services.register(poolConfig)
}
Many thanks to Jakub Jatczak for helping me to find out how this happens in Rails.
Quite late to the party but following way also does the revert and migrate command work. This code does the similar commands as answer given by #0xTim. But I have made use of Console.framework:
Mostly we use a configure.swift file like below:
import FluentPostgreSQL
import Vapor
public func configure(_ config: inout Config, _ env: inout Environment, _ services: inout Services) throws {
// Register providers first
try services.register(FluentPostgreSQLProvider())
...
/// Configure commands
var commandConfig = CommandConfig.default()
commandConfig.useFluentCommands()
services.register(commandConfig)
...
/// Configure migrations
services.register { container -> MigrationConfig in
var migrationConfig = MigrationConfig()
try migrate(migrations: &migrationConfig)
return migrationConfig
}
}
Quite late to the party but following code does execute revert and migrate
commands: (I am using Quick and Nimble so beforeSuite. Commented code is there because unless you use above configure.swift you can just uncomment the code and make use of CommandConfig directly.)
import Quick
import Vapor
import Console
import FluentPostgreSQL
...
configuration.beforeSuite {
let console: Console = Terminal()
// var commandConfig = CommandConfig()
// commandConfig.use(RevertCommand(), as: "revert")
// commandConfig.use(MigrateCommand(), as: "migrate")
var config = Config.default()
var env = Environment.testing
var services = Services.default()
do {
// try App.configure(&config, &env, &services)
let container = try Application(config: config, environment: env, services: services)
let commandConfig = try container.make(CommandConfig.self)
let commands = try commandConfig.resolve(for: container).group()
var input = CommandInput(arguments: ["vapor","revert","--all", "-y"])
try console.run(commands, input: &input, on: container).wait()
input = CommandInput(arguments: ["vapor","migrate","-y"])
try console.run(commands, input: &input, on: container).wait()
} catch let error {
console.error(error.localizedDescription)
exit(1)
}
}
For thoses who are seeking another approach that doesnt involve registering new migrations ( and, to me, adding more code complexity ) you can use a Pre-Action script for tests target ( ⌘ + < )
By using a bash script you can create a brand new postgresql database that will be used to build the project for tests only :
# Variables
export IS_TEST=true
export DB_USERNAME="`whoami`"
export DB_DBNAME="BARTENDER_TEST_DB"
#Creating dedicated Postgres DB
echo "deleting & recreating $DB_DBNAME for user $DB_USERNAME"
psql postgres<< EOF
DROP DATABASE "$DB_DBNAME";
CREATE DATABASE "$DB_DBNAME";
\list
EOF
Then in configure.swift file you create a PostgreSQLDatabaseConfig that matches the newly created database
if let _ = Environment.get("IS_TEST") { // IS_TEST is defined only in Pre-Action script
guard let username = Environment.get("DB_USERNAME") else {
fatalError("Failed to create PostgresConfig - DB_USERNAME in Environment variables")
}
guard let databasename = Environment.get("DB_DBNAME") else {
fatalError("Failed to create PostgresConfig - DB_DBNAME in Environment variables")
}
postgresqlConfig = PostgreSQLDatabaseConfig(
hostname: "127.0.0.1",
port: 5432,
username: username,
database: databasename,
password: nil
)
}
else { /* your other config here */ }
let database = PostgreSQLDatabase(config: postgresqlConfig)
...
The big advantage I found in this it that I can even trigger a vapor build and vapor run from another project that will create a brand new Webservice environment for my continuous integration testings, just by inserting the correct environment variables
Simplest way I found was to add PostgresKit to the test target and use it to setup a connection to call my "clean-up" queries.
#testable import App
import Vapor
import XCTest
import PostgresKit
final class UserTests: XCTestCase {
var pools: EventLoopGroupConnectionPool<PostgresConnectionSource>!
var postgresDb: PostgresDatabase!
var eventLoopGroup: EventLoopGroup!
override func setUp() {
let configuration = PostgresConfiguration(
hostname: "localhost",
username: "postgres",
password: "password",
database: "db_name"
)
eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 2)
pools = EventLoopGroupConnectionPool(
source: PostgresConnectionSource(configuration: configuration),
on: eventLoopGroup
)
postgresDb = pools.database(logger: Logger.init(label: "TestLogger"))
}
override func tearDown() {
let _ = try! postgresDb.query("DELETE FROM \(User.schema)").wait()
try! pools.syncShutdownGracefully()
try! eventLoopGroup.syncShutdownGracefully()
}
func testUploadUser() throws {
let app = Application(.testing)
defer { app.shutdown() }
try configure(app)
try app.testable(method: .running).test(.POST, "api/users", beforeRequest: { req in
try req.content.encode(["firstName" : "Dwide", "lastName" : "Shrewd"])
}, afterResponse: { res in
XCTAssertEqual(res.status, .ok)
let user = try res.content.decode(User.self)
XCTAssertEqual(user, User(id: user.id, firstName: "Dwide", lastName: "Shrewd"))
})
}
}
And this is my Package.swift
// swift-tools-version:5.2
import PackageDescription
let package = Package(
name: "MyVaporProject",
platforms: [
.macOS(.v10_15)
],
dependencies: [
// 💧 A server-side Swift web framework.
.package(url: "https://github.com/vapor/vapor.git", from: "4.0.0"),
.package(url: "https://github.com/vapor/fluent.git", from: "4.0.0"),
.package(url: "https://github.com/vapor/fluent-postgres-driver.git", from: "2.0.0"),
.package(url: "https://github.com/vapor/postgres-kit.git", from: "2.0.0")
],
targets: [
.target(
name: "App",
dependencies: [
.product(name: "Fluent", package: "fluent"),
.product(name: "FluentPostgresDriver", package: "fluent-postgres-driver"),
.product(name: "Vapor", package: "vapor")
],
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#building-for-production> for details.
.unsafeFlags(["-cross-module-optimization"], .when(configuration: .release))
]
),
.target(name: "Run", dependencies: [.target(name: "App")]),
.testTarget(
name: "AppTests",
dependencies: [
.target(name: "App"),
.product(name: "XCTVapor", package: "vapor"),
.product(name: "PostgresKit", package: "postgres-kit")
]
)
]
)
As in previous answers, this requires a stood-up Postgres database, already migrated, ready to take connections before the tests start.
I've built a simple Vapor 3 API that I'd like to deploy on Heroku. I'd like it to be backed by a PostgreSQL database which is also attached to another Heroku app (I have successfully attached the DB in the Heroku dashboard – and the DB works correctly in the other application). However, my Vapor app never completes starting up, crashing with the following error:
Fatal error: Error raised at top level: ⚠️ PostgreSQL Error: no pg_hba.conf entry for host "[the IP addr]", user "[heroku postgres username here]", database "[heroku psql db here]", SSL off
- id: PostgreSQLError.server.fatal.ClientAuthentication
I used vapor heroku init to set up the Heroku app. I've Googled around, and tried adding a Procfile and messing with configure.swift, but so far no luck. Here are all the relevant files I can think of:
Procfile:
web: Run serve --env production --hostname 0.0.0.0 --port $PORT --config:postgresql.url $DATABASE_URL
configure.swift:
import FluentPostgreSQL
import Vapor
/// Called before your application initializes.
public func configure(_ config: inout Config, _ env: inout Environment, _ services: inout Services) throws {
/// Register providers first
try services.register(FluentPostgreSQLProvider())
var contentConfig = ContentConfig.default()
/// Create custom JSON encoder
let jsonEncoder = JSONEncoder()
if #available(OSX 10.12, *) {
jsonEncoder.dateEncodingStrategy = .iso8601
} else {
jsonEncoder.dateEncodingStrategy = .millisecondsSince1970
}
// jsonEncoder.keyEncodingStrategy = .convertToSnakeCase
/// Register JSON encoder and content config
contentConfig.use(encoder: jsonEncoder, for: .json)
services.register(contentConfig)
/// Register routes to the router
let router = EngineRouter.default()
try routes(router)
services.register(router, as: Router.self)
/// Register middleware
var middlewares = MiddlewareConfig() // Create _empty_ middleware config
/// middlewares.use(FileMiddleware.self) // Serves files from `Public/` directory
middlewares.use(ErrorMiddleware.self) // Catches errors and converts to HTTP response
services.register(middlewares)
// Configure a database
let dbConfig: PostgreSQLDatabaseConfig
if let url = Environment.get("DATABASE_URL"), let psqlConfig = PostgreSQLDatabaseConfig(url: url) {
dbConfig = psqlConfig
} else {
dbConfig = PostgreSQLDatabaseConfig(hostname: "localhost", port: 5432, username: "admin", database: "development", password: nil)
}
let postgresql = try PostgreSQLDatabase(config: dbConfig)
/// Register the configured SQLite database to the database config.
var databases = DatabasesConfig()
databases.add(database: postgresql, as: .psql)
services.register(databases)
/// Configure migrations
var migrations = MigrationConfig()
migrations.add(model: Visit.self, database: .psql)
services.register(migrations)
}
Package.swift:
// swift-tools-version:4.0
import PackageDescription
let package = Package(
name: "SubwayNyc",
dependencies: [
// 💧 A server-side Swift web framework.
.package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"),
// 🔵 Swift ORM (queries, models, relations, etc) built on PostgreSQL.
.package(url: "https://github.com/vapor/fluent-postgresql.git", from: "1.0.0"),
.package(url: "https://github.com/vapor/sql.git", from: "2.1.0")
],
targets: [
.target(name: "App", dependencies: ["FluentPostgreSQL", "Vapor"]),
.target(name: "Run", dependencies: ["App"]),
.testTarget(name: "AppTests", dependencies: ["App"])
]
)
How can I get PostgreSQL hooked up to my Vapor 3 app on Heroku?
For Heroku we need unverifiedTLS transport.
https://api.vapor.codes/postgresql/latest/PostgreSQL/Classes/PostgreSQLConnection/TransportConfig.html
let pgURL = Environment.get("DATABASE_URL") ?? "postgres://user:password#host:port/database"
let pgConfig = PostgreSQLDatabaseConfig(url: pgURL, transport: PostgreSQLConnection.TransportConfig.unverifiedTLS)!
:D
The original error is the key here, in particular: SSL off.
This error is thrown by Heroku Postgres when the client is attempting to connect without SSL. Not familiar with Vapor myself, but a quick look around suggests that configure.swift is where you can make configuration changes. Once you enable SSL from the client, you should be able to connect to this database instance without issue.
I would like to ship my library using Apple's Swift Package Manager. However my lib includes a .bundle file with several strings translated in different languages.
Using cocoapods, I can include it using spec.resource. But in SwiftPM, I cannot do it. Any solution?
The package manager does not yet have any definition for how resources will be bundled with targets. We are aware of the need for this, but don't yet have a concrete proposal for it. I filed https://bugs.swift.org/browse/SR-2866 to ensure we have a bug tracking this.
Using Swift 5.3 it's finally possible to add localized resources 🎉
The Package initializer now has a defaultLocalization parameter which can be used for localization resources.
public init(
name: String,
defaultLocalization: LocalizationTag = nil, // New defaultLocalization parameter.
pkgConfig: String? = nil,
providers: [SystemPackageProvider]? = nil,
products: [Product] = [],
dependencies: [Dependency] = [],
targets: [Target] = [],
swiftLanguageVersions: [Int]? = nil,
cLanguageStandard: CLanguageStandard? = nil,
cxxLanguageStandard: CXXLanguageStandard? = nil
)
Let's say you have an Icon.png which you want to be localised for English and German speaking people.
The images should be included in Resources/en.lproj/Icon.png & Resources/de.lproj/Icon.png.
After you can reference them in your package like that:
let package = Package(
name: "BestPackage",
defaultLocalization: "en",
targets: [
.target(name: "BestTarget", resources: [
.process("Resources/Icon.png"),
])
]
)
Please note LocalizationTag is a wrapper of IETF Language Tag.
Credits and input from following proposals overview, please check it for more details.
starting on Swift 5.3, thanks to SE-0271, you can add bundle resources on swift package manager by adding resources on your .target declaration.
example:
.target(
name: "HelloWorldProgram",
dependencies: [],
resources: [.process(Images), .process("README.md")]
)
if you want to learn more, I have written an article on medium, discussing this topic
The solution I use for this is to build the data I need into a Swift object. To this end I have a shell script that will read an input file, base64 encode it, then write a Swift file that presents it as an InputStream. Then, when I want to add a data item to my Swift package, I run the script to read the file and write the output file. Of course the output file needs to be checked in so that the resource is available to those who use the project even if they do not have the script. (Typically I place my input files in a Resources directory and write the output to the Sources directory, but the script itself does not depend on that.)
I consider this a less than ideal solution, and am looking forward to when the package manager has this ability built in. But in the meantime, it is a workable solution.
The following example shows how it is used:
First, here is the script itself:
#!/usr/bin/env bash
# Read an input file, base64 encode it, then write an output swift file that will
# present it as an input stream.
#
# Usage: generate_resource_file.sh <inputfile> <outputfile> <streamName>
#
# The <streamName> is the name presented for the resulting InputStream. So, for example,
# generate_resource_file.sh Resources/logo.png Sources/Logo.swift logoInputStream
# will generate a file Sources/Logo.swift that will contain a computed variable
# that will look like the following:
# var logoInputStream: InputStream { ...blah...
#
set -e
if [ $# -ne 3 ]; then
echo "Usage: generate_resource_file.sh <inputfile> <outputfile> <streamName>"
exit -1
fi
inFile=$1
outFile=$2
streamName=$3
echo "Generating $outFile from $inFile"
echo "Stream name will be $streamName"
if [ ! -f "$inFile" ]; then
echo "Could not read $inFile"
exit -1
fi
echo "// This file is automatically generated by generate_resource_file.sh. DO NOT EDIT!" > "$outFile"
echo "" >> "$outFile"
echo "import Foundation" >> "$outFile"
echo "" >> "$outFile"
echo "fileprivate let encodedString = \"\"\"" >> "$outFile"
base64 -i "$inFile" >> "$outFile"
echo "\"\"\"" >> "$outFile"
echo "" >> "$outFile"
echo "var $streamName: InputStream {" >> "$outFile"
echo " get {" >> "$outFile"
echo " let decodedData = Data(base64Encoded: encodedString)!" >> "$outFile"
echo " return InputStream(data: decodedData)" >> "$outFile"
echo " }" >> "$outFile"
echo "}" >> "$outFile"
echo "Rebuilt $outFile"
Then, given an input file t.dat shown here:
Hello World!
Running the command generate_resource_file.sh t.dat HelloWorld.swift helloWorldInputStream generates the following HelloWorld.swift file:
// This file is automatically generated by generate_resource_file.sh. DO NOT EDIT!
import Foundation
fileprivate let encodedString = """
SGVsbG8gV29ybGQhCgo=
"""
var helloWorldInputStream: InputStream {
get {
let decodedData = Data(base64Encoded: encodedString)!
return InputStream(data: decodedData)
}
}
Due to framework bundles not being supported yet, the only way to provide bundle assets with an SPM target is through a Bundle. If you implement code in your framework to search for a particular bundle in your main project (supporting asset bundles), you can load resources from said bundle.
Example:
Access the bundled resources:
extension Bundle {
static func myResourceBundle() throws -> Bundle {
let bundles = Bundle.allBundles
let bundlePaths = bundles.compactMap { $0.resourceURL?.appendingPathComponent("MyAssetBundle", isDirectory: false).appendingPathExtension("bundle") }
guard let bundle = bundlePaths.compactMap({ Bundle(url: $0) }).first else {
throw NSError(domain: "com.myframework", code: 404, userInfo: [NSLocalizedDescriptionKey: "Missing resource bundle"])
}
return bundle
}
}
Utilize the Bundled resources:
let bundle = try! Bundle.myResourceBundle()
return UIColor(named: "myColor", in: bundle, compatibleWith: nil)!
You can apply the same logic for all resource files, including but not limited to storyboards, xibs, images, colors, data blobs, and files of various extensions (json, txt, etc).
Note: Sometimes this makes sense, sometimes it doesn't. Determine use to own project's discretion. It would take very specific scenarios to justify separating Storyboards/Xibs into bundled assets.
Important note:
Resources doesn't seem to be included in the Xcode project generated by
swift package generate-xcodeproj
But they are when you open the Package folder on Xcode (xed .) and then double click on the package to resolve dependencies.
I'm including a nice tutorial as well: https://medium.com/better-programming/how-to-add-resources-in-swift-package-manager-c437d44ec593
Is it possible yet to connect to an Oracle Database using Swift (raw or a Swift framework) on Linux? What I have done is tried to build a Docker VM, install the Oracle binaries, add the OCILIB package and then connect [tried] using a package called SwiftOracle, which seems unsupported (lots of build issues) and just exposes the OCILIB C code to Swift using a module map and wrapper.
I tried this using the Kitura framework and none of this seemed to work - Xcode can't compile because it isn't unable to either find the C library or create the module.
Here are the steps that I have tried to no avail:
Build Docker VM (which includes Oracle binaries): https://github.com/wnameless/docker-oracle-xe-11g
Download and install OCILIB: https://github.com/vrogier/ocilib
Add SwiftOracle package, fix build issues and try to build.
you can use the following method to connect to oracle database: ( This was possible by the help of vapor community.)
-----to make oracle driver work, I have Tied this method in Ubuntu-------------
-- oracle client need to be installed such that the header and library path can be defined, you can get these from oracle website.
oracle-instantclinet*-basic-*.rpm
oracle-instantclinet*-devel-*.rpm
oracle-instantclinet*-sqlplus-*.rpm
--install thus downloaded package using following command
sudo alien -i oracle-instantclinet*-basic-*.rpm
sudo alien -i oracle-instantclinet*-devel-*.rpm
sudo alien -i oracle-instantclinet*-sqlplus-*.rpm
-- Install libaio1 in ubuntu
sudo apt install libaio1
-- this path should be in ~/.bashrc
#oracle home and library path
export ORACLE_HOME=/usr/lib/oracle/12.2/client64
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/lib/oracle/12.2/client64/lib:/usr/local/lib
--download OCILIB library from Github
git clone https://github.com/vrogier/ocilib.git ( or download the latest version / tested on ocilib 4.5.2 )
-- extract ocilib file cd to ocilib folder, configure make and make install
tar -zxf ocilib-4.5.2-gnu.tar.gz
cd ocilib-4.5.2
./configure --with-oracle-headers-path=/usr/include/oracle/12.2/client64/ --with-oracle-lib-path=/usr/lib/oracle/12.2/client64/lib CFLAGS="-O2 -m64"
make
sudo make install
-- use this configuration if you need to deal with unicodes, generally you don't need this
./configure --with-oracle-headers-path=/usr/include/oracle/12.2/client64/ --with-oracle-lib-path=/usr/lib/oracle/12.2/client64/lib --with-oracle-charset=wide CFLAGS="-O2 -m64"
-- The above method installs OCILIB in your machine.
-- To user OCILIB library in your Vapor project Include the following in you Package.swift file
// swift-tools-version:4.0
import PackageDescription
let package = Package(
name: "myAPIProject",
dependencies: [
// 💧 A server-side Swift web framework.
.package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"),
// 🔵 Swift ORM (queries, models, relations, etc) built on SQLite 3.
.package(url: "https://github.com/vapor/fluent-sqlite.git", from: "3.0.0"),
//Oracle wrapper for swift
.package(url: "https://github.com/h1257977/SwiftOracle.git", from: "0.1.7")
],
targets: [
.target(name: "App", dependencies: ["FluentSQLite","SwiftOracle", "Vapor"]),
.target(name: "Run", dependencies: ["App"]),
.testTarget(name: "AppTests", dependencies: ["App"])
]
)
-- In Routes.swift , include the following:
import Vapor
import SwiftOracle
let service = OracleService(host: "192.168.1.12", port:"1521", service: "orcl")
let b = Connection(service: service, user:"test", pwd:"oracle")
final class VReq: Content {
var name: String?
var age: String?
init (NAME: String, AGE: String) {
self.name = NAME
self.age = AGE
}
final class VMdata {
func getData() throws -> [VReq] {
try! b.open()
b.autocommit = true
let cursor = try! b.cursor()
try! cursor.execute("select * from userlist")
//iterates each row in the cursor and maps only the values (keys are unique) from the dictionary of each rows, if its nil it will replace with "null"
var items = cursor.map { row in row.dict.mapValues { "\($0 ?? "NULL")" }} // output as [[String:String]]
//takes each dictionary in the items array and returns a VReq
let result = items.map { dict in VReq(NAME: dict["NAME"] ?? "NULL", ADDRESS: dict["ADDRESS"] ?? "NULL", USER_AGE: dict["USER_AGE"] ?? "NULL")}
return result
}
}
public func routes(_ router: Router) throws {
router.get("test") { req -> [VReq] in
let val = VMdata()
let vdata = try! val.getData()
return vdata
}
}
--To run Vapor 3 you need to link the library files
swift build -Xlinker -L/usr/local/lib && ./.build/x86_64-unknown-linux/debug/Run --hostname 0.0.0.0
-- To display unicode characters from any database column you may have to set NLS_LANG in the server hosting Vapor application.
export NLS_LANG=AMERICAN_AMERICA.AL32UTF8