Where to create your own file in Vapor? - swift

I just tried to create a web app using Vapor, but when I tried to define my own variable/constant in my project, and write it in main.swift, the following error occurred.
let websiteURL = "http://localhost:8080" // in AppConstants.swift
let url = websiteURL // in main.swift
Use of unresolved identifier: websiteURL
However, it seems that the PostController class defined in Controllers directory can be seen without any problems. I put AppConstants.swift in the same directory as main.swift, but it got the error above.
In traditional iOS/macOS app, which is the only project I have developed so far, if you just create any files in the project, they can be seen from anywhere in the project, I think.
So how and where can I create and define my custom file in Vapor?
UPDATE
Here is my Package.swift file in my project:
import PackageDescription
let package = Package(
name: "vapor_sample",
dependencies: [
.Package(url: "https://github.com/vapor/vapor.git", majorVersion: 1, minor: 0)
],
exclude: [
"Config",
"Database",
"Localization",
"Public",
"Resources",
"Tests",
]
)
UPDATE 2
I found that for some reasons, the default files created by Vapor are named with directory suffix, like Controllers/PostController.swift, not PostController.swift. I tried moving AppConstants.swift under the directory, but it doesn't show its file name with the directory suffix. Is there anything related to it? I use Xcode 8.0.

Xcode projects associated w/ SwiftPM and Server Side Swift generally tend to be a bit finicky. You should regen as often and as regularly as possible. Also cleaning and deleting derived data never hurts. Here's some situations you might want to regen:
New File
Changed File Name
New Folder
New Package
Change Package Version
Feel like it
Basically, anytime anything goes wrong in Xcode, and you don't know where to start. Clean, Delete Derived, Regen.

Related

Swift Package - How to exclude files in root git directory from the actual Swift Package?

I am creating a Swift Package that is essentially a wrapper for multiple XCFrameworks generated from Objective-C frameworks so they can be installed via SPM.
Everything works fine as far as creating the SP and ability to add it as a dependency to an app. But I have a bunch non-essential files included in the SP's repository that I don't want to include in the actual SP - i.e. They shouldn't show up in Xcode's navigator when the SP is added as a dependency.
(These consist of the source Obj-C Frameworks, README, Changelog, Xcode Workspace for demo app, Script files for generating the XCFrameworks, etc).
Is this even possible?
Or will SPM always checkout the entire repo and make all files visible to the user?
I have tried using various permutations of the Target specifiers: source, path, exclude but to no avail.
Here is the closest I can get with a valid manifest, but when I check out the SP in a dummy Xcode app, I can still see all the files from the repo included:
// 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: "WrapperSwiftPackage",
platforms: [.iOS(.v13)],
products: [
.library(name: "WrapperSwiftPackage", targets: ["WrapperSwiftPackage"])
],
dependencies: [],
targets: [
.target(
name: "WrapperSwiftPackage",
dependencies: [
"ObjCFramework1",
"ObjCFramework2"
],
path: "", // Set to root directory so we can exclude files below
exclude: [
"CHANGELOG.md",
"Dangerfile.swift",
"README.md",
"Workspace.xcworkspace",
"Scripts/generate-xcframework.sh",
"Scripts/link_git_hooks.sh",
"Objective-C Frameworks/"
],
sources: [
"Sources/WrapperSwiftPackage/main.swift",
"XCFrameworks/ObjCFramework1.xcframework",
"XCFrameworks/ObjCFramework2.xcframework"
]
),
.binaryTarget(name: "ObjCFramework1", path: "XCFrameworks/ObjCFramework1.xcframework"),
.binaryTarget(name: "ObjCFramework2", path: "XCFrameworks/ObjCFramework2.xcframework")
]
)
Not sure if that isn't a bug though, but I've accidentaly came up to one solution for this.
If you put an empty Package.swift (I mean, one like this):
// swift-tools-version:5.5
import PackageDescription
let package = Package()
into one of project subfolders, then even though SPM is checking the subfolder out, it's excluded from Xcode navigator, and thus, from the project visibility.
I would like to know if that's a bug or is it documented somewhere, every hint is appreciated.
Works with local and remote dependencies.

Exporting Package.swift for Swift Package Manager from existing Xcode project

I'm working with Xcode 11.3 on macOS Catalina 10.15.6.
I have an existing Xcode project which builds an app for iOS. I am interested in reusing some of the classes in an interactive session with the swift command line interpreter. The classes I want to work with are Core Data classes which are autogenerated from an Xcode data model and also some classes I've written which work with the Core Data classes. The app has UI screens and makes use of UIKit but I'm not trying to use any of those classes; I'm hoping that I can either compile those classes and then not refer to them, or somehow tell Swift Package Manager to ignore those classes altogether.
What I think I would like to do is to export a Package.swift for the existing Xcode project, such that swift build at the command line would be able to compile all of the project classes, or, failing that, at least the non-UI classes, and then swift run --repl would be able to load the classes via import.
I see a menu item in Xcode to create a new Swift package, but not to export an existing project. Is there a way to export an existing project?
There are no a menu command or utility to convert application to a static library, dynamic framework or swift package since they are different types of projects with different settings etc.
If you want to export a part of your project as a swift package you should make next steps manually:
1. Create Package.swift file in the root of your project
import PackageDescription
let package = Package(
name: “MyLib”,
products: [
.library(name: "MyLib", targets: ["MyLib"])
],
targets: [
.target(name: "MyLib"),
],
...
)
2. Make folder with subfolder ./Sources/MyLib under the projects’s root.
By default swift package structure requires to put all your sources files under Sources/LibraryName folder but you can change it below.
NOTE: you can simplify first two steps by using swift package init and it creates Package.swift, Sources and Test folders etc.
3. Include source files
a) Move the needed files to share from their current locations to MyLib folder.
For instance:
./Classes/MyEntity.swift -> ./Sources/MyLib/MyEntity.swift
Also you have to update locations of the moved files in your Xcode project to leave it compilable.
b) Use path, sources and exclude to point needed source files to your package from their current locations:
.target(name: "MyLib", path: "Classes"),
NOTE: Don't forget to make your classes public to access to them after import your package:
public class MyEntity {
...
}
After all you will have two working projects - old XCode's one and new Swift package.
4. REPL
Now you can use command line interpreter with your swift package:
swift run --repl
import MyLib
let entity = MyEntity()
...

Package Loading: "Ignoring duplicate product" (SwiftPM)

I'm trying to build a Swift package using the Swift Package Manager.
However, when I open the package in Xcode and click Run, I get the following warning:
How can I resolve this?
This is an issue often encountered if you are attempting to build a library/framework but also have a main.swift file in your target's root directory, like so:
.
The presence of a main.swift file makes SwiftPM believe that you are attempting to build a command line tool, and thus complains about the unnecessary (duplicate in name) library produced.
This warning may be confusing as it does not occur in projects initialized with the dynamic framework template from Xcode, for iOS, macOS, tvOS or watchOS.
In my case it was because I had the same name in both the products section and the Package e.g.
let package = Package(
name: "Example",
products: [
.library(
name: "Example",
targets: ["Example"]),
],
)
This seems to be the default behavior of swift init for some reason. I removed the .library entry and the warning went away.

How to generate a framework from a SwiftPM-generated XCode project?

I used SwiftPM to set up an XCode project for a framework; based on
// swift-tools-version:4.0
import PackageDescription
let package = Package(
name: "MyThing",
products: [
.library(
name: "MyThing",
targets: ["MyThing"]),
],
dependencies: [
],
targets: [
.target(
name: "MyThing",
dependencies: [])
)
I ran swift package generate-xcodeproj and then pod install (based on a Podfile whose content shouldn't matter). I obtain MyThing.xcworkspace.
Now I figured that
xcodebuild -workspace MyThing.xcworkspace -scheme MyThing clean build
should create the .framework -- but it doesn't, only a binary file appears. I suspect some automatism is at work here since the source folder contains a file named main.swift, among others.
What do I have to do to get the framework built?
I need to script the whole process, to please no manual workarounds.
As of Swift 4.0 swift package generate-xcodeproj doesn't automatically generate schemes for all targets, but still makes those targets accessible from Xcode.
If creating a new scheme manually once is acceptable, you can do so and add your framework target as a scheme build target.
Otherwise, new schemes can be created programmatically with libraries like xcodeswift/xcproj, which allow you to parse a newly generated Xcode project and generate a new scheme with its XCSharedData class.
After that new framework scheme is created you can run the build with xcodebuild -workspace MyThing.xcworkspace -scheme MyThingFramework clean build.
Mixing script files and framework code in one target seems to confuse SwiftPM. Moving main.swift out of the source folder clears up things.
Scheme MyThing-Package creates all build products as specified in Package.swift, including the framework.
If you want to have the script files in the same XCode Workspace (for editing convenience), put them in their own target. You can even create an executable product (which won't create anything useful since an executable can't link to dynamic frameworks).

Importing modules with Swift package manager

I am trying to use Swift's package manager to import external modules in my project. My first module come from the Vapor project. I cannot seem to get it working. I start with
swift package init
swift package generate-xcodeproj
My Package.swift looks like this:
import PackageDescription
let package = Package(
name: "OpenTools",
products: [
.library(
name: "OpenTools",
targets: ["OpenTools"]),
],
dependencies: [
.package(url: "https://github.com/vapor/json.git", from: "2.0.0")
],
targets: [
.target(name: "OpenTools", dependencies: ["JSON"]),
]
)
I then run
swift package update
swift package generate-xcodeproj # to regenerate with dependencies
and then try to import the JSON package in my main file
import JSON
The modules are there as shown below but the import gets back with an No such module 'JSON' error.
Any thoughts?
Probably the problem lies within Xcode, as it does not know yet that JSON exists, because it was not built yet. This can easily be solved by just building your project (with cmd-B). With the generated xcodeproj, Xcode should know that it first needs to build JSON and then the rest, because JSON is marked as a dependency for your target.
You can check this, by navigating in Xcode to your target (when you click on the project description file) and afterwards to "Build Phases". Under Target Dependencies you should find your JSON module.
In addition you should find a JSON module under your targets, which compiles the sources you gathered from github.
Your project should also build when executing swift build in your project root.
With Xcode 11 you should be able to open Package.swift directly which will give you a proving ground for verifying the package manifest (aka: the Package.swift file) and compiling the target. This should help see what is actually causing the error that's preventing the module from being compiled.