Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
## Installing Swift

If you don't have Swift installed, [install it first](/install). To test that you have Swift installed, run `swift --version` in the terminal.
If you don't have Swift installed, [install it first](/install).

To test that you have Swift installed, run `swift --version` in the terminal.

### Swift Package Manager

Expand Down
36 changes: 23 additions & 13 deletions getting-started/_use-cases.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,42 @@
Below are some examples of the many use cases of Swift.

<ul class="use-case-list">

<li class="use-case">
<h3>Build a Web Server (Vapor)</h3>
<h3>Command-line Tool</h3>
<p class="description">
Use the open-source Vapor framework to build a web API entirely with Swift.
Requires macOS or Linux.
Build a command-line tool using Swift.
</p>
<a href="/getting-started/vapor-web-server" class="cta-secondary">Start tutorial</a>

<a href="/getting-started/cli-tool" class="cta-secondary">Start tutorial</a>
</li>

<li class="use-case">
<h3>Build a Command-line Tool</h3>
<h3>Library</h3>
<p class="description">
Build a command-line tool using Swift and SwiftPM.
Build a library using Swift.
</p>

<a href="/getting-started/cli-tool" class="cta-secondary">Start tutorial</a>
<a href="/getting-started/library" class="cta-secondary">Start tutorial</a>
</li>

<li class="use-case">
<h3>Build an app for Apple platforms</h3>
<h3>Web Server with Vapor</h3>
<p class="description">
Use the open-source Vapor framework to build a web API entirely with Swift.
Requires macOS or Linux.
</p>

<a href="/getting-started/vapor-web-server" class="cta-secondary">Start tutorial</a>
</li>

<li class="use-case">
<h3>App for Apple platforms</h3>
<p class="description">
Use Swift and SwiftUI to build an app that works on iOS and macOS.
Requires macOS 12 and Xcode 14.
</p>

<a href="/" class="cta-secondary">Start tutorial</a>
</li>
</ul>
</ul>
187 changes: 186 additions & 1 deletion getting-started/cli-tool/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,189 @@ layout: page
title: Build a Command-line Tool
---

TODO
{% include getting-started/_installing.md %}

## Bootstrapping

Let’s write a small application with our new Swift development environment.
To start, we’ll use SwiftPM to make a new project for us. In your terminal of choice run:

~~~bash
❯ mkdir hello-swift
❯ cd hello-swift
❯ swift package init --name hello-swift --type executable
~~~

This will generate a new directory called hello-swift with the following files:

~~~no-highlight
.
├── Package.swift
├── README.md
├── Sources
│   └── hello-swift
│   └── hello_swift.swift
└── Tests
└── hello-swiftTests
└── hello_swiftTests.swift
~~~

`Package.swift` is the manifest file for Swift. It’s where you keep metadata for your project, as well as dependencies.

`Sources/hello-swift/main.swift` is the application entry point and where we’ll write our application code.
`Test/hello-swiftTests/hello_swiftTests.swift` is where we can write tests for our application.

In fact, SwiftPM generated a "Hello, world!" project for us!
We can run this program by running `swift run` in our terminal.

~~~bash
❯ swift run
[3/3] Linking hello-swift
Hello, world!
~~~

## Adding dependencies

Swift based applications are usually composed from libraries that provide useful functionality.

In this project, we’ll use a package called [swift-figlet](https://github.com/tomerd/swift-figlet) which will help us make ASCII art.

You can find more interesting libraries on [Swift Package Index](https://swiftpackageindex.com) -- the unofficial package index for Swift.

To do so, we extend our `Package.swift` file with the following information:

~~~swift
// swift-tools-version: 5.7

import PackageDescription

let package = Package(
name: "hello-swift",
dependencies: [
.package(url: "https://github.com/tomerd/swift-figlet", branch: "main"),
],
targets: [
.executableTarget(
name: "hello-swift",
dependencies: [
.product(name: "Figlet", package: "swift-figlet"),
]
),
.testTarget(
name: "hello-swiftTests",
dependencies: ["hello-swift"]
),
]
)
~~~

Running `swift build` will instruct SwiftPM to install the new dependencies and then proceed to build the code.

Running this command also created a new file for us, `Package.resolved`.
This file is a snapshot of the exact versions of the dependencies we are using locally.

To use this dependency, we can open `hello_swift.swift`, remove everything that’s in there (it’s just an example), and add this line to it:

~~~swift
import Figlet
~~~

This line means that we can now use the `Figlet` module that the `swift-figlet` package exports.

## A small application

Now let’s write a small application with our new dependency. In our `hello_swift.swift`, add the following code:

~~~swift
import Figlet // from the previous step

@main
struct FigletTool {
static func main() {
let figlet = Figlet()
figlet.say("Hello, Swift!")
}
}
~~~

Once we save that, we can run our application with `swift run`
Assuming everything went well, you should see your application print this to the screen:

~~~no-highlight
_ _ _ _ _ __ _ _
| | | | ___ | | | | ___ ___ __ __ (_) / _| | |_ | |
| |_| | / _ \ | | | | / _ \ / __| \ \ /\ / / | | | |_ | __| | |
| _ | | __/ | | | | | (_) | _ \__ \ \ V V / | | | _| | |_ |_|
|_| |_| \___| |_| |_| \___/ ( ) |___/ \_/\_/ |_| |_| \__| (_)
|/
~~~

## Argument parsing

Most command line tools need to be able and parse command line arguments.

To add this capability to our application, we add a dependency on [swift-argument-parser](https://github.com/apple/swift-argument-parser).

To do so, we extend our `Package.swift` file with the following information:

~~~swift
// swift-tools-version: 5.7

import PackageDescription

let package = Package(
name: "hello-swift",
dependencies: [
.package(url: "https://github.com/tomerd/swift-figlet", branch: "main"),
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.0.0"),
],
targets: [
.executableTarget(
name: "hello-swift",
dependencies: [
.product(name: "Figlet", package: "swift-figlet"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
]
),
.testTarget(
name: "hello-swiftTests",
dependencies: ["hello-swift"]
),
]
)
~~~

We can now import the argument parsing module provided by `swift-argument-parser` and use it in our application

~~~swift
import ArgumentParser
import Figlet

@main
struct FigletTool: ParsableCommand {
@Option(help: "Specify the input")
public var input: String

public func run() throws {
let figlet = Figlet()
figlet.say(self.input)
}
}
~~~

For more information about how [swift-argument-parser](https://github.com/apple/swift-argument-parser) parses command line options, see [swift-argument-parser documentation](https://github.com/apple/swift-argument-parser) documentation.

Once we save that, we can run our application with `swift run hello-swift --input 'Hello, world!'`

Note we need to specify the executable in this case, so we can pass the `input` argument to it.

Assuming everything went well, you should see your application print this to the screen:

~~~no-highlight
_ _ _ _ _ _ _
| | | | ___ | | | | ___ __ __ ___ _ __ | | __| | | |
| |_| | / _ \ | | | | / _ \ \ \ /\ / / / _ \ | '__| | | / _` | | |
| _ | | __/ | | | | | (_) | _ \ V V / | (_) | | | | | | (_| | |_|
|_| |_| \___| |_| |_| \___/ ( ) \_/\_/ \___/ |_| |_| \__,_| (_)
|/
~~~
4 changes: 2 additions & 2 deletions getting-started/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ layout: page
title: Getting Started
---

{% include_relative _installing.md %}
{% include getting-started/_installing.md %}
{% include_relative _use-cases.html %}
{% include_relative _go-further.html %}
{% include_relative _go-further.html %}
119 changes: 119 additions & 0 deletions getting-started/library/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
---
layout: page
title: Build a library
---

{% include getting-started/_installing.md %}

## Bootstrapping

Let’s write a small application with our new Swift development environment.
To start, we’ll use SwiftPM to make a new project for us. In your terminal of choice run:

~~~bash
❯ mkdir swift-library
cd swift-library
❯ swift package init --name swift-library --type library
~~~

This will generate a new directory called hello-swift with the following files:

~~~no-highlight
.
├── Package.swift
├── README.md
├── Sources
│   └── swift-library
│   └── swift_library.swift
└── Tests
└── swift-libraryTests
└── swift_libraryTests.swift
~~~

`Package.swift` is the manifest file for Swift. It’s where you keep metadata for your project, as well as dependencies.

`Sources/swift-library/swift-library.swift` is the library initial source file and where we’ll write our library code.
`Test/swift-libraryTests/swift-libraryTests.swift` is where we can write tests for our library.

In fact, SwiftPM generated a "Hello, world!" project for us, including some unit tests!
We can run the tests by running `swift test` in our terminal.

~~~bash
❯ swift test
Building for debugging...
[3/3] Linking swift-libraryPackageTests
Test Suite 'All tests' started at 2023-01-03 10:57:52.659
Test Suite 'swift-libraryPackageTests.xctest' started at 2023-01-03 10:57:52.660
Test Suite 'swift_libraryTests' started at 2023-01-03 10:57:52.660
Test Case '-[swift_libraryTests.swift_libraryTests testExample]' started.
Test Case '-[swift_libraryTests.swift_libraryTests testExample]' passed (0.003 seconds).
Test Suite 'swift_libraryTests' passed at 2023-01-03 10:57:52.664.
Executed 1 test, with 0 failures (0 unexpected) in 0.003 (0.004) seconds
Test Suite 'swift-libraryPackageTests.xctest' passed at 2023-01-03 10:57:52.664.
Executed 1 test, with 0 failures (0 unexpected) in 0.003 (0.004) seconds
Test Suite 'All tests' passed at 2023-01-03 10:57:52.664.
Executed 1 test, with 0 failures (0 unexpected) in 0.003 (0.005) seconds
~~~

## A small library

Now let’s write a small library.
Replace the example content of `swift-library.swift` with the following code:

~~~swift
import Foundation

struct Email: CustomStringConvertible {
var description: String

public init(_ emailString: String) throws {
let regex = #"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,64}"#
guard let _ = emailString.range(of: regex, options: .regularExpression) else {
throw InvalidEmailError(email: emailString)
}
self.description = emailString
}
}

private struct InvalidEmailError: Error {
let email: String
}
~~~

Now lets add a unit test for this strongly types Email API.
Replace the example content of `swift_libraryTests.swift` with the following code:

~~~swift
@testable import swift_library
import XCTest

final class swift_libraryTests: XCTestCase {
func testEmail() throws {
let email = try Email("[email protected]")
XCTAssertEqual(email.description, "[email protected]")

XCTAssertThrowsError(try Email("invalid"))
}
}
~~~

Once we save that, we can run our application with `swift run`
Assuming everything went well, we can run the tests successfully again:

~~~no-highlight
❯ swift test
Building for debugging...
[3/3] Linking swift-libraryPackageTests
Build complete! (0.84s)
Test Suite 'All tests' started at 2023-01-03 16:22:45.070
Test Suite 'swift-libraryPackageTests.xctest' started at 2023-01-03 16:22:45.071
Test Suite 'swift_libraryTests' started at 2023-01-03 16:22:45.071
Test Case '-[swift_libraryTests.swift_libraryTests testEmail]' started.
Test Case '-[swift_libraryTests.swift_libraryTests testEmail]' passed (0.005 seconds).
Test Suite 'swift_libraryTests' passed at 2023-01-03 16:22:45.076.
Executed 1 test, with 0 failures (0 unexpected) in 0.005 (0.005) seconds
Test Suite 'swift-libraryPackageTests.xctest' passed at 2023-01-03 16:22:45.076.
Executed 1 test, with 0 failures (0 unexpected) in 0.005 (0.005) seconds
Test Suite 'All tests' passed at 2023-01-03 16:22:45.076.
Executed 1 test, with 0 failures (0 unexpected) in 0.005 (0.007) seconds
~~~