diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..183cd92 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,41 @@ +name: Build and Deploy with Toucan + +on: + push: + branches: [main] + +permissions: + contents: read + pages: write + id-token: write + +jobs: + build-page: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Build site using Toucan + run: | + docker run --rm \ + -v ${{ github.workspace }}:/site \ + -w /site \ + --user $(id -u):$(id -g) \ + toucansites/toucan generate ./src ./docs + + - name: Upload static site to GitHub Pages + uses: actions/upload-pages-artifact@v3 + with: + path: ./docs + + deploy-page: + needs: build-page + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 \ No newline at end of file diff --git a/.gitignore b/.gitignore index e43b0f9..70f4f70 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .DS_Store +/docs \ No newline at end of file diff --git a/docs/10-little-uikit-tips-you-should-know/index.html b/docs/10-little-uikit-tips-you-should-know/index.html deleted file mode 100644 index 30c434f..0000000 --- a/docs/10-little-uikit-tips-you-should-know/index.html +++ /dev/null @@ -1,656 +0,0 @@ - - - - - - - - - - - - 10 little UIKit tips you should know - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 8 min read - -
-

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

-
- -

Custom UIColor with dark mode support

Dark mode and light mode shouldn’t follow the exact same design patterns, sometimes you’d like to use a border when your app is in light mode, but in dark mode you might want to hide the extra line.

One possible solution is to define a custom UIColor based the given UITraitCollection. You can check the userInterfaceStyle property of a trait to check for dark appearance style.

extension UIColor {
-    static var borderColor: UIColor {
-        .init { (trait: UITraitCollection) -> UIColor in
-            if trait.userInterfaceStyle == .dark {
-                return UIColor.clear
-            }
-            return UIColor.systemGray4
-        }
-    }
-}
-

Based on this condition you can easily return different colors both for light and dark mode. You can create your own set of static color variables by extending the UIColor object. It’s a must have little trick if you are planning to support dark mode and you’d like to create custom colors. 🌈

Observing trait collection changes

This next one is also related to dark mode support, sometimes you’d like to detect appearance changes of the user interface and this is where the traitCollectionDidChange function can be helpful. It’s available on views, controllers and cells too, so it’s quite an universal solution.

class MyCustomView: UIView {
-    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
-        guard traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) else {
-            return
-        }
-        layer.borderColor = UIColor.borderColor.cgColor
-    }
-}
-

For example, inside this function you can check if the trait collection has a different appearance style and you can update your CoreGraphics layers according to that. The CoreGraphics framework is a low level tool and if you work with layers and colors you have to manually update them if it comes to dark mode support, but the traitCollectionDidChange method can help you a lot. 💡

UIButton with context menus

Creating buttons got a lot easier with iOS 15, but did you know that you can also use a button to display a context menu? It’s very easy to present a UIMenu you just have to set the menu and the showsMenuAsPrimaryAction property of the button to true.

import UIKit
-
-class TestViewController: UIViewController {
-    
-    weak var button: UIButton!
-
-    override func loadView() {
-        super.loadView()
-     
-        let button = UIButton(frame: .zero)
-        button.translatesAutoresizingMaskIntoConstraints = false
-        view.addSubview(button)
-        self.button = button
-
-        NSLayoutConstraint.activate([
-            button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
-            button.leadingAnchor.constraint(equalTo: view.leadingAnchor),
-            button.trailingAnchor.constraint(equalTo: view.trailingAnchor),
-            button.heightAnchor.constraint(equalToConstant: 44),
-        ])
-    }
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        button.setTitle("Open menu", for: .normal)
-        button.setTitleColor(.systemGreen, for: .normal)
-        button.menu = getContextMenu()
-        button.showsMenuAsPrimaryAction = true
-    }
-
-    func getContextMenu() -> UIMenu {
-        .init(title: "Menu",
-              children: [
-                UIAction(title: "Edit", image: UIImage(systemName: "square.and.pencil")) { _ in
-                    print("edit button clicked")
-                },
-                UIAction(title: "Delete", image: UIImage(systemName: "trash"), attributes: .destructive) { _ in
-                    print("delete action")
-                },
-              ])
-    }
-    
-}
-

This way the UIButton will act as a menu button, you can assign various actions to your menu item. I believe this API is especially handy in some cases, nowadays I prefer to use context menus instead of swipe-to-x-y actions, because it’s a bit more convenient for the user if we visually show them (usually with 3 dots) that there are additional actions available on a given UI element. 🧐

Don’t be afraid of subclassing views

UIKit is an OOP framework and I highly recommend to subclass custom views instead of multi-line view configuration code snippets inside your view controller. The previous code snippet is a great example for the opposite, so let’s fix that real quick.

import UIKit
-
-class MenuButton: UIButton {
-
-    @available(*, unavailable)
-    override init(frame: CGRect) {
-        super.init(frame: frame)
-        
-        self.initialize()
-    }
-
-    @available(*, unavailable)
-    required public init?(coder: NSCoder) {
-        super.init(coder: coder)
-        
-        self.initialize()
-    }
-   
-    public init() {
-        super.init(frame: .zero)
-        
-        self.initialize()
-    }
-    
-    open func initialize() {
-        self.translatesAutoresizingMaskIntoConstraints = false
-
-        setTitle("Open menu", for: .normal)
-        setTitleColor(.systemGreen, for: .normal)
-        menu = getContextMenu()
-        showsMenuAsPrimaryAction = true
-    }
-    
-    func getContextMenu() -> UIMenu {
-        .init(title: "Menu",
-              children: [
-                UIAction(title: "Edit", image: UIImage(systemName: "square.and.pencil")) { _ in
-                    print("edit button clicked")
-                },
-                UIAction(title: "Delete", image: UIImage(systemName: "trash"), attributes: .destructive) { _ in
-                    print("delete action")
-                },
-              ])
-    }
-
-    func layoutConstraints(in view: UIView) -> [NSLayoutConstraint] {
-        [
-            centerYAnchor.constraint(equalTo: view.centerYAnchor),
-            leadingAnchor.constraint(equalTo: view.leadingAnchor),
-            trailingAnchor.constraint(equalTo: view.trailingAnchor),
-            heightAnchor.constraint(equalToConstant: 44),
-        ]
-    }
-}
-
-
-class TestViewController: ViewController {
-    
-    weak var button: MenuButton!
-
-    override func loadView() {
-        super.loadView()
-     
-        let button = MenuButton()
-        view.addSubview(button)
-        self.button = button
-        NSLayoutConstraint.activate(button.layoutConstraints(in: view))
-    }
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-        
-    }
-}
-

As you can see the code inside the view controller is heavily reduced and most of the button configuration related logic is now encapsulated inside the MenuButton subclass. This approach is great because you can focus less on view configuration and more on your business logic inside the view controller. It’ll also help you to think in reusable components.

One additional note here is that I tend to create my interfaces from code that’s why I mark the unnecessary init methods with the @available(*, unavailable) flag so other people in my team can’t call them accidentally, but this is just a personal preference. 😅

Always large navigation title

I don’t know about you, but for me all the apps have glitches if it comes to the large title feature in the navigation bar. For personal projects I’ve got sick and tired of this and I simply force the large title display mode. It’s relatively simple, here’s how to do it.

import UIKit
-
-class TestNavigationController: UINavigationController {
-
-    override init(rootViewController: UIViewController) {
-        super.init(rootViewController: rootViewController)
-        
-        initialize()
-    }
-
-    @available(*, unavailable)
-    required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-
-        initialize()
-    }
-    
-    open func initialize() {
-        navigationBar.prefersLargeTitles = true
-        navigationItem.largeTitleDisplayMode = .always
-        
-        // custom tint color
-        navigationBar.tintColor = .systemGreen
-        // custom background color
-        let navBarAppearance = UINavigationBarAppearance()
-        navBarAppearance.backgroundColor = .systemBackground
-        navigationBar.standardAppearance = navBarAppearance
-        navigationBar.scrollEdgeAppearance = navBarAppearance
-    }
-}
-
-class TestViewController: UIViewController {
-    
-    override func loadView() {
-        super.loadView()
-        
-        // prevent collapsing the navbar if we add scrollviews
-        view.addSubview(UIView(frame: .zero))
-        
-        // add other custom views...
-    }
-}
-
-let controller = TestNavigationController(rootViewController: TestViewController())
-

You just have to set two properties (you can subclass UINavigationController or set these inside your view controller, but I prefer subclassing) plus you have to add an empty view to your view hierarchy to prevent collapsing if you are planning to use a UIScrollView, UITableView or UICollectionView inside the view controller.

Since this tip is also based on my personal preference, I’ve also included a few more customization options in the snippet. If you take a look at the initialize method you can see how to change the tint color and the background color of the navigation bar. 👍

Custom separators for navigation and tab bars

Since many apps prefer to have a customized navigation bar and tab bar appearance it’s quite a common practice when you have to also add a separator line to distinguish user interface elements a bit more. This is how you can solve it by using a single bar separator class.

import UIKit 
-
-class BarSeparator: UIView {
-    
-    let height: CGFloat = 0.3
-
-    init() {
-        super.init(frame: CGRect(x: 0, y: 0, width: 0, height: height))
-        
-        translatesAutoresizingMaskIntoConstraints = false
-        backgroundColor = .systemGray4
-    }
-    
-    @available(*, unavailable)
-    required init?(coder: NSCoder) {
-        super.init(coder: coder)
-    }
-    
-    func layoutConstraints(for navigationBar: UINavigationBar) -> [NSLayoutConstraint] {
-        [
-            widthAnchor.constraint(equalTo: navigationBar.widthAnchor),
-            heightAnchor.constraint(equalToConstant: CGFloat(height)),
-            centerXAnchor.constraint(equalTo: navigationBar.centerXAnchor),
-            topAnchor.constraint(equalTo: navigationBar.bottomAnchor),
-        ]
-    }
-    
-    func layoutConstraints(for tabBar: UITabBar) -> [NSLayoutConstraint] {
-        [
-            widthAnchor.constraint(equalTo: tabBar.widthAnchor),
-            heightAnchor.constraint(equalToConstant: CGFloat(height)),
-            centerXAnchor.constraint(equalTo: tabBar.centerXAnchor),
-            topAnchor.constraint(equalTo: tabBar.topAnchor),
-        ]
-    }
-}
-
-class MyNavigationController: UINavigationController {
-    
-   override func viewDidLoad() {
-        super.viewDidLoad()
-        
-        let separator = BarSeparator()
-        navigationBar.addSubview(separator)
-        NSLayoutConstraint.activate(separator.layoutConstraints(for: navigationBar))
-    }
-}
-
-class MyTabBarController: UITabBarController {
-    
-    override func viewDidLoad() {
-        super.viewDidLoad()
-        
-        let separator = BarSeparator()
-        tabBar.addSubview(separator)
-        NSLayoutConstraint.activate(separator.layoutConstraints(for: tabBar))
-    }   
-}
-

This way you can reuse the BarSeparator component to add a line to the bottom of a navigation bar and to the top of a tab bar. This snippet follows the exact same principles that I showed you before, so you should be familiar with the subclassing concepts by now. 🤓

Custom tab bar items

I struggled quite a lot with tab bar item icon alignment, but this the way I can easily show / hide the title and align the icons to the center of the bar if there are no labels.

import UIKit
-
-class MyTabBarItem: UITabBarItem {
-    
-    override var title: String? {
-        get { hideTitle ? nil : super.title }
-        set { super.title = newValue }
-    }
-        
-    private var hideTitle: Bool {
-        true
-    }
-
-    private func offset(_ image: UIImage?) -> UIImage? {
-        if hideTitle {
-            return image?.withBaselineOffset(fromBottom: 12)
-        }
-        return image
-    }
-    
-    // MARK: - init
-    
-    public convenience init(title: String?, image: UIImage?, selectedImage: UIImage?) {
-        self.init()
-
-        self.title = title
-        self.image = offset(image)
-        self.selectedImage = offset(selectedImage)
-    }
-
-    override init() {
-        super.init()
-    }
-
-    @available(*, unavailable)
-    required init?(coder aDecoder: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
-}
-
-// inside some view controller init
-tabBarItem = MyTabBarItem(title: "Home", image: UIImage(systemName: "house"), selectedImage: nil)
-

I’d also like to mention that SF Symbols are amazing. If you are not using these kind of icons just yet I highly recommend to take a look. Apple made a really nice job with this collection, there are so many lovely icons that you can use to visually enrich your app, so don’t miss out. 😊

loadView vs viewDidLoad

Long story short, you should always instantiate and place constraints to your views inside the loadView method and configure your views inside the viewDidLoad function.

I always use implicitly unwrapped weak optional variables for custom views, since the addSubview function will create a strong reference to the view when it is added to the view hierarchy. We don’t want to have retain cycles, right? That’d be real bad for our application. 🙃

import UIKit
-
-class MyCollectionViewController: ViewController {
-    
-    weak var collection: UICollectionView!
-
-    override func loadView() {
-        super.loadView()
-        
-        view.addSubview(UIView(frame: .zero))
-        
-        let collection = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
-        collection.translatesAutoresizingMaskIntoConstraints = false
-        view.addSubview(collection)
-        self.collection = collection
-        NSLayoutConstraint.activate([
-            // ...
-        ])
-    }
-    
-    override func viewDidLoad() {
-        super.viewDidLoad()
-        
-        collection.backgroundColor = .systemBackground
-        collection.alwaysBounceVertical = true
-        collection.dragInteractionEnabled = true
-        collection.dragDelegate = self
-        collection.dropDelegate = self
-
-        if let flowLayout = collection.collectionViewLayout as? UICollectionViewFlowLayout {
-            flowLayout.sectionHeadersPinToVisibleBounds = true
-        }
-        
-        collection.register(MyCell.self,
-                            forCellWithReuseIdentifier: MyCell.identifier)
-    }
-

Anyway, I’d go with a custom subclass for the collection view here as well and maybe define a configure method then call that one instead of placing everything directly to the controller. The decision is always up-to-you, I’m just trying to show you the some possible solutions. 😉

Stack views & auto-layout anchors

Take advantage of stack views and auto layout anchors as much as possible. If you are going to create user interfaces programmatically in Swift with the help of UIKit, then it’s going to be an essential skill to master these techniques otherwise you’re going to struggle a lot.

I already have a tutorial about using auto layout programmatically and another one about mastering auto-layout anchors, they were published a few years ago, but the concepts are still valid and the code still works. I also have one more article that you should read if you want to learn about building forms using stack views. Learning these kind of things helped me a lot to create complex screens hassle-free. I’m also using one more “best practice” to create collection views.

When SwiftUI came out I had the feeling that eventually I’d do the same with UIKit, but of course Apple had the necessary tooling to support the framework with view builders and property wrappers. Now that we have SwiftUI I’m still not using it because I feel like it lacks quite a lot of features even in 2022. I know it’s great and I’ve created several prototypes for screens using it, but if it comes to a complex application my gut tells me that I should still go with UIKit. 🤐

Create a reusable components library

My final advice in this tutorial is that you should build a custom Swift package and move all your components there. Maybe for the first time it’s going to consume quite a lot of time but if you are working on multiple projects it will speed up development process for your second, third, etc. app.

You can move all your custom base classes into a separate library and create specific ones for your application. You just have to mark them open, you can use the availability API to manage what can be used and what should be marked as unavailable.

I have quite a lot of tutorials about the Swift Package Manager on my blog, this is a great way to get familiar with it and you can start building your own library step-by-step. 😊

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/10-short-advices-that-will-make-you-a-better-vapor-developer-right-away/index.html b/docs/10-short-advices-that-will-make-you-a-better-vapor-developer-right-away/index.html deleted file mode 100644 index 319ef06..0000000 --- a/docs/10-short-advices-that-will-make-you-a-better-vapor-developer-right-away/index.html +++ /dev/null @@ -1,362 +0,0 @@ - - - - - - - - - - - - 10 short advices that will make you a better Vapor developer right away - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

-
- -

Set a custom working directory in Xcode with just a few clicks

So you have your very first Vapor project up and running in Xcode, but for some strange reason Vapor can’t read your local .env file, Leaf can’t find the templates or maybe there is no db.sqlite file in the current project directory at all. You might ask the question:

Why the hell is Xcode trying to look for my files in the DerivedData folder?

The answer is pretty simple, you can setup a custom working directory within Xcode, you just have to right click your target name and select the Edit Scheme… menu item. If you don’t specify a custom working directory under the Run scheme options tab, Xcode will use the default location to look up user files, that’s called the working directory and it’s hidden under the DerivedData folder.

Tip #1: set up the working directory before you run the project, so you don’t have to deal with the derived data issues anymore. Also if you remove the hidden .swiftpm folder from your project, you’ll have to repeat the setup process again. 💪

Always stop previous server instances to avoid address in use errors

If you hit the “address already used” message in the console that can only mean one thing: something blocks the port that your server is trying to use. You can always start the Activity Monitor application and search for the server (Run), or you can use the lsof -i :8080 -sTCP:LISTEN command to check the port, but nowadays I’m using a more practical approach to fix this issue.

I’m using a pre-actions run script as part of the scheme runner operation. You can open the same Edit Scheme… menu item and click a little arrow next to your scheme name, this will allow you to setup both pre and post-actions that can run before or after the actual run process. Now the trick is that I always try to kill the previous process using a pre-action script.

lsof -i :8080 -sTCP:LISTEN |awk 'NR > 1 {print $2}'|xargs kill -15
-

Tip #2: always kill the previous server instance before you build & run a new one using a pre-actions script, this will eliminate the address in use errors from your life forever. 😎

Run the migration scripts automatically

One common mistake is that you forget to migrate the database before you run the project. This can be avoided if you call the autoMigrate() method in the configuration function, so the server can perform the necessary migrations before it starts to listen for incoming connections.

import Vapor
-import Fluent
-import FluentSQLiteDriver
-
-public func configure(_ app: Application) throws {
-    //...
-    app.databases.use(.sqlite(.file("db.sqlite")), as: .sqlite)
-    try app.autoMigrate().wait()
-}
-

Tip #3: don’t forget to run your Fluent database migrations, you can simply automate by calling the autoMigrate method from Swift. Be careful, sometimes when you work in a production environment you don’t want to run automatic migrations in every single case. 🙈

Install the latest toolbox version with brew

We’re in a transition period between Vapor 3 and Vapor 4, this was causing some trouble for many of my readers. There is a command line utility for Vapor, but the thing is that if are not using the latest version of it might generates a project based on an older (version 3) template. If you want to install a specific version of the Vapor toolbox you can do that by running the following commands:

git clone https://github.com/vapor/toolbox.git
-cd toolbox
-git checkout <desired version>
-swift build -c release --disable-sandbox --enable-test-discovery
-mv .build/release/vapor /usr/local/bin
-

Tip #4: always make sure that you are using the right version of the Vapor toolbox. 🔨

Use .env files to safely store secrets

Never hardcode secrets or sensitive data into your Swift files. You can use environmental variables for this purpose, even better you can store your secrets in a file called .env so you don’t have to export them always before you run the project. With a relatively easy trick you can also store multiline strings in your .env file.

Tip #5: keep your secrets safe using .env files. Never commit them to the repository, you can use the .gitignore file to ignore them automatically. This way your secrets will be safe and you can run the app using various environments (development, production, testing, etc.).

Learn the new command API, to build better tools

It is quite essential to run various scripts on the server side. Backend developers always create tools for common tasks, e.g. I have a script that can minify CSS files for me or another one for moving the views to the Resources folder, but there are many other things that you can use scripts for. Fortunately you don’t have to learn bash anymore, but can write scripts using your favorite programming language: Swift. You can use swift-sh or the official Swift argument parser, but the best part of being a full-stack Swift developer is that Vapor has such an amazing command API.

Tip #6: learn the Vapor command API so you can create your own backend tools and scripts without learning anything about shell scripts, zsh or bash at all. 🐚

Use https & letsencrypt for better security

If you have never heard about the Let’s Encrypt service or you don’t know what’s the main difference between HTTP and HTTPS, you should definitely take a look at the linked pages. Virtual privacy, security is more important nowadays than it was ever before. 🛡

Tip #7: use HTTPS by default, don’t risk giving out sensitive by data using unencrypted channels. Pro tip: you can test your server’s certificate and configuration using the free SSL Labs testing tool.

Use the SQLLite driver for rapid development

I already mentioned that it’s good to automatically migrate your Fluent database during development, but what if you mess up something and you have to reset the entire database? Well you can perform a complete reset using both the PostgreSQL, MySQL or MongoDB drivers, but isn’t it way more easy to delete just one single file?

Tip #8: if you don’t have specific requirements or needs for a given database driver, just use the FluentSQLiteDriver for development purposes. You can iterate way faster, you can reset the db with just a few clicks and start over everything right ahead. 💡

Always update your project to avoid bugs

Why the hell is my cookie parser broken? Why is this feature not working? Why is the server crashing? Well, sometimes things can go wrong, people make mistakes, but the good news is that team Vapor is doing an amazing job. This is an extremely friendly and helpful community (one of the best if it comes to Swift developers) you can always ask questions on the official Discord server (just look for the proper channel for your question), or file a bug report on the GitHub repositories.

Tip #9: however, before you raise a new issue, you should try to update your Swift dependencies. Vapor related package releases are coming quite often so it is worth to start your day by hitting the File > Swift Packages > Update to Latest Package Versions button in Xcode. ✅

Use nginx for faster performance

Nginx is an extremely fast easy to use HTTP & proxy server. Nginx can be used as a proxy server, this way it can forward the incoming traffic to your Vapor application. It can also help you as a load balancer, you can setup your HTTPS SSL certificate once using nginx, plus you can completely ditch the file middleware since nginx can server static content as well.

Tip #10: use nginx combined with your Vapor server if you want to achieve better safety, scalability and performance. By the way enabling HTTP/2 is just a few lines of configuration. 😉

Conclusion

Becoming a full-stack Swift developer can be hard, but hopefully these tips will help you to overcome the initial difficulties. If you don’t know where to start or what to do next, you should take a look at my recently released Practical Server Side Swift book. It was made for Vapor 4, it can help you to build modular and scalable web applications through a real-world example project.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/404.html b/docs/404.html deleted file mode 100644 index 33e5307..0000000 --- a/docs/404.html +++ /dev/null @@ -1,164 +0,0 @@ - - - - - - - - - - - - 404 - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
func main() {
-    print(404)
-    print("Page not found!")
-}
-
-main()
-

Something went wrong. 🤷‍♂️

-
- -
- - - -
- - - - diff --git a/docs/CNAME b/docs/CNAME deleted file mode 100644 index 9832714..0000000 --- a/docs/CNAME +++ /dev/null @@ -1 +0,0 @@ -theswiftdev.com \ No newline at end of file diff --git a/docs/a-generic-crud-solution-for-vapor-4/index.html b/docs/a-generic-crud-solution-for-vapor-4/index.html deleted file mode 100644 index 605336e..0000000 --- a/docs/a-generic-crud-solution-for-vapor-4/index.html +++ /dev/null @@ -1,618 +0,0 @@ - - - - - - - - - - - - A generic CRUD solution for Vapor 4 - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 10 min read - -
-

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

-
- -

CRUD ~ Create, Read, Update and Delete

We should start by implementing the non-generic version of our code, so after we see the pattern we can turn it into a more generalized Swift code. If you start with the API template project there is a pretty good example for almost everything using a Todo model.

Start a new project using the toolbox, just run vapor new myProject

Open the project by double clicking the Package.swift file, that’ll fire up Xcode (you should be on version 11.4 or later). If you open the Sources/App/Controllers folder you’ll find a sample controller file there called TodoController.swift. We’re going to work on this, but first…

A controller is a collection of request handler functions around a specific model.

HTTP basics: Request -> Response

HTTP is a text transfer protocol that is widely used around the web. In the beginning it was only used to transfer HTML files, but nowadays you can use it to request almost anything. It’s mostly a stateless protocol, this means you request something, you get back a response and that’s it.

It’s like ordering a pizza from a place through phone. You need a number to call (URL), you pick up the phone, dial the place, the phone company initializes the connection between (you & the pizza place) the two participants (the network layer does the same thing when you request an URL from a server). The phone on the other side starts ringing. 📱

Someone picks up the phone. You both introduce yourselves, also exchange some basic info such as the delivery address (server checks HTTP headers & discovers what needs to be delivered to where). You tell the place what kind of pizza you’d like to have & you wait for it. The place cooks the pizza (the server gathers the necessary data for the response) & the pizza boy arrives with your order (the server sends back the actual response). 🍕

Everything happens asynchronously, the place (server) can fulfill multiple requests. If there is only one person who is taking orders & cooking pizzas, sometimes the cooking process will be blocked by answering the phone. Anyways, using non-blocking i/o is important, that’s why Vapor uses Futures & Promises from SwiftNIO under the hood.

In our case the request is a URL with some extra headers (key, value pairs) and a request body object (encoded data). The response is usually made of a HTTP status code, optional headers and response body. If we are talking about a RESTful API, the encoding of the body is usually JSON.

All right then, now you know the basics it’s time to look at some Swift code.

Contents and models in Vapor

Defining a data structure in Swift is pretty easy, you just have to create a struct or a class. You can also convert them back and forth to JSON using the built-in Codable protocol. Vapor has an extension around this called Content. If you conform the the protocol (no need to implement any new functions, the object just needs to be Codable) the system can decode these objects from requests and encode them as responses.

Models on the other hand represent rows from your database. The Fluent ORM layer can take care of the low level abstractions, so you don’t have to mess around with SQL queries. This is a great thing to have, read my other article if you like to know more about Fluent. 💾

The problem starts when you have a model and it has different fields than the content. Imagine if this Todo model was a User model with a secret password field? Would you like to expose that to the public when you encode it as a response? Nope, I don’t think so. 🙉

I believe that in most of the Cases the Model and the Content should be separated. Taking this one step further, the content of the request (input) and the content of the response (output) is sometimes different. I’ll stop it now, let’s change our Todo model according to this.

import Fluent
-import Vapor
-
-final class Todo: Model {
-    
-    struct Input: Content {
-        let title: String
-    }
-
-    struct Output: Content {
-        let id: String
-        let title: String
-    }
-    
-    static let schema = "todos"
-
-    @ID(key: .id) var id: UUID?
-    @Field(key: "title") var title: String
-
-    init() { }
-
-    init(id: UUID? = nil, title: String) {
-        self.id = id
-        self.title = title
-    }
-}
-

We expect to have a title when we insert a record (we can generate the id), but when we’re returning Todos we can expose the id property as well. Now back to the controller.

Don’t forget to run Fluent migrations first: swift run Run migrate

Create

The flow is pretty simple. Decode the Input type from the content of the request (it’s created from the HTTP body) and use it to construct a new Todo class. Next save the newly created item to the database using Fluent. Finally after the save operation is done (it returns nothing by default), map the future into a proper Output, so Vapor can encode this to JSON format.

import Fluent
-import Vapor
-
-struct TodoController {
-
-    /*
-         curl -i -X POST "http://127.0.0.1:8080/todos" \
-         -H "Content-Type: application/json" \
-         -d '{"title": "Hello World!"}'
-     */
-    func create(req: Request) throws -> EventLoopFuture<Todo.Output> {
-        let input = try req.content.decode(Todo.Input.self)
-        let todo = Todo(title: input.title)
-        return todo.save(on: req.db)
-            .map { Todo.Output(id: todo.id!.uuidString, title: todo.title) }
-    }
-
-    // ...
-}
-

I prefer cURL to quickly check my endpoints, but you can also create unit tets for this purpose. Run the server using Xcode or type swift run Run to the command line. Next if you copy & paste the commented snippet it should create a new todo item and return the output with some additional HTTP info. You should also validate the input, but this time let’s just skip that part. 😅

Read

Getting back all the Todo objects is a simple task, but returning a paged response is not so obvious. Fortunately with Fluent 4 we have a built-in solution for this. Let me show you how it works, but first I’d like to alter the routes a little bit.

import Fluent
-import Vapor
-
-func routes(_ app: Application) throws {
-    let todoController = TodoController()
-    app.post("todos", use: todoController.create)
-    app.get("todos", use: todoController.readAll)
-    app.get("todos", ":id", use: todoController.read)
-    app.post("todos", ":id", use: todoController.update)
-    app.delete("todos", ":id", use: todoController.delete)
-}
-

As you can see I tend to use read instead of index, plus :id is a much shorter parameter name, plus I’ll already know the returned model type based on the context, no need for additional prefixes here. Ok, let me show you the controller code for the read endpoints:

struct TodoController {
-
-    /*
-       curl -i -X GET "http://127.0.0.1:8080/todos?page=2&per=2" \
-        -H "Content-Type: application/json"
-    */
-    func readAll(req: Request) throws -> EventLoopFuture<Page<Todo.Output>> {
-        return Todo.query(on: req.db).paginate(for: req).map { page in
-            page.map { Todo.Output(id: $0.id!.uuidString, title: $0.title) }
-        }
-    }
-
-    //...
-}
-

As I mentioned this before Fluent helps with pagination. You can use the page and per query parameters to retrieve a page with a given number of elements. The newly returned response will contain two new (items & metadata) keys. Metadata inclues the total number of items in the database. If you don’t like the metadata object you can ship your own paginator:

// the first 10 items
-Todo.query(on: req.db).range(..<10)
-
-// returns 10 items from the 2nd element
-Todo.query(on: req.db).range(2..<10).all()
-
-// limit - offset
-Todo.query(on: req.db).range(offset..<limit).all()
-
-// page - per
-Todo.query(on: req.db).range(((page - 1) * per)..<(page * per)).all()
-The QueryBuilder range support is a great addition. Now let's talk about reading one element.
-
-struct TodoController {
-
-    /*
-        curl -i -X GET "http://127.0.0.1:8080/todos/<id>" \
-            -H "Content-Type: application/json"
-     */
-    func read(req: Request) throws -> EventLoopFuture<Todo.Output> {
-        guard let id = req.parameters.get("id", as: UUID.self) else {
-            throw Abort(.badRequest)
-        }
-        return Todo.find(id, on: req.db)
-            .unwrap(or: Abort(.notFound))
-            .map { Todo.Output(id: $0.id!.uuidString, title: $0.title) }
-    }
-
-    //...
-}
-

You can get named parameters by key, I already mentioned this in my beginner’s guide article. The new thing here is that you can throw Abort(error) anytime you want to break something. Same thing happens in the unwrap method, that just checks if the value wrapped inside the future object. If it is nil it’ll throws the given error, if the value is present the promise chain will continue.

Update

Update is pretty straightforward, it’s somewhat the combination of the read & create methods.

struct TodoController {
-
-    /*
-        curl -i -X POST "http://127.0.0.1:8080/todos/<id>" \
-            -H "Content-Type: application/json" \
-            -d '{"title": "Write Vapor 4 book"}'
-     */
-    func update(req: Request) throws -> EventLoopFuture<Todo.Output> {
-        guard let id = req.parameters.get("id", as: UUID.self) else {
-            throw Abort(.badRequest)
-        }
-        let input = try req.content.decode(Todo.Input.self)
-        return Todo.find(id, on: req.db)
-            .unwrap(or: Abort(.notFound))
-            .flatMap { todo in
-                todo.title = input.title
-                return todo.save(on: req.db)
-                    .map { Todo.Output(id: todo.id!.uuidString, title: todo.title) }
-            }
-    }
-    
-    //...
-}
-

You need an id to find the object in the database, plus some input to update the fields. You fetch the item, update the corresponding properties based on the input, save the model and finally return the newly saved version as a public output object. Piece of cake. 🍰

Delete

Delete is just a little bit tricky, since usually you don’t return anything in the body, but just a simple status code. Vapor has a nice HTTPStatus enum for this purpose, so e.g. .ok is 200.

struct TodoController {
-
-    /*
-        curl -i -X DELETE "https://127.0.0.1:8080/todos/<id>"
-     */
-    func delete(req: Request) throws -> EventLoopFuture<HTTPStatus> {
-        guard let id = req.parameters.get("id", as: UUID.self) else {
-            throw Abort(.badRequest)
-        }
-        return Todo.find(id, on: req.db)
-            .unwrap(or: Abort(.notFound))
-            .flatMap { $0.delete(on: req.db) }
-            .map { .ok }
-    }
-
-    //...
-}
-

Pretty much that sums everything. Of course you can extend this with a PATCH method, but that’s quite a good task for practicing. I’ll leave this “unimplemented” just for you… 😈

A protocol oriented generic CRUD

Long story short, if you introduce new models you’ll have to do this exact same thing over and over again if you want to have CRUD endpoints for every single one of them.

That’s a boring task to do, plus you’ll end up having a lot of boilerplate code. So why not come up with a more generic solution, right? I’ll show you one possible implementation.

protocol ApiModel: Model {
-    associatedtype Input: Content
-    associatedtype Output: Content
-
-    init(_: Input) throws
-    var output: Output { get }
-    func update(_: Input) throws
-}
-

The first thing I did is that I created a new protocol called ApiModel, it has two associatedType requirements, those are the i/o structs from the non-generic example. I also want to be able to initialize or update a model using an Input type, and transform it to an Output.

protocol ApiController {
-    var idKey: String { get }
-
-    associatedtype Model: ApiModel
-
-    // generic helper functions
-    func getId(_: Request) throws -> Model.IDValue
-    func find(_: Request) throws -> EventLoopFuture<Model>
-
-    // generic crud methods
-    func create(_: Request) throws -> EventLoopFuture<Model.Output>
-    func readAll(_: Request) throws -> EventLoopFuture<Page<Model.Output>>
-    func read(_: Request) throws -> EventLoopFuture<Model.Output>
-    func update(_: Request) throws -> EventLoopFuture<Model.Output>
-    func delete(_: Request) throws -> EventLoopFuture<HTTPStatus>
-    
-    // router helper
-    @discardableResult
-    func setup(routes: RoutesBuilder, on endpoint: String) -> RoutesBuilder
-}
-

Next thing todo (haha) is to come up with a controller interface. This is also going to be “generic”, plus I’d like to be able to set a custom id parameter key. One small thing here is that you can’t 100% generalize the decoding of the identifier parameter, but only if it’s LosslessStringConvertible.

extension ApiController where Model.IDValue: LosslessStringConvertible {
-
-    func getId(_ req: Request) throws -> Model.IDValue {
-        guard let id = req.parameters.get(self.idKey, as: Model.IDValue.self) else {
-            throw Abort(.badRequest)
-        }
-        return id
-    }
-}
-

Trust me in 99.9% of the cases you’ll be just fine right with this. Final step is to have a generic version of what we’ve just made above with each CRUD endpoint. 👻

extension ApiController {
-    
-    var idKey: String { "id" }
-
-    func find(_ req: Request) throws -> EventLoopFuture<Model> {
-        Model.find(try self.getId(req), on: req.db).unwrap(or: Abort(.notFound))
-    }
-
-    func create(_ req: Request) throws -> EventLoopFuture<Model.Output> {
-        let request = try req.content.decode(Model.Input.self)
-        let model = try Model(request)
-        return model.save(on: req.db).map { _ in model.output }
-    }
-    
-    func readAll(_ req: Request) throws -> EventLoopFuture<Page<Model.Output>> {
-        Model.query(on: req.db).paginate(for: req).map { $0.map { $0.output } }
-    }
-
-    func read(_ req: Request) throws -> EventLoopFuture<Model.Output> {
-        try self.find(req).map { $0.output }
-    }
-
-    func update(_ req: Request) throws -> EventLoopFuture<Model.Output> {
-        let request = try req.content.decode(Model.Input.self)
-        return try self.find(req).flatMapThrowing { model -> Model in
-            try model.update(request)
-            return model
-        }
-        .flatMap { model in
-            return model.update(on: req.db).map { model.output }
-        }
-    }
-    
-    func delete(_ req: Request) throws -> EventLoopFuture<HTTPStatus> {
-        try self.find(req).flatMap { $0.delete(on: req.db) }.map { .ok }
-    }
-    
-    @discardableResult
-    func setup(routes: RoutesBuilder, on endpoint: String) -> RoutesBuilder {
-        let base = routes.grouped(PathComponent(stringLiteral: endpoint))
-        let idPathComponent = PathComponent(stringLiteral: ":\(self.idKey)")
-        
-        base.post(use: self.create)
-        base.get(use: self.readAll)
-        base.get(idPathComponent, use: self.read)
-        base.post(idPathComponent, use: self.update)
-        base.delete(idPathComponent, use: self.delete)
-
-        return base
-    }
-}
-

Example time. Here is our generic model:

final class Todo: ApiModel {
-    
-    struct _Input: Content {
-        let title: String
-    }
-
-    struct _Output: Content {
-        let id: String
-        let title: String
-    }
-    
-    typealias Input = _Input
-    typealias Output = _Output
-    
-    // MARK: - model
-
-    static let schema = "todos"
-
-    @ID(key: .id) var id: UUID?
-    @Field(key: "title") var title: String
-
-    init() { }
-
-    init(id: UUID? = nil, title: String) {
-        self.id = id
-        self.title = title
-    }
-    
-    // MARK: - api
-    
-    init(_ input: Input) throws {
-        self.title = input.title
-    }
-    
-    func update(_ input: Input) throws {
-        self.title = input.title
-    }
-    
-    var output: Output {
-        .init(id: self.id!.uuidString, title: self.title)
-    }
-}
-

If the input is the same as the output, you just need one (Context?) struct instead of two.

This is what’s left off the controller (not much, haha):

struct TodoController: ApiController {
-    typealias Model = Todo
-}
-

The router object also shortened a bit:

func routes(_ app: Application) throws {
-    let todoController = TodoController()
-    todoController.setup(routes: routes, on: "todos")
-}
-

Try to run the app, everything should work just as before.

This means that you don’t have to write controllers anymore? Yes, mostly, but still this method lacks a few things, like fetching child objects for nested models or relations. If you are fine with that please go ahead and copy & paste the snippets into your codebase. You won’t regret, because this code is as simple as possible, plus you can override everything in your controller if you don’t like the default implementation. This is the beauty of the protocol oriented approach. 😎

Conclusion

There is no silver bullet, but if it comes to CRUD, but please DRY. Using a generic code can be a proper solution, but maybe it won’t cover every single use case. Taken together I like the fact that I don’t have to focus anymore on writing API endpoints, but only those that are quite unique. 🤓

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/a-simple-http2-server-using-vapor-4/index.html b/docs/a-simple-http2-server-using-vapor-4/index.html deleted file mode 100644 index b92c193..0000000 --- a/docs/a-simple-http2-server-using-vapor-4/index.html +++ /dev/null @@ -1,384 +0,0 @@ - - - - - - - - - - - - A simple HTTP/2 server using Vapor 4 - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 4 min read - -
-

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

-
- -

What is HTTP/2?

In short, it’s the second major version of Hypertext Transfer Protocol (HTTP), but obviously you’re not here for the short version. HTTP/2 is a huge upgrade, it was derived from the experimental SPDY protocol, nowadays it’s widely used by about 40% of all the websites. Yes it’s time to upgrade your infrastructure (soon). 😉

HTTP

The HTTP protocol is basically a client-server (request-response) communication protocol where the client asks for a resource and the server returns a response (a HTML document, a stylesheet, a JavaScript file, or anything else…). This all happens on top of a TCP/IP connection layer using sockets. If you don’t know anything about TCP/IP ports and sockets, you should read the linked article.

HTTP2 is secure by default, so it only works via TLS/SSL, but for the sake of simplicity I’m not going into the details of HTTPS, cryptography or secure connection.

HTTP is an application layer protocol, that describes how you can interact with various resources identified by an URL/URI (or URN). HTTP is simple (a few methods like GET, POST), yet extensible (via headers), stateless, but not sessionless (just think about Cookies) and it’s definitely dominating the world wide web (browsers). 🌎

HTTP version 1.1 has some disadvantages. It is a text based unencrypted protocol, plus as websites evolved and more and more resources were needed in order to render a webpage, HTTP/1.1 started to face some speed issues, because you are only allowed to download only one resource at a time on a HTTP/1.1 connection.

You have to wait for it…

Request multiplexing

The best (and most advanced feature) of HTTP/2 is request multiplexing. It allows you to download multiple files asynchronously from the server. This enables browsers and other applications to think about loading resources in a nice promie-like way instead of the old-fashioned blocking connection. You can send all your requests on the same connection and they can be fulfilled in parallel. 🚀

Server Push

First of all HTTP/2 server push is not a push notification system for applications. You can use it to send additional cache-able resources to the client that is not requested, but it’s highly anticipated in future requests. Real quick example: if the client requests for an index.html file, you can push back the corresponding sytle.css and main.js files in the response, so they’ll be there by the time the client actually needs them.

Header compression, encryption, binary format, etc.

I could continue with the benefits of the HTTP/2 but I believe the most important factor here is speed. HTTP/2 has a lighter network footprint and also eliminates some security concerns which is great for everyone. You can read more about the protocol on other sites, but for now let’s just stop right here.

Let’s start creating our HTTP/2 server in Swift using Vapor 4! 🤓

SwiftNIO2 + Vapor4 = HTTP/2 support

Apple’s cross-platform asynchronous event-driven network application framework supports HTTP/2 for a while. Vapor uses SwiftNIO since version 3, but only the 4th major version will have the brand new protocol support. Anyway it was a very long road, but we’re finally getting there and I’m really glad that this is happening now.

Both Swift, SwiftNIO and Vapor matured a lot in the past few years, if you’d like to spend more time on the server-side now it’s the best time to start learning these technologies and frameworks. Vapor 4 is going to be amazing, and I hope that server-side Swift apps will dominate the market in a few years. #swifttotalworlddomination

Backend language “hype” evolution: PHP -> node.js -> Swift?

Project setup

As usual, let’s start by creating a brand new project using the Vapor toolbox:

vapor new HTTP2Server
-cd HTTP2Server
-vapor update -y
-

This will give you a starter Xcode project template, based on the latest Vapor 4 branch. If you are completely new to Vapor, you should read my beginners tutorial about Vapor to get a basic understanding of the main components of the framework.

If you have an issue with Vapor, you should join the official Discord server, you’ll find some surprisingly good stuff and a really helpful community there. 😊

Certificate generation

Also because HTTP/2 is a secure protocol by default, you’ll need your own SSL certificate. You can generate a self-signed cert.pem and a key.pem files with the following command (fill out the details with some fake data and press enter). 🔐

openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem
-

That’s it, you should use these files for testing purposes only, also you still have to trust this self-signed local certificate. Your browser will tell you how to do it. 🤷‍♂️

Vapor 4 configuration with HTTP/2 support

In order to enable HTTP/2 support in Vapor 4, you have to register a new HTTPServer Configuration service. You can do this in the configure.swift file.

import Vapor
-import NIOSSL
-
-public func configure(_ app: Application) throws {
-
-    // access home directory:
-    // let homePath = NSString(string: "~").expandingTildeInPath
-
-    // use .env file to provide cert / key paths:
-    // let certPath = Environment.get("CERT_PATH")!
-    // let keyPath = Environment.get("KEY_PATH")!
-
-    let homePath = app.directory.workingDirectory
-    let certPath = homePath + "/cert.pem"
-    let keyPath = homePath + "/key.pem"
-
-    let certs = try! NIOSSLCertificate.fromPEMFile(certPath)
-        .map { NIOSSLCertificateSource.certificate($0) }
-
-    let tls = TLSConfiguration.forServer(
-        certificateChain: certs, 
-        privateKey: .file(keyPath)
-    )
-
-    app.http.server.configuration = .init(
-        hostname: "127.0.0.1",
-        port: 8080,
-        backlog: 256,
-        reuseAddress: true,
-        tcpNoDelay: true,
-        responseCompression: .disabled,
-        requestDecompression: .disabled,
-        supportPipelining: false,
-        supportVersions: Set<HTTPVersionMajor>([.two]),
-        tlsConfiguration: tls,
-        serverName: nil,
-        logger: nil
-    )
-}
-

First you have to load your certificate chain with the corresponding private key file. Next you have to make a proper TLS configuration using the SSL certificate. The last thing that you have to create is a new HTTP configuration object.

If you run the project and accept the self-signed certificate you should see in the inspector that the protocol is h2, which means HTTP/2 is alive. Congratulations! 🎉

Vapor HTTP/2 response

As you can see this article is more like a quick starting point to get HTTP/2 up and running in Vapor 4. Please share the article if you liked it & subscribe to my monthly newsletter below. Thanks for your help, bye! 🙏

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/about/index.html b/docs/about/index.html deleted file mode 100644 index e1067cb..0000000 --- a/docs/about/index.html +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - - - - - - - About - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -

About the The.Swift.Dev

- -
- - - -
- - - - diff --git a/docs/ajax-calls-using-vapor-4/index.html b/docs/ajax-calls-using-vapor-4/index.html deleted file mode 100644 index 229b471..0000000 --- a/docs/ajax-calls-using-vapor-4/index.html +++ /dev/null @@ -1,489 +0,0 @@ - - - - - - - - - - - - AJAX calls using Vapor 4 - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

-
- -

What is AJAX?

Asynchronous JavaScript and XML (AJAX) is a technology that allows us you to send HTTP requests to your web server from a web page. Based on the response you can use JavaScript to manipulate the HTML Document Object Model (DOM). In short, with the help of AJAX, you can ask for some data, then you can update the contents of the web site based on that.

The good thing about AJAX is that you don’t have to reload the entire page, but you can update just a portion of the site. The HTTP request will work on the background so from a user perspective the whole browsing experience will seem faster, than a full page load. ⌛️

Frontend vs backend

AJAX is a frontend technology. It’s a simple JavaScript function call, but some smart people gave it a fancy name. The X in the name comes from the early days of the web, when servers usually returned a “pre-rendered” partial HTML string that you could inject into the DOM without further data manipulation. Nowadays computers are so powerful that most of the servers can return JSON data and then the client can build the necessary HTML structure before the insertion.

In order to support AJAX calls on the server side we only have to implement the endpoint that the frontend can ask for. The communication is made through a standard HTTP call, so from a backend developer perspective we don’t really have to put any extra effort to support AJAX calls. 💪

Creating the server

Enough from the introduction, we now know what is AJAX and we are going to build a simple Vapor server to render our HTML document using Leaf Tau.

Tau was an experimental release, bit it was pulled from the final Leaf 4.0.0 release.

Tau will be available later on as a standalone repository with some new features, until that you can still use it if you pin the Leaf dependency to the exact release tag… 🤫

// swift-tools-version:5.3
-import PackageDescription
-
-let package = Package(
-    name: "myProject",
-    platforms: [
-       .macOS(.v10_15)
-    ],
-    dependencies: [
-        .package(url: "https://github.com/vapor/vapor", from: "4.35.0"),
-        .package(url: "https://github.com/vapor/leaf", .exact("4.0.0-tau.1")),
-        .package(url: "https://github.com/vapor/leaf-kit", .exact("1.0.0-tau.1.1")),
-    ],
-    targets: [
-        .target(
-            name: "App",
-            dependencies: [
-                .product(name: "Leaf", package: "leaf"),
-                .product(name: "LeafKit", package: "leaf-kit"),
-                .product(name: "Vapor", package: "vapor"),
-            ],
-            swiftSettings: [
-                .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"),
-        ])
-    ]
-)
-

Open the project with Xcode and set a custom working directory for the executable target. First we are going to build a very simple index.leaf file, you should add it to the Resources/Views directory. If there is no such directory structure in your project yet, please create the necessary folders.

<!DOCTYPE html>
-<html>
-  <head>
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <title>AJAX example</title>
-  </head>
-  <body>
-    <h1>AJAX example</h1>
-    
-    <button type="button" onclick="performAJAXCall()">Request data</button>
-
-    <div id="container"></div>
-
-    <script>
-    function performAJAXCall() {
-      var xhttp = new XMLHttpRequest();
-      xhttp.onreadystatechange = function() {
-          if (this.readyState == 4 && this.status == 200) {
-            document.getElementById("container").innerHTML = this.responseText;
-          }
-      };
-      xhttp.open("GET", "/ajax", true);
-      xhttp.send();
-    }
-    </script>
-
-  </body>
-</html>
-

Now if you take a closer look at our index.leaf file, you should notice that this template is actually a perfectly valid HTML file. We don’t need anything special in order to perform AJAX calls, but only a few lines of HTML and JavaScript code.

We can use a simple button and use the onclick attribute to call a JavaScript function, in our case this function is defined between the script tags and it is called performAJAXCall, but of course you can change this name to anything you’d like to use.

We create XMLHttpRequest object and set the onreadystatechange property to a custom anonymous function. This is the response handler, it will be called when the server returned a response. You should check both the readyState property of the XMLHttpRequest object and the returned status code if you only want to perform some operation when a valid response arrived and the operation finished. In our case, we are going to update our container with the response text.

The very last step is to call the open method using a HTTP method as the first parameter, a URL as a second, and make it asynchronous with a third (true) boolean value. This will initialize the request, so we still have to use the send() function to actually send it to our web server.

We actually need a working Vapor server that can render the index page when you enter the http://localhost:8080/ address to your browser. We also have to setup a new /ajax path and return some string that our frontend JavaScript code can place into the container HTML element, here’s one possible implementation of our backend application.

import Vapor
-import Leaf
-
-public func configure(_ app: Application) throws {
-
-    /// setup Leaf template engine
-    LeafRenderer.Option.caching = .bypass
-    app.views.use(.leaf)
-
-    /// index route
-    app.get { req in
-        req.leaf.render(template: "index")
-    }
-    
-    /// simple ajax response
-    app.get("ajax") { req in
-        "<strong>Lorem ipsum dolor sit amet</strong>"
-    }
-}
-

This is a 100% complete AJAX example using Vanilla JS (JavaScript without additional frameworks). It should work in most of the major browsers and it’s just about 10 lines of code. 💪

AJAX vs AJAJ

Asynchronous JavaScript and JSON. Let’s be honest, this is the real deal and in 99% of the cases this is what you actually want to implement. First we’re going to alter our server and return a JSON response instead of the plain old HTML string. 🤮

import Vapor
-import Leaf
-
-struct Album: Content {
-    let icon: String
-    let name: String
-    let artist: String
-    let year: String
-    let link: String
-}
-
-public func configure(_ app: Application) throws {
-
-    /// setup Leaf template engine
-    LeafRenderer.Option.caching = .bypass
-    app.views.use(.leaf)
-
-    /// index route
-    app.get { req in
-        req.leaf.render(template: "index")
-    }
-
-    /// pretty simple ajaj response
-    app.get("ajaj") { req  in
-        [
-            Album(icon: "❤️", name: "Amo", artist: "Bring me the Horizon", year: "2019", link: "https://music.apple.com/hu/album/amo/1439239477"),
-            Album(icon: "🔥", name: "Black Flame", artist: "Bury Tomorrow", year: "2018", link: "https://music.apple.com/hu/album/black-flame/1368696224"),
-            Album(icon: "💎", name: "Pressure", artist: "Wage War", year: "2019", link: "https://music.apple.com/hu/album/pressure/1470142125"),
-            Album(icon: "☀️", name: "When Legends Rise", artist: "Godsmack", year: "2018", link: "https://music.apple.com/hu/album/when-legends-rise/1440902339"),
-            Album(icon: "🐘", name: "Eat the Elephant", artist: "A Perfect Circle", year: "2018", link: "https://music.apple.com/hu/album/eat-the-elephant/1340651075"),
-        ]
-    }
-}
-

If you open the http://localhost:8080/ajaj URL you should see the returned JSON response. It is an array of the album objects, we are going to parse this JSON using JavaScript and display the results as a HTML structure.

<!DOCTYPE html>
-<html>
-  <head>
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <title>AJAX example</title>
-    <style>
-        .album {
-            border: 1px solid gray;
-            border-radius: 8px;
-            margin: 16px;
-            padding: 16px;
-            text-align: center;
-        }
-    </style>
-  </head>
-  <body>
-    <h1>AJAX example</h1>
-    
-    <button type="button" onclick="performAJAXCall()">Request data</button>
-
-    <div id="container"></div>
-
-    <script>
-    function performAJAXCall() {
-      var xhttp = new XMLHttpRequest();
-      xhttp.onreadystatechange = function() {
-          if (this.readyState == 4 && this.status == 200) {
-              var html = '';
-              var albums = JSON.parse(this.responseText);
-              if ( Array.isArray(albums) ) {
-                  albums.forEach(function(album, index) {
-                      html += '<div class="album">'
-                      html += '<h1>' + album.icon + '</h1>';
-                      html += '<h2>' + album.name + '</h2>';
-                      html += '<p>' + album.artist + '</p>';
-                      html += '<a href="' + album.link + '" target="_blank">Listen now</a>'
-                      html += '</div>'
-                  });
-              }
-              document.getElementById("container").innerHTML = html;
-          }
-      };
-      xhttp.open("GET", "/ajaj", true);
-      xhttp.send();
-    }
-    </script>
-
-  </body>
-</html>
-

The XMLHttpRequest method remains the same, but now take advantage of the built-in JSON.parse JavaScript function. This can parse any JSON object and returns the parsed object. We should always check if the result is the right type that we want to work with (in our case we only accept an array). Then we can use the properties of the album objects to construct our HTML code.

I’m not doing further validations and type checking, but you should always ensure that objects are not nil or undefined values. Anyway, this example shows us how to perform an AJAJ call, parse the response JSON and display the result in a nice way. 😅

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/all-about-authentication-in-vapor-4/index.html b/docs/all-about-authentication-in-vapor-4/index.html deleted file mode 100644 index f1ad7cb..0000000 --- a/docs/all-about-authentication-in-vapor-4/index.html +++ /dev/null @@ -1,704 +0,0 @@ - - - - - - - - - - - - All about authentication in Vapor 4 - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 16 min read - -
-

All about authentication in Vapor 4

-
-

Learn how to implement a user login mechanism with various auth methods using sessions, JWTs, written in Swift only.

-
- -

Authentication, authorization, sessions, tokens what the f*** is this all about???

The official Vapor docs about authentication are pretty good, but for a beginner it can be a little hard to understand, since it covers a lot. In this article I’ll try to explain everything as simple as possible from a different perspective. First let’s define some basic terms.

Authentication

Authentication is the act of verifying a user’s identity.

In other words, authentication is the process of transforming a unique key (identifier) to actual user data. This can be a cookie with a session identifier stored in a browser, or another one kept by the API client, but based on this id the backend can retrieve the associated user object.

The end user signs in using a login form on a website (or an API endpoint), sends the usual credentials (email, password) to the backend. If those credentials were valid, then the server will return a (randomly generated) identifier to the client. We usually call this identifier, session or token, based on some other principles I’ll cover later on. ⬇️

Next time the client wants to make a request it just needs to send the locally stored id, instead of the sensitive email, password combination. The server just needs to validate the id somehow, if it’s valid then the user is authenticated, we can use it to fetch more details about the user.

Authorization

The act of verifying a previously authenticated user’s permissions to perform certain tasks.

How do we know if the authenticated user has access to some endpoint on the server? Is it just a regular visitor, or an admin user? The method of figuring out user roles, permissions, access level is called authorization. It ensures that the authorized user can only access specific resources. 🔒

Consider the following scenario: there are two types of user roles: editors and visitors. An editor can create a new article, but a visitor can only view them (these are the permissions associated to the roles). EditorUser is in the group of editors, but VisitorUser only has the visitor role. We can figure out the authority (access level) for each user by checking the roles & permissions.

Session ID (authentication)> User (authorization)> Roles & Permissions

Vapor only gives you some help to authenticate the user using various methods. Authorization is usually part of your app’s business logic, this means that you have to figure out the details for your own needs, but this is just fine, don’t worry too much about it just yet. 😬

Sessions

If there is a record on the server side with an identifier, then it is a session.

For the sake of simplicity, let’s say that a session is something that you can look up on the server inside some kind of storage. This session is linked to exactly one user account so when you receive a session identifier you can look up the corresponding user through the relation.

The session identifier is exchanged to the client after a successful email + password based login request. The client stores session id somewhere for further usage. The storage can be anything, but browsers mainly use cookies or the local storage. Applications can store session identifiers in the keychain, but I’ve seen some really bad practices using a plain-text file. 🙉

Tokens

Tokens (JWTs) on the other hand have no server side records. A token can be given to the client by the authentication API after a successful login request. The key difference between a token and a session is that a token is cryptographically signed. Thanks to asymmetric keys, the signature can be verified by the application server without knowing the private key that was used to sign the token. A token usually self-contains some other info about the user, expiration date, etc. This additional “metadata” can also be verified by the server, this gives us an extra layer of security.

Nowadays JSON Web Token is the golden standard if it comes to tokens. JWT is getting more and more popular, implementations are available for almost every programming language with a wide variety of signing algorithms. There is a really amazing guide to JSON Web Tokens, you should definitely read it if you want to know more about this technology. 📖

Enough theory, time to write some code using Swift on the server.

Implementing auth methods in Vapor

As I mentioned this in the beginning of the article authentication is simply turning a request into actual user data. Vapor has built-in protocols to help us during the process. There is quite an abstraction layer here, which means that you don’t have to dig yourself into HTTP headers or incoming body parameters, but you can work with higher level functions to verify identify.

Let me show you all the auth protocols from Vapor 4 and how you can use them in practice. Remember: authentication in Vapor is about turning requests into models using the input.

Authentication using a Model

Each and every authentication protocol requires a model that is going to be retrieved during the authentication process. In this example I’ll work with a UserModel entity, here’s mine:

import Vapor
-import Fluent
-
-final class UserModel: Model {
-        
-    static let schema = "users"
-
-    struct FieldKeys {
-        static var email: FieldKey { "email" }
-        static var password: FieldKey { "password" }
-    }
-    
-    // MARK: - fields
-    
-    @ID() var id: UUID?
-    @Field(key: FieldKeys.email) var email: String
-    @Field(key: FieldKeys.password) var password: String
-    
-    init() { }
-    
-    init(id: UserModel.IDValue? = nil,
-         email: String,
-         password: String)
-    {
-        self.id = id
-        self.email = email
-        self.password = password
-    }
-}
-

If you don’t understand the code above, please read my comprehensive tutorial about Fluent, for now I’ll skip the migration part, so you have to write that on your own to make things work. ⚠️

Now that we have a model, it’s time to convert an incoming request to an authenticated model using an authenticator object. Let’s begin with the most simple one:

RequestAuthenticator

This comes handy if you have a custom authentication logic and you need the entire request object. Implementing the protocol is relatively straightforward. Imagine that some dumb-ass manager wants to authenticate users using the fragment identifier from the URL.

Not the smartest way of creating a safe authentication layer, but let’s make him happy with a nice solution. Again, if you can guess the user identifier and you pass it as a fragment, you’re signed in. (e.g. http://localhost:8080/sign-in#). If a user exists in the database with the provided UUID then we’ll authenticate it (yes without providing a password 🤦‍♂️), otherwise we’ll respond with an error code.

import Vapor
-import Fluent
-
-extension UserModel: Authenticatable {}
-
-struct UserModelFragmentAuthenticator: RequestAuthenticator {
-    typealias User = UserModel
-
-    func authenticate(request: Request) -> EventLoopFuture<Void> {
-        User.find(UUID(uuidString: request.url.fragment ?? ""), on: request.db)
-        .map {
-            if let user = $0 {
-                request.auth.login(user)
-            }
-        }
-    }
-}
-

Firstly, we create a typealias for the associated User type as our UserModel. It is a generic protocol, that’s why you need the typealias.

Inside the authenticator implementation you should look up the given user based on the incoming data, and if everything is valid you can simply call the req.auth.login([user]) method, this will authenticate the user. You should return a Void future from these authenticator protocol methods, but please don’t throw user related errors or use failed futures in this case. You should only supposed to forward database related errors or similar. If the authenticator can’t log in the user, just don’t call the login method, it’s that simple.

The second and final step is to write our authentication logic, in the auth method. You’ll get the request as an input, and you have to return a future with the authenticated user or nil if the authentication was unsuccesful. Pretty easy, fragment is available through the request, and you can look up the entity using Fluent. That’s it, we’re ready. 😅

The fragment URL part is never going to be available on the server side at all. 💡

How do we use this authenticator? Well the Authenticator protocol itself extends the Middleware protocol, so we can register it right away as a group member. You can use a middleware to alter incoming requests before the next request handler will be called. This definition fits perfectly for the authenticators so it makes sense that they are defined as middlewares.

We’ll need one more (guard) middleware that’s coming from the Authenticatable protocol to respond with an error to unauthenticated requests.

func routes(_ app: Application) throws {
-    
-    app.grouped(UserModelFragmentAuthenticator(),
-                UserModel.guardMiddleware())
-    .get("sign-in") { req in
-        "I'm authenticated"
-    }
-}
-

Now if you navigate to the http://localhost:8080/sign-in# URL, with a valid UUID of an existing user from the db, the page should display “I’m authenticated”, otherwise you’ll get an HTTP error. The magic happens in the background. I’ll explain the flow one more time.

The “sign-in” route has two middlewares. The first one is the authenticator which will try to turn the request into a model using the implemented authentication method. If the authentication was succesful it’ll store the user object inside a generic request.auth property.

The second middleware literally guards the route from unauthenticated requests. It checks the request.auth variable, if it contains an authenticated user object or not. If it finds a previously authenticated user it’ll continue with the next handler, otherwise it’ll throw an error. Vapor can automatically turn thrown errors into HTTP status codes, that’s why you’ll get a 401.

The names of the HTTP standard response codes are a little big misleading. You should respond with 401 (unauthorized) for unsuccesful authentication requests, and 403 (forbidden) responses for unauthorized requests. Strange, huh? 😳

You don’t necessary need this second middleware, but I’d recommend using it. You can manually check the existence of an authenticated object using try req.auth.require(UserModel.self) inside the request handler. A guard middleware is available on every Authenticatable object, essentially it is doing the same thing as I mentioned above, but in a more generic, reusable way.

Finally the request handler will only be called if the user is already authenticated, otherwise it’ll never be executed. This is how you can protect routes from unauthenticated requests.

BasicAuthenticator

A BasicAuthenticator is just an extension over the RequestAuthenticator protocol. During a basic authentication the credentials are arriving base64 encoded inside the Authorization HTTP header. The format is Authorization: Basic email:password where the email:password or username:password credentials are only base64 encoed. Vapor helps you with the decoding process, that’s what the protocol adds over the top of the request authentication layer, so you can write a basic authenticator like this:

struct UserModelBasicAuthenticator: BasicAuthenticator {
-
-    typealias User = UserModel
-    
-    func authenticate(basic: BasicAuthorization, for request: Request) -> EventLoopFuture<Void> {
-        User.query(on: request.db)
-            .filter(\.$email == basic.username)
-            .first()
-            .map {
-                do {
-                    if let user = $0, try Bcrypt.verify(basic.password, created: user.password) {
-                        request.auth.login(user)
-                    }
-                }
-                catch {
-                    // do nothing...
-                }
-        }
-    }
-}
-

Usage is pretty much the same, you just swap the authenticator or you can combine this one with the previous one to support multiple authentication methods for a single route. 😉

Basic auth using the ModelAuthenticatable protocol

You don’t always need to implement your own custom BasicAuthenticator. You can conform to the ModelAuthenticatable protocol. This way you can just write a password verifier and the underlying generic protocol implementation will take care of the rest.

extension UserModel: ModelAuthenticatable {
-    static let usernameKey = \UserModel.$email
-    static let passwordHashKey = \UserModel.$password
-
-    func verify(password: String) throws -> Bool {
-        try Bcrypt.verify(password, created: self.password)
-    }
-}
-
-// usage
-UserModel.authenticator()
-

This is pretty much the same as writing the UserModelBasicAuthenticator, the only difference is that this time I don’t have to implement the entire authentication logic, but I can simply provide the keypath for the username and password hash, and I just write the verification method. 👍

BearerAuthenticator

The bearer authentication is just a schema where you can send tokens inside the Authorization HTTP header field after the Bearer keyword. Nowadays this is the recommended way of sending JWTs to the backend. In this case Vapor helps you by fetching the value of the token.

struct UserModelBearerAuthenticator: BearerAuthenticator {
-    
-    typealias User = UserModel
-    
-    func authenticate(bearer: BearerAuthorization, for request: Request) -> EventLoopFuture<Void> {
-        // perform auth using the bearer.token value here...
-    }
-}
-

Custom Bearer auth using the ModelAuthenticatable protocol

I lied a little bit in the beginning, regarding sessions and tokens. We developers can call something that’s stored in a backend database as a token. Also we’re using the Authorization HTTP header field to authenticate users. The joke must be true, if it comes to naming things we are the worst. 😅

Back to the topic, storing a token in the database is more like an extended session, but fine, let’s just go with the token name this time. This ModelUserToken allows you to create a custom token in the database and use it to authenticate users through an Authorization Bearer header.

Let’s make a new Fluent model with an associated user to see how this works in practice.

final class UserTokenModel: Model {
-   
-   static let schema = "tokens"
-   
-   struct FieldKeys {
-       static var value: FieldKey { "value" }
-       static var userId: FieldKey { "user_id" }
-   }
-   
-   // MARK: - fields
-   
-   @ID() var id: UUID?
-   @Field(key: FieldKeys.value) var value: String
-   @Parent(key: FieldKeys.userId) var user: UserModel
-
-   init() { }
-   
-   init(id: UserTokenModel.IDValue? = nil,
-        value: String,
-        userId: UserModel.IDValue)
-   {
-       self.id = id
-       self.value = value
-       self.$user.id = userId
-   }
-}
-

Now all what’s left to do is to extend the protocol by providing the required keyPaths. This protocol allows you to perform extra checks on a given token, such as expiration date. The good news is that the protocol gives you a BearerAuthenticator middleware as a “gratis”.

extension UserTokenModel: ModelAuthenticatable {
-   static let valueKey = \UserTokenModel.$value
-   static let userKey = \UserTokenModel.$user
-   
-   var isValid: Bool {
-       true // you can check expiration or anything else...
-   }
-}
-
-// a middleware that confroms to the BearerAuthenticator protocol
-UserTokenModel.authenticator()
-

How do you give a token to the end user? Well, you can open up an endpoint with a basic auth protection, generate a token, save it to the database and finally return it back as a response. All of this is nicely written in the official authentication docs on the Vapor website. If you read that I belive that you’ll understand the whole purpose of these protocols. 💧

CredentialsAuthenticator

This authenticator can decode a specific Content from the HTTP body, so you can use the type-safe content fields right ahead. For example this comes handy when you have a login form on your website and you would like to submit the credentails through it. Regular HTML forms can send values encoded as multipart/form-data using the body, Vapor can decode every field on the other side. Another example is when you are sending the email, password credentials as a JSON object through a post body. curl -X POST "URL" -d '{"email": "", "password": ""}'

struct UserModelCredentialsAuthenticator: CredentialsAuthenticator {
-    
-    struct Input: Content {
-        let email: String
-        let password: String
-    }
-
-    typealias Credentials = Input
-
-    func authenticate(credentials: Credentials, for req: Request) -> EventLoopFuture<Void> {
-        UserModel.query(on: req.db)
-            .filter(\.$email == credentials.email)
-            .first()
-            .map {
-                do {
-                    if let user = $0, try Bcrypt.verify(credentials.password, created: user.password) {
-                        req.auth.login(user)
-                    }
-                }
-                catch {
-                    // do nothing...
-                }
-            }
-    }
-}
-

So as you can see most of these authenticator protocols are just helpers to transform HTTP data into Swift code. Nothing to worry about, you just have to know the right one for you needs.

So shouldn’t we put the pieces together already? Yes, but if you want to know more about auth you should check the source of the AuthenticationTests.swift file in the Vapor package. Now let me show you how to implement a session auth for your website.

Session based authentication

By default sessions will be kept around until you restart the server (or it crashes). We can change this by persisting sessions to an external storage, such as a Fluent database or a redis storage. In this example I’m going to show you how to setup sessions inside a postgresql database.

import Vapor
-import Fluent
-import FluentPostgresDriver
-
-extension Application {
-    static let databaseUrl = URL(string: Environment.get("DB_URL")!)!
-}
-
-public func configure(_ app: Application) throws {
-
-    try app.databases.use(.postgres(url: Application.databaseUrl), as: .psql)
-    
-    // setup persistent sessions
-    app.sessions.use(.fluent)
-    app.migrations.add(SessionRecord.migration)
-}
-

Setting up persistent sessions using Fluent as a storage driver is just two lines of code. ❤️

extension UserModel: SessionAuthenticatable {
-    typealias SessionID = UUID
-
-    var sessionID: SessionID { self.id! }
-}
-
-struct UserModelSessionAuthenticator: SessionAuthenticator {
-
-    typealias User = UserModel
-    
-    func authenticate(sessionID: User.SessionID, for req: Request) -> EventLoopFuture<Void> {
-        User.find(sessionID, on: req.db).map { user  in
-            if let user = user {
-                req.auth.login(user)
-            }
-        }
-    }
-}
-

As a next step you have to extend the UserModel with the unique session details, so the system can look up users based on the session id. Lastly you have to connect the routes.

import Vapor
-import Fluent
-
-func routes(_ app: Application) throws {
-
-    let session = app.routes.grouped([
-        SessionsMiddleware(session: app.sessions.driver),
-        UserModelSessionAuthenticator(),
-        UserModelCredentialsAuthenticator(),
-    ])
-
-    session.get { req -> Response in
-        guard let user = req.auth.get(UserModel.self) else {
-            return req.redirect(to: "/sign-in")
-        }
-
-        let body = """
-        <b>\(user.email)</b> is logged in <a href="/logout">Logout</a>
-        """
-
-        return .init(status: .ok,
-              version: req.version,
-              headers: HTTPHeaders.init([("Content-Type", "text/html; charset=UTF-8")]),
-              body: .init(string: body))
-    }
-    
-    session.get("sign-in") { req -> Response in
-        let body = """
-        <form action="/sign-in" method="post">
-            <label for="email">Email:</label>
-            <input type="email" id="email" name="email" value="">
-            
-            <label for="password">Password:</label>
-            <input type="password" id="password" name="password" value="">
-            
-            <input type="submit" value="Submit">
-        </form>
-        """
-
-        return .init(status: .ok,
-              version: req.version,
-              headers: HTTPHeaders.init([("Content-Type", "text/html; charset=UTF-8")]),
-              body: .init(string: body))
-    }
-
-    session.post("sign-in") { req -> Response in
-        guard let user = req.auth.get(UserModel.self) else {
-            throw Abort(.unauthorized)
-        }
-        req.session.authenticate(user)
-        return req.redirect(to: "/")
-    }
-    
-    session.get("logout") { req -> Response in
-        req.auth.logout(UserModel.self)
-        req.session.unauthenticate(UserModel.self)
-        return req.redirect(to: "/")
-    }
-
-}
-

First we setup the session routes by adding the sessions middleware using the database storage driver. Next we create an endpoint where we can display the profile if the user is authenticated, otherwise we redirect to the sign-in screen. The get sign in screen renders a basic HTML form (you can also use the Leaf templating engine for a better looking view) and the post sign-in route handles the authentication process. The req.session.authenticate method will store the current user info in the session storage. The logout route will remove the current user from the auth store, plus we’d also like to remove the associated user link from the session storage. That’s it. 😎

JWT based authentication

Vapor 4 comes with great JWT support as an external Swift package:

// swift-tools-version:5.2
-import PackageDescription
-
-let package = Package(
-    //...
-    dependencies: [
-        //...
-        .package(url: "https://github.com/vapor/jwt.git", from: "4.0.0-rc.1"),
-    ],
-    targets: [
-        .target(name: "App", dependencies: [
-            .product(name: "JWT", package: "jwt"),
-            //...
-        ]),
-        //...
-    ]
-)
-

In order to use sign and verify JWTs you’ll need a key-pair. The lib can generate one for you on the fly, but that’s not going to work so well, because each time you restart the application a new public and private key will be used in the core of the JWT signer. It’s better to have one sitting somewhere on the disk, you can generate one (RS256) by running:

ssh-keygen -t rsa -b 4096 -m PEM -f jwtRS256.key
-openssl rsa -in jwtRS256.key -pubout -outform PEM -out jwtRS256.key.pub
-

I usually put thes generated files into my working directory. Since the algorithm (RS256) I’m using to sign the token is asymmetric I’ll create 2 signers with different identifiers. A private signer is used to sign JWTs, a public one is used to verify the signature of the incoming JWTs.

import Vapor
-import JWT
-
-extension String {
-    var bytes: [UInt8] { .init(self.utf8) }
-}
-
-extension JWKIdentifier {
-    static let `public` = JWKIdentifier(string: "public")
-    static let `private` = JWKIdentifier(string: "private")
-}
-
-public func configure(_ app: Application) throws {
-    
-    //...
-
-    let privateKey = try String(contentsOfFile: app.directory.workingDirectory + "jwtRS256.key")
-    let privateSigner = try JWTSigner.rs256(key: .private(pem: privateKey.bytes))
-    
-    let publicKey = try String(contentsOfFile: app.directory.workingDirectory + "jwtRS256.key.pub")
-    let publicSigner = try JWTSigner.rs256(key: .public(pem: publicKey.bytes))
-     
-    app.jwt.signers.use(privateSigner, kid: .private)
-    app.jwt.signers.use(publicSigner, kid: .public, isDefault: true)
-}
-

Verifying and signing a token is just a one-liner. You can use some of the authenticators from above to pass around a token to the request handler, somewhat the same way as we did it in the sessions example. However you’ll need to define a custom JWTPayload object that contains all the fields used in the token. This payload protocol should implement a verify method that can help you with the verification process. Here’s a really simple example how to sign and return a JWTPayload:

import Vapor
-import JWT
-
-struct Example: JWTPayload {
-    var test: String
-
-    func verify(using signer: JWTSigner) throws {}
-}
-
-func routes(_ app: Application) throws {
-    let jwt = app.grouped("jwt")
-
-    jwt.get { req in
-        // sign the payload using the private key
-        try req.jwt.sign(Example(test: "Hello world!"), kid: .private)
-
-        // verify a token using the public key
-        //try request.jwt.verify(token, as: Example.self)
-    }
-}
-

A payload contains small pieces of information (claims). Each of them can be verified through the previously mentioned verify method. The good thing is that the JWT package comes with lots of handy claim types (including validators), feel free to pick the ones you need from the package (JWTKit/Sources/Claims directory). Since there are no official docs yet, you should check the source in this case, but don’t be afraid claims are very easy to understand. 🤐

struct TestPayload: JWTPayload, Equatable {
-    var sub: SubjectClaim // a subject claim
-    var name: String
-    var admin: Bool
-    var exp: ExpirationClaim // an expiration claim
-
-    func verify(using signer: JWTSigner) throws {
-        try self.exp.verifyNotExpired()
-    }
-}
-let payload = TestPayload(sub: "vapor",
-                          name: "Foo",
-                          admin: false,
-                          exp: .init(value: .init(timeIntervalSince1970: 2_000_000_000)))
-
-let signed = try app.jwt.signers.get(kid: .private)!.sign(payload)
-
-// Verification tests:
-//print(try! app.jwt.signers.get()!.verify(signed.bytes, as: TestPayload.self) == payload)
-//print(try! app.jwt.signers.get(kid: .private)!.verify(signed.bytes, as: TestPayload.self) == payload)
-

Tokens can be verified using both the public & the private keys. The public key can be shared with anyone, but you should NEVER give away the private key. There is an best practice to share keys with other parties called: JWKS. Vapor comes with JWKS support, so you can load keys from a remote URLs using this method. This time I won’t get into the details, but I promise that I’m going to make a post about how to use JWKS endpoints later on (Sign in with Apple tutorial). 🔑

Based on this article now you should be able to write your own authentication layer that can utilize a JWT token as a key. A possible authenticator implementation could look like this:

extension UserModel: Authenticatable {}
-
-struct JWTUserModelBearerAuthenticator: BearerAuthenticator {
-    typealias User = UserModel
-    
-    func authenticate(bearer: BearerAuthorization, for request: Request) -> EventLoopFuture<User?> {
-        do {
-            let jwt = try request.jwt.verify(bearer.token, as: JWTAuth.self)
-            return User.find(UUID(uuidString: jwt.userId), on: request.db)
-        }
-        catch {
-            return request.eventLoop.makeSucceededFuture(nil)
-        }
-    }
-}
-

The other thing that you’ll need is an endpoint that can exchange a JWT for the login credentials. You can use some other authenticators to support multiple authentication methods, such as basic or credentials. Don’t forget to guard the protected routes using the correct middleware. 🤔

Conclusion

Authentication is a really heavy topic, but fortunately Vapor helps a lot with the underlying tools. As you can see I tried to cover a lot in this artilce, but still I could write more about JWKS, OAuth, etc.

I really hope that you’ll find this article useful to understand the basic concepts. The methods described here are not bulletproof, the purpose here is not to demonstrate a secure layer, but to educate people about how the authentication layer works in Vapor 4. Keep this in mind. 🙏

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/all-about-the-bool-type-in-swift/index.html b/docs/all-about-the-bool-type-in-swift/index.html deleted file mode 100644 index 10c5b6d..0000000 --- a/docs/all-about-the-bool-type-in-swift/index.html +++ /dev/null @@ -1,484 +0,0 @@ - - - - - - - - - - - - All about the Bool type in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 6 min read - -
-

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

-
- -

Booleans in the Swift language

Computers essentially understand two things: ones and zeros. Of course the whole story it’s a bit more complicated, but if we dig down deep enough the underlying data it’s going to be either a true or a false value that represents something. 1 means true, 0 means false. 🙃

In Swift we can express these kind of boolean values by using the Bool data type, which you can create using true or false literals. The Bool type is a struct, that you can create multiple ways.

let thisIsTrue: Bool = true
-
-let thisIsFalse = false
-
-let foo = Bool(true) 
-
-let bar = Bool("false")!
-
-let baz = Bool.random() // true or false
-

It is possible to transform these values, there are plenty of logical operators available on the Bool struct, the most common ones are the following:

  • NOT: ! -> toggle a boolean value
  • OR: || -> if one of the conditions are true, it’s true
  • AND: && -> if both conditions are true, it’s true otherwise false

All the comparison operators produce boolean values to indicate whether the statement is true or false. In Swift you can compare most of the basic data types, in this example I’ll show you a few number comparison statements, since it’s quite a trivial showcase for demoing the bool results. ☺️

var foo = true
-foo.toggle()            // foo is now false
-print(foo)              // false
-
-print(!foo)             // true
-print(foo && true)      // false
-print(foo || true)      // true
-
-print(3 == 4)           // false
-print(3 != 4)           // true
-print(3 > 2)            // true
-print(3 >= 3)           // true
-print(3 < 1)            // false
-print(3 <= 4)           // true
-
-// it works with other built-in types as well...
-print("foo" == "bar")   // false
-print(3.14 < 5.23)      // true
-print(true != false)    // true
-

This is quite straightforward so far, but what can you do with a boolean in Swift? Well, turns out there are quite a lot of options. First of all, conditional statements (if, else if, else) usually require a true boolean value to execute the code inside the conditional block.

let foo = Bool.random()
-/// it the condition is true, perfrom the first if block, otherwise the else
-if foo {
-    print("I was lucky. 🍀")
-}
-else {
-    print("No luck this time. 🥲")
-}
-
-// or 
-
-print(foo ? "I was lucky. 🍀" : "No luck this time. 🥲")
-

You can evaluate multiple conditions by using a logical operator, this way you can create more complex conditions, but it is worth to mention that if you combine them with and operators and the condition is dynamically calculated (e.g. a return of a function call), the entire chain will be called until you reach the very first false condition. This optimization is very handy in most of the cases.

var firstCondition = false
-
-func secondCondition() -> Bool {
-    print("⚠️ This won't be called at all.")
-    return true
-}
-
-if firstCondition && secondCondition() {
-    print("if branch is called")
-}
-else {
-    print("else branch is called")
-}
-

We also use a Bool value to run a cycle until a specific condition happens. In Swift there are multiple types of loops to execute a blcok of code multiple types. In this case here is an example using the while loop. While the condition is true, the loop will continue iterating, but if you make it false, the cycle will break. It is possible to have 0 iterations if the initial condition is false. 👌

The repeat-while loop is kind of a special form of the while loop, if you are sure that you want to execute your code at least 1 times before evaluating the ‘escape’ condition you should use this one. Until the condition is true the loop goes on, when it is false, it’ll break and it’ll exit the cycle. ☝️

var counter = 0
-var counterIsNotTen = true
-// while the condition is true, perform the code in the block
-while counterIsNotTen {
-    counter += 1
-    print(counter)
-    counterIsNotTen = counter != 10
-}
-
-// or
-
-var counter = 0
-var counterIsNotTen = true
-// repeat the block while the condition is true 
-repeat {
-    counter += 1
-    print(counter)
-    counterIsNotTen = counter != 10
-} while counterIsNotTen
-

There are some ‘special’ functions that require a block that returns a Bool value in order to make something happen. This might sounds complicated at first sight, but it’s quite simple if you take a closer look at the example. There is a filter method defined on the Sequence protocol that you can use and provide a custom Bool returning closure to filter elements.

In our case the sequence is a simple array that contains numbers from 0 until 100. Now the task is to get back only the elements under 50. We could use a for cycle and apply a where condition to collect all the elements into a new array, but fortunately the filter method gives us a better alternative. We pass a closure using the brackets and check if the current element ($0) value is less than 50. If the condition is true, the element will be returned and our bar array will be filled with only those elements that match the condition inside the block / closure.

let foo = Array(0...100)
-
-for x in foo where x < 50 {
-    print(x)
-}
-
-let bar = foo.filter { $0 < 50 }
-print(bar)
-

It is also possible to create a custom object that represents a bool value. There is a really old blog post about this on the official Apple dev blog, but let me show you how to define such a value using Swift 5. There are just a few changes and I’ll ignore the bitwise operators for now, that’s going to be a topic of another blog post in the future… 😉

enum MyBool {
-    case myTrue
-    case myFalse
-    
-    init() {
-        self = .myFalse
-    }
-}
-
-extension MyBool: Equatable {
-    static func == (lhs: Self, rhs: Self) -> Bool {
-        switch (lhs, rhs) {
-        case (.myTrue,.myTrue), (.myFalse,.myFalse):
-            return true
-        default:
-            return false
-        }
-    }
-}
-
-extension MyBool: ExpressibleByBooleanLiteral {
-    init(booleanLiteral value: BooleanLiteralType) {
-        self = value ? .myTrue : .myFalse
-    }
-}
-
-extension MyBool {
-    var boolValue: Bool {
-        switch self {
-        case .myTrue:
-            return true
-        case .myFalse:
-            return false
-        }
-    }
-}
-
-let foo = MyBool()          // init with false default
-print(foo)                  // myFalse
-print(foo.boolValue)        // false
-print(foo == true)          // .myFalse == .myTrue -> false
-

Did you know that there is a legacy boolean type, coming from the Objective-C times?

Boolean algebra in Swift

If it comes to the Bool type in any programming language, I feel like it is necessary to talk a bit about the Boolean algebra and truth tables. There are some basic operations that we can perform on Bool values (NOT, AND, OR), we’ve already talked about these, here is how we can express the corresponding truth tables in Swift (don’t worry it’s pretty easy). 💪

// not   x   is    z
-print(!true)    // false
-print(!false)   // true
-
-//     x   and   y    is     z
-print(false && false)   // false
-print(true && false)    // false
-print(false && true)    // false
-print(true && true)     // true
-
-//      x   or   y    is     z
-print(false || false)   // false
-print(true || false)    // true
-print(false || true)    // true
-print(true || true)     // true
-

We can also visualize the AND and OR operations using set algebra. The AND operation is often called conjunction which means the common elements from both sets. The OR operation is called logical disjunction and it refers to elements from either sets. Ok, that’s enough math for now. 😅

There are some secondary operations that we still have to talk about, this might involves some more basic math, but I’ll try to explain it as simple as possible. Let’s start with the exclusive or operation (XOR), which only results in a true result if exactly one of the conditions is true and the other is false. Compared to the OR operation it excludes the possibility of two true values.

/// custom XOR operator
-infix operator ⊕
-func ⊕(_ lhs: Bool, _ rhs: Bool) -> Bool {
-    lhs && !rhs || !lhs && rhs
-}
-
-//      x   xor   y    is     z
-print(false ⊕ false)     // false
-print(false ⊕ true)      // true
-print(true ⊕ false)      // true
-print(true ⊕ true)       // false
-

In Swift you can create custom operator functions, in our case we’ve assigned the ⊕ symbol as our XOR infix operator and used the equation from Wikipedia to compose the actual implementation of the function body from the basic logical operations.

Let’s do the same for the next secondary operation called: material conditional.

/// custom material conditional operation
-infix operator →
-func →(_ lhs: Bool, _ rhs: Bool) -> Bool {
-    !lhs || rhs
-}
-
-//      x   →   y    is     z
-print(false → false)     // true
-print(false → true)      // true
-print(true → false)      // false
-print(true → true)       // true
-

I’ll not go too much into the details here, you can read all about material implication on the linked Wikipedia article. Our final secondary operation is the logical equivalence, here’s how it looks like:

/// custom logical equivalence operator
-infix operator ≡
-func ≡(_ lhs: Bool, _ rhs: Bool) -> Bool {
-    lhs && rhs || !lhs && !rhs
-}
-
-//      x   ≡   y    is     z
-print(false ≡ false)     // true
-print(false ≡ true)      // false
-print(true ≡ false)      // false
-print(true ≡ true)       // true
-

Of course we could talk a lot more about laws, completeness and other things, but in most of the cases you don’t need the secondary operations, except the XOR, that’s quite “popular”. As you can see conditions are everywhere and it is possible to do some magical things using boolean values. Anyway, I hope you enjoyed this tutorial about the Bool type in the Swift language. 🤓

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/all-about-the-swift-package-manager-and-the-swift-toolchain/index.html b/docs/all-about-the-swift-package-manager-and-the-swift-toolchain/index.html deleted file mode 100644 index 1ea7947..0000000 --- a/docs/all-about-the-swift-package-manager-and-the-swift-toolchain/index.html +++ /dev/null @@ -1,351 +0,0 @@ - - - - - - - - - - - - All about the Swift Package Manager and the Swift toolchain - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 6 min read - -
-

All about the Swift Package Manager and the Swift toolchain

-
-

Learn everything about the SPM architecture. I'll also teach you how to integrate your binary executable into the Swift toolchain.

-
- -

If you don’t know too much about the Swift Package Manager, but you are looking for the basics please read my tutorial about SPM that explains pretty much everything. The aim of this article is to go deep into the SPM architecture, also before you start reading this I’d recommend to also read my article about frameworks and tools. 📖

Ready? Go! I mean Swift! 😂

Swift Package Manager

Have you ever wondered about how does SPM parse it’s manifest file in order to install your packages? Well, the Package.swift manifest is a strange beast. Let me show you an quick example of a regular package description file:

// swift-tools-version:4.2
-import PackageDescription
-
-let package = Package(
-    name: "HelloSwift",
-    dependencies: [
-        // .package(url: /* package url */, from: "1.0.0"),
-    ],
-    targets: [
-        .target(
-            name: "HelloSwift",
-            dependencies: []),
-        .testTarget(
-            name: "HelloSwiftTests",
-            dependencies: ["HelloSwift"]),
-    ]
-)
-

The first line contains the version information, next we have to import the PackageDescription module which contains all the required elements to properly describe a Swift package. If you run for example swift package update all your dependencies in this manifest file will be resolved & you can use them inside your own code files. ✅

But how the heck are they doing this magic? 💫

That question was bugging me for a while, so I did a little research. First I was trying to replicate this behavior without looking at the original implementation of the Swift Package Manager at GitHub. I knew I shouldn’t parse the Swift file, because that’d be a horrible thing to do - Swift files are messy - so let’s try to import it somehow… 🙃

Dynamic library loading approach

I searched for the “dynamic swift library” keywords and found an interesting forum topic on swift.org. Yeah, I’m making some progress I thought. WRONG! I was way further from the actual solution than I though, but it was fun, so I was looking into the implementation details of how to open a compiled .dylib file using dlopen & dlsym from Swift. How does one create a .dylib file? Ah, I already know this! 👍

I always wanted to understand this topic better, so I started to read more and more both about static and dynamic libraries. Long story short, you can create a dynamic (or static) library with the following product definition:

// swift-tools-version:4.2
-import PackageDescription
-
-let package = Package(
-    name: "example",
-    products: [
-        .library(name: "myStaticLib", type: .static, targets: ["myStaticLib"]),
-        .library(name: "myDynamicLib", type: .dynamic, targets: ["myDynamicLib"]),
-    ],
-    targets: [
-        .target(
-            name: "myStaticLib",
-            dependencies: []),
-        .target(
-            name: "myDynamicLib",
-            dependencies: []),
-    ]
-)
-

The important files are going to be located inside the .build/debug folder. The .swiftmodule is basically the public header file, this contains all the available API for your library. The .swiftdoc file contains the documentation for the compiled module, and depending on the type you’ll also get a .dylib or a .a file. Guess which one is which.

So I could load the .dylib file by using dlopen & dlsym (some @_cdecl magic involved to get constant names instead of the “fuzzy” ones), but I was constantly receiving the same warning over and over again. The dynamic loading worked well, but I wanted to get rid of the warning, so I tried to remove the embedded the lib dependency from my executable target. (Hint: not really possible… afaik. anyone? 🙄)

I was messing around with rpaths & the install_name_tool for like hours, but even after I succesfully removed my library from the executable, “libSwift*things” were still embedded into it. So that’s the sad state of an unstable ABI, I thought… anyway at least I’ve learned something very important during the way here:

Importing Swift code into Swift!

Yes, you heard that. It’s possible to import compiled Swift libraries into Swift, but not a lot of people heard about this (I assume). It’s not a popular topic amongs iOS / UIKit developers, but SPM does this all the time behind the scenes. 😅

How the heck can we import the pre-built libraries? Well, it’s pretty simple.

// using swiftc with compiler flags
-
-swiftc dynamic_main.swift -I ./.build/debug -lmyDynamicLib -L ./.build/debug
-swiftc static_main.swift -I ./.build/debug -lmyStaticLib -L ./.build/debug
-
-// using the Swift Package Manager with compiler flags
-
-swift build -Xswiftc -I -Xswiftc ./.build/debug -Xswiftc -L -Xswiftc ./.build/debug -Xswiftc -lmyStaticLib
-swift build -Xswiftc -I -Xswiftc ./.build/debug -Xswiftc -L -Xswiftc ./.build/debug -Xswiftc -lmyDynamicLib
-

You just have to append a few compiler flags. The -I stands for the import search path, -L is the library search path, -l links the given library. Check swiftc -h for more details and flags you won’t regret it! Voilá now you can distribute closed source Swift packages. At least it was good to know how SPM does the “trick”. 🤓

Please note that until Swift 5 & ABI stability arrives you can use the precompiled libraries with the same Swift version only! So if you compile a lib with Swift 4.2, your executable also needs to be compiled with 4.2., but this will change pretty soon. 👏

The Swift Package Manager method

After 2 days of research & learning I really wanted to solve this, so I’ve started to check the source code of SPM. The first thing I’ve tried was adding the --verbose flag after the swift build command. Here is the important thing:

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc \
---driver-mode=swift \
--L /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/pm/4_2 \
--lPackageDescription \
--suppress-warnings \
--swift-version 4.2 \
--I /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/pm/4_2 \
--target x86_64-apple-macosx10.10 \
--sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk \
-/Users/tib/example/Package.swift \
--fileno 5
-

Whoa, this spits out a JSON based on my Package.swift file!!! 🎉

How the hell are they doing this?

It turns out, if you change the -fileno parameter value to 1 (that’s the standard output) you can see the results of this command on the console. Now the trick here is that SPM simply compiles the Package.swift and if there is a -fileno flag present in the command line arguments, well it prints out the encoded JSON representation of the Package object after the process exits. That’s it, fuckn’ easy, but it took 1 more day for me to figure this out… parenting 2 kids & coding is a hard combination. 🤷‍♂️

If you open the /Applications/Xcode.app/Contents/Developer/ Toolchains/XcodeDefault.xctoolchain/ usr/lib/swift/pm/4_2 folder you’ll see 3 familiar files there. Exactly. I also looked at the source of the Package.swift file from the SPM repository, and followed the registerExitHandler method. After a successful Package initialization it simply registers an exit handler if a -fileno argument is present encodes itself & dumps the result by using the file handler number. Sweet! 😎

Since I was pretty much in the finish lap, I wanted to figure out one more thing: how did they manage to put the swift package command under the swift command?

Swift toolchain

I just entered swift lol into my terminal. This is what happened:

tib@~: swift lol
-error: unable to invoke subcommand:
-/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-lol
-(No such file or directory)
-

Got ya! The toolchain is the key to everything:

  1. Apple is compiling the PackageDescription library from the Swift Package Manager and puts the .swiftmodule, .swiftdoc, .dylib files into the proper places under Xcode’s default toolchain library path.
  2. The swift build, run, test subcommands are just another Swift binary executables placed inside the toolchain’s binary path. (Named like: swift-package, swift-build, swift-run, swift-test)
  3. The swift command tries to invoke the proper subcommand if there is any and it’s a valid (Swift) binary. (Tried with a shell script, it failed miserably…)
  4. SPM uses the PackageDescription library from the toolchain in order to compile & turn the manifest file into JSON output.
  5. The rest is history. 🤐

Swift can resolve subcommands from anywhere “inside” the PATH variable. You just have to prefix your Swift script with swift- and you’re good to go.

SwiftCI - a task runner for Swift

I had this idea that it’d be nice to have a grunt / gulp like task runner also a continuous integration service on a long term by using this technique I explained above. So I’ve made a similar extension wired into the heart of the Swift toolchain: SwiftCI. ❤️

You can grab the proof-of-concept implementation of SwiftCI from GitHub. After installing it you can create your own CI.swift files and run your workflows.

import CI
-
-let buildWorkflow = Workflow(
-    name: "default",
-    tasks: [
-        Task(name: "HelloWorld",
-             url: "git@github.com:BinaryBirds/HelloWorld.git",
-             version: "1.0.0",
-             inputs: [:]),
-
-        Task(name: "OutputGenerator",
-             url: "~/ci/Tasks/OutputGenerator",
-             version: "1.0.0",
-             inputs: [:]),
-
-        Task(name: "SampleTask",
-             url: "git@github.com:BinaryBirds/SampleTask.git",
-             version: "1.0.1",
-             inputs: ["task-input-parameter": "Hello SampleTask!"]),
-    ])
-
-let testWorkflow = Workflow(
-    name: "linux",
-    tasks: [
-        Task(name: "SampleTask",
-             url: "https://github.com/BinaryBirds/SampleTask.git",
-             version: "1.0.0",
-             inputs: ["task-input-parameter": "Hello SampleTask!"]),
-        ])
-
-let project = Project(name: "Example",
-                      url: "git@github.com:BinaryBirds/Example.git",
-                      workflows: [buildWorkflow, testWorkflow])
-

The code above is a sample from a CI.swift file, you can simply run any workflow with the swift CI run workflow-name command. Everything is 100% written in Swift, even the CI workflow descriptor file. I’m planning to extend my CI namespace with some helpful sub-commands later on. PR’s are more than welcomed!

I’m very happy with the result, not just because of the final product (that’s only a proof of concept implementation), but mostly because of the things I’ve learned during the creation process.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Swift Package Manager and the Swift toolchain

-
-

Learn everything about the SPM architecture. I'll also teach you how to integrate your binary executable into the Swift toolchain.

- -
-
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/assets/a-simple-http2-server-using-vapor-4/vapor-http-2-response.jpg b/docs/assets/a-simple-http2-server-using-vapor-4/vapor-http-2-response.jpg deleted file mode 100644 index e958c75..0000000 Binary files a/docs/assets/a-simple-http2-server-using-vapor-4/vapor-http-2-response.jpg and /dev/null differ diff --git a/docs/assets/about/cover.jpg b/docs/assets/about/cover.jpg deleted file mode 100644 index 8fc650a..0000000 Binary files a/docs/assets/about/cover.jpg and /dev/null differ diff --git a/docs/assets/authors/tibor-bodecs/tibor-bodecs.jpeg b/docs/assets/authors/tibor-bodecs/tibor-bodecs.jpeg deleted file mode 100644 index 1c4bce5..0000000 Binary files a/docs/assets/authors/tibor-bodecs/tibor-bodecs.jpeg and /dev/null differ diff --git a/docs/assets/beginners-guide-to-server-side-swift-using-vapor-4/xcode-custom-working-directory.png b/docs/assets/beginners-guide-to-server-side-swift-using-vapor-4/xcode-custom-working-directory.png deleted file mode 100644 index 87d2f48..0000000 Binary files a/docs/assets/beginners-guide-to-server-side-swift-using-vapor-4/xcode-custom-working-directory.png and /dev/null differ diff --git a/docs/assets/beginners-guide-to-server-side-swift-using-vapor-4/xcode-custom-working-directory~dark.png b/docs/assets/beginners-guide-to-server-side-swift-using-vapor-4/xcode-custom-working-directory~dark.png deleted file mode 100644 index ff9f99f..0000000 Binary files a/docs/assets/beginners-guide-to-server-side-swift-using-vapor-4/xcode-custom-working-directory~dark.png and /dev/null differ diff --git a/docs/assets/beginners-guide-to-server-side-swift-using-vapor-4/xcode-environment.png b/docs/assets/beginners-guide-to-server-side-swift-using-vapor-4/xcode-environment.png deleted file mode 100644 index adc1e0b..0000000 Binary files a/docs/assets/beginners-guide-to-server-side-swift-using-vapor-4/xcode-environment.png and /dev/null differ diff --git a/docs/assets/beginners-guide-to-server-side-swift-using-vapor-4/xcode-environment~dark.png b/docs/assets/beginners-guide-to-server-side-swift-using-vapor-4/xcode-environment~dark.png deleted file mode 100644 index 66de0b2..0000000 Binary files a/docs/assets/beginners-guide-to-server-side-swift-using-vapor-4/xcode-environment~dark.png and /dev/null differ diff --git a/docs/assets/clockkit-complications-cheat-sheet/clockkit-cheatsheet.jpg b/docs/assets/clockkit-complications-cheat-sheet/clockkit-cheatsheet.jpg deleted file mode 100644 index ba13770..0000000 Binary files a/docs/assets/clockkit-complications-cheat-sheet/clockkit-cheatsheet.jpg and /dev/null differ diff --git a/docs/assets/conventions-for-xcode/xcode-naming-conventions.jpg b/docs/assets/conventions-for-xcode/xcode-naming-conventions.jpg deleted file mode 100644 index a443060..0000000 Binary files a/docs/assets/conventions-for-xcode/xcode-naming-conventions.jpg and /dev/null differ diff --git a/docs/assets/conventions-for-xcode/xcode-targets.jpg b/docs/assets/conventions-for-xcode/xcode-targets.jpg deleted file mode 100644 index dedf867..0000000 Binary files a/docs/assets/conventions-for-xcode/xcode-targets.jpg and /dev/null differ diff --git a/docs/assets/custom-uiview-subclass-from-a-xib-file/ib.png b/docs/assets/custom-uiview-subclass-from-a-xib-file/ib.png deleted file mode 100644 index b333fef..0000000 Binary files a/docs/assets/custom-uiview-subclass-from-a-xib-file/ib.png and /dev/null differ diff --git a/docs/assets/custom-uiview-subclass-from-a-xib-file/ownership.jpg b/docs/assets/custom-uiview-subclass-from-a-xib-file/ownership.jpg deleted file mode 100644 index 23f9e83..0000000 Binary files a/docs/assets/custom-uiview-subclass-from-a-xib-file/ownership.jpg and /dev/null differ diff --git a/docs/assets/custom-uiview-subclass-from-a-xib-file/storyboard.png b/docs/assets/custom-uiview-subclass-from-a-xib-file/storyboard.png deleted file mode 100644 index b9e2894..0000000 Binary files a/docs/assets/custom-uiview-subclass-from-a-xib-file/storyboard.png and /dev/null differ diff --git a/docs/assets/custom-working-directory-in-xcode/custom-working-directory.png b/docs/assets/custom-working-directory-in-xcode/custom-working-directory.png deleted file mode 100644 index 3ee3061..0000000 Binary files a/docs/assets/custom-working-directory-in-xcode/custom-working-directory.png and /dev/null differ diff --git a/docs/assets/custom-working-directory-in-xcode/custom-working-directory~dark.png b/docs/assets/custom-working-directory-in-xcode/custom-working-directory~dark.png deleted file mode 100644 index 0b7c3b2..0000000 Binary files a/docs/assets/custom-working-directory-in-xcode/custom-working-directory~dark.png and /dev/null differ diff --git a/docs/assets/custom-working-directory-in-xcode/edit-scheme.png b/docs/assets/custom-working-directory-in-xcode/edit-scheme.png deleted file mode 100644 index 4f28f20..0000000 Binary files a/docs/assets/custom-working-directory-in-xcode/edit-scheme.png and /dev/null differ diff --git a/docs/assets/custom-working-directory-in-xcode/edit-scheme~dark.png b/docs/assets/custom-working-directory-in-xcode/edit-scheme~dark.png deleted file mode 100644 index 1859c07..0000000 Binary files a/docs/assets/custom-working-directory-in-xcode/edit-scheme~dark.png and /dev/null differ diff --git a/docs/assets/custom-working-directory-in-xcode/target.png b/docs/assets/custom-working-directory-in-xcode/target.png deleted file mode 100644 index 887fd53..0000000 Binary files a/docs/assets/custom-working-directory-in-xcode/target.png and /dev/null differ diff --git a/docs/assets/custom-working-directory-in-xcode/target~dark.png b/docs/assets/custom-working-directory-in-xcode/target~dark.png deleted file mode 100644 index 7150cf4..0000000 Binary files a/docs/assets/custom-working-directory-in-xcode/target~dark.png and /dev/null differ diff --git a/docs/assets/custom-working-directory-in-xcode/warning-no-custom-working-directory-set.png b/docs/assets/custom-working-directory-in-xcode/warning-no-custom-working-directory-set.png deleted file mode 100644 index a95229c..0000000 Binary files a/docs/assets/custom-working-directory-in-xcode/warning-no-custom-working-directory-set.png and /dev/null differ diff --git a/docs/assets/custom-working-directory-in-xcode/warning-no-custom-working-directory-set~dark.png b/docs/assets/custom-working-directory-in-xcode/warning-no-custom-working-directory-set~dark.png deleted file mode 100644 index 7c7ae51..0000000 Binary files a/docs/assets/custom-working-directory-in-xcode/warning-no-custom-working-directory-set~dark.png and /dev/null differ diff --git a/docs/assets/deep-dive-into-swift-frameworks/build-settings.png b/docs/assets/deep-dive-into-swift-frameworks/build-settings.png deleted file mode 100644 index 74fd3d9..0000000 Binary files a/docs/assets/deep-dive-into-swift-frameworks/build-settings.png and /dev/null differ diff --git a/docs/assets/deep-dive-into-swift-frameworks/dynamic-linking.png b/docs/assets/deep-dive-into-swift-frameworks/dynamic-linking.png deleted file mode 100644 index 3b9dc5a..0000000 Binary files a/docs/assets/deep-dive-into-swift-frameworks/dynamic-linking.png and /dev/null differ diff --git a/docs/assets/deep-dive-into-swift-frameworks/static-linking.png b/docs/assets/deep-dive-into-swift-frameworks/static-linking.png deleted file mode 100644 index 654d0e1..0000000 Binary files a/docs/assets/deep-dive-into-swift-frameworks/static-linking.png and /dev/null differ diff --git a/docs/assets/getting-started-with-swiftio/architecture.jpg b/docs/assets/getting-started-with-swiftio/architecture.jpg deleted file mode 100644 index 8f09761..0000000 Binary files a/docs/assets/getting-started-with-swiftio/architecture.jpg and /dev/null differ diff --git a/docs/assets/getting-started-with-swiftio/board.png b/docs/assets/getting-started-with-swiftio/board.png deleted file mode 100644 index 0ba1bb7..0000000 Binary files a/docs/assets/getting-started-with-swiftio/board.png and /dev/null differ diff --git a/docs/assets/getting-started-with-swiftio/ports.png b/docs/assets/getting-started-with-swiftio/ports.png deleted file mode 100644 index 5a17eab..0000000 Binary files a/docs/assets/getting-started-with-swiftio/ports.png and /dev/null differ diff --git a/docs/assets/getting-started-with-swiftio/swift-io.jpg b/docs/assets/getting-started-with-swiftio/swift-io.jpg deleted file mode 100644 index 499cb2e..0000000 Binary files a/docs/assets/getting-started-with-swiftio/swift-io.jpg and /dev/null differ diff --git a/docs/assets/how-to-create-your-first-website-using-vapor-4-and-leaf/xcode-custom-working-directory.png b/docs/assets/how-to-create-your-first-website-using-vapor-4-and-leaf/xcode-custom-working-directory.png deleted file mode 100644 index a12c5f6..0000000 Binary files a/docs/assets/how-to-create-your-first-website-using-vapor-4-and-leaf/xcode-custom-working-directory.png and /dev/null differ diff --git a/docs/assets/how-to-create-your-first-website-using-vapor-4-and-leaf/xcode-custom-working-directory~dark.png b/docs/assets/how-to-create-your-first-website-using-vapor-4-and-leaf/xcode-custom-working-directory~dark.png deleted file mode 100644 index b212233..0000000 Binary files a/docs/assets/how-to-create-your-first-website-using-vapor-4-and-leaf/xcode-custom-working-directory~dark.png and /dev/null differ diff --git a/docs/assets/how-to-launch-a-macos-app-at-login/background-only.png b/docs/assets/how-to-launch-a-macos-app-at-login/background-only.png deleted file mode 100644 index 6bfd0d5..0000000 Binary files a/docs/assets/how-to-launch-a-macos-app-at-login/background-only.png and /dev/null differ diff --git a/docs/assets/how-to-launch-a-macos-app-at-login/copy-files.png b/docs/assets/how-to-launch-a-macos-app-at-login/copy-files.png deleted file mode 100644 index f36d07f..0000000 Binary files a/docs/assets/how-to-launch-a-macos-app-at-login/copy-files.png and /dev/null differ diff --git a/docs/assets/how-to-launch-a-macos-app-at-login/frameworks.png b/docs/assets/how-to-launch-a-macos-app-at-login/frameworks.png deleted file mode 100644 index ed63660..0000000 Binary files a/docs/assets/how-to-launch-a-macos-app-at-login/frameworks.png and /dev/null differ diff --git a/docs/assets/how-to-launch-a-macos-app-at-login/skip-install.png b/docs/assets/how-to-launch-a-macos-app-at-login/skip-install.png deleted file mode 100644 index 54b4619..0000000 Binary files a/docs/assets/how-to-launch-a-macos-app-at-login/skip-install.png and /dev/null differ diff --git a/docs/assets/how-to-make-a-swift-framework/xcode-project-framework-setup.png b/docs/assets/how-to-make-a-swift-framework/xcode-project-framework-setup.png deleted file mode 100644 index d9df0e5..0000000 Binary files a/docs/assets/how-to-make-a-swift-framework/xcode-project-framework-setup.png and /dev/null differ diff --git a/docs/assets/how-to-use-icloud-drive-documents/capabilities.png b/docs/assets/how-to-use-icloud-drive-documents/capabilities.png deleted file mode 100644 index 723ca9f..0000000 Binary files a/docs/assets/how-to-use-icloud-drive-documents/capabilities.png and /dev/null differ diff --git a/docs/assets/how-to-use-icloud-drive-documents/debug.png b/docs/assets/how-to-use-icloud-drive-documents/debug.png deleted file mode 100644 index 5ac9882..0000000 Binary files a/docs/assets/how-to-use-icloud-drive-documents/debug.png and /dev/null differ diff --git a/docs/assets/how-to-use-icloud-drive-documents/icloud.png b/docs/assets/how-to-use-icloud-drive-documents/icloud.png deleted file mode 100644 index 01ab071..0000000 Binary files a/docs/assets/how-to-use-icloud-drive-documents/icloud.png and /dev/null differ diff --git a/docs/assets/practical-server-side-swift/cover.png b/docs/assets/practical-server-side-swift/cover.png deleted file mode 100644 index 50b3b9f..0000000 Binary files a/docs/assets/practical-server-side-swift/cover.png and /dev/null differ diff --git a/docs/assets/practical-server-side-swift/sample.pdf b/docs/assets/practical-server-side-swift/sample.pdf deleted file mode 100644 index 16cff66..0000000 Binary files a/docs/assets/practical-server-side-swift/sample.pdf and /dev/null differ diff --git a/docs/assets/self-sizing-cells-with-rotation-support/collectionview.jpg b/docs/assets/self-sizing-cells-with-rotation-support/collectionview.jpg deleted file mode 100644 index b851c55..0000000 Binary files a/docs/assets/self-sizing-cells-with-rotation-support/collectionview.jpg and /dev/null differ diff --git a/docs/assets/self-sizing-cells-with-rotation-support/storyboard.jpg b/docs/assets/self-sizing-cells-with-rotation-support/storyboard.jpg deleted file mode 100644 index a55730e..0000000 Binary files a/docs/assets/self-sizing-cells-with-rotation-support/storyboard.jpg and /dev/null differ diff --git a/docs/assets/self-sizing-cells-with-rotation-support/tableviewcell.jpg b/docs/assets/self-sizing-cells-with-rotation-support/tableviewcell.jpg deleted file mode 100644 index d71e21d..0000000 Binary files a/docs/assets/self-sizing-cells-with-rotation-support/tableviewcell.jpg and /dev/null differ diff --git a/docs/assets/swift-adapter-design-pattern/usb-adapter.jpg b/docs/assets/swift-adapter-design-pattern/usb-adapter.jpg deleted file mode 100644 index 332a13e..0000000 Binary files a/docs/assets/swift-adapter-design-pattern/usb-adapter.jpg and /dev/null differ diff --git a/docs/assets/swift-adapter-design-pattern/usb-adapter~dark.jpg b/docs/assets/swift-adapter-design-pattern/usb-adapter~dark.jpg deleted file mode 100644 index 4a92eaa..0000000 Binary files a/docs/assets/swift-adapter-design-pattern/usb-adapter~dark.jpg and /dev/null differ diff --git a/docs/assets/tags/design-pattern/framework.png b/docs/assets/tags/design-pattern/framework.png deleted file mode 100644 index 63e2665..0000000 Binary files a/docs/assets/tags/design-pattern/framework.png and /dev/null differ diff --git a/docs/assets/tags/hummingbird/hummingbird.png b/docs/assets/tags/hummingbird/hummingbird.png deleted file mode 100644 index 44399f5..0000000 Binary files a/docs/assets/tags/hummingbird/hummingbird.png and /dev/null differ diff --git a/docs/assets/tags/ios/ios18-logo.png b/docs/assets/tags/ios/ios18-logo.png deleted file mode 100644 index f8d47fb..0000000 Binary files a/docs/assets/tags/ios/ios18-logo.png and /dev/null differ diff --git a/docs/assets/tags/ipados/ios18-logo.png b/docs/assets/tags/ipados/ios18-logo.png deleted file mode 100644 index f8d47fb..0000000 Binary files a/docs/assets/tags/ipados/ios18-logo.png and /dev/null differ diff --git a/docs/assets/tags/macos/macos-logo.png b/docs/assets/tags/macos/macos-logo.png deleted file mode 100644 index 49d4705..0000000 Binary files a/docs/assets/tags/macos/macos-logo.png and /dev/null differ diff --git a/docs/assets/tags/server/server.png b/docs/assets/tags/server/server.png deleted file mode 100644 index e224e39..0000000 Binary files a/docs/assets/tags/server/server.png and /dev/null differ diff --git a/docs/assets/tags/swift-package-manager/spm.png b/docs/assets/tags/swift-package-manager/spm.png deleted file mode 100644 index da0ae70..0000000 Binary files a/docs/assets/tags/swift-package-manager/spm.png and /dev/null differ diff --git a/docs/assets/tags/swift/swift-logo.png b/docs/assets/tags/swift/swift-logo.png deleted file mode 100644 index 1e84b62..0000000 Binary files a/docs/assets/tags/swift/swift-logo.png and /dev/null differ diff --git a/docs/assets/tags/swiftui/swiftui.png b/docs/assets/tags/swiftui/swiftui.png deleted file mode 100644 index cbf953c..0000000 Binary files a/docs/assets/tags/swiftui/swiftui.png and /dev/null differ diff --git a/docs/assets/tags/tooling/tooling.png b/docs/assets/tags/tooling/tooling.png deleted file mode 100644 index 8c6b2c2..0000000 Binary files a/docs/assets/tags/tooling/tooling.png and /dev/null differ diff --git a/docs/assets/tags/tvos/tvos.png b/docs/assets/tags/tvos/tvos.png deleted file mode 100644 index 1e57ceb..0000000 Binary files a/docs/assets/tags/tvos/tvos.png and /dev/null differ diff --git a/docs/assets/tags/uikit/uikit.png b/docs/assets/tags/uikit/uikit.png deleted file mode 100644 index c0ea1af..0000000 Binary files a/docs/assets/tags/uikit/uikit.png and /dev/null differ diff --git a/docs/assets/tags/vapor/vapor-4-logo.png b/docs/assets/tags/vapor/vapor-4-logo.png deleted file mode 100644 index 4d23c78..0000000 Binary files a/docs/assets/tags/vapor/vapor-4-logo.png and /dev/null differ diff --git a/docs/assets/tags/viper/viper.png b/docs/assets/tags/viper/viper.png deleted file mode 100644 index 5b94d2e..0000000 Binary files a/docs/assets/tags/viper/viper.png and /dev/null differ diff --git a/docs/assets/tags/visionos/visionos.png b/docs/assets/tags/visionos/visionos.png deleted file mode 100644 index 6e728e0..0000000 Binary files a/docs/assets/tags/visionos/visionos.png and /dev/null differ diff --git a/docs/assets/tags/watchos/watchos.png b/docs/assets/tags/watchos/watchos.png deleted file mode 100644 index e8ff887..0000000 Binary files a/docs/assets/tags/watchos/watchos.png and /dev/null differ diff --git a/docs/assets/tags/xcode/xcode-logo.png b/docs/assets/tags/xcode/xcode-logo.png deleted file mode 100644 index 50dba78..0000000 Binary files a/docs/assets/tags/xcode/xcode-logo.png and /dev/null differ diff --git a/docs/assets/uicollectionview-cells-with-circular-images-plus-rotation-support/circles.jpg b/docs/assets/uicollectionview-cells-with-circular-images-plus-rotation-support/circles.jpg deleted file mode 100644 index a345065..0000000 Binary files a/docs/assets/uicollectionview-cells-with-circular-images-plus-rotation-support/circles.jpg and /dev/null differ diff --git a/docs/assets/uicollectionview-cells-with-circular-images-plus-rotation-support/imageview.png b/docs/assets/uicollectionview-cells-with-circular-images-plus-rotation-support/imageview.png deleted file mode 100644 index c278b1f..0000000 Binary files a/docs/assets/uicollectionview-cells-with-circular-images-plus-rotation-support/imageview.png and /dev/null differ diff --git a/docs/assets/ultimate-uicollectionview-guide-with-ios-examples-written-in-swift/cell.png b/docs/assets/ultimate-uicollectionview-guide-with-ios-examples-written-in-swift/cell.png deleted file mode 100644 index 297d4a3..0000000 Binary files a/docs/assets/ultimate-uicollectionview-guide-with-ios-examples-written-in-swift/cell.png and /dev/null differ diff --git a/docs/assets/ultimate-uicollectionview-guide-with-ios-examples-written-in-swift/layout.png b/docs/assets/ultimate-uicollectionview-guide-with-ios-examples-written-in-swift/layout.png deleted file mode 100644 index 12a7055..0000000 Binary files a/docs/assets/ultimate-uicollectionview-guide-with-ios-examples-written-in-swift/layout.png and /dev/null differ diff --git a/docs/assets/ultimate-uicollectionview-guide-with-ios-examples-written-in-swift/music.png b/docs/assets/ultimate-uicollectionview-guide-with-ios-examples-written-in-swift/music.png deleted file mode 100644 index 1b4f7a8..0000000 Binary files a/docs/assets/ultimate-uicollectionview-guide-with-ios-examples-written-in-swift/music.png and /dev/null differ diff --git a/docs/assets/ultimate-uicollectionview-guide-with-ios-examples-written-in-swift/section.png b/docs/assets/ultimate-uicollectionview-guide-with-ios-examples-written-in-swift/section.png deleted file mode 100644 index 1595f86..0000000 Binary files a/docs/assets/ultimate-uicollectionview-guide-with-ios-examples-written-in-swift/section.png and /dev/null differ diff --git a/docs/async-http-api-clients-in-swift/index.html b/docs/async-http-api-clients-in-swift/index.html deleted file mode 100644 index 9613449..0000000 --- a/docs/async-http-api-clients-in-swift/index.html +++ /dev/null @@ -1,435 +0,0 @@ - - - - - - - - - - - - Async HTTP API clients in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 4 min read - -
-

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

-
- -

Introducing SwiftHttp

An awesome Swift HTTP library to rapidly create communication layers with API endpoints. The library tries to separate the client request logic from the request building and response handling. That’s the main reason why it has a HttpClient protocol which can be used to perform data, download and upload tasks. You can implement your own HttpClient, but SwiftHttp comes with a built-in UrlSessionHttpClient based on Foundation networking.

So the client is responsible for executing the requests, but we still have to describe the request itself somehow. This is where the HttpRawRequest object comes into play. You can easily create a base HttpUrl and perform a request using the HttpRawRequest object. When working with a raw request you can specify additional header fields and a raw body data object too. 💪

let url = HttpUrl(scheme: "https",
-                  host: "jsonplaceholder.typicode.com",
-                  port: 80,
-                  path: ["todos"],
-                  resource: nil,
-                  query: [:],
-                  fragment: nil)
-
-let req = HttpRawRequest(url: url, method: .get, headers: [:], body: nil)
-
-/// execute the request using the client
-let client = UrlSessionHttpClient(session: .shared, log: true)
-let response = try await client.dataTask(req)
-
-/// use the response data
-let todos = try JSONDecoder().decode([Todo].self, from: response.data)
-// response.statusCode == .ok
-// response.headers -> response headers as a dictionary
-

The HTTP client can perform network calls using the new async / await Swift concurrency API. It is possible to cancel a network request by wrapping it into a structured concurrency Task.

let task = Task {
-    let api = TodoApi()
-    _ = try await api.list()
-}
-
-DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(10)) {
-    task.cancel()
-}
-
-do {
-    let _ = try await task.value
-}
-catch {
-    if (error as? URLError)?.code == .cancelled {
-        print("cancelled")
-    }
-}
-

This is a neat tick, you can also check the reason inside the catch block, if it is an URLError with a .cancelled code then the request was cancelled, otherwise it must be some sort of network error.

So this is how you can use the client to perform or cancel a network task, but usually you don’t want to work with raw data, but encodable and decodable objects. When you work with such objects, you might want to validate the response headers and send additional headers to inform the server about the type of the body data. Just think about the Content-Type / Accept header fields. 🤔

So we might want to send additional headers alongside the request, plus it’d be nice to validate the status code and response headers before we try to parse the data. This seems like a flow of common operations, first we encode the data, set the additional header fields, and when the response arrives we validate the status code and the header fields, finally we try to decode the data object. This is a typical use case and SwiftHttp calls this workflow as a pipeline.

There are 4 types of built-in HTTP pipelines:

  • Raw - Send a raw data request, return a raw data response
  • Encodable - Send an encodable object, return a raw data response
  • Decodable - Send a raw data request, return a decodable object
  • Codable - Send an encodable object, return a decodable object

We can use a HttpRawPipeline and execute our request using a client as an executor.

let baseUrl = HttpUrl(host: "jsonplaceholder.typicode.com")
-let client = UrlSessionHttpClient(session: .shared, log: true)
-
-let pipeline = HttpRawPipeline(url: baseUrl.path("todos"), method: .get)
-
-let response = try await pipeline.execute(client.dataTask)
-let todos = try JSONDecoder().decode([Todo].self, from: response.data)
-print(response.statusCode)
-print(todos.count)
-

In this case we were using the dataTask function, but if you expect the response to be a huge file, you might want to consider using a downloadTask, or if you’re uploading a large amount of data when sending the request, you should choose the uploadTask function. 💡

So in this case we had to manually decode the Todo object from the raw HTTP response data, but we can use the decodable pipeline to make things even more simple.

let baseUrl = HttpUrl(host: "jsonplaceholder.typicode.com")
-let client = UrlSessionHttpClient(session: .shared, log: true)
-
-
-let pipeline = HttpDecodablePipeline<[Todo]>(url: baseUrl.path("todos"),
-                                             method: .get,
-                                             decoder: .json(JSONDecoder(), validators: [
-                                                HttpStatusCodeValidator(.ok),
-                                                HttpHeaderValidator(.key(.contentType)) {
-                                                    $0.contains("application/json")
-                                                },
-                                             ]))
-
-let todos = try await pipeline.execute(client.dataTask)
-print(todos.count)
-

As you can see, in this case the instead of returning the response, the pipeline can perform additional validation and the decoding using the provided decoder and validators. You can create your own validators, there is a HttpResponseValidator protocol for this purpose.

The encodable pipeline works like the same, you can specify the encoder, you can provide the encodable object and you’ll get back a HttpResponse instance.

let client = UrlSessionHttpClient(session: .shared, log: true)
-        
-let todo = Todo(id: 1, title: "lorem ipsum", completed: false)
-
-let pipeline = HttpEncodablePipeline(url: baseUrl.path("todos"),
-                                     method: .post,
-                                     body: todo,
-                                     encoder: .json())
-
-let response = try await pipeline.execute(client.dataTask)
-
-print(response.statusCode == .created)
-

The codable pipeline is a combination of the encodable and decodable pipeline. 🙃

let baseUrl = HttpUrl(host: "jsonplaceholder.typicode.com")
-let client = UrlSessionHttpClient(session: .shared, log: true)
-
-let todo = Todo(id: 1, title: "lorem ipsum", completed: false)
-
-let pipeline = HttpCodablePipeline<Todo, Todo>(url: baseUrl.path("todos", String(1)),
-                                               method: .put,
-                                               body: todo,
-                                               encoder: .json(),
-                                               decoder: .json())
-
-let todo = try await pipeline.execute(client.dataTask)
-print(todo.title)
-

As you can see this is quite a common pattern, and when we’re communicating with a REST API, we’re going to perform more or less the exact same network calls for every single endpoint. SwiftHttp has a pipeline collection protocol that you can use to perform requests without the need of explicitly setting up these pipelines. Here’s an example:

import SwiftHttp
-
-struct Todo: Codable {
-    let id: Int
-    let title: String
-    let completed: Bool
-}
-
-struct TodoApi: HttpCodablePipelineCollection {
-
-    let client: HttpClient = UrlSessionHttpClient(log: true)
-    let apiBaseUrl = HttpUrl(host: "jsonplaceholder.typicode.com")
-
-    
-    func list() async throws -> [Todo] {
-        try await decodableRequest(executor: client.dataTask,
-                                   url: apiBaseUrl.path("todos"),
-                                   method: .get)
-    }    
-}
-
-let todos = try await api.list()
-

When using a HttpCodablePipelineCollection you can perform an encodable, decodable or codable request using an executor object. This will reduce the boilerplate code needed to perform a request and everything is going to be type safe thanks to the generic protocol oriented networking layer. You can setup as many pipeline collections as you need, it is possible to use a shared client or you can create a dedicated client for each.

By the way, if something goes wrong with the request, or one of the validators fail, you can always check for the errors using a do-try-catch block. 😅

do {
-    _ = try await api.list()
-}
-catch HttpError.invalidStatusCode(let res) {
-    // decode custom error message, if the status code was invalid
-    let decoder = HttpResponseDecoder<CustomError>(decoder: JSONDecoder())
-    do {
-        let error = try decoder.decode(res.data)
-        print(res.statusCode, error)
-    }
-    catch {
-        print(error.localizedDescription)
-    }
-}
-catch {
-    print(error.localizedDescription)
-}
-

That’s how SwiftHttp works in a nutshell, of course you can setup custom encoders and decoders, but that’s another topic. If you are interested in the project, feel free to give it a star on GitHub. We’re going to use it in the future quite a lot both on the client and server side. ⭐️⭐️⭐️

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/asynchronous-validation-for-vapor/index.html b/docs/asynchronous-validation-for-vapor/index.html deleted file mode 100644 index 2973846..0000000 --- a/docs/asynchronous-validation-for-vapor/index.html +++ /dev/null @@ -1,847 +0,0 @@ - - - - - - - - - - - - Asynchronous validation for Vapor - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 13 min read - -
-

Asynchronous validation for Vapor

-
-

Learn how to validate input data using an async technique. Unified request validation API for your server side Swift app.

-
- -

Vapor’s validation API

The very first thing I’d like to show you is an issue that I have with the current validation API for the Vapor framework. I always wanted to use it, because I really like the validator functions but unfortunately the API lacks quite a lot of features that are crucial for my needs.

If we take a look at our previously created Todo example code, you might remember that we’ve only put some validation on the create API endpoint. That’s not very safe, we should fix this. I’m going to show you how to validate endpoints using the built-in API, to see what’s the issue with it. 🥲

In order to demonstrate the problems, we’re going to add a new Tag model to our Todo items.

import Vapor
-import Fluent
-
-final class TagModel: Model {
-
-    static let schema = "tags"
-    static let idParamKey = "tagId"
-   
-    struct FieldKeys {
-        static let name: FieldKey = "name"
-        static let todoId: FieldKey = "todo_id"
-    }
-    
-    @ID(key: .id) var id: UUID?
-    @Field(key: FieldKeys.name) var name: String
-    @Parent(key: FieldKeys.todoId) var todo: TodoModel
-    
-    init() { }
-    
-    init(id: UUID? = nil, name: String, todoId: UUID) {
-        self.id = id
-        self.name = name
-        self.$todo.id = todoId
-    }
-}
-

So the main idea is that we’re going to be able to tag our todo items and save the todoId reference for each tag. This is not going to be a global tagging solution, but more like a simple tag system for demo purposes. The relation will be automatically validated on the database level (if the db driver supports it), since we’re going to put a foreign key constraint on the todoId field in the migration.

import Fluent
-
-struct TagMigration: Migration {
-
-    func prepare(on db: Database) -> EventLoopFuture<Void> {
-        db.schema(TagModel.schema)
-            .id()
-            .field(TagModel.FieldKeys.name, .string, .required)
-            .field(TagModel.FieldKeys.todoId, .uuid, .required)
-            .foreignKey(TagModel.FieldKeys.todoId, references: TodoModel.schema, .id)
-            .create()
-    }
-
-    func revert(on db: Database) -> EventLoopFuture<Void> {
-        db.schema(TagModel.schema).delete()
-    }
-}
-

It is important to mention this again: NOT every single database supports foreign key validation out of the box. This is why it will be extremely important to validate our input data. If we let users to put random todoId values into the database that can lead to data corruption and other problems.

Now that we have our database model & migration, here’s how the API objects will look like. You can put these into the TodoApi target, since these DTOs could be shared with a client side library. 📲

import Foundation
-
-public struct TagListObject: Codable {
-    
-    public let id: UUID
-    public let name: String
-
-    public init(id: UUID, name: String) {
-        self.id = id
-        self.name = name
-    }
-}
-
-public struct TagGetObject: Codable {
-    
-    public let id: UUID
-    public let name: String
-    public let todoId: UUID
-    
-    public init(id: UUID, name: String, todoId: UUID) {
-        self.id = id
-        self.name = name
-        self.todoId = todoId
-        
-    }
-}
-
-public struct TagCreateObject: Codable {
-
-    public let name: String
-    public let todoId: UUID
-    
-    public init(name: String, todoId: UUID) {
-        self.name = name
-        self.todoId = todoId
-    }
-}
-
-public struct TagUpdateObject: Codable {
-    
-    public let name: String
-    public let todoId: UUID
-    
-    public init(name: String, todoId: UUID) {
-        self.name = name
-        self.todoId = todoId
-    }
-}
-
-public struct TagPatchObject: Codable {
-
-    public let name: String?
-    public let todoId: UUID?
-    
-    public init(name: String?, todoId: UUID?) {
-        self.name = name
-        self.todoId = todoId
-    }
-}
-

Next we extend our TagModel to support CRUD operations, if you followed my first tutorial about how to build a REST API using Vapor, this should be very familiar, if not please read it first. 🙏

import Vapor
-import TodoApi
-
-extension TagListObject: Content {}
-extension TagGetObject: Content {}
-extension TagCreateObject: Content {}
-extension TagUpdateObject: Content {}
-extension TagPatchObject: Content {}
-
-extension TagModel {
-    
-    func mapList() -> TagListObject {
-        .init(id: id!, name: name)
-    }
-
-    func mapGet() -> TagGetObject {
-        .init(id: id!, name: name, todoId: $todo.id)
-    }
-    
-    func create(_ input: TagCreateObject) {
-        name = input.name
-        $todo.id = input.todoId
-    }
-        
-    func update(_ input: TagUpdateObject) {
-        name = input.name
-        $todo.id = input.todoId
-    }
-    
-    func patch(_ input: TagPatchObject) {
-        name = input.name ?? name
-        $todo.id = input.todoId ?? $todo.id
-    }
-}
-

The tag controller is going to look very similar to the todo controller, for now we won’t validate anything, the following snippet is all about having a sample code that we can fine tune later on.

import Vapor
-import Fluent
-import TodoApi
-
-struct TagController {
-
-    private func getTagIdParam(_ req: Request) throws -> UUID {
-        guard let rawId = req.parameters.get(TagModel.idParamKey), let id = UUID(rawId) else {
-            throw Abort(.badRequest, reason: "Invalid parameter `\(TagModel.idParamKey)`")
-        }
-        return id
-    }
-
-    private func findTagByIdParam(_ req: Request) throws -> EventLoopFuture<TagModel> {
-        TagModel
-            .find(try getTagIdParam(req), on: req.db)
-            .unwrap(or: Abort(.notFound))
-    }
-
-    // MARK: - endpoints
-    
-    func list(req: Request) throws -> EventLoopFuture<Page<TagListObject>> {
-        TagModel.query(on: req.db).paginate(for: req).map { $0.map { $0.mapList() } }
-    }
-    
-    func get(req: Request) throws -> EventLoopFuture<TagGetObject> {
-        try findTagByIdParam(req).map { $0.mapGet() }
-    }
-
-    func create(req: Request) throws -> EventLoopFuture<Response> {
-        let input = try req.content.decode(TagCreateObject.self)
-
-        let tag = TagModel()
-        tag.create(input)
-        return tag
-            .create(on: req.db)
-            .map { tag.mapGet() }
-            .encodeResponse(status: .created, for: req)
-    }
-    
-    func update(req: Request) throws -> EventLoopFuture<TagGetObject> {
-        let input = try req.content.decode(TagUpdateObject.self)
-
-        return try findTagByIdParam(req)
-            .flatMap { tag in
-                tag.update(input)
-                return tag.update(on: req.db).map { tag.mapGet() }
-            }
-    }
-    
-    func patch(req: Request) throws -> EventLoopFuture<TagGetObject> {
-        let input = try req.content.decode(TagPatchObject.self)
-
-        return try findTagByIdParam(req)
-            .flatMap { tag in
-                tag.patch(input)
-                return tag.update(on: req.db).map { tag.mapGet() }
-            }
-    }
-
-    func delete(req: Request) throws -> EventLoopFuture<HTTPStatus> {
-        try findTagByIdParam(req)
-            .flatMap { $0.delete(on: req.db) }
-            .map { .ok }
-    }
-}
-

Of course we could use a generic CRUD controller class that could highly reduce the amount of code required to create similar controllers, but that’s a different topic. So we just have to register these newly created functions using a router.

import Vapor
-
-struct TagRouter: RouteCollection {
-
-    func boot(routes: RoutesBuilder) throws {
-
-        let tagController = TagController()
-        
-        let id = PathComponent(stringLiteral: ":" + TagModel.idParamKey)
-        let tagRoutes = routes.grouped("tags")
-        
-        tagRoutes.get(use: tagController.list)
-        tagRoutes.post(use: tagController.create)
-        
-        tagRoutes.get(id, use: tagController.get)
-        tagRoutes.put(id, use: tagController.update)
-        tagRoutes.patch(id, use: tagController.patch)
-        tagRoutes.delete(id, use: tagController.delete)
-    }
-}
-

Also a few more changes in the configure.swift file, since we’d like to take advantage of the Tag functionality we have to register the migration and the new routes using the TagRouter.

import Vapor
-import Fluent
-import FluentSQLiteDriver
-
-public func configure(_ app: Application) throws {
-
-    if app.environment == .testing {
-        app.databases.use(.sqlite(.memory), as: .sqlite, isDefault: true)
-    }
-    else {
-        app.databases.use(.sqlite(.file("Resources/db.sqlite")), as: .sqlite)
-    }
-
-    app.http.server.configuration.hostname = "192.168.8.103"
-    app.migrations.add(TodoMigration())
-    app.migrations.add(TagMigration())
-    try app.autoMigrate().wait()
-
-    try TodoRouter().boot(routes: app.routes)
-    try TagRouter().boot(routes: app.routes)
-}
-

One more thing, before we start validating our tags, we have to put a new @Children(for: \.$todo) var tags: [TagModel] property into our TodoModel, so it’s going to be way more easy to fetch tags.

If you run the server and try to create a new tag using cURL and a fake UUID, the database query will fail if the db supports foreign keys.

curl -X POST "http://127.0.0.1:8080/tags/" \
-    -H 'Content-Type: application/json' \
-    -d '{"name": "test", "todoId": "94234a4a-b749-4a2a-97d0-3ebd1046dbac"}'
-

This is not ideal, we should protect our database from invalid data. Well, first of all we don’t want to allow empty or too long names, so we should validate this field as well, this can be done using the validation API from the Vapor framework, let me show you how.

// TagModel+Api.swift
-extension TagCreateObject: Validatable {
-    public static func validations(_ validations: inout Validations) {
-        validations.add("title", as: String.self, is: !.empty)
-        validations.add("title", as: String.self, is: .count(...100) && .alphanumeric)
-    }
-}
-// TagController.swift
-func create(req: Request) throws -> EventLoopFuture<Response> {
-    try TagCreateObject.validate(content: req)
-    let input = try req.content.decode(TagCreateObject.self)
-
-    let tag = TagModel()
-    tag.create(input)
-    return tag
-        .create(on: req.db)
-        .map { tag.mapGet() }
-        .encodeResponse(status: .created, for: req)
-}
-/* 
-curl -X POST "http://127.0.0.1:8080/tags/" \
-    -H 'Content-Type: application/json' \
-    -d '{"name": "", "todoId": "94234a4a-b749-4a2a-97d0-3ebd1046dbac"}'
-
-{"error":true,"reason":"name is empty"}
-
-/// some other cases:
-name: 123
-{"error":true,"reason":"name is not a(n) String, name is not a(n) String"}
-name: ?
-{"error":true,"reason":"name contains '?' (allowed: A-Z, a-z, 0-9)"}
-name: very-lenghty-string
-{"error":true,"reason":"name is greater than maximum of 100 character(s)"}
-*/
-

Ok, it looks great, but this solution lacks a few things:

  • You can’t provide custom error messages
  • The detail is always a concatenated result string (if there are multiple errors)
  • You can’t get the error message for a given key (e.g. “title”: “Title is required”)
  • Validation happens synchronously (you can’t validate based on a db query)

This is very unfortunate, because Vapor has really nice validator functions. You can validate characters (.ascii, .alphanumeric, .characterSet(_:)), various length and range requirements (.empty, .count(_:), .range(_)), collections (.in(_:)), check null inputs, validate emails and URLs. We should try to validate the todo identifier based on the available todos in the database.

It is possible to validate todoId’s by running a query with the input id and see if there is an existing record in our database. If there is no such todo, we won’t allow the creation (or update / patch) operation. The problem is that we have to put this logic into the controller. 😕

func create(req: Request) throws -> EventLoopFuture<Response> {
-    try TagCreateObject.validate(content: req)
-    let input = try req.content.decode(TagCreateObject.self)
-    return TodoModel.find(input.todoId, on: req.db)
-        .unwrap(or: Abort(.badRequest, reason: "Invalid todo identifier"))
-        .flatMap { _ in
-            let tag = TagModel()
-            tag.create(input)
-            return tag
-                .create(on: req.db)
-                .map { tag.mapGet() }
-                .encodeResponse(status: .created, for: req)
-        }
-}
-

This will do the job, but isn’t it strange that we are doing validation in two separate places?

My other problem is that using the validatable protocol means that you can’t really pass parameters for these validators, so even if you asynchronously fetch some required data and somehow you move the logic inside the validator, the whole process is going to feel like a very hacky solution. 🤐

Honestly, am I missing something here? Is this really how the validation system works in the most popular web framework? It’s quite unbelievable. There must be a better way… 🤔

Async input validation
This method that I’m going to show you is already available in Feather CMS, I believe it’s quite an advanced system compared to Vapor’s validation API. I’ll show you how I created it, first we start with a protocol that’ll contain the basic stuff needed for validation & result management.

import Vapor
-
-public protocol AsyncValidator {
-    
-    var key: String { get }
-    var message: String { get }
-
-    func validate(_ req: Request) -> EventLoopFuture<ValidationErrorDetail?>
-}
-
-public extension AsyncValidator {
-
-    var error: ValidationErrorDetail {
-        .init(key: key, message: message)
-    }
-}
-

This is a quite simple protocol that we’re going to be the base of our asynchronous validation flow. The key will be used to just like the same way as Vapor uses validation keys, it’s basically an input key for a given data object and we’re going to use this key with an appropriate error message to display detailed validation errors (as an output content).

import Vapor
-
-public struct ValidationErrorDetail: Codable {
-
-    public var key: String
-    public var message: String
-    
-    public init(key: String, message: String) {
-        self.key = key
-        self.message = message
-    }
-}
-
-extension ValidationErrorDetail: Content {}
-

So the idea is that we’re going to create multiple validation handlers based on this AsyncValidator protocol and get the final result based on the evaluated validators. The validation method can look like magic at first sight, but it’s just calling the async validator methods if a given key is already invalidated then it’ll skip other validations for that (for obvious reasons), and based on the individual validator results we create a final array including the validation error detail objects. 🤓

import Vapor
-
-public struct RequestValidator {
-
-    public var validators: [AsyncValidator]
-    
-    public init(_ validators: [AsyncValidator] = []) {
-        self.validators = validators
-    }
-    
-    /// this is magic, don't touch it
-    public func validate(_ req: Request, message: String? = nil) -> EventLoopFuture<Void> {
-        let initial: EventLoopFuture<[ValidationErrorDetail]> = req.eventLoop.future([])
-        return validators.reduce(initial) { res, next -> EventLoopFuture<[ValidationErrorDetail]> in
-            return res.flatMap { arr -> EventLoopFuture<[ValidationErrorDetail]> in
-                if arr.contains(where: { $0.key == next.key }) {
-                    return req.eventLoop.future(arr)
-                }
-                return next.validate(req).map { result in
-                    if let result = result {
-                        return arr + [result]
-                    }
-                    return arr
-                }
-            }
-        }
-        .flatMapThrowing { details in
-            guard details.isEmpty else {
-                throw Abort(.badRequest, reason: details.map(\.message).joined(separator: ", "))
-            }
-        }
-    }
-
-    public func isValid(_ req: Request) -> EventLoopFuture<Bool> {
-        return validate(req).map { true }.recover { _ in false }
-    }
-}
-

Don’t wrap your head too much about this code, I’ll show you how to use it right away, but before we could perform a validation using our new tools, we need something that implements the AsyncValidator protocol and we can actually initialize. I have something that I really like in Feather, because it can perform both sync & async validations, of course you can come up with more simple validators, but this is a nice generic solution for most of the cases.

import Vapor
-
-public struct KeyedContentValidator<T: Codable>: AsyncValidator {
-
-    public let key: String
-    public let message: String
-    public let optional: Bool
-
-    public let validation: ((T) -> Bool)?
-    public let asyncValidation: ((T, Request) -> EventLoopFuture<Bool>)?
-    
-    public init(_ key: String,
-                _ message: String,
-                optional: Bool = false,
-                _ validation: ((T) -> Bool)? = nil,
-                _ asyncValidation: ((T, Request) -> EventLoopFuture<Bool>)? = nil) {
-        self.key = key
-        self.message = message
-        self.optional = optional
-        self.validation = validation
-        self.asyncValidation = asyncValidation
-    }
-    
-    public func validate(_ req: Request) -> EventLoopFuture<ValidationErrorDetail?> {
-        let optionalValue = try? req.content.get(T.self, at: key)
-
-        if let value = optionalValue {
-            if let validation = validation {
-                return req.eventLoop.future(validation(value) ? nil : error)
-            }
-            if let asyncValidation = asyncValidation {
-                return asyncValidation(value, req).map { $0 ? nil : error }
-            }
-            return req.eventLoop.future(nil)
-        }
-        else {
-            if optional {
-                return req.eventLoop.future(nil)
-            }
-            return req.eventLoop.future(error)
-        }
-    }
-}
-

The main idea here is that we can pass either a sync or an async validation block alongside the key, message and optional arguments and we perform our validation based on these inputs.

First we try to decode the generic Codable value, if the value was optional and it is missing we can simply ignore the validators and return, otherwise we should try to call the sync validator or the async validator. Please note that the sync validator is just a convenience tool, because if you don’t need async calls it’s more easy to return with a bool value instead of an EventLoopFuture.

So, this is how you can validate anything using these new server side Swift validator components.

func create(req: Request) throws -> EventLoopFuture<Response> {
-    let validator = RequestValidator.init([
-        KeyedContentValidator<String>.init("name", "Name is required") { !$0.isEmpty },
-        KeyedContentValidator<UUID>.init("todoId", "Todo identifier must be valid", nil) { value, req in
-            TodoModel.query(on: req.db).filter(\.$id == value).count().map {
-                $0 == 1
-            }
-        },
-    ])
-    return validator.validate(req).flatMap {
-        do {
-            let input = try req.content.decode(TagCreateObject.self)
-            let tag = TagModel()
-            tag.create(input)
-            return tag
-                .create(on: req.db)
-                .map { tag.mapGet() }
-                .encodeResponse(status: .created, for: req)
-        }
-        catch {
-            return req.eventLoop.future(error: Abort(.badRequest, reason: error.localizedDescription))
-        }
-    }
-}
-

This seems like a bit more code at first sight, but remember that previously we moved out our validator into a separate method. We can do the exact same thing here and return an array of AsyncValidator objects. Also a “real throwing flatMap EventLoopFuture” extension method could help us greatly to remove unnecessary do-try-catch statements from our code.

Anyway, I’ll leave this up for you, but it’s easy to reuse the same validation for all the CRUD endpoints, for patch requests you can set the optional flag to true and that’s it. 💡

I still want to show you one more thing, because I don’t like the current JSON output of the invalid calls. We’re going to build a custom error middleware with a custom context object to display more details about what went wrong during the request. We need a validation error content for this.

import Vapor
-
-public struct ValidationError: Codable {
-
-    public let message: String?
-    public let details: [ValidationErrorDetail]
-    
-    public init(message: String?, details: [ValidationErrorDetail]) {
-        self.message = message
-        self.details = details
-    }
-}
-
-extension ValidationError: Content {}
-

This is the format that we’d like to use when something goes wrong. Now it’d be nice to support custom error codes while keeping the throwing nature of errors, so for this reason we’ll define a new ValidationAbort that’s going to contain everything we’ll need for the new error middleware.

import Vapor
-
-public struct ValidationAbort: AbortError {
-
-    public var abort: Abort
-    public var message: String?
-    public var details: [ValidationErrorDetail]
-
-    public var reason: String { abort.reason }
-    public var status: HTTPStatus { abort.status }
-    
-    public init(abort: Abort, message: String? = nil, details: [ValidationErrorDetail]) {
-        self.abort = abort
-        self.message = message
-        self.details = details
-    }
-}
-

This will allow us to throw ValidationAbort objects with a custom Abort & detailed error description. The Abort object is used to set the proper HTTP response code and headers when building the response object inside the middleware. The middleware is very similar to the built-in error middleware, except that it can return more details about the given validation issues.

import Vapor
-
-public struct ValidationErrorMiddleware: Middleware {
-
-    public let environment: Environment
-    
-    public init(environment: Environment) {
-        self.environment = environment
-    }
-
-    public func respond(to request: Request, chainingTo next: Responder) -> EventLoopFuture<Response> {
-        return next.respond(to: request).flatMapErrorThrowing { error in
-            let status: HTTPResponseStatus
-            let headers: HTTPHeaders
-            let message: String?
-            let details: [ValidationErrorDetail]
-
-            switch error {
-            case let abort as ValidationAbort:
-                status = abort.abort.status
-                headers = abort.abort.headers
-                message = abort.message ?? abort.reason
-                details = abort.details
-            case let abort as Abort:
-                status = abort.status
-                headers = abort.headers
-                message = abort.reason
-                details = []
-            default:
-                status = .internalServerError
-                headers = [:]
-                message = environment.isRelease ? "Something went wrong." : error.localizedDescription
-                details = []
-            }
-
-            request.logger.report(error: error)
-
-            let response = Response(status: status, headers: headers)
-
-            do {
-                response.body = try .init(data: JSONEncoder().encode(ValidationError(message: message, details: details)))
-                response.headers.replaceOrAdd(name: .contentType, value: "application/json; charset=utf-8")
-            }
-            catch {
-                response.body = .init(string: "Oops: \(error)")
-                response.headers.replaceOrAdd(name: .contentType, value: "text/plain; charset=utf-8")
-            }
-            return response
-        }
-    }
-}
-

Based on the given environment we can report the details or hide the internal issues, this is totally up-to-you, for me this approach works the best, because I can always parse the problematic keys and display error messages inside the client apps based on this response.

We just have to alter one line in the RequestValidator & register our newly created middleware for better error reporting. Here’s the updated request validator:

// RequestValidator.swift
-// (simply change the throwed object in the flatMapThrowing block)
-.flatMapThrowing { details in
-    guard details.isEmpty else {
-        throw ValidationAbort(abort: Abort(.badRequest, reason: message), details: details)
-    }
-}
-
-// configure.swift
-app.middleware.use(ValidationErrorMiddleware(environment: app.environment))
-

Now if you run the same invalid cURL request, you should get a way better error response.

curl -i -X POST "http://192.168.8.103:8080/tags/" \
-    -H 'Content-Type: application/json' \
-    -d '{"name": "eee", "todoId": "94234a4a-b749-4a2a-97d0-3ebd1046dbac"}'
-
-# HTTP/1.1 400 Bad Request
-# content-length: 72
-# content-type: application/json; charset=utf-8
-# connection: keep-alive
-# date: Wed, 12 May 2021 14:52:47 GMT
-#
-# {"details":[{"key":"todoId","message":"Todo identifier must be valid"}]}
-

You can even add a custom message for the request validator when you call the validate function, that’ll be available under the message key inside the output.

As you can see this is quite a nice way to deal with errors and unify the flow of the entire validation chain. I’m not saying that Vapor did a bad job with the official validation APIs, but there’s definitely room for improvements. I really love the wide variety of the available validators, but on the other hand I freakin’ miss this async validation logic from the core framework. ❤️💩

Another nice thing about this approach is that you can define validator extensions and greatly simplify the amount of Swift code required to perform server side validation.

I know I’m not the only one with these issues, and I really hope that this little tutorial will help you create better (and more safe) backend apps using Vapor. I can only say that feel free to improve the validation related code for this Todo project, that’s a good practice for sure. Hopefully it won’t be too hard to add more validation logic based on the provided examples. 😉

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/authors/index.html b/docs/authors/index.html deleted file mode 100644 index 4e3d1cc..0000000 --- a/docs/authors/index.html +++ /dev/null @@ -1,168 +0,0 @@ - - - - - - - - - - - - Authors - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -

Authors


Browse posts by authors.

- -
- -
-
- Tibor Bödecs -

Tibor Bödecs

-

140 posts

-
-
- -
- -
- - - -
- - - - diff --git a/docs/authors/tibor-bodecs/index.html b/docs/authors/tibor-bodecs/index.html deleted file mode 100644 index 23f6d99..0000000 --- a/docs/authors/tibor-bodecs/index.html +++ /dev/null @@ -1,3304 +0,0 @@ - - - - - - - - - - - - Tibor Bödecs - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
-
- -
- Tibor Bödecs -

Tibor Bödecs

-

Server side Swift enthusiast, book author, content creator.

-
- -


140 posts

- -
- - - -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Hummingbird routing and requests

-
-

Beginner's guide to learn all about routing and request handling using the Hummingbird server-side Swift framework.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to server-side Swift using the Hummingbird framework

-
-

Learn about Swift on the server by creating a simple application using the brand new HTTP server library called: Hummingbird.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Running and testing async Vapor commands

-
-

In this article I'll show you how to build asynchronous Vapor commands and how to test them using ConsoleKit.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Running tasks in parallel

-
-

Learn how to run tasks in parallel using the old-school tools and frameworks plus the new structured concurrency API in Swift.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

The abstract Vapor service factory design pattern

-
-

In this tutorial I'm going to show you how you can create an abstract driver-based component for the Vapor framework.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

SwiftNIO tutorial - The echo server

-
-

This is a beginner's guide to learn the basics of the SwiftNIO network app framework by building a basic TCP echo server.

- -
- Server -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Easy multipart file upload for Swift

-
-

Let me show you how to create HTTP requests using multipart (form data) body without a third party library. Simple solution.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Utilizing Makefiles for Swift projects

-
-

In this tutorial I'll show you how to use Makefiles for server-side Swift projects to help running utility tasks in a more simple way.

- -
- Tooling -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Lenses and prisms in Swift

-
-

Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Introduction to SPM artifact bundles

-
-

In this tutorial I'm going to show you how to use the new binary target related artifact bundle using the Swift package manager.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginner's guide to Swift package manager command plugins

-
-

Learn how to create command plugins for the Swift Package Manager to execute custom actions using SPM and other tools.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Swift visitor design pattern

-
-

The visitor design pattern in Swift allows us to add new features to an existing group of objects without altering the original code.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

Working with diffable data sources and table views using UIKit

-
-

In this tutorial we're going to build a screen to allow single and multiple selections using diffable data source and a table view.

- -
- UIKit -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Beginner's guide to Swift arrays

-
-

Learn how to manipulate arrays in Swift like a pro. This tutorial covers lots of useful array related methods, tips and tricks.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

The repository pattern for Vapor 4

-
-

In this article I'm going to talk about the repository design pattern and give you a few Fluent ORM tips for your Vapor 4 app.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

How to use a Swift library in C

-
-

In this tutorial, we're going to build a C app by importing a Swift library and talk a bit about the Swift / C Interoperability in general.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

UIKit - loadView vs viewDidLoad

-
-

When to use these methods? Common questions and answers about the iOS view hierarchy including memory management.

- -
- UIKit -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 12 min read -
- -

Progressive Web Apps on iOS

-
-

This is a beginner's guide about creating PWAs for iOS including custom icons, splash screens, safe area and dark mode support.

- -
- iOS -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

How to create a Swift package collection?

-
-

In this tutorial I'm going to show you how to create your own package collection from your favorite Swift libraries.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

How to write HTML in Swift?

-
-

This tutorial is all about rendering HTML docs using a brand new DSL library called SwiftHtml and the Vapor web framework.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

The future of server side Swift

-
-

What's going to happen with Swift on the Server in 2022? Distributed actors, Vapor 5, some predictions and wishes.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Building a global storage for Vapor

-
-

This tutorial is about a shared global storage that you can implement using a common design pattern in Vapor 4.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Building tree data structures in Swift

-
-

This tutorial is about showing the pros and cons of various Swift tree data structures using structs, enums and classes.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Practical guide to binary operations using the UInt8 type in Swift

-
-

Introduction to the basics of signed number representation and some practical binary operation examples in Swift using UInt8.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

How to build better command line apps and tools using Swift?

-
-

These tips will help you to create amazing CLI tools, utility apps, server side projects or terminal scripts using the Swift language.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Swift structured concurrency tutorial

-
-

Learn how to work with the Task object to perform asynchronous operations in a safe way using the new concurrency APIs in Swift.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Swift actors tutorial - a beginner's guide to thread safe concurrency

-
-

Learn how to use the brand new actor model to protect your application from unwanted data-races and memory issues.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

Beginner's guide to the async/await concurrency API in Vapor & Fluent

-
-

Learn how to convert your existing EventLoopFuture based Vapor server app using the new async/await Swift feature.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Introduction to async/await in Swift

-
-

Beginners guide to the new async/await API's in Swift 5.5. Interacting with sync code, structured concurrency, async let.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

Dynamic libraries and code replacements in Swift

-
-

How to load a dynamic library and use native method swizzling in Swift? This article is all about the magic behind SwiftUI previews.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

Declarative unit tests for Vapor

-
-

Learn how to test your server side Swift backend app in a declarative style using a lightweight library called Spec.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

How to design type safe RESTful APIs using Swift & Vapor?

-
-

Learn to make proper data transfer objects for CRUD operations and integrate them both into the client and server side API layer.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

Unsafe memory pointers in Swift

-
-

Learn how to use raw pointer references, interact with unsafe pointers and manually manage memory addresses in Swift.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

Memory layout in Swift

-
-

Start learning about how Swift manages, stores and references various data types and objects using a memory safe approach.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

How to use C libraries in Swift?

-
-

Learn how to use system libraries and call C code from Swift. Interoperability between the Swift language and C for beginners.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

Building static and dynamic Swift libraries using the Swift compiler

-
-

This tutorial is all about emitting various Swift binaries without the Swift package manager, but only using the Swift compiler.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

The Swift compiler for beginners

-
-

Learn how to build executable files using the swiftc command, meet the build pipeline, compilers and linkers under the hood.

- -
- Swift - Tooling -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Custom working directory in Xcode

-
-

Learn how to set a custom working directory in Xcode to solve one of the most common beginner issue when using Vapor.

- -
- Tooling - Xcode -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

File upload API server in Vapor 4

-
-

Learn how to build a very simple file upload API server using Vapor 4 and URLSession upload task on the client side.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

File upload using Vapor 4

-
-

Learn how to implement a basic HTML file upload form using the Leaf template engine and Vapor, all written in Swift of course.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Event-driven generic hooks for Swift

-
-

In this article I am going to show you how to implement a basic event processing system for your modular Swift application.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Getting started with SwiftIO

-
-

SwiftIO is an electronic circuit board that runs Swift on the bare metal. It can control sensors, displays, lights, motors and more.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

How to build macOS apps using only the Swift Package Manager?

-
-

In this article we're going to create a macOS application without ever touching an Xcode project file, but only working with SPM.

- -
- macOS -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Logging for beginners in Swift

-
-

Learn how to print variables to the debug console using different functions such as print, dump, NSLog and the unified os.log API.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

How to define strings, use escaping sequences and interpolations?

-
-

As a beginner it can be hard to understand String interpolation and escaping sequences, in this tutorial I'll teach you the basics.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

Swift on the Server in 2020

-
-

Why choose Swift as a backend language in 2020? What are the available frameworks to build your server? Let me guide you.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

How to store keys in env files?

-
-

In this tutorial I'll show you how to save and load secret keys as base64 encoded strings using dotenv files in Vapor 4.

- -
- Server - Tooling - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Table joins in Fluent 4

-
-

In this quick tutorial I'm going to show you how to join and query database models using the Fluent ORM framework in Vapor 4.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 13 min read -
- -

Websockets for beginners using Vapor 4 and Vanilla JavaScript

-
-

Learn how to create a websocket server using Swift & Vapor. Multiplayer game development using JavaScript in the browser.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Building and loading dynamic libraries at runtime in Swift

-
-

Learn how to create a plugin system using dynamic libraries and the power of Swift, aka. modular frameworks on the server-side.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

What's new in Swift 5.3?

-
-

Swift 5.3 is going to be an exciting new release. This post is a showcase of the latest Swift programming language features.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 11 min read -
- -

Sign in with Apple using Vapor 4

-
-

A complete tutorial for beginners about how to implement the Sign in with Apple authentication service for your website.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

The Swift package manifest file

-
-

This article is a complete Swift Package Manager cheatsheet for the package manifest file, using the latest Swift 5.2 tools version.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

Server side Swift projects inside Docker using Vapor 4

-
-

Learn how to setup Vapor 4 projects inside a Docker container. Are you completely new to Docker? This article is just for you.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Modules and hooks in Swift

-
-

Learn how to extend your application with new functionalities using a loosely coupled modular plugin system written in Swift.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 16 min read -
- -

All about authentication in Vapor 4

-
-

Learn how to implement a user login mechanism with various auth methods using sessions, JWTs, written in Swift only.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

The anatomy of Vapor commands

-
-

Learn how to build and run your existing Vapor apps using various command line arguments, flags and environments.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

How to use middlewares in Vapor 4?

-
-

Learn how to create middlewares for a Vapor based server side Swift application to handle common routing functionalities.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

How to write Swift scripts using the new Command API in Vapor 4?

-
-

Shell scripts are essentials on the server side. Learn how to build Swift scripts for your backend apps using property wrappers.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 15 min read -
- -

Get started with the Fluent ORM framework in Vapor 4

-
-

Learn how to use the Fluent ORM framework. Migrations, schemas, relations powered by PostgreSQL, written in Swift.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

How to set up pgSQL for Fluent 4?

-
-

This is a tutorial for beginners about using PostgreSQL. I'll show you how to automatically backup and restore the database.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

How to create your first website using Vapor 4 and Leaf?

-
-

Let's build a web page in Swift. Learn how to use the brand new template engine of the most popular server side Swift framework.

- -
-
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 12 min read -
- -

How to download files with URLSession using Combine Publishers and Subscribers?

-
-

Learn how to load a remote image into an UIImageView asynchronously using URLSessionDownloadTask and the Combine framework in Swift.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 18 min read -
- -

Beginner's guide to Server side Swift using Vapor 4

-
-

Learn how to build and host your very first backend application using Vapor 4 and the brief history of server side Swift.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 13 min read -
- -

What are the best practices to learn iOS / Swift in 2020?

-
-

Are you learning iOS development? Looking for Swift best practices? This is the right place to start your journey as a mobile application developer.

- -
- iOS -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

How to write services for VIPER?

-
-

Not everything is a VIPER module. In this article I'll show you how do I separate the service layer from the modules, using Swift.

- -
- VIPER -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

How to build SwiftUI apps using VIPER?

-
-

In this tutorial I'll show you how to combine SwiftUI with the VIPER architecture in a real world iOS application example.

- -
- VIPER -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Picking and playing videos in Swift

-
-

Learn how to record or select a video file using a video picker controller and the AVPlayer class, written entirely in Swift 5.

- -
- UIKit - iOS -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

What's new in Vapor 4?

-
-

Vapor is the most popular server side Swift web application framework. This time we'll cover what's new in Vapor 4.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Swift init patterns

-
-

The ultimate guide how to init your Swift data types, with the help of designated, convenience, failable intitializers and more.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

URLSession and the Combine framework

-
-

Learn how to make HTTP requests and parse the response using the brand new Combine framework with foundation networking.

- -
- iOS -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

Promises in Swift for beginners

-
-

Everything you ever wanted to know about futures and promises. The beginner's guide about asynchronous programming in Swift.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Uniquely identifying views

-
-

Learn how to use string based UIView identifiers instead of tags. If you are tired of tagging views, check out these alternative solutions.

- -
- UIKit - iOS -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Mastering the VIPER architecture

-
-

Learn how to master the VIPER architectural design pattern, with some protocol oriented programming techniques using Swift.

- -
- VIPER -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

VIPER best practices for iOS developers

-
-

In this tutorial I'm going to show you a complete guide about how to build a VIPER based iOS application, written entirely in Swift.

- -
- VIPER -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Styling by subclassing

-
-

Learn how to design and build reusable user interface elements by using custom view subclasses from the UIKit framework in Swift.

- -
- UIKit - iOS -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Picking images with UIImagePickerController in Swift 5

-
-

Learn how to get an image from the photo library or directly from the camera by using the UIImagePickerController class in Swift 5.

- -
- UIKit - iOS -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

How to use the result type to handle errors in Swift 5?

-
-

From this tutorial you can learn how to utilize the do-try-catch syntax with the brand new result type to handle errors in Swift.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Swift Package Manager and the Swift toolchain

-
-

Learn everything about the SPM architecture. I'll also teach you how to integrate your binary executable into the Swift toolchain.

- -
-
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Lazy initialization in Swift

-
-

Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift object pool design pattern

-
-

In this quick tutorial I'll explain & show you how to implement the object pool design pattern using the Swift programming language.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

UITableView tutorial in Swift

-
-

This guide is made for beginners to learn the foundations of the UITableView class programmatically with auto layout in Swift.

- -
- UIKit -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Swift 5 and ABI stability

-
-

Apple's Swift 5 language version will be a huge milestone for the developer community, let's see what are the possible benefits of it.

- -
- Swift - Tooling -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift facade design pattern

-
-

The facade design pattern is a simplified interface over a complex subsystem. Let me show you a real quick example using Swift.

- -
-
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Iterator design pattern in Swift

-
-

Learn the iterator design pattern by using some custom sequences, conforming to the IteratorProtocol from the Swift standard library.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Generating random numbers in Swift

-
-

Learn everything what you'll ever need to generate random values in Swift using the latest methods and covering some old techniques.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Swift adapter design pattern

-
-

Turn an incompatible object into a target interface or class by using a real world example and the adapter design pattern in Swift.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Swift dependency injection design pattern

-
-

Want to learn the Dependency Injection pattern using Swift? This tutorial will show you how to write loosely coupled code using DI.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 11 min read -
- -

Ultimate Grand Central Dispatch tutorial in Swift

-
-

Learn the principles of multi-threading with the GCD framework in Swift. Queues, tasks, groups everything you'll ever need I promise.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift delegate design pattern

-
-

The delegate design pattern is a relatively easy way to communicate between two objects through a common interface, protocol in Swift.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

UICollectionView data source and delegates programmatically

-
-

In this quick UIKit tutorial I'll show you how to create a simple UICollectionView without Interface Builder, but only using Swift.

- -
- UIKit - iOS -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Mastering iOS auto layout anchors programmatically from Swift

-
-

Looking for best practices of using layout anchors? Let's learn how to use the iOS autolayout system in the proper way using Swift.

- -
- UIKit - iOS -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift command design pattern

-
-

This time I'm going to show you a behavioral pattern. Here is a little example of the command design patten written in Swift.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift prototype design pattern

-
-

The prototype design pattern is used to create clones of a base object, so let's see some practical examples written in Swift.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Comparing factory design patterns

-
-

Learn what's the difference between static factory, simple factory, factory method and abstract factory using the Swift language.

- -
-
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift abstract factory design pattern

-
-

Let's combine factory method with simple factory voilá: here is the abstract factory design pattern written in Swift language!

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift factory method design pattern

-
-

The factory method design pattern is a dedicated non-static method for hiding the creation logic of an object. Let's make it in Swift!

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift simple factory design pattern

-
-

This time let's talk about the simple factory design pattern to encapsulate object creation in a really simple way using Swift.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Swift static factory design pattern

-
-

In this article I'll teach you about the static factory design pattern and show some use cases using the Swift programming language.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Swift builder design pattern

-
-

Learn how to implement the builder pattern in Swift to hide the complexity of creating objects with lots of individual properties.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Swift singleton design pattern

-
-

Singleton is the most criticized design pattern of all time. Learn the proper way of using Swift singleton classes inside iOS projects.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

How to use iCloud drive documents?

-
-

Learn how to sync files and data through a shared iCloud drive folder using the latest version of Swift programming language.

- -
- iOS -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

UIColor best practices in Swift

-
-

Learn what are color models, how to convert hex values to UIColor and back, generate random colors, where to find beautiful palettes.

- -
- UIKit -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

iOS custom transition tutorial in Swift

-
-

In this tutorial, you'll learn how to replace the push, pop and modal animations with custom transitions & percent driven interactions.

- -
- UIKit - iOS -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 12 min read -
- -

Ultimate UICollectionView guide with iOS examples written in Swift

-
-

Learn how to use UICollectionView, with highly reusable UIKit components and some MVVM pattern without the going nuts with index path calculations.

- -
- UIKit -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

The ultimate VIPER architecture tutorial

-
-

Learn how to write scalable iOS code using the VIPER architecture with some MVVM and MVC tricks and coordinators in mind.

- -
- VIPER -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Networking examples for appleOS

-
-

Learn how to use Bonjour, with UDP/TCP sockets, streams and how to communicate through CoreBluetooth or the watch APIs.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

How to parse JSON in Swift using Codable protocol?

-
-

In this Swift tutorial, I'd like to give you an example about getting and parsing JSON data using URLSession and Codable protocol.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 11 min read -
- -

Deep dive into Swift frameworks

-
-

Learn everything about Swift modules, libraries, packages, closed source frameworks, command line tools and more.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

UICollectionView cells with circular images plus rotation support

-
-

Learn how to make rounded corners for UIImageView items wrapped inside collection view cells, with rotation support.

- -
- UIKit - iOS -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Self sizing cells with rotation support

-
-

How to make self sizing cells in Swift both for table & collection views supporting orientation changes and dynamic font types.

- -
- UIKit - iOS -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

How to call C code from Swift

-
-

Interacting with C libraries from the Swift language is really amazing, from this post can learn the most of C interoperability.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Swift Package Manager tutorial

-
-

Learn how to use the Swift Package Manager to handle external dependencies, create your library or app on macOS and Linux.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

iOS Auto Layout tutorial programmatically

-
-

In this great iOS Auto Layout tutorial I'll teach you how to support rotation, use constraints, work with layers, animate corner radius.

- -
- UIKit - iOS -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

How to launch a macOS app at login?

-
-

In this tutorial I'll show you how to launch a completely sandboxed macOS application on system startup written in Swift.

- -
- Tooling - macOS -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

How to make a Swift framework?

-
-

Creating a Swift framework shouldn't be hard. This tutorial will help you making a universal framework for complex projects.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift enum all values

-
-

In this quick tutorial I'll show you how to get all the possible values for a Swift enum type with a generic solution written in Swift.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 13 min read -
- -

Asynchronous validation for Vapor

-
-

Learn how to validate input data using an async technique. Unified request validation API for your server side Swift app.

- -
- Server - Vapor -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Everything about public and private Swift attributes

-
-

Have you ever heard about Swift language attributes? In this article I'm trying to gather all the @ annotations and their meanings.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

How to create reusable views for modern collection views?

-
-

A quick intro to modern collection views using compositional layout, diffable data source and reusable view components.

- -
- UIKit -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 12 min read -
- -

Result builders in Swift

-
-

If you want to make a result builder in Swift, this article will help you to deal with the most common cases when creating a DSL.

- -
- Swift -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

The ultimate Combine framework tutorial in Swift

-
-

Get started with the brand new declarative Combine framework in practice using Swift. I'll teach you all the goodies from zero to hero.

- -
- UIKit - iOS -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Top 20 iOS libraries written in Swift

-
-

I gathered the best open source Swift frameworks on github that will help you to speed up mobile application development in 2019.

- -
- UIKit - iOS -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

UIKit init patterns

-
-

Learn about the initialization process of the two well known classes in UIKit. Say hello to UIViewcontroller, and UIView init patterns.

- - -
- -
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Awesome native Xcode extensions

-
-

This is the biggest and the best collection of the currently available natively created source editor extensions for Xcode.

- -
- Xcode -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Conventions for Xcode

-
-

Learn how to organize your codebase. If you are struggling with Xcode project structure, files, naming conventions, read this.

- -
- Tooling - Xcode -
-
- -
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

ClockKit complications cheatsheet

-
-

ClockKit families and templates, there are so many of them. It's a little bit time consuming if you are looking for the right one.

- -
- watchOS -
-
-
- -
- -
- - - -
- - - - diff --git a/docs/awesome-native-xcode-extensions/index.html b/docs/awesome-native-xcode-extensions/index.html deleted file mode 100644 index ec602a8..0000000 --- a/docs/awesome-native-xcode-extensions/index.html +++ /dev/null @@ -1,296 +0,0 @@ - - - - - - - - - - - - Awesome native Xcode extensions - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 1 min read - -
-

Awesome native Xcode extensions

-
-

This is the biggest and the best collection of the currently available natively created source editor extensions for Xcode.

-
- -

Xcode extensions

You can find the actual list hosted on GitHub (~3k ⭐️).

The main reason for this is that the project is almost entirely driven by the community and this fact is truely amazing! In under just one year, the repository had a thousand stars on GitHub and it’s counting! The list of awesome native Xcode extensions is open for everyone, so if you have an extension, please don’t hesitate & create a pull request!

I’d like to say thank you for all the contributors, especially for takasek who categorized the list.

Are you using Xcode extensions at all?

Personally I’m using 1-2 extensions, but nothing more. Maybe everything should be collected under one gigantic extension application with the ability of turning individual features on or off?

Does anybody have time for that? 🤔

How to create an Xcode extension?

You might feel the urge to implement your own extension. Not a problem. Here are some really amazing resources, to teach you how to make an Xcode plugin.

Both of these articles are very great ones, I don’t want to get into the details of building an Xcode source editor extension, you should just go read (watch) them and start building your own stuff. 😉

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Awesome native Xcode extensions

-
-

This is the biggest and the best collection of the currently available natively created source editor extensions for Xcode.

- -
- Xcode -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Conventions for Xcode

-
-

Learn how to organize your codebase. If you are struggling with Xcode project structure, files, naming conventions, read this.

- -
- Tooling - Xcode -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Custom working directory in Xcode

-
-

Learn how to set a custom working directory in Xcode to solve one of the most common beginner issue when using Vapor.

- -
- Tooling - Xcode -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/beginners-guide-to-functional-swift/index.html b/docs/beginners-guide-to-functional-swift/index.html deleted file mode 100644 index 703de4d..0000000 --- a/docs/beginners-guide-to-functional-swift/index.html +++ /dev/null @@ -1,602 +0,0 @@ - - - - - - - - - - - - Beginners guide to functional Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 9 min read - -
-

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

-
- -

Functional programming explained

First of all let me emphasize one thing:

Do not be afraid of functional programming!

Even if you are a beginner developer, you’ll see that functional programming is not so hard that you might imagine. If you only learn the basics, it’ll save you lots of time & helps you to write way better applications. The main concept of the FP paradigm is to eliminate mutable states and data, by using functions in a special way. 💫

First-class functions

If a programming language treats functions as first-class citizens (same behavior as we’d expect from a type) we say that it has first class functions.

This means the language supports passing functions as arguments to other functions, returning them as the values from other functions, and assigning them to variables or storing them in data structures.

In Swift you can use function pointers, closures (anonymous functions), so yes, Swift is pretty much designed to be a real functional language. Quick sample time:

// an old-school function
-func hello() {
-    print("Hello!")
-}
-
-// it's a block!
-let hi: () -> Void = {
-    print("Hi!")
-}
-
-// this points to a function
-let function = hello
-// this is a copy of the closure
-let block = hi
-
-hello() // simple function call
-function() // call through "function pointer"
-
-hi() // simple closure call
-block() // closure call through another variable
-
-// closure parameter
-func async(completion: () -> Void) {
-    // usually we'd do something here first...
-    completion()
-}
-
-// calling the method with a closure
-async(completion: {
-    print("Completed.")
-})
-// trailing closure syntax
-async {
-    print("Completed.")
-}
-

Please note that sometimes I refer to closures as blocks, for the sake of simplicity let’s pretend that they’re the exact same thing, and don’t go too much into the details. 🙄

Function composition, currying and variadic parameters

Composing functions is basically passing the output of a function to another. This is not so interesting, we do it all the time. On the other hand currying functions is a more exciting topic. Currying is basically converting functions with multiple arguments into functions with one arguments and a returning function.

What is currying used for? Well, some say it’s just a syntactic sugar, others say it’s useful, because you can split logic into smaller more specialized chunks. I leave it up to you whether you find currying useful or not, but in my opinion it’s a quite interesting technique, and it’s worth learning the basics of currying. 🍛

Using a variadic parameter means accepting zero or more values of a specified type. So this means you can for example input as many integers as you want by using a variadic Int parameter. Creating a variadic argument is pretty simple, you just have to append three dots after your type… let’s see these things in action:

// function composition
-func increment(_ x: Int) -> Int {
-    return x + 1
-}
-let x = increment(increment(increment(increment(10))))
-print(x)
-
-
-// function currying
-func decrement(_ x: Int) -> (Int) -> Int {
-     return { $0 * x }
-}
-let y = decrement(10)(1)
-print(y)
-
-
-// this is a variadic function that accepts a block as a parameter
-func variadic(_ blocks: (() -> Void)...) {
-    for block in blocks {
-        block()
-    }
-}
-
-// it means you can pass as many parameters as you want...
-variadic({ print("a") }, { print("b") }, { print("c") })
-
-// lol, trailing closure syntax works with variadic block params.
-variadic {
-    print("d")
-}
-

Pretty much that was a quick intro to Swift function capabilities. Of course you can add more parameters (but only one variadic parameter is allowed), use generics and many more, but let’s wait just a little bit more, before we dive into the deep water. 🏊‍♂️

Higher order functions

A function is a higher order function if at least one of the following rule is satisfied:

  • takes one or more functions as arguments
  • returns a function as its result.

In other words, or maybe even in Swift:

// a function that takes another function as a parameter
-func transform(value: Int, _ transformation: (Int) -> Int) -> Int {
-    return transformation(value)
-}
-let x = transform(value: 10) { value -> Int in
-    return value * 2
-}
-print(x)
-
-// a function that returns another function
-func increase(withMultiplication shouldMultiply: Bool) -> (Int, Int) -> Int {
-    func add(_ x: Int, _ y: Int) -> Int { return x + y }
-    func multiply(_ x: Int, _ y: Int) -> Int { return x * y }
-    return shouldMultiply ? multiply : add
-}
-
-let y = increase(withMultiplication: true)(10, 10)
-print(y)
-

So as you can see it’s not like magic, we’re just passing around functions. At first sight the syntax can seem quite complicated, but trust me, it’s not that hard. If you are having trouble, try to define your own typealiases for the function types, that’ll make the code a little bit more readable. typealias VoidBlock = () -> Void 👍

Generic functions

The real problem starts if you’re trying to generalize your higher order functions. With generics involved, the syntax can look a little bit messy. Generics (aka. parametric polymorphism) allows us to abstract away regular types. So for example:

// this only works for integers
-func chooseInt(_ x: Int, or y: Int) -> Int {
-    return Bool.random() ? x : y
-}
-
-// whoa, this is a generic function
-func choose<T>(_ x: T, or y: T) -> T {
-    return Bool.random() ? x : y
-}
-
-let x = chooseInt(1, or: 2)
-print(x) // 1 or 2, but who knows this for sure
-
-let y = choose("heads", or: "tails")
-print(y) // maybe heads or maybe tails
-

In the example above we abstracted away the integer type with a generic T type, that can be anything. If we call our generic function with a string as a first parameter, all the remaining T types will be used as strings. Does this make any sense? If yes, then congratulations, now you know what are generic functions. 🎊

Containers and boxes 📦

Let’s start with a generic box. You can put any value into the box (it’s just like an ordinary paper box like you’d use in real life), you can always open the box and directly get the value from inside by using the value property.

struct Box<T> {
-
-    let value: T
-
-    init(_ value: T) {
-        self.value = value
-    }
-}
-
-let x = Box<Int>(360)
-print(x.value)
-

Next continue with a little bit more theory, but I promise I’ll keep things very short, just because Russ Bishop already explained functors, applicatives and monads in plain English. I’ll try to do my best in order to make it even more simple. 😉

Functors

Functors are containers you can call map on.

Challenge accepted! Let’s make a functor from our box type, but what exactly does map? Well, it basically transforms a value into another. You can provide your own transformation method, where you’ll receive the original value as a parameter and you have to return a “new” value form the same or a different type. Code time!

extension Box {
-    func map<U>(_ transformation: (T) -> U) -> Box<U> {
-        return Box<U>(transformation(self.value))
-    }
-}
-
-let x = Box<Int>(360)
-let y = x.map { "\($0) degrees" }
-print(y.value)
-

So map is just a generic higher order function! Just a higher order function… 🤔 Just a function passed into another function. Oh, this is only possible, because Swift supports first-class functions! Now you get it! Nothing magical, just functions!

Monads

Monads are containers you can call flatMap on.

This one is ridiculously easy. flatMap is a function that transforms a value, then re-wrap it in the original container type. It’s like map, but you have to provide the container inside your transformation function. I’ll show you the implementation:

extension Box {
-    func flatMap<U>(_ transformation: (T) -> Box<U>) -> Box<U> {
-        return transformation(self.value)
-    }
-}
-
-let x = Box<Int>(360)
-let y = x.flatMap { Box<String>("\($0) degrees") }
-print(y.value)
-

Are you ready for something more complicated? 😅

Applicatives

An applicative lets you put the transformation function inside a container. So you have to unwrap your transformation function first, only after you can apply the function into the wrapped value. That means you have to “unbox” the value as well, before the transformation. Explaining things is a though job, let me try in Swift:

extension Box {
-    func apply<U>(_ transformation: Box<(T) -> U>) -> Box<U> {
-        return Box<U>(transformation.value(self.value))
-    }
-}
-
-let x = Box<Int>(360)
-
-let transformation = Box<((Int) -> String)>({ x -> String in
-    return "\(x) degrees"
-})
-
-let y = x.apply(transformation)
-print(y.value)
-

As you can see it all depends on the container, so if you’d like to extend the Optional enum with an apply function that’d look a little different. Containerization is hard! 🤪

Quick recap:

  • Container = M
  • Functor = map(f: T -> U) -> M
  • Monad = flatMap(f: T -> M) -> M
  • Applicative = apply(f: M U)>) -> M

Higher kinded types

The idea of higher-rank types is to make polymorphic functions first-class

Currently this is not implemented in the Swift programming language, and it’s NOT going to be part of the Swift 5 release, but you can simulate HKT functionality in Swift with some tricks. Honestly speaking I really don’t want to talk more about higher kinded types now, because it’s a really hardcore topic, maybe in the next functional programming tutorial, if you’d like to have more like this. 😉

Futures

Let’s talk a little bit about futures. By definition they are read-only references to a yet-to-be-computed value. Another words: future is a placeholder object for a result that does not exists yet. This can be super useful when it comes to asynchronous programming. Have you ever heard about the callback hell? 😈

A future is basically a generic result wrapper combined with callbacks and some extra state management. A future is both a functor and a monad, this means that you can usually call both map & flatMap on it, but because of the read-only nature of futures you usually have to make a promise in order to create a new future object. You can find a really nice implementation in SwiftNIO. 😎

Promises

A promise is a writable, single-assignment container, which completes a future.

In a nutshell, you have to make promises, instead of futures, because futures are read-only by design. The promise is the only object that can complete a future (normally only once). We can say that the result of a future will always be set by someone else (private result variable), while the result of a promise (underlying future) will be set by you, since it has a public reject & resolve methods. 🚧

Some promises also implement the future interface, so this means that you can directly call map, flatMap (usually both called as a simple overloaded then method) on a promise.

Are you Ready for some functional Swift code?

Functional Programming in Swift 5

It’s time to practice what we’ve learned. In this section I’ll go through the most popular functional methods in Swift 5 and show you some of the best practices.

map

The map function in Swift works on all the Sequence types plus the brand new Result type in Swift also has a map function, so you can transform values on these types as you want, which is quite nice in some cases. Here are a few examples:

// array
-let numbers = Array(0...100)
-numbers.map { $0 * 10 } // 0, 10, 20 ... 1000
-numbers.map(String.init) // "0", "1", "2" ... "100"
-
-
-// dictionary
-let params: [String: Any] = [
-    "sort": "name",
-    "order": "desc",
-    "limit": 20,
-    "offset": 2,
-]
-
-// mapValues is basically map for the dictionary values
-let queryItems = params.mapValues { "\($0)" }
-                       .map(URLQueryItem.init)
-
-
-// set
-let fruits = Set<String>(arrayLiteral: "apple", "banana", "pear")
-fruits.map { $0.capitalized }
-
-// range
-(0...100).map(String.init)
-

flatMap

The flatMap method is also available on most of the types that implements the map functionality. Essentially flatMap does the following thing: it maps and flattens. This means you’ll get the flattened array of subarrays. Let me show you how it works:

// flatMap
-let groups = [
-    "animals": ["🐔", "🦊", "🐰", "🦁"],
-    "fruits": ["🍎", "🍉", "🍓", "🥝"]
-]
-let emojis = groups.flatMap { $0.value }
-// "🐔", "🦊", "🐰", "🦁", "🍎", "🍉", "🍓", "🥝"
-

compactMap

So what’s the deal with flatMap vs compactMap? In the past flatMap could be used to remove optional elements from arrays, but from Swift 4.1 there is a new function called compactMap which should be used for this purpose. The compiler will give you a warning to replace flatMap with compactMap in most of the cases.

// compactMap
-[1, nil, 3, nil, 5, 6].compactMap { $0 } // 1, 3, 5, 6
-
-let possibleNumbers = ["1", "two", "3", "four", "five", "6"]
-possibleNumbers.compactMap { Int($0) } //1, 3, 6
-

reduce

The reduce method is a powerful tool. It can be used to combine all the elemens from a collection into a single one. For example you can use it to summarize elements, but it’s also quite handy for joining elements together with an initial component.

let sum = (0...100).reduce(0, +)
-print(sum) //5050
-
-let cats = ["🦁", "🐯", "🐱"]
-cats.reduce("Cats: ") { sum, cat in "\(sum)\(cat)"} // Cats: 🦁🐯🐱
-
-
-let basketballScores = [
-    "team one": [2,2,3,2,3],
-    "team two": [3,2,3,2,2],
-]
-
-let points = basketballScores.reduce(0) { sum, element in
-    return sum + element.value.reduce(0, +)
-}
-print(points) // 24 (team one + team two scores together)
-

filter

You can filter sequences with the filter method, it’s pretty obvious! You can define a condition block for each element, and if the condition is true, the given element will be included in the result. It’s like looping through elements & picking some. 🤪

let evenNumbers = (0...100).filter { $0.isMultiple(of: 2) }
-let oddNumbers = (0...100).filter { !evenNumbers.contains($0) }
-
-let numbers = [
-    "odd": oddNumbers,
-    "even": evenNumbers,
-]
-
-let luckyThirteen = numbers
-.filter { element in
-    return element.key == "odd"
-}
-.mapValues { element in
-    return element.filter { $0 == 13 }
-}
-

promises

I love promises, and you should learn them too if you don’t know how they work. Otherwise you can still go with the Dispatch framework, but I prefer promises, because passing variables around is way more easy by using a promise framework.

Promise<String> { fulfill, reject in
-    fulfill("Hello")
-}
-.thenMap { result in
-    return result + " World!"
-}
-.then { result in
-    return Promise<String>(value: result)
-}
-.tap { result in
-    print("debug: \(result)")
-}
-.onSuccess(queue: .main) { result in
-    print(result)
-}
-.onFailure { error in
-    print(error.localizedDescription)
-}
-.always {
-    print("done!")
-}
-

What’s next?

There is a game for practicing functional methods! It’s called cube composer, and it is totally awesome and fun! Just play a few rounds, you won’t regret it! 🎮

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/beginners-guide-to-modern-generic-programming-in-swift/index.html b/docs/beginners-guide-to-modern-generic-programming-in-swift/index.html deleted file mode 100644 index 546ebb1..0000000 --- a/docs/beginners-guide-to-modern-generic-programming-in-swift/index.html +++ /dev/null @@ -1,420 +0,0 @@ - - - - - - - - - - - - Beginner's guide to modern generic programming in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

-
- -

Protocols (with associated types)

According to the Swift language guide a protocol can define a blueprint of methods, properties and other requirements. It’s pretty easy to pre-define properties and methods using a protocol, the syntax is pretty straightforward, the problem starts to occur when we start to work with associated types. The very first question that we have to answer is this: what are associated types exactly?

An associated type is a generic placeholder for a specific type. We don’t know that type until the protocol is being adopted and the exact type is specified by the implementation.

protocol MyProtocol {
-    associatedtype MyType
-    
-    var myVar: MyType { get }
-    
-    func test()
-}
-
-extension MyProtocol {
-    
-    func test() {
-        print("is this a test?")
-    }
-}
-struct MyIntStruct: MyProtocol {
-    typealias MyType = Int
-    
-    var myVar: Int { 42 }
-}
-
-struct MyStringStruct: MyProtocol {
-    let myVar = "Hello, World!"
-}
-
-let foo = MyIntStruct()
-print(foo.myVar)
-foo.test()
-
-let bar = MyStringStruct()
-print(bar.myVar)
-bar.test()
-

As you can see, associated MyType placeholder can have different types, when we implement the protocol. In the first case (MyIntStruct) we have explicitly told the compiler - by using a typealias - to use an Int type, and in the second case (MyStringStruct) the Swift compiler is smart enough to figure out the type of the myVar based on the provided String value.

Of course we can explicitly write let myVar: String = "Hello, World!" or use a computed property or a regular variable, it really doesn’t matter. The key takeaway is that we’ve defined the type of the MyType placeholder when we implemented the protocol using the two struct. 🔑

You can use an associated type to serve as a generic placeholder object so you don’t have to duplicate code if you need support for multiple different types.

Existentials (any)

Great, our generic protocol has a default test method implementation that we can use on both objects, now here’s the thing, I don’t really care about the type that’s going to implement my protocol, I just want to call this test function and use the protocol as a type, can I do that? Well, if you are using Swift 5.6+ the answer is yes, otherwise…

// 
-// ERROR:
-//
-// Protocol 'MyProtocol' can only be used as a generic constraint 
-// because it has Self or associated type requirements
-//
-let myObject: MyProtocol 
-
-// even better example, an array of different types using the same protocol
-let items: [MyProtocol]
-

I bet that you’ve seen this famous error message before. What the hell is happening here?

The answer is quite simple, the compiler can’t figure out the underlying associated type of the protocol implementations, since they can be different types (or should I say: dynamic at runtime 🤔), anyway, it’s not determined at compile time.

The latest version of the Swift programming language solves this issue by introducing a new any keyword, which is a type-erasing helper that will box the final type into a wrapper object that can be used as an existential type. Sounds complicated? Well it is. 😅

// ...
-
-let myObject: any MyProtocol 
-
-let items: [any MyProtocol] = [MyIntStruct(), MyStringStruct()]
-
-for item in items {
-    item.test()
-}
-

By using the any keyword the system can create an invisible box type that points to the actual implementation, the box has the same type and we can call the shared interface functions on it.

  • any HiddenMyProtocolBox: MyProtocol — pointer —> MyIntStruct
  • any HiddenMyProtocolBox: MyProtocol — pointer —> MyStringStruct

This approach allows us to put different protocol implementations with Self associated type requirements into an array and call the test method on both of the objects.

If you really want to understand how these things work, I highly recommend to watch the Embrace Swift Generics WWDC22 session video. The entire video is a gem. 💎

There is one more session called Design protocol interfaces in Swift that you should definitely watch if you want to learn more about generics.

From Swift 5.7 the any keyword is mandatory when creating an existential type, this is a breaking change, but it is for the greater good. I really like how Apple tackled this issue and both the any and some keywords are really helpful, however understanding the differences can be hard. 🤓

Opaque types (some)

An opaque type can hide the type information of a value. By default, the compiler can infer the underlying type, but in case of a protocol with an associated type the generic type info can’t be resolved, and this is where the some keyword and the opaque type can help.

The some keyword was introduced in Swift 5.1 and you must be familiar with it if you’ve used SwiftUI before. First it was a return type feature only, but with Swift 5.7 you can now use the some keyword in function parameters as well.

import SwiftUI
-
-struct ContentView: View {
-
-    // the compiler knows that the body is always a Text type
-    var body: some View {
-        Text("Hello, World!")
-    }
-}
-

By using the some keyword you can tell the compiler that you are going to work on a specific concrete type rather than the protocol, this way the compiler can perform additional optimizations and see the actual return type. This means that you won’t be able to assign a different type to a variable with a some ‘restriction’. 🧐

var foo: some MyProtocol = MyIntStruct()
-
-// ERROR: Cannot assign value of type 'MyStringStruct' to type 'some MyProtocol'
-foo = MyStringStruct()
-

Opaque types can be used to hide the actual type information, you can find more great code examples using the linked article, but since my post focuses on the generics, I’d like to show you one specific thing related to this topic.

func example<T: MyProtocol>(_ value: T) {}
-
-func example<T>(_ value: T) where T: MyProtocol {}
-
-func example(_ value: some MyProtocol) {}
-

Believe or not, but the 3 functions above are identical. The first one is a generic function where the T placeholder type conforms to the MyProtocol protocol. The second one describes the exact same thing, but we’re using the where claues and this allows us to place further restrictions on the associated types if needed. e.g. where T: MyProtocol, T.MyType == Int. The third one uses the some keyword to hide the type allowing us to use anything as a function parameter that conforms to the protocol. This is a new feature in Swift 5.7 and it makes the generic syntax more simple. 🥳

If you want to read more about the differences between the some and any keyword, you can read this article by Donny Wals, it’s really helpful.

Primary associated types (Protocol)

To constraint opaque result types you can use the where clause, or alternatively we can ‘tag’ the protocol with one or more primary associated types. This will allow us to make further constraints on the primary associated type when using some.

protocol MyProtocol<MyType> {
-    associatedtype MyType
-    
-    var myVar: MyType { get }
-    
-    func test()
-}
-
-//...
-
-func example(_ value: some MyProtocol<Int>) {
-    print("asdf")
-}
-

If you want to learn more about primary associated types, you should read Donny’s article too. 💡

Generics ()

So far we haven’t really talked about the standard generic features of Swift, but we were mostly focusing on protocols, associated types, existentials and opaque types. Fortunately you write generic code in Swift without the need to involve all of these stuff.

struct Bag<T> {
-    var items: [T]
-}
-
-let bagOfInt = Bag<Int>(items: [4, 2, 0])
-print(bagOfInt.items)
-
-let bagOfString = Bag<String>(items: ["a", "b", "c"])
-print(bagOfString.items)
-

This bag type has a placeholder type called T, which can hold any kind of the same type, when we initialize the bag we explicitly tell which type are we going to use. In this example we’ve created a generic type using a struct, but you can also use an enum, a class or even an actor, plus it is also possible to write even more simple generic functions. 🧐

func myPrint<T>(_ value: T) {
-    print(value)
-}
-
-myPrint("hello")
-myPrint(69)
-

If you want to learn more about generics you should read this article by Paul Hudson, it’s a nice introduction to generic programming in Swift. Since this article is more about providing an introduction I don’t want to get into the more advanced stuff. Generics can be really difficult to understand, especially if we involve protocols and the new keywords.

I hope this article will help you to understand these things just a bit better.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/beginners-guide-to-server-side-swift-using-the-hummingbird-framework/index.html b/docs/beginners-guide-to-server-side-swift-using-the-hummingbird-framework/index.html deleted file mode 100644 index 70652db..0000000 --- a/docs/beginners-guide-to-server-side-swift-using-the-hummingbird-framework/index.html +++ /dev/null @@ -1,478 +0,0 @@ - - - - - - - - - - - - Beginner's guide to server-side Swift using the Hummingbird framework - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

Beginner's guide to server-side Swift using the Hummingbird framework

-
-

Learn about Swift on the server by creating a simple application using the brand new HTTP server library called: Hummingbird.

-
- -

Swift on the Server in 2023

Three years ago I started to focus on Vapor, the most popular web-framework written in Swift, which served me very well over the years, but now it is time to start a new chapter in my life.

As I learned more and more about how servers work I realized that Vapor has it’s own pros and cons. The community changed a lot during the past 3 years, some core members left and new people started to maintain the framework. I also had some struggles with the default template engine (Leaf) and recently I started to turn away from the abstract database layer (Fluent) too. Another pain point for me is the growing number of dependencies, I barely use websockets & multipart-kit, but Vapor has these dependencies by default and you can’t get rid of them. 😢

Vapor has some really nice things to offer, and for most of the people it’s still going to be a great choice for building backends for frontends (BFFs). For me, Vapor reached its limits and I wanted to use something that feels a bit lighter. Somethings that is modular, something that can be easily extended and fits my exact needs without additional (unused) package dependencies.

This shiny new thing is called Hummingbird and it looks very promising. It was created by Adam Fowler who is a member of the SSWG and also the main author of the Soto library (AWS Swift).

Hummingbird has a comprehensive documentation available online and a nice example repository containing various demo apps written using the Hummingbird Swift server framework. I believe that the best part of the the framework is modularity & extensibility. By the way, Hummingbird works without Foundation, but it has extensions for Foundation objects, this is a huge plus for me, but maybe that’s just my personal preference nowadays. Hummingbird can be extended easily, you can find some very useful extensions under the Hummingbird project page, long story short it works with Fluent and it’s relatively easy to get along with it if you have some Vapor knowledge… 🤔

Getting started with Hummingbird

First of all, there is no toolbox or command line utility to help the kickoff process, but you can always download the examples repository and use one of the projects as a starting point. Alternatively you can set everything up by hand, that’s what we’re going to do now. 🔨

In order to build a new application using the Hummingbird framework you should create a new directory and initialize a new Swift package using the following commands:

mkdir server && cd $_
-swift package init --type executable
-open Package.swift
-

This will create a new Swift package and open the Package.swift file in Xcode. You can use your own editor if you don’t like Xcode, but either way you’ll have to add Hummingbird to your package manifest file as a dependency. We’re going to setup an App target for the application itself, and a Server target for the main executable, which will use the application and configure it as needed.

// swift-tools-version: 5.7
-import PackageDescription
-
-let package = Package(
-    name: "server",
-    platforms: [
-        .macOS(.v10_15),
-    ],
-    dependencies: [
-        .package(
-            url: "https://github.com/hummingbird-project/hummingbird",
-            from: "1.0.0"
-        ),
-        .package(
-            url: "https://github.com/apple/swift-argument-parser",
-            from: "1.0.0"
-        ),
-    ],
-    targets: [
-        .executableTarget(
-            name: "Server",
-            dependencies: [
-                .product(
-                    name: "ArgumentParser",
-                    package: "swift-argument-parser"
-                ),
-                .target(name: "App"),
-            ]
-        ),
-        .target(
-            name: "App",
-            dependencies: [
-                .product(
-                    name: "Hummingbird",
-                    package: "hummingbird"
-                ),
-                .product(
-                    name: "HummingbirdFoundation",
-                    package: "hummingbird"
-                ),
-            ],
-            swiftSettings: [
-                .unsafeFlags(
-                    ["-cross-module-optimization"],
-                    .when(configuration: .release)
-                ),
-            ]
-        ),
-        .testTarget(
-            name: "AppTests",
-            dependencies: [
-                .product(
-                    name: "HummingbirdXCT",
-                    package: "hummingbird"
-                ),
-                .target(name: "App"),
-            ]
-        ),
-    ]
-)
-

Please create the necessary file and directory structure, as listed below, before you proceed to the next steps. It is very important to name things as they appear, otherwise SPM won’t work and the project won’t compile. Anyway, the project structure is kind-of Vapor-like as you can see. 💧

.
-├── Package.resolved
-├── Package.swift
-├── README.md
-├── Sources
-│ ├── App
-│ │ └── HBApplication+Configure.swift
-│ └── Server
-│     └── main.swift
-└── Tests
-    └── AppTests
-        └── AppTests.swift
-

The next step is to create the main entry point for the application. For this purpose Hummingbird uses the Swift Argument Parser library. Place the following contents into the main.swift file:

import ArgumentParser
-import Hummingbird
-import App
-
-struct HummingbirdCommand: ParsableCommand {
-
-    @Option(name: .shortAndLong)
-    var hostname: String = "127.0.0.1"
-
-    @Option(name: .shortAndLong)
-    var port: Int = 8080
-
-    func run() throws {
-        let app = HBApplication(
-            configuration: .init(
-                address: .hostname(hostname, port: port),
-                serverName: "Hummingbird"
-            )
-        )
-        try app.configure()
-        try app.start()
-        app.wait()
-    }
-}
-
-HummingbirdCommand.main()
-

The HummingbirdCommand has two options, you can setup a custom hostname and port by providing these values as command line options (I’ll show it later on), the application itself will setup the address using the input and then it’ll start listening on the specified port.

The configure method comes from the App target, this is where you can customize your server instance, register route handlers and stuff like that, just like you would do this in Vapor. The main difference is that Hummingbird uses the HB namespace, which is pretty handy, and the configure method is written as an extension. Let’s write it and register a basic route handler. 🧩

import Hummingbird
-import HummingbirdFoundation
-
-public extension HBApplication {
-
-    func configure() throws {
-
-        router.get("/") { _ in
-            "Hello, world!"
-        }
-    }
-}
-

That’s it. Now you should be able to run your server, you can press the Play button in Xcode that’ll start your application or enter one of the following commands into the Terminal application:

# just run the server
-swift run Server
-
-# custom hostname and port
-swift run Server --port 3000
-swift run Server --hostname 0.0.0.0 --port 3000
-
-# short version
-swift run Server -p 3000
-swift run Server -h 0.0.0.0 -p 3000
-
-# set the log level (https://github.com/apple/swift-log#log-levels)
-LOG_LEVEL=notice swift run Server -p 3000
-
-# make release build
-swift build -c release
-
-# copy release build to the local folder
-cp .build/release/Server ./Server
-
-# run the executable
-LOG_LEVEL=notice ./Server -p 3000
-

You can set these values in Xcode too, just click on the server scheme and select the Edit Scheme… menu item. Make sure that you’re on the Run target, displaying the Arguments tag. Simply provde the Arguments Passed On Launch options to set a custom hostname or port and you can set the log level by adding a new item into the Environment Variables section.

If you’d like to unit test your application, I’ve got a good news for you. Hummingbird also comes with a nice utility tool called HummingbirdXCT, which you can easily setup & use if you’d like to run some tests against your API. In our project, simply alter the AppTests.swift file.

import Hummingbird
-import HummingbirdXCT
-import XCTest
-@testable import App
-
-final class AppTests: XCTestCase {
-    
-    func testHelloWorld() throws {
-        let app = HBApplication(testing: .live)
-        try app.configure()
-
-        try app.XCTStart()
-        defer { app.XCTStop() }
-
-        try app.XCTExecute(uri: "/", method: .GET) { response in
-            XCTAssertEqual(response.status, .ok)
-
-            let expectation = "Hello, world!"
-            let res = response.body.map { String(buffer: $0) }
-            XCTAssertEqual(res, expectation)
-        }
-    }
-}
-

Instead of creating the application from the main entry point, we can set up a new HBApplication instance, import the App framework and call the configure method on it. the XCT framework comes with a custom XCTStart and XCTStop method, and you can execute HTTP requests using the XCTExecute function. The response is available in a completion block and it’s possible to examine the status code and extract the body using a convenient String initializer.

As you can see Hummingbird is quite similar to Vapor, but it’s lightweight and you can still add those extra things to your server when it is needed. Hummingbird feels like the next iteration of Vapor. I really don’t know if Vapor 5, is going to fix the issues I’m currently having with the framework or not, but I don’t really care, because that release won’t happen anytime soon.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/beginners-guide-to-server-side-swift-using-vapor-4/index.html b/docs/beginners-guide-to-server-side-swift-using-vapor-4/index.html deleted file mode 100644 index 49195e7..0000000 --- a/docs/beginners-guide-to-server-side-swift-using-vapor-4/index.html +++ /dev/null @@ -1,501 +0,0 @@ - - - - - - - - - - - - Beginner's guide to Server side Swift using Vapor 4 - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 18 min read - -
-

Beginner's guide to Server side Swift using Vapor 4

-
-

Learn how to build and host your very first backend application using Vapor 4 and the brief history of server side Swift.

-
- -

Brief history of my backend career

For me, it all started with PHP. It was my first real programming language (HTML & CSS doesn’t count). I always loved to work on backend projects, I’ve written my very first modular backend framework with one of my good friend during the university years. It was an amazing experience, I learned so much from it.

Fast forward a decade. The backend ecosystem have changed a lot during this time. The term “full-stack” developer was born alongside with node.js and people slowly started to turn their backs on PHP. I really don’t mind that, but still PHP was revolutionary in some ways. It was easy to learn, OOP (from PHP5) and for some reason it got real popular. Sometimes I really miss those times… #entropy

Node.js on the other hand was a really good step forward the right direction. It brought JavaScript to the backend, so developers could write both the frontend and the backend code in the same programming language. The V8 engine with and the event-loop was extremely efficient compared to PHP’s approach.

The problem with the node ecosystem is npm and JavaScript itself. We’ve seen the rise and fall of io.js, ayo also there is CoffeScript, TypeScript, oh did I mentioned Babel already? I mean it’s fine, evolution is a good thing, the ECMAScript standards tries to keep everything under control, but here’s the real deal:

JavaScript is rotten at it’s core.

Don’t get me wrong, in the past I loved JS. It was amazing to see such a dynamic “functional” programming language. I’ve written lots of JavaScript (both frontend and node.js) code but nowadays I only see that nothing of the issues have been really fixed (only patched) from the past 10 years. Haters gona hate. I don’t care. 🤷‍♂️

Now what? Should I use Go, Ruby, Python or old-school C on the server side? Well I’ve tried all of them. Both Ruby, Go and Python is a little bit harder to learn, since they have a “strange” syntax compared to JS or PHP. C on the other hand is a low-level language, so you have to deal with pointers a lot. Believe me: that’s not how you want to spend your time. What about Java? Netty seems cool, but I’m not a big fan of the language at all.

So I was getting bored with the server side, that’s why I left it and started to work as an iOS developer. I had to write Objective-C code before the ARC times. Foundation and UIKit was brand new for me, anyway after a few years Apple released Swift. Most of the people reacted like this:

Swift is just like (type safe) JavaScript

The state of server side Swift in 2020

Apple open sourced the Swift programming language in the end of 2015. This event started everything. Lots of server side frameworks were born that time. Unfortunately Swift was quite a young language and it changed a lot. ABI stability was just a dream and the buggy Foundation framework on linux was quite a bad environment to develop a stable backend application. Long story short, most of them are dead by now, except: Vapor. 💀

Let’s have a silent minute for all the other frameworks (some are still alive):

I belive that the cause of this problem was that back in the days everyone had to implement it’s own solution for server side networking (low level, socket base) including security and encryption features (for SSL/TLS based secure transport) plus HTTP and websocket service support. That’s quite a lot of work already.

The Swift Server Work Group was formed (end of 2016) to create a cross platform, portable, low level native server side API framework to act as a basic building block for server side projects. The SSWG was moving forward slowly (they just released one proof of concept version in 2017), but then suddenly in 2018 Apple released SwiftNIO. Wait, what? Bastards. They secretly developed SwiftNIO and it changed everything. It was like Netty, but written in 100% Swift. NIO is a really low level asynchronous event-driven application framework designed for high performance (non-blocking IO) & scalability for servers and clients.

It seems like Apple has some real plans for SwiftNIO. Maybe they just want to replace all the Java based internal system on a long term. Who knows, but one thing is for sure:

SwiftNIO is here to stay.

SwiftNIO added support for the HTTP/2 protocol in early 2019, Vapor was the first framework that used NIO under the hood. Perfect, Vapor and Kitura were the most popular Swift frameworks, but Perfect slowly faded away and IBM announced that they won’t work anymore on Kitura from 2020. Vapor is still doing great, it has a great community (~18k GitHub stars), so we can only hope for the best.

I started to work with Kitura in the past, but I migrated away since the development of Kitura was already too slow for me. Vapor on the other hand became extremely popular and surprisingly well-designed. Vapor 3 was a huge step into the right direction and trust me: Vapor 4 is amazing! It’s your best option to create backend apps using Swift. Of course you can use SwiftNIO, but if you are looking for a high level framework instead of a low level tool, maybe Vapor is your ONLY option. Is this bad? I don’t think so.

Sorry about the long intro, but it was quite a journey. As you can see a lot happened during the past few years, Swift is now a mature language, SwiftNIO arrived, Vapor is better than ever. Some people think that server side Swift is dead, because of the past events and now IBM also left the party. Vapor also announced that they’ll shut down Vapor Cloud a hosting service for Vapor applications. IMHO this means that now they can focus more time & resources on the core building blocks.

I believe that this is just the beginning of the server side Swift era.

Should I use SwiftNIO or Vapor?

SwiftNIO is a low level framework that relies on non-blocking IO. Network operations are non-blocking from the processing thread perspective. All the blocking operations are delegated to additional channels, those trigger events on network operations. Yep, this means that if you choose NIO you have to deal with all the low level stuff by yourself. This is amazing if you know a lot about networking technologies. 🤓

The purpose of SwiftNIO is being a fast, stable and scalable underlying toolkit for building high performance web frameworks like Kitura, Vapor and other network service (not just HTTP) providers.

With NIO you can build a lot more, you can make database connectors like postgres-nio, push notification services (APNSwift), basically you can support any kind of network protocols.

On the other hand, if you are planning to build a REST API or a similar backend for your existing (or future) mobile application please, do not use SwiftNIO directly unless you have a superior understanding of network layers, event loops, pipelines, channels, futures and many more… 😳

Vapor is a web framework for Swift written on top of SwiftNIO. It gives you an easy to use foundation for your next website, API, or cloud based service project. If you are new to the server side, I’d highly recommend to get familiar with Vapor instead of NIO. Vapor is way more easy to learn, you don’t have to make your hands dirty with low level components, instead you can focus on building your app.

How to get started with Vapor?

First of all, you don’t need extra tools to start with Vapor. If you have a PC or a mac you can start using the framework right ahead. You just need a working Swift installation on your device.

You can grab the API template project from Vapor’s GitHub repository. However I’d like to show you the Vapor toolbox, which is a really convenient helper tool for managing your projects.

Vapor’s command line interface provides shortcuts and assistance for common tasks.

It’s available both for macOS and Linux, you can simply install it through brew or apt-get. 📦

# macOS
-brew install vapor/tap/vapor
-
-# Linux
-eval $(curl -sL https://apt.vapor.sh)
-sudo apt-get update
-sudo apt-get install vapor
-

Now you are ready to use the vapor command. Let’s create a brand new project.

vapor new myProject
-cd myProject
-vapor update -y
-

The vapor update -y command is almost equivalent with swift package generate-xcodeproj. It’ll update the required dependencies and it’ll generate an Xcode project file. Starting from Xcode 11 you can double click on the Package.swift file as well. This means you don’t have to run anything from the command line, since SPM is now integrated into Xcode, the app can load all the dependencies for you.

The major difference between the two approaches is that if you geneate an .xcodeproj file, your dependencies are going to be linked dynamically, but if you are using the Package.swift file the system will use static linking. Don’t worry too much about this, unless you are using a package with a reserved system name, like Ink by John Sundell. If so, you have to go with static linking.

You can also use vapor build to build your project and vapor run to execute it. This comes useful if you don’t want to mess around with makefiles or interact directly with the Swift Package Manager tool. You can enter vapor --help if you want to learn more about the Vapor toolbox.

The architecture of a Vapor application

Let’s examine the project template. I’ll quickly walk you through everything.

Run

The entire project is separated into two major targets.. The first one is App and the second one is called Run. You’ll find the source code for every target inside the Sources directory. The Run executable target is the beginning of everything. It’ll load your App library (target) and fires up the Vapor backend server with proper configs and environmental variables. It contains just one single main.swift file that you can run. 🏃

App

This one is where you put your actual backend application code. It’s a library package by default which you can import inside the Run executable target. There are some top level functions that you have to define, these are going to be under the App namespace. e.g. app(_:), configure(_:), routes(_:). Under the App target you’ll find three major files. The app.swift file is responsible for returning the configured application instance itself. It uses an environment object as an input so you can run the app in prod, dev or test mode (this is on of the reasons why Vapor apps have a dedicated run target). Also if you want to perform some initial actions before your server starts, you should put those here, since there is no boot.swift file anymore.

Config

In the configure.swift file you can customize your application. This is where you should register all the various services, use middlewares, set the router object, etc. For example if you want to use a database connection, a static file hosting service or a template engine this is the place where you can set it up.

Services is a dependency injection (also called inversion of control) framework for Vapor. The services framework allows you to register, configure, and initialize anything you might need in your application.

Services are the “low-level” components in Vapor. This means that most of the underlying components are written as a service. The router is a service, middleware system works as a service, database connections are services, even the HTTP server engine is implemented as a service.

This is incredibly useful, because you can configure or replace anything inside your configuration file, there are only a few hardcoded elements, but everything is customizable. In Vapor 4 there is a brand new dependency injection API based on Swift extensions. Letting the compiler do the hard work is always nice, plus this way services are more easy to discover, since the type system knows everything. 😉

Routes

The routes.swift file is where you can add the actual routes for your router. But first, what is routing? If you don’t know what’s HTTP, please stop here and start reading about networks first. Sorry.😅

Routing refers to how an application’s endpoints respond to client requests.

This is already well-explained in the expressjs docs. Let’s say that routing is the subsystem that connects your code with the API endpoints. You can define these connections inside the routes function. For example if you have a Cat class with a returnAllKittens method you can hook that up to the GET /cats endpoint by declaring a route. Now if you send a GET HTTP request to the /cats endpoint, the return all kitten method will be called and you’ll see lots of happy kittens. 🐱🐱🐱

Controllers

Controllers are code organization tools. With the help of them you can group related API endpoints together. In the sample project there is a Todo controller which is responsible of CRUD operations on Todo models. The router connects the endpoints by using this controller, and the controller will query (create, request, update, delete) the appropriate models using the available database connection.

Models

Vapor has a neat database abstraction tool (an ORM framework) called Fluent. Models represent database entries usually related to this Fluent library. In the sample project the Todo class defines the name of the database scheme as a static property. Also each field in the table has a corresponding property in the entity. These properties are marked with a special thing called Property Wrappers. Through them you can customize the name and the behavior of the db columns. Personally I love this new approach! ❤️

Migrations

Just like models, migrations have changed a lot through time. In Vapor 4 you have even more power to customize how you want to migrate from one database scheme to another. For example if you need to introduce a new field in your model, you can alter your database according to your needs by using migrator functions. Same thing applies for other scheme alteration methods. I’m really happy with this new approach, Fluent matured a lot and this new concept reminds me to my old PHP framework. 👍

Tests

I was missing this from Vapor 3, but finally Vapor 4 includes a new testing framework called XCTVapor. This framework makes easier to test your application with just a few lines of code. If you look at the Tests folder you’ll some basic test scenarios for the Todo application. It’s a good starting point. ✅

Tips & tricks for using to Vapor 4

Let’s write some server side Swift code, shall we? Well, let me show you some best practices that I learned during the creation of this website. Yes, that’s right, this site is made with Swift and Vapor 4. 😎

Custom working directory in Xcode

If you run your project through Xcode, you might want to setup a custom working directory, otherwise your application will look for assets from a cursed place called DerivedData. This can cause some issues if you are using a templating engine or the public file middleware with the default config, since the system won’t find proper routes. In order to fix this you just click your target name next to the stop button and select the Edit Scheme… menu item. Select Run and click on the Options tab.

Xcode custom working directory

Here is the original issue on GitHub.

Using system provided directories

There are a few built-in directories available through the application object.

func configure(_ app: Application) throws {
-
-    print(app.directory.workingDirectory)
-    print(app.directory.publicDirectory)
-    print(app.directory.resourcesDirectory)
-    print(app.directory.viewsDirectory)
-    //...
-}
-

Using the environment

You can pass your secrets to a Vapor application by using environment variables. You can also check the current env for run modes like dev, prod, test, but the best thing is that Vapor 4 supports .env files! 🎉

func configure(_ app: Application) throws {
-    let variable = Environment.get("EXAMPLE") ?? "undefined"
-    print(variable)
-    print(app.environment.name)
-    print(app.environment.arguments)
-    print(app.environment.commandInput)
-
-    if app.environment.isRelease {
-        print("production mode")
-    }
-
-    //...
-}
-

Ok, but how the hell can I run the app in production mode? Also how do I provide the EXAMPLE variable? Don’t worry, it’s actually pretty simple. You can use the command line like this:

export EXAMPLE="hello"; swift run Run serve --env production
-

This way the application will run in production mode and the EXAMPLE variable will have the hello value. Good news is if you don’t like to export variables you can store them in a .env file just like this:

EXAMPLE="hello"
-

Just put this file to the root folder of your project, it’s also quite a good practice simply .gitignore it. Now you can run with the same command or use the vapor toolbox:

swift run Run serve --env production
-# NOTE: toolbox command is not accepting env in the current beta
-vapor build && vapor run serve --env production
-

You can also set custom environment variables and launch arguments if you edit your scheme in Xcode. It’s called Arguments right next to the Options tab inside the scheme editor popup.

Xcode environment

Change port number and hostname

The most simple way to change port number and hostname is to override the HTTP server config:

func configure(_ app: Application) throws {
-    app.http.server.configuration.hostname = "127.0.0.1"
-    app.http.server.configuration.port = 8081
-    //...
-}
-

Alternatively you can run Vapor with the following commands:

swift run Run serve --hostname api.example.com --port 8081
-

This way you don’t have to hardcode anything, but you can run your application with a custom config.

Router parameters

Routing in Vapor 4 changed a little bit, but for the good. You can name your router parameters. If you want to have a route with a param, you should define something like this /hello/:world. So in this example the world is a dynamic parameter key that you can use to access the underlying value through the request.

app.get("hello", ":world") { req -> String in
-    let param = req.parameters.get("world") ?? "default"
-    //let number = req.parameters.get("world", as: Int.self)
-    return "Hello, \(param.capitalized)!"
-}
-

Type casting is also supported, you can provide the type as a second parameter for the .get() method.

Dynamic routes and custom HTTP responses

Responding to all the routes is not that hard, there are two built-in options available. You can use the * string or the .anything path component case. Also there is the ** route which is equivalent with the .catchall component if you need to handle multiple route levels like: /a/b/c.

Returning a custom HTTP Response is also straightforward, but let me show you a quick example:

app.routes.get(.catchall) { req -> Response in
-    .init(status: .ok,
-          version: req.version,
-          headers: ["Content-Type": "text/xml; charset=utf-8"],
-          body: .init(string: "<h1>Hello world</h1>"))
-}
-

Custom JSON encoding / decoding strategy

I don’t like to use de default JSON encoder / decoder, since they come with an “ugly” strategy for dates. Have no worries, in Vapor 4 you can customize literally everything. The ContentConfiguration object is what you are looking for. You can set new strategies for all the urls and media types.

let jsonEncoder = JSONEncoder()
-jsonEncoder.dateEncodingStrategy = .secondsSince1970
-ContentConfiguration.global.use(encoder: jsonEncoder, for: .json)
-

From now on every single JSON object will use this encoder strategy. Problem solved. 🙃

How to return custom content types?

Well, the answer is simple. You just have to conform to the Content protocol. If you do so you can simply return your own objects in the response handler. Now if you check the /cats API endpoint, all of the three cats will be there waiting just for you to feed them (encoded using the global JSON encoder by default).

struct Cat: Content {
-    let name: String
-    let emoji: String
-}
-
-func routes(_ app: Application) throws {
-    app.get("cats") { req -> [Cat] in
-        return [
-            .init(name: "Lucky", emoji: "🐱"),
-            .init(name: "Biscuit", emoji: "🍪"),
-            .init(name: "Peanut", emoji: "🥜"),
-        ]
-    }
-}
-

Codable routing is amazing, it means that you don’t have to mess with manual encoding / decoding. 😻

How to deploy & host your Swift server?

Writing your backend server is just one part of the whole story. If you want to make it available for everyone else you have to deploy it to the cloud. This means that you need a hosting provider. Since Vapor Cloud is shutting down you have to find alternative hosting solutions. If you are looking for FREE alternatives, Heroku is one of your best chance. There is a migration guide from Vapor Cloud to Heroku.

On the other hand, I prefer AWS, since it has everything that a backend developer or a devops guy can dream about. You should note that if you choose AWS, you can use a T2.nano instance completely FREE for 1 year. You can fire up your instance in about 10 minutes including your account registration and by the end of the process you’ll have a working Linux machine on Amazon. 💪

Running the server forever

Whats next? Your Swift application server needs to run constantly. By default if a crash happens it’ll stop running. That ain’t good, because you won’t be able to serve clients anymore. This is the main reason why we need to daemonize the app first. Daemons can run constantly, if they stop they’ll be automatically re-spawned, so if a crash happens the app will start again from scratch. 👹

Under Linux you can create a systemctl upstart proces to run an application as a daemon. There is a great tutorial about how to setup upstart script and respawn process. I’ll just make a quick walkthrough about what you should do. First, create a new file under /lib/systemd/system/todo.service with the following contents.

[Unit]
-Description=Todo server daemon
-
-[Service]
-User=ubuntu
-Group=ubuntu
-WorkingDirectory=/path/to/my/server/
-ExecStart=/path/to/my/run/script
-Restart=always
-
-[Install]
-WantedBy=multi-user.target
-

Of course you should provide your own configuration (path, user, group and exec command). The ExecStart parameter can be swift run Run, but please be careful you might have to use your full path of your swift installation (which swift). When you are ready with the service file you have to give some permissions and then you should reload the daemons. Finally you should enable your service and start it. 👻

chmod +x /lib/systemd/system/todo.service
-systemctl daemon-reload
-systemctl enable todo.service
-systemctl start todo
-systemctl status todo
-

From now on you can use sudo service todo start|stop|restart to manage your backend server.

Reverse proxy using nginx

I usually put my servers behind a proxy. Nginx can be used as web server, reverse proxy, load balancer and HTTP cache. You can install it by running the sudo apt-get install nginx command. Maybe the hardest part is to setup a proper nginx configuration for your Vapor application server with HTTP2 and SSL support. A very basic HTTP nginx configuration should look something like this.

server {
-    listen 80;
-    server_name mytododomain.com;
-
-    location / {
-        proxy_pass              http://localhost:8080;
-        proxy_set_header        Host $host;
-        proxy_set_header        X-Real-IP $remote_addr;
-        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
-        proxy_set_header        X-Forwarded-Proto $scheme;
-        proxy_read_timeout      90;
-    }
-}
-

You should put this configuration file inside the /etc/nginx/sites-available/mytododomain.com folder. This setup simply proxies the incoming traffic from the domain to the local port through pure HTTP without the S-ecurity. Symlink the file by using ln -svf [source] [target] into the sites-enabled folder and run the following command to reload nginx configurations: sudo service reload nginx. Alternatively you can restart nginx sudo service nginx restart. If you messed up someting you can always use sudo nginx -t.

How to support HTTPS?

Remember HTTP is a cleartext protocol, so basically everyone can read your network traffic. Apple says all data is sensitive - they are damn right about that - and using a secure channel will give you benefits like encryption, confidentiality, integrity, authentication and identity. If you want a proper server you have to use HTTPS. 🔒

HTTP + SSL = HTTPS ❤️ ATS

In order to support secure HTTP connections, first you’ll need an SSL certificate. Letsencrypt can give you one for FREE. You just have to install certbot. You can request a new certificate and setup SSL automatically for your nginx sites by using certbot. Follow the instructions and enjoy your secure API service written in Swift language.

sudo apt-get update
-sudo apt-get install software-properties-common
-sudo add-apt-repository ppa:certbot/certbot
-sudo apt-get update
-sudo apt-get install python-certbot-nginx
-
-sudo certbot --nginx
-

Don’t forget to set up a cron job to renew your certificates periodically sudo certbot renew --dry-run.

You can check the strength of your server configuration at ssllabs.com. They are going to measure how secure is your server. By default letsencrypt will give you an A result, which is perfectly fine, but you can aim for an A+ grade if you want. I don’t want to get into the details now. 🤫

App Transport Security (ATS) was introduced to make iOS apps more secure. It enforces developers to talk only through secure HTTPS channels to your backend server. You can always disable ATS, but instead of that you should try to solve the underlying issues. The first thing that you can do is to enable CFNetwork Diagnostic Logging inside your iOS application. Now your network requests will log more information to the console. You can also check your server connection from terminal with the nscurl or openssl commands.

nscurl --ats-diagnostics http://example.com/api/endpoint
-openssl s_client -connect example.com:443
-

That’s all folks. 🐰

Building, running, hosting your own Swift application on the server requires a lot of work. If you are new to the topic it can be challenging to find proper resources, since Vapor tutorials are mostly for version 3. I really hope that in this article I covered everything that noone else did. Vapor 4 is going to be a great release, I can’t wait to work with the final version. I also hope that more and more Server side Swift applications will be born.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/beginners-guide-to-swift-arrays/index.html b/docs/beginners-guide-to-swift-arrays/index.html deleted file mode 100644 index 2e5abf0..0000000 --- a/docs/beginners-guide-to-swift-arrays/index.html +++ /dev/null @@ -1,481 +0,0 @@ - - - - - - - - - - - - Beginner's guide to Swift arrays - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 4 min read - -
-

Beginner's guide to Swift arrays

-
-

Learn how to manipulate arrays in Swift like a pro. This tutorial covers lots of useful array related methods, tips and tricks.

-
- -

An array can hold multiple elements of a given type. We can use them to store numbers, strings, classes, but in general elements can be anything. With the Any type you can actually express this and you can put anything into this random access collection. There are quite many ways to create an array in Swift. You can explicitly write the Array word, or use the [] shorthand format. 🤔

// array of integers
-let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
-// array of strings
-let strings = ["a", "b", "c"]
-// array of any items
-let anything: [Any] = [1, "a", 3.14]
-
-// array init
-let empty = Array<Int>()
-let a: Array<Int> = Array()
-let b: [Int] = [Int]()
-let d = [Int](repeating: 1, count: 3)
-let e = Array<String>(repeating: "a", count: 3)
-

The Array struct is a generic Element type, but fortunately the Swift compiler is smart enough to figure out the element type, so we don’t have to explicitly write it every time. The Array type implements both the Sequence and the Collection protocols, this is nice because the standard library comes with many powerful functions as protocol extensions on these interfaces.

let array = [1, 2, 3, 4]
-
-// check if an array is empty
-print(array.isEmpty) // false
-
-// count the elements of the array
-print(array.count) // 4
-
-// check if contains an element
-print(array.contains(2)) // true
-
-// get the element at the zero index
-print(array[0]) // 1
-
-// elements between the 1st and 2nd index
-print(array[1...2]) // [2, 3]
-
-// first 2 elements
-print(array.prefix(2)) // [1, 2]
-
-// last 2 elements
-print(array.suffix(2)) // [3, 4]
-

Above are some basic functions that you can use to get values from an array. You have to be careful when working with indexes, if you provide an index that is out of range your app will crash (e.g. anything smaller than 0 or greater than 4 for the sample code). 💥

Working with collection types can be hard if it comes to index values, but there are some cool helper methods available. When you work with an array it’s very likely that you won’t use these methods that much, but they are derived from a lower layer and it’s nice to have them.

let array = [1, 2, 3, 4]
-
-// start index of the array
-print(array.startIndex) // 0
-
-// end index of the array
-print(array.endIndex) // 4
-
-// range of index values
-print(array.indices) // 0..<4
-
-// start index + count
-print(array.startIndex.advanced(by: array.count)) // 4
-
-// first index of the value 3
-print(array.firstIndex(of: 3) ?? "n/a") // 2
-
-// first index for the value greater than 3
-print(array.firstIndex { $0 > 3 } ?? "n/a") // 3
-
-// the value at the start index + 1
-print(array[array.startIndex.advanced(by: 1)]) // 2
-
-// returns the index after a given index
-print(array.index(after: 2))
-
-// returns the index before a given index
-print(array.index(before: 2))
-
-// returns an index from an start index with an offset, limited by an end index
-print(array.index(array.startIndex, offsetBy: 2, limitedBy: array.endIndex) ?? "n/a")
-

We can also manipulate the elements of a given array by using the following methods. Please note that these methods won’t alter the original array, in other words they are non-mutating methods.

let array = [1, 5, 2, 3, 2, 4]
-
-// drop the first 2 elements
-print(array.dropLast(2)) // [1, 5, 2, 3]
-
-// drop the last 2 elements
-print(array.dropFirst(2)) // [2, 3, 2, 4]
-
-// reverse the elements in the array
-print(Array(array.reversed())) // [4, 2, 3, 2, 5, 1]
-
-// get the unique elements from the array using a set
-print(Array(Set(array))) // [2, 1, 3, 4, 5]
-
-// split the array into an array of arrays using a needle
-print(array.split(separator: 2)) // [[1, 5], [3], [4]]
-
-// iterate through the elements of the array using index values
-for index in array.indices {
-    print(array[index]) // 1 2 3 4
-}
-
-// iterate through the elements of the array
-for element in array {
-    print(element) // 1 2 3 4
-}
-
-/// iterate through the index and element values using an enumerated tuple
-for (index, element) in array.enumerated() {
-    print(index, "-", element) // 0 - 1, 1 - 2, 2 - 3, 3 - 4
-}
-

There are mutating methods that you can use to alter the original array. In order to call a mutating method on an array you have to create it as a variable (var), instead of a constant (let).

var array = [4, 2, 0]
-
-// set element at index with a value
-array[2] = 3
-print(array) // [4, 2, 3]
-
-// add new elements to the array
-array += [4]
-print(array) // [4, 2, 3, 4]
-
-// replace elements at range with a new collection
-array.replaceSubrange(0...1, with: [1, 2])
-print(array) // [1, 2, 3, 4]
-
-// removes the last element and returns it
-let element = array.popLast() // 4
-print(array) // [1, 2, 3]
-
-// add a new element to the end of the array
-array.append(4)
-print(array) // [1, 2, 3, 4]
-
-// insert an element to a given index (shift others)
-array.insert(5, at: 1)
-print(array) // [1, 5, 2, 3, 4]
-
-// remove elements matching the criteria
-array.removeAll { $0 > 3 }
-print(array) // [1, 2, 3]
-
-// swap elements at the given indexes
-array.swapAt(0, 2)
-print(array) // [3, 2, 1]
-
-// removes the first element
-array.removeFirst()
-print(array) // [2, 1]
-
-// removes the last element
-array.removeLast()
-print(array) // [2]
-
-// add the contents of a sequence
-array.append(contentsOf: [1, 2, 3])
-print(array) // [2, 1, 2, 3]
-
-// remove element at index
-array.remove(at: 0)
-print(array) // [1, 2, 3]
-

One last thing I’d like to show you are the functional methods that you can use to transform or manipulate the elements of a given array. Personally I use these functions on a daily basis, they are extremely useful I highly recommend to learn more about them, especially map & reduce. 💪

let array = [1, 5, 2, 3, 2, 4]
-
-// sort the elements using an ascending order
-print(array.sorted(by: <)) // [1, 2, 2, 3, 4, 5]
-
-// sort the elements using a descending order
-print(array.sorted { $0 > $1 }) // [5, 4, 3, 2, 2, 1]
-
-// get the first element that matches the criteria
-print(array.first { $0 == 3 } ?? "n/a") // 3
-
-// filter the array, only return elements greater than 3
-print(array.filter { $0 > 3 }) // [5, 4]
-
-// transform every element, double their values
-print(array.map { $0 * 2 }) // [2, 10, 4, 6, 4, 8]
-
-// map the elements to strings and join them using a separator
-print(array.map(String.init).joined(separator: ", ")) // 1, 5, 2, 3, 2, 4
-
-// check if all the elements are greater than 1
-print(array.allSatisfy { $0 > 1 }) // false
-
-// sum the values of all the elements
-print(array.reduce(0, +)) // 17
-
-// check if there is an element matching the criteria (some)
-print(array.reduce(false) { $0 || $1 > 3 }) // true
-
-// check if all the elements are matching the criteria (every / allSatisfy)
-print(array.reduce(true) { $0 && $1 > 1 }) // false
-

As you can see arrays are quite capable data structures in Swift. With the power of functional methods we can do amazing things with them, I hope this little cheat-sheet will help you to understand them a bit better. 😉

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - -
-
- -
- - - -
- - - - diff --git a/docs/beginners-guide-to-swift-package-manager-command-plugins/index.html b/docs/beginners-guide-to-swift-package-manager-command-plugins/index.html deleted file mode 100644 index 0650576..0000000 --- a/docs/beginners-guide-to-swift-package-manager-command-plugins/index.html +++ /dev/null @@ -1,688 +0,0 @@ - - - - - - - - - - - - Beginner's guide to Swift package manager command plugins - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 9 min read - -
-

Beginner's guide to Swift package manager command plugins

-
-

Learn how to create command plugins for the Swift Package Manager to execute custom actions using SPM and other tools.

-
- -

Introduction to Swift Package Manager plugins

First of all I’d like to talk a few words about the new SPM plugin infrastructure, that was introduced in the Swift 5.6 release. The very first proposal describes the detailed design of the plugin API with some plugin examples, which are quite handy. Honestly speaking I was a bit to lazy to carefully read through the entire documentation, it’s quite long, but long story short, you can create the following plugin types with the currently existing APIs:

  • Build tools - can be invoked via the SPM targets
    • pre-build - runs before the build starts
    • build - runs during the build
  • Commands - can be invoked via the command line
    • source code formatting - modifies the code inside package
    • documentation generation - generate docs for the package
    • custom - user defined intentions

For the sake of simplicity in this tutorial I’m only going to write a bit about the second category, aka. the command plugins. These plugins were a bit more interesting for me, because I wanted to integrate my deployment workflow into SPM, so I started to experiment with the plugin API to see how hard it is to build such a thing. Turns out it’s quite easy, but the developer experience it’s not that good. 😅

Building a source code formatting plugin

The very first thing I wanted to integrate with SPM was SwiftLint, since I was not able to find a plugin implementation that I could use I started from scratch. As a starting point I was using the example code from the Package Manager Command Plugins proposal.

mkdir Example
-cd Example
-swift package init --type=library
-

I started with a brand new package, using the swift package init command, then I modified the Package.swift file according to the documentation. I’ve also added SwiftLint as a package dependency so SPM can download & build the and hopefully my custom plugin command can invoke the swiftlint executable when it is needed.

// swift-tools-version: 5.6
-import PackageDescription
-
-let package = Package(
-    name: "Example",
-    platforms: [
-        .macOS(.v10_15),
-    ],
-    products: [
-        .library(name: "Example", targets: ["Example"]),
-        .plugin(name: "MyCommandPlugin", targets: ["MyCommandPlugin"]),
-    ],
-    dependencies: [
-        .package(url: "https://github.com/realm/SwiftLint", branch: "master"),
-    ],
-    targets: [
-        .target(name: "Example", dependencies: []),
-        .testTarget(name: "ExampleTests", dependencies: ["Example"]),
-       
-        .plugin(name: "MyCommandPlugin",
-                capability: .command(
-                    intent: .sourceCodeFormatting(),
-                    permissions: [
-                        .writeToPackageDirectory(reason: "This command reformats source files")
-                    ]
-                ),
-                dependencies: [
-                    .product(name: "swiftlint", package: "SwiftLint"),
-                ]),
-    ]
-)
-

I’ve created a Plugins directory with a main.swift file right next to the Sources folder, with the following contents.

import PackagePlugin
-import Foundation
-
-@main
-struct MyCommandPlugin: CommandPlugin {
-    
-    func performCommand(context: PluginContext, arguments: [String]) throws {
-        let tool = try context.tool(named: "swiftlint")
-        let toolUrl = URL(fileURLWithPath: tool.path.string)
-        
-        for target in context.package.targets {
-            guard let target = target as? SourceModuleTarget else { continue }
-
-            let process = Process()
-            process.executableURL = toolUrl
-            process.arguments = [
-                "\(target.directory)",
-                "--fix",
-               // "--in-process-sourcekit" // this line will fix the issues...
-            ]
-
-            try process.run()
-            process.waitUntilExit()
-            
-            if process.terminationReason == .exit && process.terminationStatus == 0 {
-                print("Formatted the source code in \(target.directory).")
-            }
-            else {
-                let problem = "\(process.terminationReason):\(process.terminationStatus)"
-                Diagnostics.error("swift-format invocation failed: \(problem)")
-            }
-        }
-    }
-}
-

The snippet above should locate the swiftlint tool using the plugins context then it’ll iterate through the available package targets, filter out non source-module targets and format only those targets that contains actual Swift source files. The process object should simply invoke the underlying tool, we can wait until the child (swiftlint invocation) process exists and hopefully we’re good to go. 🤞

Update: kalKarmaDev told me that it is possible to pass the --in-process-sourcekit argument to SwiftLint, this will fix the underlying issue and the source files are actually fixed.

I wanted to list the available plugins & run my source code linter / formatter using the following shell commands, but unfortunately seems like the swiftlint invocation part failed for some strange reason.

swift package plugin --list
-swift package format-source-code #won't work, needs access to source files
-swift package --allow-writing-to-package-directory format-source-code
-
-# error: swift-format invocation failed: NSTaskTerminationReason(rawValue: 2):5
-# what the hell happened? 🤔
-

Seems like there’s a problem with the exit code of the invoked swiftlint process, so I removed the success check from the plugin source to see if that’s causing the issue or not also tried to print out the executable command to debug the underlying problem.

import PackagePlugin
-import Foundation
-
-@main
-struct MyCommandPlugin: CommandPlugin {
-    
-    func performCommand(context: PluginContext, arguments: [String]) throws {
-        let tool = try context.tool(named: "swiftlint")
-        let toolUrl = URL(fileURLWithPath: tool.path.string)
-        
-        for target in context.package.targets {
-            guard let target = target as? SourceModuleTarget else { continue }
-
-            let process = Process()
-            process.executableURL = toolUrl
-            process.arguments = [
-                "\(target.directory)",
-                "--fix",
-            ]
-
-            print(toolUrl.path, process.arguments!.joined(separator: " "))
-
-            try process.run()
-            process.waitUntilExit()
-        }
-    }
-}
-

Intentionally made a small “mistake” in the Example.swift source file, so I can see if the swiftlint –fix command will solve this issue or not. 🤔

public struct Example {
-    public private(set) var text = "Hello, World!"
-
-    public init() {
-        let xxx :Int = 123
-    }
-}
-

Turns out, when I run the plugin via the Process invocation, nothing happens, but when I enter the following code manually into the shell, it just works.

/Users/tib/Example/.build/arm64-apple-macosx/debug/swiftlint /Users/tib/Example/Tests/Example --fix
-/Users/tib/Example/.build/arm64-apple-macosx/debug/swiftlint /Users/tib/Example/Tests/ExampleTests --fix
-

All right, so we definitely have a problem here… I tried to get the standard output message and error message from the running process, seems like swiftlint runs, but something in the SPM infrastructure blocks the code changes in the package. After several hours of debugging I decided to give a shot to swift-format, because that’s what the official docs suggest. 🤷‍♂️

// swift-tools-version: 5.6
-import PackageDescription
-
-let package = Package(
-    name: "Example",
-    platforms: [
-        .macOS(.v10_15),
-    ],
-    products: [
-        .library(name: "Example", targets: ["Example"]),
-        .plugin(name: "MyCommandPlugin", targets: ["MyCommandPlugin"]),
-    ],
-    dependencies: [
-        .package(url: "https://github.com/apple/swift-format", exact: "0.50600.1"),
-    ],
-    targets: [
-        .target(name: "Example", dependencies: []),
-        .testTarget(name: "ExampleTests", dependencies: ["Example"]),
-       
-        .plugin(name: "MyCommandPlugin",
-                capability: .command(
-                    intent: .sourceCodeFormatting(),
-                    permissions: [
-                        .writeToPackageDirectory(reason: "This command reformats source files")
-                    ]
-                ),
-                dependencies: [
-                    .product(name: "swift-format", package: "swift-format"),
-                ]),
-    ]
-)
-

Changed both the Package.swift file and the plugin source code, to make it work with swift-format.

import PackagePlugin
-import Foundation
-
-@main
-struct MyCommandPlugin: CommandPlugin {
-    
-    func performCommand(context: PluginContext, arguments: [String]) throws {
-        let swiftFormatTool = try context.tool(named: "swift-format")
-        let swiftFormatExec = URL(fileURLWithPath: swiftFormatTool.path.string)
-//        let configFile = context.package.directory.appending(".swift-format.json")
-        
-        for target in context.package.targets {
-            guard let target = target as? SourceModuleTarget else { continue }
-
-            let process = Process()
-            process.executableURL = swiftFormatExec
-            process.arguments = [
-//                "--configuration", "\(configFile)",
-                "--in-place",
-                "--recursive",
-                "\(target.directory)",
-            ]
-            try process.run()
-            process.waitUntilExit()
-
-            if process.terminationReason == .exit && process.terminationStatus == 0 {
-                print("Formatted the source code in \(target.directory).")
-            }
-            else {
-                let problem = "\(process.terminationReason):\(process.terminationStatus)"
-                Diagnostics.error("swift-format invocation failed: \(problem)")
-            }
-        }
-    }
-}
-

I tried to run again the exact same package plugin command to format my source files, but this time swift-format was doing the code formatting instead of swiftlint.

swift package --allow-writing-to-package-directory format-source-code
-// ... loading dependencies
-Build complete! (6.38s)
-Formatted the source code in /Users/tib/Linter/Tests/ExampleTests.
-Formatted the source code in /Users/tib/Linter/Sources/Example.
-

Worked like a charm, my Example.swift file was fixed and the : was on the left side… 🎊

public struct Example {
-    public private(set) var text = "Hello, World!"
-
-    public init() {
-        let xxx: Int = 123
-    }
-}
-

Yeah, I’ve made some progress, but it took me quite a lot of time to debug this issue and I don’t like the fact that I have to mess around with processes to invoke other tools… my gut tells me that SwiftLint is not following the standard shell exit status codes and that’s causing some issues, maybe it’s spawning child processes and that’s the problem, I really don’t know but I don’t wanted to waste more time on this issue, but I wanted to move forward with the other category. 😅

Integrating the DocC plugin with SPM

As a first step I added some dummy comments to my Example library to be able to see something in the generated documentation, nothing fancy just some one-liners. 📖

/// This is just an example struct
-public struct Example {
-
-    /// this is the hello world text
-    public private(set) var text = "Hello, World!"
-    
-    /// this is the init method
-    public init() {
-        let xxx: Int = 123
-    }
-}
-

I discovered that Apple has an official DocC plugin, so I added it as a dependency to my project.

// swift-tools-version: 5.6
-import PackageDescription
-
-let package = Package(
-    name: "Example",
-    platforms: [
-        .macOS(.v10_15),
-    ],
-    products: [
-        .library(name: "Example", targets: ["Example"]),
-        .plugin(name: "MyCommandPlugin", targets: ["MyCommandPlugin"]),
-    ],
-    dependencies: [
-        .package(url: "https://github.com/apple/swift-format", exact: "0.50600.1"),
-        .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
-
-    ],
-    targets: [
-        .target(name: "Example", dependencies: []),
-        .testTarget(name: "ExampleTests", dependencies: ["Example"]),
-       
-        .plugin(name: "MyCommandPlugin",
-                capability: .command(
-                    intent: .sourceCodeFormatting(),
-                    permissions: [
-                        .writeToPackageDirectory(reason: "This command reformats source files")
-                    ]
-                ),
-                dependencies: [
-                    .product(name: "swift-format", package: "swift-format"),
-                ]),
-    ]
-)
-

Two new plugin commands were available after I executed the plugin list command.

swift package plugin --list
-
-# ‘format-source-code’ (plugin ‘MyCommandPlugin’ in package ‘Example’)
-# ‘generate-documentation’ (plugin ‘Swift-DocC’ in package ‘SwiftDocCPlugin’)
-# ‘preview-documentation’ (plugin ‘Swift-DocC Preview’ in package ‘SwiftDocCPlugin’)
-

Tried to run the first one, and fortunately the doccarchive file was generated. 😊

swift package generate-documentation
-# Generating documentation for 'Example'...
-# Build complete! (0.16s)
-# Converting documentation...
-# Conversion complete! (0.33s)
-# Generated DocC archive at '/Users/tib/Linter/.build/plugins/Swift-DocC/outputs/Example.doccarchive'
-

Also tried to preview the documentation, there was a note about the –disable-sandbox flag in the output, so I simply added it to my original command and…

swift package preview-documentation 
-# Note: The Swift-DocC Preview plugin requires passing the '--disable-sandbox' flag
-swift package --disable-sandbox preview-documentation
-

Magic. It worked and my documentation was available. Now this is how plugins should work, I loved this experience and I really hope that more and more official plugins are coming soon. 😍

Building a custom intent command plugin

I wanted to build a small executable target with some bundled resources and see if a plugin can deploy the executable binary with the resources. This could be very useful when I deploy feather apps, I have multiple module bundles there and now I have to manually copy everything… 🙈

// swift-tools-version: 5.6
-import PackageDescription
-
-let package = Package(
-    name: "Example",
-    platforms: [
-        .macOS(.v10_15),
-    ],
-    products: [
-        .library(name: "Example", targets: ["Example"]),
-        .executable(name: "MyExample", targets: ["MyExample"]),
-        .plugin(name: "MyCommandPlugin", targets: ["MyCommandPlugin"]),
-        .plugin(name: "MyDistCommandPlugin", targets: ["MyDistCommandPlugin"]),
-    ],
-    dependencies: [
-        .package(url: "https://github.com/apple/swift-format", exact: "0.50600.1"),
-        .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
-
-    ],
-    targets: [
-        .executableTarget(name: "MyExample",
-                          resources: [
-                            .copy("Resources"),
-                          ], plugins: [
-                            
-                          ]),
-        .target(name: "Example", dependencies: []),
-        .testTarget(name: "ExampleTests", dependencies: ["Example"]),
-       
-        .plugin(name: "MyCommandPlugin",
-                capability: .command(
-                    intent: .sourceCodeFormatting(),
-                    permissions: [
-                        .writeToPackageDirectory(reason: "This command reformats source files")
-                    ]
-                ),
-                dependencies: [
-                    .product(name: "swift-format", package: "swift-format"),
-                ]),
-        
-        .plugin(name: "MyDistCommandPlugin",
-                capability: .command(
-                    intent: .custom(verb: "dist", description: "Create dist archive"),
-                    permissions: [
-                        .writeToPackageDirectory(reason: "This command deploys the executable")
-                    ]
-                ),
-                dependencies: [
-                ]),
-    ]
-)
-

As a first step I created a new executable target called MyExample and a new MyDistCommandPlugin with a custom verb. Inside the Sources/MyExample/Resources folder I’ve placed a simple test.json file with the following contents.

{
-    "success": true
-}
-

The main.swift file of the MyExample target looks like this. It just validates that the resource file is available and it simply decodes the contents of it and prints everything to the standard output. 👍

import Foundation
-
-guard let jsonFile = Bundle.module.url(forResource: "Resources/test", withExtension: "json") else {
-    fatalError("Bundle file not found")
-}
-let jsonData = try Data(contentsOf: jsonFile)
-
-struct Json: Codable {
-    let success: Bool
-}
-
-let json = try JSONDecoder().decode(Json.self, from: jsonData)
-
-print("Is success?", json.success)
-

Inside the Plugins folder I’ve created a main.swift file under the MyDistCommandPlugin folder.

import PackagePlugin
-import Foundation
-
-@main
-struct MyDistCommandPlugin: CommandPlugin {
-    
-    func performCommand(context: PluginContext, arguments: [String]) throws {
-        
-        // ...
-    }
-}
-

Now I was able to re-run the swift package plugin –list command and the dist verb appeared in the list of available commands. Now the only question is: how do we get the artifacts out of the build directory? Fortunately the 3rd example of the commands proposal is quite similar.

import PackagePlugin
-import Foundation
-
-@main
-struct MyDistCommandPlugin: CommandPlugin {
-    
-    func performCommand(context: PluginContext, arguments: [String]) throws {
-        let cpTool = try context.tool(named: "cp")
-        let cpToolURL = URL(fileURLWithPath: cpTool.path.string)
-
-        let result = try packageManager.build(.product("MyExample"), parameters: .init(configuration: .release, logging: .concise))
-        guard result.succeeded else {
-            fatalError("couldn't build product")
-        }
-        guard let executable = result.builtArtifacts.first(where : { $0.kind == .executable }) else {
-            fatalError("couldn't find executable")
-        }
-        
-        let process = try Process.run(cpToolURL, arguments: [
-            executable.path.string,
-            context.package.directory.string,
-        ])
-        process.waitUntilExit()
-
-        let exeUrl = URL(fileURLWithPath: executable.path.string).deletingLastPathComponent()
-        let bundles = try FileManager.default.contentsOfDirectory(atPath: exeUrl.path).filter { $0.hasSuffix(".bundle") }
-
-        for bundle in bundles {
-            let process = try Process.run(cpToolURL, arguments: ["-R",
-                                                                    exeUrl.appendingPathComponent(bundle).path,
-                                                                    context.package.directory.string,
-                                                                ])
-            process.waitUntilExit()
-        }
-    }
-}
-

So the only problem was that I was not able to get back the bundled resources, so I had to use the URL of the executable file, drop the last path component and read the contents of that directory using the FileManager to get back the .bundle packages inside of that folder.

Unfortunately the builtArtifacts property only returns the executables and libraries. I really hope that we’re going to get support for bundles as well in the future so this hacky solution can be avoided for good. Anyway it works just fine, but still it’s a hack, so use it carefully. ⚠️

swift package --allow-writing-to-package-directory dist
-./MyExample 
-#Is success? true
-

I was able to run my custom dist command without further issues, of course you can use additional arguments to customize your plugin or add more flexibility, the examples in the proposal are pretty much okay, but it’s quite unfortunate that there is no official documentation for Swift package manager plugins just yet. 😕

Conclusion

Learning about command plugins was fun, but in the beginning it was annoying because I expected a bit better developer experience regarding the tool invocation APIs. In summary I can say that this is just the beginning. It’s just like the async / await and actors addition to the Swift language. The feature itself is there, it’s mostly ready to go, but not many developers are using it on a daily basis. These things will require time and hopefully we’re going to see a lot more plugins later on… 💪

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginner's guide to Swift package manager command plugins

-
-

Learn how to create command plugins for the Swift Package Manager to execute custom actions using SPM and other tools.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

How to create a Swift package collection?

-
-

In this tutorial I'm going to show you how to create your own package collection from your favorite Swift libraries.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Introduction to SPM artifact bundles

-
-

In this tutorial I'm going to show you how to use the new binary target related artifact bundle using the Swift package manager.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Swift Package Manager tutorial

-
-

Learn how to use the Swift Package Manager to handle external dependencies, create your library or app on macOS and Linux.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/beginners-guide-to-the-asyncawait-concurrency-api-in-vapor-fluent/index.html b/docs/beginners-guide-to-the-asyncawait-concurrency-api-in-vapor-fluent/index.html deleted file mode 100644 index 1d7b024..0000000 --- a/docs/beginners-guide-to-the-asyncawait-concurrency-api-in-vapor-fluent/index.html +++ /dev/null @@ -1,482 +0,0 @@ - - - - - - - - - - - - Beginner's guide to the async/await concurrency API in Vapor & Fluent - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 7 min read - -
-

Beginner's guide to the async/await concurrency API in Vapor & Fluent

-
-

Learn how to convert your existing EventLoopFuture based Vapor server app using the new async/await Swift feature.

-
- -

Is async/await going to improve Vapor?

So you might wonder why do we even need to add async/await support to our codebase? Well, let me show you a dirty example from a generic controller inside the Feather CMS project.

func update(req: Request) throws -> EventLoopFuture<Response> {
-    accessUpdate(req: req).flatMap { hasAccess in
-        guard hasAccess else {
-            return req.eventLoop.future(error: Abort(.forbidden))
-        }
-        let updateFormController = UpdateForm()
-        return updateFormController.load(req: req)
-            .flatMap { updateFormController.process(req: req) }
-            .flatMap { updateFormController.validate(req: req) }
-            .throwingFlatMap { isValid in
-                guard isValid else {
-                    return renderUpdate(req: req, context: updateFormController).encodeResponse(for: req)
-                }
-                return findBy(try identifier(req), on: req.db)
-                    .flatMap { model in
-                        updateFormController.context.model = model as? UpdateForm.Model
-                        return updateFormController.write(req: req).map { model }
-                    }
-                    .flatMap { beforeUpdate(req: req, model: $0) }
-                    .flatMap { model in model.update(on: req.db).map { model } }
-                    .flatMap { model in updateFormController.save(req: req).map { model } }
-                    .flatMap { afterUpdate(req: req, model: $0) }
-                    .map { req.redirect(to: req.url.path) }
-            }
-    }
-}
-

What do you think? Is this code readable, easy to follow or does it look like a good foundation of a historical monumental building? Well, I’d say it’s hard to reason about this piece of Swift code. 😅

I’m not here to scare you, but I suppose that you’ve seen similar (hopefully more simple or better) EventLoopFuture-based code if you’ve worked with Vapor. Futures and promises are just fine, they’ve helped us a lot to deal with asynchronous code, but unfortunately they come with maps, flatMaps and other block related solutions that will eventually lead to quite a lot of trouble.

Completion handlers (callbacks) have many problems:

  • Pyramid of doom
  • Memory management
  • Error handling
  • Conditional block execution

We can say it’s easy to make mistakes if it comes to completion handlers, that’s why we have a shiny new feature in Swift 5.5 called async/await and it aims to solve these problems I mentioned before. If you are looking for an introduction to async/await in Swift you should read my other tutorial first, to learn the basics of this new concept.

So Vapor is full of EventLoopFutures, these objects are coming from the SwiftNIO framework, they are the core building blocks of all the async APIs in both frameworks. By introducing the async/await support we can eliminate quite a lot of unnecessary code (especially completion blocks), this way our codebase will be more easy to follow and maintain. 🥲

Most of the Vapor developers were waiting for this to happen for quite a long time, because everyone felt that EventLoopFutures (ELFs) are just freakin’ hard to work with. If you search a bit you’ll find quite a lot of complains about them, also the 4th major version of Vapor dropped the old shorthand typealiases and exposed NIO’s async API directly. I think this was a good decision, but still the framework god many complaints about this. 👎

Vapor will greatly benefit from adapting to the new async/await feature. Let me show you how to convert an existing ELF-based Vapor project and take advantage of the new concurrency features.

How to convert a Vapor project to async/await?

We’re going to use our previous Todo project as a base template. It has a type-safe RESTful API, so it’s happens to be just the perfect candidate for our async/await migration process. ✅

The new async/await API for Vapor & Fluent are only available yet as a feature branch, so we have to alter our Package.swift manifest file if we’d like to use these new features.

// swift-tools-version:5.3
-import PackageDescription
-
-let package = Package(
-    name: "myProject",
-    platforms: [
-       .macOS(.v10_15)
-    ],
-    dependencies: [
-        .package(url: "https://github.com/vapor/vapor", from: "4.0.0"),
-        .package(url: "https://github.com/vapor/fluent", from: "4.0.0"),
-        .package(url: "https://github.com/vapor/fluent-kit", from: "4.0.0"),
-        .package(url: "https://github.com/vapor/fluent-sqlite-driver", from: "4.0.0"),
-    ],
-    targets: [
-        .target(
-            name: "App",
-            dependencies: [
-                .product(name: "Fluent", package: "fluent"),
-                .product(name: "FluentSQLiteDriver", package: "fluent-sqlite-driver"),
-                .product(name: "Vapor", package: "vapor"),
-            ]
-        ),
-        .target(name: "Run", dependencies: [.target(name: "App")]),
-    ]
-)
-

We’re going to convert the following TodoController object, because it has quite a lot of ELF related functions that can take advantage of the new Swift concurrency features.

import Vapor
-import Fluent
-import TodoApi
-
-struct TodoController {
-
-    private func getTodoIdParam(_ req: Request) throws -> UUID {
-        guard let rawId = req.parameters.get(TodoModel.idParamKey), let id = UUID(rawId) else {
-            throw Abort(.badRequest, reason: "Invalid parameter `\(TodoModel.idParamKey)`")
-        }
-        return id
-    }
-
-    private func findTodoByIdParam(_ req: Request) throws -> EventLoopFuture<TodoModel> {
-        TodoModel
-            .find(try getTodoIdParam(req), on: req.db)
-            .unwrap(or: Abort(.notFound))
-    }
-
-    // MARK: - endpoints
-    
-    func list(req: Request) throws -> EventLoopFuture<Page<TodoListObject>> {
-        TodoModel.query(on: req.db).paginate(for: req).map { $0.map { $0.mapList() } }
-    }
-    
-    func get(req: Request) throws -> EventLoopFuture<TodoGetObject> {
-        try findTodoByIdParam(req).map { $0.mapGet() }
-    }
-
-    func create(req: Request) throws -> EventLoopFuture<TodoGetObject> {
-        let input = try req.content.decode(TodoCreateObject.self)
-        let todo = TodoModel()
-        todo.create(input)
-        return todo.create(on: req.db).map { todo.mapGet() }
-    }
-    
-    func update(req: Request) throws -> EventLoopFuture<TodoGetObject> {
-        let input = try req.content.decode(TodoUpdateObject.self)
-
-        return try findTodoByIdParam(req)
-            .flatMap { todo in
-                todo.update(input)
-                return todo.update(on: req.db).map { todo.mapGet() }
-            }
-    }
-    
-    func patch(req: Request) throws -> EventLoopFuture<TodoGetObject> {
-        let input = try req.content.decode(TodoPatchObject.self)
-
-        return try findTodoByIdParam(req)
-            .flatMap { todo in
-                todo.patch(input)
-                return todo.update(on: req.db).map { todo.mapGet() }
-            }
-    }
-
-    func delete(req: Request) throws -> EventLoopFuture<HTTPStatus> {
-        try findTodoByIdParam(req)
-            .flatMap { $0.delete(on: req.db) }
-            .map { .ok }
-    }
-}
-

The very first method that we’re going to convert is the findTodoByIdParam. Fortunately this version of FluentKit comes with a set of async functions to query and modify database models.

We just have to remove the EventLoopFuture type and write async before the throws keyword, this will indicate that our function is going to be executed asynchronously.

It is worth to mention that you can only call an async function from async functions. If you want to call an async function from a sync function you’ll have to use a special (deatch) method. You can call however sync functions inside async methods without any trouble. 🔀

We can use the new async find method to fetch the TodoModel based on the UUID parameter. When you call an async function you have to await for the result. This will let you use the return type just like it it was a sync call, so there is no need for completion blocks anymore and we can simply guard the optional model result and throw a notFound error if needed. Async functions can throw as well, so you might have to write try await when you call them, note that the order of the keywords is fixed, so try always comes before await, and the signature is always async throws.

func findTodoByIdParam(_ req: Request) async throws -> TodoModel {
-    guard let model = try await TodoModel.find(try getTodoIdParam(req), on: req.db) else {
-        throw Abort(.notFound)
-    }
-    return model
-}
-

Compared to the previous method I think this one changed just a little, but it’s a bit cleaner since we were able to use a regular guard statement instead of the “strange” unwrap thingy. Now we can start to convert the REST functions, first let me show you the async version of the list handler.

func list(req: Request) async throws -> [TodoListObject] {
-    try await TodoModel.query(on: req.db).all().map { $0.mapList() }
-}
-

Same pattern, we’ve replaced the EventLoopFuture generic type with the async function signature and we can return the TodoListObject array just as it is. In the function body we were able to take advantage of the async all() method and map the returned array of TodoModels using a regular Swift map instead of the mapEach function from the SwiftNIO framework. This is also a minor change, but it’s always better to used standard Swift functions, because they tend to be more efficient and future proof, sorry NIO authors, you did a great job too. 😅🚀

func get(req: Request) throws -> EventLoopFuture<TodoGetObject> {
-    try findTodoByIdParam(req).map { $0.mapGet() }
-}
-

The get function is relatively straightforward, we call our findTodoByIdParam method by awaiting for the result and use a regular map to convert our TodoModel item into a TodoGetObject.

In case you haven’t read my previous article (go and read it please), we’re always converting the TodoModel into a regular Codable Swift object so we can share these API objects as a library (iOS client & server side) without additional dependencies. We’ll use such DTOs for the create, update & patch operations too, let me show you the async version of the create function next. 📦

func create(req: Request) async throws -> TodoGetObject {
-    let input = try req.content.decode(TodoCreateObject.self)
-    let todo = TodoModel()
-    todo.create(input)
-    try await todo.create(on: req.db)
-    return todo.mapGet()
-}
-

This time the code looks more sequential, just like you’d expect when writing synchronous code, but we’re actually using async code here. The change in the update function is even more notable.

func update(req: Request) async throws -> TodoGetObject {
-    let input = try req.content.decode(TodoUpdateObject.self)
-    let todo = try await findTodoByIdParam(req)
-    todo.update(input)
-    try await todo.update(on: req.db)
-    return todo.mapGet()
-}
-

Instead of utilizing a flatMap and a map on the futures, we can simply await for both of the async function calls, there is no need for completion blocks at all, and the entire function is more clean and it makes more sense even if you just take a quick look at it. 😎

func patch(req: Request) async throws -> TodoGetObject {
-    let input = try req.content.decode(TodoPatchObject.self)
-    let todo = try await findTodoByIdParam(req)
-    todo.patch(input)
-    try await todo.update(on: req.db)
-    return todo.mapGet()
-}
-

The patch function looks just like the update, but as a reference let me insert the original snippet for the patch function here real quick. Please tell me, what do you think of both versions… 🤔

func patch(req: Request) throws -> EventLoopFuture {
-    let input = try req.content.decode(TodoPatchObject.self)
-
-    return try findTodoByIdParam(req)
-        .flatMap { todo in
-            todo.patch(input)
-            return todo.update(on: req.db).map { todo.mapGet() }
-        }
-}
-

Yeah, I thought so. Code should be self-explanatory, the second one is harder to read, you have to examine it line-by-line, even take a look at the completion handlers to understand what does this function actually does. By using the new concurrency API the patch handler function is just trivial.


-func delete(req: Request) async throws -> HTTPStatus {
-    let todo = try await findTodoByIdParam(req)
-    try await todo.delete(on: req.db)
-    return .ok
-}
-

Finally the delete operation is a no-brainer, and the good news is that Vapor is also updated to support async/await route handlers, this means that we don’t have to alter anything else inside our Todo project, except this controller of course, we can now build and run the project and everything should work just fine. This is a great advantage and I love how smooth is the transition.

So what do you think? Is this new Swift concurrency solution something that you could live with on a long term? I strongly believe that async/await is going to be utilized way more on the server side. iOS (especially SwiftUI) projects can take more advantage of the Combine framework, but I’m sure that we’ll see some new async/await features there as well. 😉

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/book/practical-server-side-swift-cover.png b/docs/book/practical-server-side-swift-cover.png deleted file mode 100644 index 50b3b9f..0000000 Binary files a/docs/book/practical-server-side-swift-cover.png and /dev/null differ diff --git a/docs/book/practical-server-side-swift-sample.pdf b/docs/book/practical-server-side-swift-sample.pdf deleted file mode 100644 index 16cff66..0000000 Binary files a/docs/book/practical-server-side-swift-sample.pdf and /dev/null differ diff --git a/docs/building-a-global-storage-for-vapor/index.html b/docs/building-a-global-storage-for-vapor/index.html deleted file mode 100644 index 0f202e2..0000000 --- a/docs/building-a-global-storage-for-vapor/index.html +++ /dev/null @@ -1,457 +0,0 @@ - - - - - - - - - - - - Building a global storage for Vapor - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 4 min read - -
-

Building a global storage for Vapor

-
-

This tutorial is about a shared global storage that you can implement using a common design pattern in Vapor 4.

-
- -

The problem with app services

Vapor has a thing called services, you can add new functionality to the system by following the pattern described in the documentation. Read-only services are great there is no issue with them, they always return a new instance of a given object that you want to access.

The problem is when you want to access a shared object or in other words, you want to define a writable service. In my case I wanted to create a shared cache dictionary that I could use to store some preloaded variables from the database.

My initial attempt was to create a writable service that I can use to store these key-value pairs. I also wanted to use a middleware and load everything there upfront, before the route handlers. 💡

import Vapor
-
-private extension Application {
-    
-    struct VariablesStorageKey: StorageKey {
-        typealias Value = [String: String]
-    }
-
-    var variables: [String: String] {
-        get {
-            self.storage[VariablesStorageKey.self] ?? [:]
-        }
-        set {
-            self.storage[VariablesStorageKey.self] = newValue
-        }
-    }
-}
-
-public extension Request {
-    
-    func variable(_ key: String) -> String? {
-        application.variables[key]
-    }
-}
-
-struct CommonVariablesMiddleware: AsyncMiddleware {
-
-    func respond(to req: Request, chainingTo next: AsyncResponder) async throws -> Response {
-        let variables = try await CommonVariableModel.query(on: req.db).all()
-        var tmp: [String: String] = [:]
-        for variable in variables {
-            if let value = variable.value {
-                tmp[variable.key] = value
-            }
-        }
-        req.application.variables = tmp
-        return try await next.respond(to: req)
-    }
-}
-

Now you might think that hey this looks nice and it’ll work and you are right, it works, but there is a HUGE problem with this solution. It’s not thread-safe at all. ⚠️

When you open the browser and type http://localhost:8080/ the page will load, but when you start bombarding the server with multiple requests using multiple threads (wrk -t12 -c400 -d30s http://127.0.0.1:8080/) the application will simply crash.

There is a similar issue on GitHub, which describes the exact same problem. Unfortunately I was unable to solve this with locks, I don’t know why but it messed up even more things with strange errors and since I’m also not able to run instruments on my M1 Mac Mini, because Swift packages are not code signed by default. I’ve spent so many hours on this and I’ve got very frustrated.

Building a custom global storage

After a break this issue was still bugging my mind, so I’ve decided to do some more research. Vapor’s discord server is usually a great place to get the right answers.

I’ve also looked up other web frameworks, and I was quite surprised that Hummingbird offers an EventLoopStorage by default. Anyway, I’m not going to switch, but still it’s a nice to have feature.

As I was looking at the suggestions I realized that I need something similar to the req.auth property, so I’ve started to investigate the implementation details more closely.

First, I removed the protocols, because I only needed a plain [String: Any] dictionary and a generic way to return the values based on the keys. If you take a closer look it’s quite a simple design pattern. There is a helper struct that stores the reference of the request and this struct has an private Cache class that will hold our pointers to the instances. The cache is available through a property and it is stored inside the req.storage.

import Vapor
-
-public extension Request {
-
-    var globals: Globals {
-        return .init(self)
-    }
-
-    struct Globals {
-        let req: Request
-
-        init(_ req: Request) {
-            self.req = req
-        }
-    }
-}
-
-public extension Request.Globals {
-
-    func get<T>(_ key: String) -> T? {
-        cache[key]
-    }
-    
-    func has(_ key: String) -> Bool {
-        get(key) != nil
-    }
-    
-    func set<T>(_ key: String, value: T) {
-        cache[key] = value
-    }
-    
-    func unset(_ key: String) {
-        cache.unset(key)
-    }
-}
-
-
-private extension Request.Globals {
-
-    final class Cache {
-        private var storage: [String: Any]
-
-        init() {
-            self.storage = [:]
-        }
-
-        subscript<T>(_ type: String) -> T? {
-            get { storage[type] as? T }
-            set { storage[type] = newValue }
-        }
-        
-        func unset(_ key: String) {
-            storage.removeValue(forKey: key)
-        }
-    }
-
-    struct CacheKey: StorageKey {
-        typealias Value = Cache
-    }
-
-    var cache: Cache {
-        get {
-            if let existing = req.storage[CacheKey.self] {
-                return existing
-            }
-            let new = Cache()
-            req.storage[CacheKey.self] = new
-            return new
-        }
-        set {
-            req.storage[CacheKey.self] = newValue
-        }
-    }
-}
-

After changing the original code I’ve come up with this solution. Maybe it’s still not the best way to handle this issue, but it works. I was able to store my variables inside a global storage without crashes or leaks. The req.globals storage property is going to be shared and it makes possible to store data that needs to be loaded asynchronously. 😅

import Vapor
-
-public extension Request {
-    
-    func variable(_ key: String) -> String? {
-        globals.get(key)
-    }
-}
-
-struct CommonVariablesMiddleware: AsyncMiddleware {
-
-    func respond(to req: Request, chainingTo next: AsyncResponder) async throws -> Response {
-        let variables = try await CommonVariableModel.query(on: req.db).all()
-        for variable in variables {
-            if let value = variable.value {
-                req.globals.set(variable.key, value: value)
-            }
-            else {
-                req.globals.unset(variable.key)
-            }
-        }
-        return try await next.respond(to: req)
-    }
-}
-

After I’ve run several more tests using wrk I was able to confirm that the solution works. I had no issues with threads and the app had no memory leaks. It was a relief, but still I’m not sure if this is the best way to handle my problem or not. Anyway I wanted to share this with you because I believe that there is not enough information about thread safety.

The introduction of async / await in Vapor will solve many concurrency problems, but we’re going to have some new ones as well. I really hope that Vapor 5 will be a huge improvement over v4, people are already throwing in ideas and they are having discussions about the future of Vapor on discord. This is just the beginning of the async / await era both for Swift and Vapor, but it’s great to see that finally we’re going to be able to get rid of EventLoopFutures. 🥳

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/building-and-loading-dynamic-libraries-at-runtime-in-swift/index.html b/docs/building-and-loading-dynamic-libraries-at-runtime-in-swift/index.html deleted file mode 100644 index 33b6cc4..0000000 --- a/docs/building-and-loading-dynamic-libraries-at-runtime-in-swift/index.html +++ /dev/null @@ -1,438 +0,0 @@ - - - - - - - - - - - - Building and loading dynamic libraries at runtime in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 4 min read - -
-

Building and loading dynamic libraries at runtime in Swift

-
-

Learn how to create a plugin system using dynamic libraries and the power of Swift, aka. modular frameworks on the server-side.

-
- -

Why should we make a plugin system?

In the modules and hooks article I was writing about how modules (plugins) can work together by using various invocation points and hooks. The only problem with that approach is that you can’t really turn on or off modules on-the-fly, since we usually build our apps in a static way.

A good plugin system should let us alter the behavior of our code at runtime. Wordpress plugins are extremely successful, because you can add extra functionality to the CMS without recompiling or altering the core. Outside the Apple ecosystem, there is a huge world that could take advantage of this concept. Yes, I am talking about Swift on the server and backend applications.

My idea here is to build an open-source modular CMS that can be fast, safe and extensible through plugins. Fortunately now we have this amazing type-safe programming language that we can use. Swift is fast and reliable, it is the perfect choice for building backend apps on the long term. ✅

In this article I would like to show you a how to build a dynamic plugin system. The whole concept is based on Lopdo’s GitHub repositories, he did quite an amazing job implementing it. Thank you very much for showing me how to use dlopen and other similar functions. 🙏

The magic of dynamic linking

Handmade iOS frameworks are usually bundled with the application itself, you can learn pretty much everything about a framework if you know some command line tools. This time we are only going to focus on static and dynamic linking. By default Swift package dependencies are linked statically into your application, but you can change this if you define a dynamic library product.

First we are going to create a shared plugin interface containing the plugin API as a protocol.

// swift-tools-version:5.2
-import PackageDescription
-
-let package = Package(
-    name: "PluginInterface",
-    products: [
-        .library(name: "PluginInterface", type: .dynamic, targets: ["PluginInterface"]),
-    ],
-    targets: [
-        .target(name: "PluginInterface", dependencies: []),
-    ]
-)
-

This dynamic PluginInterface package can produce a .dylib or .so file, soon there will be a .dll version as well, based on the operating system. All the code bundled into this dynamic library can be shared between other applications. Let’s make a simple protocol.

public protocol PluginInterface {
-
-    func foo() -> String
-}
-

Since we are going to load the plugin dynamically we will need something like a builder to construct the desired object. We can use a new abstract class for this purpose.

open class PluginBuilder {
-    
-    public init() {}
-
-    open func build() -> PluginInterface {
-        fatalError("You have to override this method.")
-    }
-}
-

That’s our dynamic plugin interface library, feel free to push this to a remote repository.

Building a dynamic plugin

For the sake of simplicity we’ll build a module called PluginA, this is the manifest file:

// swift-tools-version:5.2
-import PackageDescription
-
-let package = Package(
-    name: "PluginA",
-    products: [
-        .library(name: "PluginA", type: .dynamic, targets: ["PluginA"]),
-    ],
-    dependencies: [
-        .package(url: "path/to/the/PluginInterface/repository", from: "1.0.0"),
-    ],
-    targets: [
-        .target(name: "PluginA", dependencies: [
-            .product(name: "PluginInterface", package: "PluginInterface")
-        ]),
-    ]
-)
-

The plugin implementation will of course implement the PluginInterface protocol. You can extend this protocol based on your needs, you can also use other frameworks as dependencies.

import PluginInterface
-
-struct PluginA: PluginInterface {
-
-    func foo() -> String {
-        return "A"
-    }
-}
-

We have to subclass the PluginBuilder class and return our plugin implementation. We are going to use the @_cdecl attributed create function to access our plugin builder from the core app. This Swift attribute tells the compiler to save our function under the “createPlugin” symbol name.

import PluginInterface
-
-@_cdecl("createPlugin")
-public func createPlugin() -> UnsafeMutableRawPointer {
-    return Unmanaged.passRetained(PluginABuilder()).toOpaque()
-}
-
-final class PluginABuilder: PluginBuilder {
-
-    override func build() -> PluginInterface {
-        PluginA()
-    }
-}
-

We can build the plugin using the command line, just run swift build in the project folder. Now you can find the dylib file under the binary path, feel free to run swift build --show-bin-path, this will output the required folder. We will need both .dylib files for later use.

Loading the plugin at runtime

The core application will also use the plugin interface as a dependency.

// swift-tools-version:5.2
-import PackageDescription
-
-let package = Package(
-    name: "CoreApp",
-    dependencies: [
-        .package(url: "path/to/the/PluginInterface/repository", from: "1.0.0"),
-    ],
-    targets: [
-        .target(name: "CoreApp", dependencies: [
-            .product(name: "PluginInterface", package: "PluginInterface")
-        ]),
-    ]
-)
-

This is an executable target, so we can place the loading logic to the main.swift file.

import Foundation
-import PluginInterface
-
-typealias InitFunction = @convention(c) () -> UnsafeMutableRawPointer
-
-func plugin(at path: String) -> PluginInterface {
-    let openRes = dlopen(path, RTLD_NOW|RTLD_LOCAL)
-    if openRes != nil {
-        defer {
-            dlclose(openRes)
-        }
-
-        let symbolName = "createPlugin"
-        let sym = dlsym(openRes, symbolName)
-
-        if sym != nil {
-            let f: InitFunction = unsafeBitCast(sym, to: InitFunction.self)
-            let pluginPointer = f()
-            let builder = Unmanaged<PluginBuilder>.fromOpaque(pluginPointer).takeRetainedValue()
-            return builder.build()
-        }
-        else {
-            fatalError("error loading lib: symbol \(symbolName) not found, path: \(path)")
-        }
-    }
-    else {
-        if let err = dlerror() {
-            fatalError("error opening lib: \(String(format: "%s", err)), path: \(path)")
-        }
-        else {
-            fatalError("error opening lib: unknown error, path: \(path)")
-        }
-    }
-}
-
-let myPlugin = plugin(at: "path/to/my/plugin/libPluginA.dylib")
-let a = myPlugin.foo()
-print(a)
-

We can use the dlopen function to open the dynamic library file, then we are trying to get the createPlugin symbol using the dlsym method. If we have a pointer we still need to cast that into a valid PluginBuilder object, then we can call the build method and return the plugin interface.

Running the app

Now if you try to run this application using Xcode you’ll get a warning like this:

Class _TtC15PluginInterface13PluginBuilder is implemented in both… One of the two will be used. Which one is undefined.

This is related to an old bug, but fortunately that is already resolved. This time Xcode is the bad guy, since it is trying to link everything as a static dependency. Now if you build the application through the command line (swift build) and place the following files in the same folder:

  • CoreApp
  • libPluginA.dylib
  • libPluginInterface.dylib

You can run the application ./CoreApp without further issues. The app will print out A without the warning message, since the Swift package manager is recognizing that you would like to link the libPluginInterface framework as a dynamic framework, so it won’t be embedded into the application binary. Of course you have to set up the right plugin path in the core application.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/building-input-forms-for-ios-apps/index.html b/docs/building-input-forms-for-ios-apps/index.html deleted file mode 100644 index 12bafb8..0000000 --- a/docs/building-input-forms-for-ios-apps/index.html +++ /dev/null @@ -1,464 +0,0 @@ - - - - - - - - - - - - Building input forms for iOS apps - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

-
- -

This method is not working, since cells in the form are going to be reused and this leads to some inconsistency… please read my other post. 🤷‍♂️

CollectionView and input forms

My CollectionView framework just got a HUGE update. There are lots of new changes, but one of the biggest improvement is the way I deal with view models. In the past, you had to use long function names in your view model including the generic view & model class names. If you have ever read my ultimate UICollectionView guide you should know what I’m talking about. Good news: I have a way better solution now! 😉

This update not just cleans up my code a lot, but it allows me to add custom view model handlers, so I can interact with input fields, toggles, etc. in a ridiculously easy way. Another huge improvement is that I started to use view identifiers. It was accidental discovery, I only wanted to look for an alternative solution for identifying views by tags, then I had this brilliant idea: why not look up cells by ids as well?

As a result I’m now able to create forms by using the framework. I still believe that collection views are the ultimate building blocks for most of the applications. Yeah, you can still say that there is no silver bullet, but I’m just fine if this solution can cover 90% of the my use-cases. After all, most of the apps are just visualizing JSON data in a nice, or not-so-nice way. 🤷‍♂️ #sarcasm

Reusable form components

Let’s build a form by using the brand new framework. First of all, you’ll need to integrate it by using a package manager. I really hope that in a few weeks we can use Swift Package Manager, until than you you should go with CocoaPods or carthage.

# cocoapods
-source 'https://github.com/CoreKit/CocoaPods.git'
-pod 'CollectionView', '~> 2.0.0'
-
-# carthage
-github "CoreKit/CollectionView" "2.0.0"
-

Now let’s create a reusable cell for our input fields. Feel free to use a xib file as usual, the only difference in the implementation is going to be that I remove the target listener in the reset method. We’ll add one later on in the view-model. 🎯

import Foundation
-import CollectionView
-
-class InputCell: Cell {
-
-    @IBOutlet weak var textField: UITextField!
-
-    override func reset() {
-        super.reset()
-
-        self.textField.removeTarget(nil, action: nil, for: .editingChanged)
-    }
-}
-

I’m also going to create a simple entity for displaying a placeholder if the form field is empty and storing the actual value of the input field, let’s call this InputEntity.

import Foundation
-
-struct InputEntity {
-    var placeholder: String
-    var value: String?
-}
-

Now the hardest part: making a connection between the view and the model.

import Foundation
-import CollectionView
-
-class InputViewModel: ViewModel<InputCell, InputEntity> {
-
-    var editingChangeHandler: ViewModelHandler?
-
-    override var height: CGFloat {
-        return 60
-    }
-
-    override func updateView() {
-        self.view?.textField.placeholder = self.model.placeholder
-        self.view?.textField.text = self.model.value
-
-        self.view?.textField.addTarget(self,
-                                       action: #selector(self.editingChanged(_:)),
-                                       for: .editingChanged)
-        self.view?.textField.addTarget(self,
-                                       action: #selector(self.editingDidEnd(_:)),
-                                       for: .editingDidEnd)
-    }
-
-    func onEditingChange(_ handler: @escaping ViewModelHandler) -> Self {
-        self.editingChangeHandler = handler
-        return self
-    }
-
-    @objc func editingChanged(_ textField: UITextField) {
-        self.model.value = textField.text
-        self.editingChangeHandler?(self)
-    }
-
-    @objc func editingDidEnd(_ textField: UITextField) {
-        print("nothing-to-do-here-now...")
-    }
-}
-

It’s quite a complex view model, but it can do a lot as well. The first thing that you should understand is the ViewModelHandler which is basically a generic alias that you can utilize in the view models. It gives you the ability to pass around the type-safe view-model for the callbacks. You’ll see that later on.

The second major change is the updateView method, which is used to update the view based on the data coming from the model. I’m also adding my target listeners to my view, so I can handle user input directly inside the view-model class.

The onEditingChange method is the “public” api of the view-model. I use the on prefix now for adding handlers, and listeners to my view-models. It basically calls the stored block if a change event happens. You can add as many event handler blocks as you want. I really hope that you’ll get the hang of this approach.

One more thing: returning the the height of the cell is a one-liner now! 🎊

Composing forms and more
The plan is for now to have an input form with two input fields. One for the email address, the other is going to be used for the password. The trick is going to be that this time I won’t show you the entire code, but you have to figure out the rest.

However I’ll show you everything that you’ll ever need to know in order to make your own forms, even some complex ones. Don’t worry, it’s just a few lines of code.

import UIKit
-import CollectionView
-
-class ViewController: CollectionViewController {
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        let grid = Grid(columns: 1, margin: UIEdgeInsets(all: 16), padding: .zero)
-        self.collectionView.source = .init(grid: grid, [
-            [
-                InputViewModel(id: "email-input", .init(placeholder: "Email", value: nil))
-                .onEditingChange { viewModel in
-                    guard let passwordViewModel = viewModel.by(id: "password-input") as? InputViewModel else {
-                        return
-                    }
-                    passwordViewModel.model.value = viewModel.model.value ?? ""
-                    passwordViewModel.updateView()
-                },
-                InputViewModel(id: "password-input", .init(placeholder: "Password", value: nil)),
-            ],
-        ])
-        self.collectionView.reloadData()
-    }
-}
-

If you’ve ever worked with my collection view framework, you should know that I always use a grid system, because I don’t really like to calculate numbers.

The source is a set of view-models, grouped by sections. The only interesting part here is that sources can now be initialized with an array of sections and view-models.

If you initialize a view-model with and identifier, later on you can query that one by the id. This is exactly whats happening inside the editing change handler block. Every view-model has the ability to return some other view-model by the id. View-models are type-safe by default, the viewModel passed inside the block too, thanks to the generic ViewModelHandler alias.

So in this little example, if you type something into the first input field, the exact same text will appear in the second text field. You can get all the view models by id when you need them. For example if you have to submit this form, you can grab the email and password fields by using the same approach.

Building a login form

I challenge you to build a login form on your own by using my framework. I guarantee yout that it shouldn’t take more than 30mins of work. I’ll show you the final view controller that I would use, so this might gives you some help.

If you want to spice up things a little bit, you can even add a checkbox for accepting the privacy policy. The main idea here is that you should create reusable components for every single item in your form. So for example a ToggleView with a corresponding view-model would be a good approach (also works for buttons). 🤫

Here is the final hint, you only have to make your own view-models and views…

import UIKit
-import CollectionView
-
-class ViewController: CollectionViewController {
-
-    enum Ids: String {
-        case email = "email-input"
-        case password = "password-input"
-        case privacyPolicy = "privacy-policy-checkbox"
-        case submit = "submit-button"
-    }
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        let grid = Grid(columns: 1, margin: UIEdgeInsets(all: 16), padding: .zero)
-        self.collectionView.source = .init(grid: grid, [
-            [
-                InputViewModel(id: Ids.email.rawValue, .init(placeholder: "Email", value: nil))
-                .onEditingEnd { viewModel in
-                    guard let passwordViewModel = viewModel.by(id: Ids.password.rawValue) as? InputViewModel else {
-                        return
-                    }
-                    passwordViewModel.view?.textField.becomeFirstResponder()
-                },
-                InputViewModel(id: Ids.password.rawValue, .init(placeholder: "Password", value: nil, secure: true))
-                .onEditingEnd { viewModel in
-                    viewModel.view?.textField.endEditing(true)
-                },
-            ],
-            [
-                ToggleViewModel(id: Ids.privacyPolicy.rawValue, .init(label: "Privacy policy", value: false))
-                .onValueChange { viewModel in
-                    guard let submitViewModel = viewModel.by(id: Ids.submit.rawValue) as? ButtonViewModel else {
-                        return
-                    }
-                    var model = submitViewModel.model
-                    model.enabled = viewModel.model.value
-                    submitViewModel.model = model
-                    submitViewModel.updateView()
-                },
-            ],
-            [
-                ButtonViewModel(id: Ids.submit.rawValue, .init(title: "Submit", enabled: false))
-                .onSubmit { viewModel in
-                    guard
-                        let emailViewModel = viewModel.by(id: Ids.email.rawValue) as? InputViewModel,
-                        let passwordViewModel = viewModel.by(id: Ids.password.rawValue) as? InputViewModel
-                    else {
-                        return
-                    }
-                    /* ... */
-                },
-            ],
-        ])
-        self.collectionView.reloadData()
-    }
-}
-

That’s it for now, an almost complete login form, with just a few lines of code. Of course there is an underlying framework, but if you check the source code, you’ll actually see that it contains nothing that would be considered as black magic. 💫

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/building-static-and-dynamic-swift-libraries-using-the-swift-compiler/index.html b/docs/building-static-and-dynamic-swift-libraries-using-the-swift-compiler/index.html deleted file mode 100644 index e0f2d3a..0000000 --- a/docs/building-static-and-dynamic-swift-libraries-using-the-swift-compiler/index.html +++ /dev/null @@ -1,371 +0,0 @@ - - - - - - - - - - - - Building static and dynamic Swift libraries using the Swift compiler - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 8 min read - -
-

Building static and dynamic Swift libraries using the Swift compiler

-
-

This tutorial is all about emitting various Swift binaries without the Swift package manager, but only using the Swift compiler.

-
- -

What the heck is a library?

A library is a collection of Swift components that other applications can use.

Imagine that you are creating a simple application to pluralize a string. It works great, you finish the app and you start working on your next one. In your next application, you face the exact same issue, you have to print countable items (e.g 2 bananas). What would you do? 🤔

The first thing that can cross your mind is to copy all the source code from the first application into the second one. Well, this could work of course, but what happens if you discover a bug in the pluralization component? Now you have to fix the issue at two places, since you’ve just duplicated the entire stuff. There must be a better way… 🧠

Fortunately computer programmers faced the exact same issue, so they invented shared libraries. A shared library is a special kind of binary component that you can use in your main application. This way you can outsource Swift code into a separate file (or bunch of files), throw in some access control to allow other apps to use public methods and call functions from your library and here we go, we just shared our common code between our applications.

Oh wait, there is a bug in the lib, how can I fix it? Well, this is where things get a bit complicated, but don’t worry too much, I’ll try to explain how it works. So, last time, you know, when we talked about the Swift compiler and linker, I mentioned, that they can resolve dependencies in your program. When you use a library you can choose between two approaches.

  • static linking
  • dynamic linking

Static linking means that the source code inside the library will be literally copy-pasted into your application binary. Dynamic linking on the other hand means that your library dependencies will be resolved at runtime. By the way, you have to decide this upfront, since you have to build either a static or a dynamic library. Huhh? Ok, let me try this again… 🙃

The static library approach is more simple. You can easily build a static library using the compiler (you’ll see how to make one later on), then you can import this library inside your application source (import MyLibrary). Now when you compile the main app, you have to tell the compiler the location of your static (binary) library, and the publicly accessible objects (headers or module map) that are available to use. This way when your app is composed the symbols from the lib (classes, methods, etc) can be copied to the main executable file). When you run the app, required objects will be there already inside the binary file, so you can run it as it is.

The main difference between a static and a dynamic library is that you don’t copy every required symbol to the executable application binary when you use a dylib file, but some of the “undefined” symbols will be resolved at runtime. First you have to build your library as a dynamic dependency using the Swift compiler, this will produce a dynamic (binary) library file and a module map (header files). When you make the final version of your app, the system will put references of the dynamic library to your executable instead of copying the contents of the dylib file. If you want to run your application you have to make sure that the referenced dynamic library is available to use. The operating system will try to load the generated dylib file so the application resolves the symbols based on the reference pointers. 👈

Should I choose dynamic or static linking?

Well, it depends on the environment. For example the Swift Package Manager prefers to use static linking, but Xcode will try to build SPM packages as dynamic dependencies. You can also explicitly tell SPM to build a static or dynamic library, but in most of the cases you should stick with the automatic value, so the system can build the right module dependency for you.

// swift-tools-version:5.3
-import PackageDescription
-
-let package = Package(
-    name: "MyLibrary",
-    products: [
-        /// type: automatic, based on the environment
-        .library(name: "MyLibrary", targets: ["MyLibrary"]),
-        //.library(name: "MyLibrary", type: .dynamic, targets: ["MyLibrary"]),
-        //.library(name: "MyLibrary", type: .static, targets: ["MyLibrary"]),
-    ],
-    targets: [
-        .target(name: "MyLibrary", dependencies: []),
-    ]
-)
-

By the way if you are confused enough, I have an article for beginners about Swift packages, modules, frameworks and the tools that makes this whole dependency management possible. You should definitely take a look, it’s a some sort of a deep dive into FAT frameworks, but the first part of the article is full of useful definitions and introductions to various commands.

Back to the original question: static vs dynamic? Do you remember the bug in the library that we have to fix? If you use a static library you have to rebuild all the apps that are depending on it (they must be linked with the fixed library of course) in order to make the issue disappear. 🐛

Since a dynamic library is loaded at runtime and the symbols are not embedded into the application binary, you can simply build a new dylib file and replace the old one to fix the bug. This way all the apps that are referencing to this dependency will have the fix for free. There is no need to recompile everyting, except the faulty code in the framework itself. 💪

It is also worth to mention that the final app size is smaller when you use a dylib.

Ok, but why should I ever use static linking if dylibz are so cool? The truth is that sometimes you want to encapsulate everything into a single binary, instead of installing lots of other dylib files into the system. Also what happens if something deletes a dylib that your app would require to work flawlessly? That’d suck for sure, especially if it is a mission-critical script on a server… 😳

Hopefully, I over-explained things, so we can start building our very first static library.

Compiling a static Swift library

Do you still have that little Point struct from the previous tutorial? Let’s build a static library from that file, but before we do so, we have to explicitly mark it as public, plus we need a public init method in order to be able to create a Point struct from our application. You know, in Swift, access control allows us, programmers, to hide specific parts of a library from other developers.

public struct Point {
-    public let x: Int
-    public let y: Int
-
-    public init(x: Int, y: Int) {
-        self.x = x
-        self.y = y
-    }
-}
-

Now we’re ready to build our static library based on this single point.swift source file. As I mentioned this before, we need a binary file and a module map file that contains the publicly accessible interface for the lib. You can use the -emit-library flat to tell the Swift compiler that we need a binary library file plus using the -emit-module parameter will produce a Swift module info file with all the API and docs needed for other modules. By default the compiler would emit a dylib (on macOS at least), so we have to use the -static flat to explicitly generate a static dependency. 🔨

swiftc point.swift -emit-module -emit-library -static
-

The command above should produce 4 new files:

Move these files inside a lib folder, so it’ll be more easy to work with them. That’s really it, we’ve just created a working static library, but how can we use it to link them against our main application? 🤔

First of all, we have to import our newly created module inside the main.swift file if we want to use the objects (in our case the Point struct) from it. By the way you can add a custom module name to your library if you use the -module-name [name] argument with the previous swiftc command.

import point
-
-let p = Point(x: 4, y: 20)
-
-print("Hello library!", p.x, p.y)
-

So, all of our library files are located in a lib folder, and our default module name is point (based on our single input file). We can use the swiftc command again, to compile the main file, this time we use the -L flag to add a library search path, so the compiler can locate our binary libpoint.a file. We also have to set a search path for imports, the -I property will help us, this way the public API (headers) of the module will be available in our source file. The very last thing that we have to append to the end of the command is the -l[name] flag, this specifies the library name we would like to link against. Be careful, there is no space in between the -l and the name value! ⚠️

swiftc main.swift -L ./lib/ -I ./lib/ -lpoint
-
-# run the app
-./main
-# Hello library! 4 20
-

Voilá, we’ve just separated a file from the main application by using a static dependency. 👏

Compiling a dynamic Swift library

In theory, we can use the same code and build a dynamic library from the point.swift file and compile our main.swift file using that shared framework. We just drop the -static flag first.

swiftc point.swift -emit-module -emit-library
-

This time the output is slightly different. We’ve got a libpoint.dylib binary instead of the libpoint.a, but all the other files look identical. Extension my vary per operating system:

  • macOS - static: .a, dynamic: .dylib
  • Linux - static: .so, dynamic: .dylib
  • Windows - static: .lib, dynamic: .dll

So we have our dylib file, but the real question is: can we build the main.swift file with it?

swiftc main.swift -L ./lib/ -I ./lib/ -lpoint
-
-# run the app
-./main
-# Hello library! 4 20
-

Now rename the libpoint.dylib file into libpoint.foo and run the main app again.

./main
-
-# dyld: Library not loaded: libpoint.dylib
-#   Referenced from: /Users/tib/./main
-#   Reason: image not found
-# zsh: abort      ./main
-

Whoops, seems like we have a problem. Don’t worry, this is the expected output, since we renamed the dynamic library and the application can’t find it. When the loader tries to get the referenced symbols from the file it looks up dynamic libraries at a few different places.

  • The directory you specified through the -L flag (./lib/).
  • The directory where your executable file is (./)
  • The /usr/lib/ or the /usr/local/lib/ directories

Since the /usr/lib/ directory is protected by the famous SIP “guard”, you should ship your dylib files next to your executable binary, or alternatively you can install them under the /usr/local/lib/ folder. Unfortunately, this lookup strategy can lead to all sort of issues, I really don’t want to get into the details this time, but it can lead to compatibility and security issues. 🤫

The good news is that now if you change something in the dylib, and you simply rebuild & replace the file then you run the ./main again (without recompiling), the altered dynamic library will be used. Just try to put a print statement into the init method of the Point struct…

Summary

Honestly, I’d rather go with a static library in most of the cases because using a static library will guarantee that your application has every necessary dependency embedded into the binary file.

Of course dynamic libraries are great if you are the author of a commonly used framework, such the Swift standard library, Foundation or UIKit. These modules are shipped as shared libraries, because they are huge and almost every single app imports them. Just think about it, if we’d link these three frameworks statically that’d add a lot to the size of our apps, plus it’d be way harder to fix system-wide bugs. This is the reason why these packages are shipped as shared libz, plus Apple can gives us a promise that these components will always be available as part of the operating system. 😅

Anyways, there are some tools that you can use to alter library loader paths, I’ll tell you more about this next time. It’s going to be a more advanced topic including different languages. I’m going to show you how to build a library using C and how to call it using Swift, without SPM. 🤓

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/building-tree-data-structures-in-swift/index.html b/docs/building-tree-data-structures-in-swift/index.html deleted file mode 100644 index bd625d5..0000000 --- a/docs/building-tree-data-structures-in-swift/index.html +++ /dev/null @@ -1,477 +0,0 @@ - - - - - - - - - - - - Building tree data structures in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 6 min read - -
-

Building tree data structures in Swift

-
-

This tutorial is about showing the pros and cons of various Swift tree data structures using structs, enums and classes.

-
- -

What is a tree?

A tree is an abstract data structure that can be used to represent hierarchies. A tree usually contains nodes with associated data values. Each node can have child nodes and these nodes are linked together via a parent-child relationship.

The name tree comes from the real-world, both digital and the physical trees have branches, there is usually one node that has many children, and those can also have subsequent child nodes. 🌳

Each node in the tree can have an associated data value and a reference to the child nodes.

The root object is where the tree begins, it’s the trunk of the tree. A branch node is just some part of the tree that has another branches and we call nodes without further branches as leaves.

Of course there are various types of tree structures, maybe the most common one is the binary tree. Walking through the items in a tree is called traversal, there are multiple ways to step through the tree, in-order, pre-order, post-order and level-order. More about this later on. 😅

Data trees using structs in Swift

After the quick intro, I’d like to show you how to build a generic tree object using structs in Swift. We’re going to create a simple struct that can hold any value type, by using a generic placeholder. We’re also going to store the child objects in an array that uses the exact same node type. First we’re going to start with a simple Node object that can store a String value.

struct Node {
-    var value: String
-    var children: [Node]
-}
-
-var child = Node(value: "child", children: [])
-var parent = Node(value: "parent", children: [child])
-
-print(parent) 
-// Node(value: "parent", children: [Node(value: "child", children: [])])
-

Let’s alter this code by introducing a generic variable instead of using a String type. This way we’re going to be able to reuse the same Node struct to store all kinds of values of the same type. We’re also going to introduce a new init method to make the Node creation process just a bit more simple.

struct Node<Value> {
-    var value: Value
-    var children: [Node]
-    
-    init(_ value: Value, children: [Node] = []) {
-        self.value = value
-        self.children = children
-    }
-}
-
-var child = Node(2)
-var parent = Node(1, children: [child])
-
-print(parent)
-// Node<Int>(value: 1, children: [Node<Int>(value: 2, children: [])])
-

As you can see the underlying type is an Int, Swift is smart enough to figure this out, but you can also explicitly write Node(2) or of course any other type that you’d like to use.

One thing that you have to note when using structs is that these objects are value types, so if you want to modify a tree you’ll need a mutating function and you have to be careful when defining nodes, you might want to store them as variables instead of constants if you need to alter them later on. The order of your code also matters in this case, let me show you an example. 🤔

struct Node<Value> {
-    var value: Value
-    var children: [Node]
-    
-    init(_ value: Value, children: [Node] = []) {
-        self.value = value
-        self.children = children
-    }
-    
-    mutating func add(_ child: Node) {
-        children.append(child)
-    }
-}
-
-var a = Node("a")
-var b = Node("b")
-var c = Node("c")
-
-a.add(b)
-
-print(a)
-// Node<String>(value: "a", children: [Node<String>(value: "b", children: [])])
-
-b.add(c) // this won't affect a at all
-
-print(a)
-// Node<String>(value: "a", children: [Node<String>(value: "b", children: [])])
-
-print(b)
-// Node<String>(value: "b", children: [Node<String>(value: "c", children: [])])
-

We’ve tried to add a child node to the b object, but since the copy of b is already added to the a object, it won’t affect a at all. You have to be careful when working with structs, since you’re going to pass around copies instead of references. This is usually a great advantage, but sometimes it won’t give you the expected behavior.

One more thing to note about structs is that you are not allowed to use them as recursive values, so for example if we’d like to build a linked list using a struct, we won’t be able to set the next item.

struct Node {
-    let value: String
-    // ERROR: Value type `Node` cannot have a stored property that recursively contains it.
-    let next: Node?
-}
-

The explanation of this issue is well-written here, it’s all about the required space when allocating the object. Please try to figure out the reasons on your own, before you click on the link. 🤔

How to create a tree using a Swift class?

Most common examples of tree structures are using classes as a base type. This solves the recursion issue, but since we’re working with reference types, we have to be extremely careful with memory management. For example if we want to place a reference to the parent object, we have to declare it as a weak variable.

class Node<Value> {
-    var value: Value
-    var children: [Node]
-    weak var parent: Node?
-
-    init(_ value: Value, children: [Node] = []) {
-        self.value = value
-        self.children = children
-
-        for child in self.children {
-            child.parent = self
-        }
-    }
-
-    func add(child: Node) {
-        child.parent = self
-        children.append(child)
-    }
-}
-
-let a = Node("a")
-let b = Node("b")
-
-a.add(child: b)
-
-let c = Node("c", children: [Node("d"), Node("e")])
-a.add(child: c)
-
-print(a) // tree now contains a, b, c, d, e
-

This time when we alter a node in the tree, the original tree will be updated as well. Since we’re now working with a reference type instead of a value type, we can safely build a linked list or binary tree by using the exact same type inside our class.

class Node<Value> {
-    var value: Value
-    // the compiler is just fine with these types below...
-    var left: Node?
-    var right: Node?
-    
-    init(
-        _ value: Value, 
-        left: Node? = nil,
-        right: Node? = nil
-    ) {
-        self.value = value
-        self.left = left
-        self.right = right
-    }
-}
-
-
-let right = Node(3)
-let left = Node(2)
-let tree = Node(1, left: left, right: right)
-print(tree) // 1, left: 2, right: 3
-

Of course you can still use protocols and structs if you prefer value types over reference types, for example you can come up with a Node protocol and then two separate implementation to represent a branch and a leaf. This is how a protocol oriented approach can look like.

protocol Node {
-    var value: Int { get }
-}
-
-struct Branch: Node {
-    var value: Int
-    var left: Node
-    var right: Node
-}
-
-struct Leaf: Node {
-    var value: Int
-}
-
-
-let tree = Branch(
-    value: 1, 
-    left: Leaf(value: 2), 
-    right: Leaf(value: 3)
-)
-print(tree)
-

I like this solution quite a lot, but of course the actual choice is yours and it should always depend on your current use case. Don’t be afraid of classes, polymorphism might saves you quite a lot of time, but of course there are cases when structs are simply a better way to do things. 🤓

Implementing trees using Swift enums

One last thing I’d like to show you in this article is how to implement a tree using the powerful enum type in Swift. Just like the recursion issue with structs, enums are also problematic, but fortunately there is a workaround, so we can use enums that references itself by applying the indirect keyword.

enum Node<Value> {
-    case root(value: Value)
-    indirect case leaf(parent: Node, value: Value)
-
-    var value: Value {
-        switch self {
-        case .root(let value):
-            return value
-        case .leaf(_, let value):
-            return value
-        }
-    }
-}
-let root = Node.root(value: 1)
-let leaf1 = Node.leaf(parent: root, value: 2)
-let leaf2 = Node.leaf(parent: leaf1, value: 3)
-

An indirect enum case can reference the enum itself, so it’ll allo us to create cases with the exact same type. This way we’re going to be able to store a parent node or alternatively a left or right node if we’re talking about a binary tree. Enums are freaking powerful in Swift.

enum Node<Value> {
-    case empty
-    indirect case node(Value, left: Node, right: Node)
-}
-
-let a = Node.node(1, left: .empty, right: .empty)
-let b = Node.node(2, left: a, right: .empty)
-print(b)
-

These are just a few examples how you can build various tree data structures in Swift. Of course there is a lot more to the story, but for now I just wanted to show you what are the pros and cons of each approach. You should always choose the option that you like the best, there is no silver bullet, but only options. I hope you enjoyed this little post. ☺️

If you want to know more about trees, you should read the linked articles, since they are really well-written and it helped me a lot to understand more about these data structures. Traversing a tree is also quite an interesting topic, you can learn a lot by implementing various traversal methods. 👋

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/clockkit-complications-cheat-sheet/index.html b/docs/clockkit-complications-cheat-sheet/index.html deleted file mode 100644 index 17308e6..0000000 --- a/docs/clockkit-complications-cheat-sheet/index.html +++ /dev/null @@ -1,369 +0,0 @@ - - - - - - - - - - - - ClockKit complications cheatsheet - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 1 min read - -
-

ClockKit complications cheatsheet

-
-

ClockKit families and templates, there are so many of them. It's a little bit time consuming if you are looking for the right one.

-
- -

The official ClockKit documentation on Apple’s site is well written, but it lacks a generic overview of all the existing complications. I’ve created a little cheatsheet for you to simplify the searching process for the right complication style.

This cheatsheet supports watchOS 5. In order to get the template name you just have to add up the names in the proper order. Usually left to right, top to bottom. Don’t worry you’ll get it. 😅

Swift sample code is below the cheatsheet, please scroll! 👇

ClockKit cheatsheet

Feel free to right click and download the cheatsheet image.

ClockKit code sample in Swift
This little snippet contains all the ClockKit complication families and templates. 😎

import ClockKit
-
-class ComplicationDataSource: NSObject, CLKComplicationDataSource {
-
-    func getSupportedTimeTravelDirections(
-        for complication: CLKComplication,
-        withHandler handler: @escaping (CLKComplicationTimeTravelDirections
-    ) -> Void) {
-        handler([.forward, .backward])
-    }
-
-    func getCurrentTimelineEntry(
-        for complication: CLKComplication,
-        withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void
-    ) {
-        let date = Date()
-        var template: CLKComplicationTemplate!
-
-        switch complication.family {
-        case .circularSmall:
-            template = CLKComplicationTemplateCircularSmallStackText()
-            template = CLKComplicationTemplateCircularSmallStackImage()
-
-            template = CLKComplicationTemplateCircularSmallSimpleText()
-            template = CLKComplicationTemplateCircularSmallSimpleImage()
-
-            template = CLKComplicationTemplateCircularSmallRingText()
-            template = CLKComplicationTemplateCircularSmallRingImage()
-
-            break;
-        case .extraLarge:
-            template = CLKComplicationTemplateExtraLargeStackText()
-            template = CLKComplicationTemplateExtraLargeStackImage()
-
-            template = CLKComplicationTemplateExtraLargeSimpleText()
-            template = CLKComplicationTemplateExtraLargeSimpleImage()
-
-            template = CLKComplicationTemplateExtraLargeRingText()
-            template = CLKComplicationTemplateExtraLargeRingImage()
-
-            template = CLKComplicationTemplateExtraLargeColumnsText()
-            break;
-        case .modularSmall:
-            template = CLKComplicationTemplateModularSmallStackText()
-            template = CLKComplicationTemplateModularSmallStackImage()
-
-            template = CLKComplicationTemplateModularSmallSimpleText()
-            template = CLKComplicationTemplateModularSmallSimpleImage()
-
-            template = CLKComplicationTemplateModularSmallRingText()
-            template = CLKComplicationTemplateModularSmallRingImage()
-
-            template = CLKComplicationTemplateModularSmallColumnsText()
-            break;
-        case .modularLarge:
-            template = CLKComplicationTemplateModularLargeTable()
-            template = CLKComplicationTemplateModularLargeColumns()
-            template = CLKComplicationTemplateModularLargeTallBody()
-            template = CLKComplicationTemplateModularLargeStandardBody()
-            break;
-        case .utilitarianSmall:
-            template = CLKComplicationTemplateUtilitarianSmallFlat()
-            template = CLKComplicationTemplateUtilitarianSmallSquare()
-            template = CLKComplicationTemplateUtilitarianSmallRingText()
-            template = CLKComplicationTemplateUtilitarianSmallRingImage()
-            break;
-        case .utilitarianSmallFlat:
-            template = CLKComplicationTemplateUtilitarianSmallFlat()
-        case .utilitarianLarge:
-            template = CLKComplicationTemplateUtilitarianLargeFlat()
-            break;
-        case .graphicCorner:
-            template = CLKComplicationTemplateGraphicCornerCircularImage()
-            template = CLKComplicationTemplateGraphicCornerGaugeText()
-            template = CLKComplicationTemplateGraphicCornerGaugeImage()
-            template = CLKComplicationTemplateGraphicCornerStackText()
-            template = CLKComplicationTemplateGraphicCornerTextImage()
-            break;
-        case .graphicCircular:
-            template = CLKComplicationTemplateGraphicCircularImage()
-            template = CLKComplicationTemplateGraphicCircularOpenGaugeImage()
-            template = CLKComplicationTemplateGraphicCircularOpenGaugeRangeText()
-            template = CLKComplicationTemplateGraphicCircularOpenGaugeSimpleText()
-            template = CLKComplicationTemplateGraphicCircularClosedGaugeText()
-            template = CLKComplicationTemplateGraphicCircularClosedGaugeImage()
-            break;
-        case .graphicBezel:
-            template = CLKComplicationTemplateGraphicBezelCircularText()
-            break;
-        case .graphicRectangular:
-            template = CLKComplicationTemplateGraphicRectangularLargeImage()
-            template = CLKComplicationTemplateGraphicRectangularStandardBody()
-            template = CLKComplicationTemplateGraphicRectangularTextGauge()
-            break;
-        }
-        let entry = CLKComplicationTimelineEntry(
-            date: date,
-            complicationTemplate: template
-        )
-        handler(entry)
-    }
-}
-

That’s it for now. Time is over. ⏰

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

ClockKit complications cheatsheet

-
-

ClockKit families and templates, there are so many of them. It's a little bit time consuming if you are looking for the right one.

- -
- watchOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Networking examples for appleOS

-
-

Learn how to use Bonjour, with UDP/TCP sockets, streams and how to communicate through CoreBluetooth or the watch APIs.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - -
-
- -
- - - -
- - - - diff --git a/docs/comparing-factory-design-patterns/index.html b/docs/comparing-factory-design-patterns/index.html deleted file mode 100644 index 31c57ce..0000000 --- a/docs/comparing-factory-design-patterns/index.html +++ /dev/null @@ -1,338 +0,0 @@ - - - - - - - - - - - - Comparing factory design patterns - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 1 min read - -
-

Comparing factory design patterns

-
-

Learn what's the difference between static factory, simple factory, factory method and abstract factory using the Swift language.

-
- -

I thought that I’d be nice to have a summarized comparison between all the factory patterns, so here it is everything that you should know about them. Constructing them is relatively straightforward, in this example I’m going to use some UIColor magic written in the Swift programming language to show you the basics. 🧙‍♂️

Static factory

  • no separate factory class
  • named static method to initialize objects
  • can have cache & can return subtypes
extension UIColor {
-    static var primary: UIColor { return .black }
-    static var secondary: UIColor { return .white }
-}
-
-let primary = UIColor.primary
-let secondary = UIColor.secondary
-

Simple factory

  • one factory class
  • switch case objects inside of it
  • encapsulates varying code
  • if list is too big use factory method instead
class ColorFactory {
-    enum Style {
-        case primary
-        case secondary
-    }
-
-    func create(_ style: Style) {
-        switch style
-        case .primary:
-            return .black
-        case .secondary:
-            return .white
-    }
-}
-let factory = ColorFactory()
-let primary = factory.create(.primary)
-let secondary = factory.create(.secondary)
-

Factory method

  • multiple (decoupled) factory classes
  • per-instance factory method
  • create a simple protocol for factory
protocol ColorFactory {
-    func create() -> UIColor
-}
-
-class PrimaryColorFactory: ColorFactory {
-    func create() -> UIColor {
-        return .black
-    }
-}
-
-class SecondaryColorFactory: ColorFactory {
-    func create() -> UIColor {
-        return .white
-    }
-}
-
-let primaryColorFactory = PrimaryColorFactory()
-let secondaryColorFactory = SecondaryColorFactory()
-let primary = primaryColorFactory.create()
-let secondary = secondaryColorFactory.create()
-

Abstract factory

  • combines simple factory with factory method
  • has a global effect on the whole app
// exact same factory method pattern from above
-protocol ColorFactory {
-    func create() -> UIColor
-}
-
-class PrimaryColorFactory: ColorFactory {
-    func create() -> UIColor {
-        return .black
-    }
-}
-
-class SecondaryColorFactory: ColorFactory {
-    func create() -> UIColor {
-        return .white
-    }
-}
-
-// simple factory pattern from above using the factory methods
-class AppColorFactory: ColorFactory {
-
-    enum Theme {
-        case dark
-        case light
-    }
-
-    func create(_ theme: Theme) -> UIColor {
-        switch theme {
-        case .dark:
-            return PrimaryColorFactory().create()
-        case .light:
-            return SecondaryColorFactory().create()
-        }
-    }
-}
-
-let factory = AppColorFactory()
-let primaryColor = factory.create(.dark)
-let secondaryColor = factory.create(.light)
-

So these are all the factory patterns using practical real world examples written in Swift. I hope my series of articles will help you to gain a better understanding. 👍

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Comparing factory design patterns

-
-

Learn what's the difference between static factory, simple factory, factory method and abstract factory using the Swift language.

- -
-
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/conventions-for-xcode/index.html b/docs/conventions-for-xcode/index.html deleted file mode 100644 index b8628da..0000000 --- a/docs/conventions-for-xcode/index.html +++ /dev/null @@ -1,346 +0,0 @@ - - - - - - - - - - - - Conventions for Xcode - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 4 min read - -
-

Conventions for Xcode

-
-

Learn how to organize your codebase. If you are struggling with Xcode project structure, files, naming conventions, read this.

-
- -

Apple has so much frameworks and APIs that I don’t even know many of them. We are also living in the age of application extensions. If you are trying to create a brand new target in Xcode, you might end up scratching your head. 🤔

Xcode targets

This is great for both for developers and end-users, but after creating a few targets and platforms (your project grows and) you might ask the question:

How should I organise my codebase?

Don’t worry too much about it, I might have the right answer for you! 😉

The problem with complex projects

You can create apps in Xcode for all the major operating systems: iOS, macOS, tvOS, watchOS. In the latest version of Xcode you can also add more than 20 extension just for iOS, plus there are lots of app extensions available for macOS as well. Imagine a complex application with multiple extensions & targets. This situation can lead to inconsistent bundle identifiers and more ad-hoc naming solutions. Oh, by the way watchOS applications are just a special extensions for iOS targets and don’t forget about your tests, those are individual targets as well! ⚠️

As far as I can see, if you are trying to support multiple platforms you are going to have a lot of targets inside your Xcode project, additionally every new target will contain some kind of source files and assets. Should I mention schemes too? 😂

Even Apple removed it’s Lister sample code, that demonstrated one of a hellish Xcode project with 14 targets, 11 schemes, but the overall project contained only 71 Swift source files. That’s not too much code, but you can see the issue here, right?

It’s time to learn how to organise your project! 💡

Xcode project organization

So my basic idea is to have a reasonable naming conceptand folder structure inside the project. This involves targets, schemes, bundle identifiers, location of source files and assets on the disk. Let’s start with a simple example that contains multiple targets to have a better understanding. 🤓

If you are using the Swift Package Manager eg. for Swift backends, SPM will generate your Xcode project files for you, so you shoudn’t care too much about conventions and namings at all. 🤷‍♂️

Project name

Are you creating a new application? Feel free to name your project as you want. 😉

Are you going to make a framework? Extend your project name with the Kit suffix. People usually prefer to use the ProjectKit style for libraries so that’s the correct way to go. If you have a killer name, use that instead of the kit style! 😛

Available platforms

Always use the following platform names:

  • iOS
  • macOS
  • watchOS
  • tvOS

Target naming convention

Name your targets like:

[platform] [template name]
-

Don’t include project name in the targets (that would be just a duplicate)
Use the extension names from the new target window (eg. Today Extension)
Use “Application” template name for the main application targets
Use “Framework” as template name for framework targets
Order your targets in a logical way (see the example)

Scheme names

Simply use target names for schemes too (prefix with project name if required).

[project] - [platform] [template name]
-

You can prefix schemes with your project name if you want, but the generic rule is here to use the exact same name as your target. I also like to separate framework schemes visually from the schems that contain application logic, that’s why I always move them to the top of the list. However a better approach is to separate frameworks into a standalone git repository & connect them through a package manager. 📦

Bundle identifiers

This one is hard because of code signing. You can go with something like this:

[reverse domain].[project].[platform].[template name]
-

Here are the rules:

  • Start with your reverse domain name (com.example)
  • After the domain, insert your project name
  • Include platform names, except for iOS, I don’t append that one.
  • Use the template name as a suffix (like .todayextension)
  • Don’t add application as a template name
  • Use .watchkitapp, .watchkitextension for legacy watchOS targets
  • Don’t use more than 4 dots (see example below)!

If you are going to use com.example.project.ios.today.extension that’s not going to work, because it contains more than 4 dots. So you should simply go with com.example.project.ios.todayextension and names like that. 😢

Anyway, just always try to sign your app and submit to the store. Good luck. 🍀

Project folders

The thing is that I always create physical folders on the disk. If you make a group in Xcode, well by default that’s not going to be an actual folder and all your source files and assets will be located under the project’s main directory.

I know it’s a personal preference but I don’t like to call a giant “wasteland” of files as a project. I’ve seen many chaotic projects without proper file organization. 🤐

No matter what, but I always follow this basic pattern:

  • Create folders for the targets
  • Create a Sources folder for the Swift source files
  • Create an Assets folder for everything else (images, etc).

Under the Sources I always make more subfolders for individual VIPER modules, or simply for controllers, models, objects, etc.

Example use case

Here is a quick example project in Xcode that uses my conventions.

Xcode naming conventions

As you can see I followed the pattern from above. Let’s assume that my project name is TheSwiftDev. Here is a quick overview of the full setup:

Target & scheme names (with bundle identifiers):

  • iOS Application (com.tiborbodecs.theswiftdev)
  • iOS Application Unit Tests (n/a)
  • iOS Application UI Tests (n/a)
  • iOS Today Extension (com.tiborbodecs.theswiftdev.todayextension)
  • watchOS Application (com.tiborbodecs.theswiftdev.watchos)
  • watchOS Application Extension (com.tiborbodecs.theswiftdev.watchos.extension)
  • tvOS Application (com.tiborbodecs.theswiftdev.macos)
  • macOS Application (com.tiborbodecs.theswiftdev.tvos)

If you rename your iOS target with a WatchKit companion app, be careful!!! You also have to change the WKCompanionAppBundleIdentifier property inside your watch application target’s Info.plist file by hand. ⚠️

This method might looks like an overkill at first sight, but trust me it’s worth to follow these conventions. As your app grows, eventually you will face the same issues as I mentioned in the beginning. It’s better to have a plan for the future.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Awesome native Xcode extensions

-
-

This is the biggest and the best collection of the currently available natively created source editor extensions for Xcode.

- -
- Xcode -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Conventions for Xcode

-
-

Learn how to organize your codebase. If you are struggling with Xcode project structure, files, naming conventions, read this.

- -
- Tooling - Xcode -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Custom working directory in Xcode

-
-

Learn how to set a custom working directory in Xcode to solve one of the most common beginner issue when using Vapor.

- -
- Tooling - Xcode -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

How to launch a macOS app at login?

-
-

In this tutorial I'll show you how to launch a completely sandboxed macOS application on system startup written in Swift.

- -
- Tooling - macOS -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/css/base.css b/docs/css/base.css deleted file mode 100644 index e4d117c..0000000 --- a/docs/css/base.css +++ /dev/null @@ -1,101 +0,0 @@ -a:link { - color: var(--link-color); -} -a:visited { - color: var(--link-color-visited); -} -a:hover { - color: var(--link-color-hover); -} -a:active { - color: var(--link-color-active); -} -body { - background: var(--background-color); - color: var(--text-color); -} -h1 { - font-size: 2.5rem; - line-height: 3rem; -} -h2 { - font-size: 1.75rem; - line-height: 2.25rem; - margin-top: 1.5rem; -} -h3 { - font-size: 1.5rem; - line-height: 2rem; - margin-top: 1.5rem; -} -h4 { - font-size: 1.25rem; - line-height: 1.75; -} -h5 { - font-size: 1.125rem; - line-height: 1.625rem; -} -h6 { - font-size: 1rem; - line-height: 1.5rem; -} -p { - font-size: 1rem; - line-height: 1.5rem; - margin-top: 16px; - margin-bottom: 32px; -} -pre { - font-size: 0.8rem; - line-height: 1.25rem; - background: var(--background-color); - margin: 2rem auto; - border: 3px solid var(--background-color-1); -} -pre code { - text-shadow: none; - font-family: monospace; - display: block; - overflow-x: auto; - white-space: pre; - -webkit-overflow-scrolling: touch; -} -pre code a { - border-bottom: 1px dotted var(--text-color); -} -pre.snippet { - margin-bottom: 0; -} -a.source.github::before { - content: 'Source: '; - font-weight: bold; -} -a.source.github { - display: block; - margin-top: 4px; - margin-left: 16px; - margin-bottom: 32px; -} - -li { - margin-bottom: 16px; -} -p code, li code { - font-family: monospace; - font-weight: bold; -} -p code a, li code a { - font-weight: normal; -} -li p { - margin: 0; -} -p code a, li code a { - border-bottom: 1px dotted var(--text-color); -} -hr { - border: none; - border-bottom: 3px solid var(--link-color); - width: 64px; -} diff --git a/docs/css/footer.css b/docs/css/footer.css deleted file mode 100644 index e47fc23..0000000 --- a/docs/css/footer.css +++ /dev/null @@ -1,45 +0,0 @@ -#site-footer { - border-top: 3px solid var(--background-color-1); - margin-top: 32px; -} -#site-footer .grid { - padding: 16px; -} -#site-footer img#logo-image { - width: 256px; -} -#site-footer h4 { - margin-top: 8px; - margin-bottom: 0; - line-height: 1.5rem; -} -#site-footer p { - margin-top: 0; -} -#site-footer ul { - list-style-type: none; - padding-left: 0; -} -#site-footer ul li { - margin-bottom: 8px; -} -#site-footer ul li a { - color: var(--text-color) -} -#site-footer ul li a:hover { - color: var(--link-color-hover) -} -#site-footer section { - border-top: 3px solid var(--background-color-1); - padding-top: 16px; - margin-bottom: 16px; -} -#site-footer section small:last-child { - text-align: left; -} - -@media screen and (min-width: 600px) { - #site-footer section small:last-child { - text-align: right; - } -} diff --git a/docs/css/grid.css b/docs/css/grid.css deleted file mode 100644 index 6e85d68..0000000 --- a/docs/css/grid.css +++ /dev/null @@ -1,35 +0,0 @@ -.grid { - display: grid; - column-gap: 32px; - row-gap: 32px; - grid-column: span 1; - grid-template-columns: repeat(1, 1fr); -} -.grid > * { - overflow: hidden; -} -.grid.grid-2 { - grid-template-columns: repeat(2, 1fr); -} -.grid.grid-3 { - grid-template-columns: repeat(3, 1fr); -} -.grid.grid-4 { - grid-template-columns: repeat(4, 1fr); -} -@media screen and (min-width: 600px) { - .grid.grid-221, .grid.grid-321, .grid.grid-421 { - grid-template-columns: repeat(2, 1fr); - } -} -@media screen and (min-width: 900px) { - .grid.grid-211 { - grid-template-columns: repeat(2, 1fr); - } - .grid.grid-311, .grid.grid-321 { - grid-template-columns: repeat(3, 1fr); - } - .grid.grid-411,.grid.grid-421 { - grid-template-columns: repeat(4, 1fr); - } -} diff --git a/docs/css/modern-base.css b/docs/css/modern-base.css deleted file mode 100644 index 0a4d2c1..0000000 --- a/docs/css/modern-base.css +++ /dev/null @@ -1,140 +0,0 @@ -/*! modern-base | MIT License | https://github.com/sindresorhus/modern-normalize */ - - -/** - * Improve font rendering on HiDPI devices. - */ - - @media - screen and (-webkit-min-device-pixel-ratio: 2), - screen and (min-resolution: 2dppx) { - body { - -moz-osx-font-smoothing: grayscale; - -webkit-font-smoothing: antialiased; - } -} - -/** -* Remove default spacing. -*/ - -h1, -h2, -h3, -h4, -h5, -h6, -p, -figure, -blockquote, -dd, -dl, -pre { - margin: 0; -} - -/** -* Show link underline only on hover. -*/ - -a { - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - -/** -* Remove outline on active/hovered focused links. -*/ - -a:active, -a:hover { - outline-width: 0; -} - -/** -* Correct the text style of placeholders in Chrome and Safari. -* See discussion: https://github.com/necolas/normalize.css/issues/550 -*/ - -::-webkit-input-placeholder { - color: inherit; - opacity: 0.54; -} - -/** -* Remove text shadow from selections. -*/ - -::selection { - background: #b3d7fd; - text-shadow: none; -} - -/** -* Remove outline from `:focus` pseudo element. -* Note: Don't forget to apply some styling for the focus state of elements that support it. -*/ - -:focus { - outline-width: 0; -} - -/** -* Remove the list style on navigation lists. -*/ - -nav ol, -nav ul { - list-style: none; -} - -/** -* Better looking default horizontal rule -*/ - -hr { - display: block; - margin: 1em 0; - padding: 0; - border: 0; - border-top: 1px solid #ccc; -} - -/** -* Remove default fieldset styles. -*/ - -fieldset { - margin: 0; - padding: 0; - border: 0; -} - -/** -* Allow only vertical resizing of textareas. -*/ - -textarea { - resize: vertical; -} - -svg, -img, -canvas, -video, -audio, -iframe { - /** - * Remove the gap between these elements and the bottom of their containers. - * See: https://github.com/h5bp/html5-boilerplate/issues/440 - */ - vertical-align: middle; - - /** - * Prevent overflow. - */ - max-width: 100%; -} \ No newline at end of file diff --git a/docs/css/modern-normalize.css b/docs/css/modern-normalize.css deleted file mode 100644 index 64a56d1..0000000 --- a/docs/css/modern-normalize.css +++ /dev/null @@ -1,274 +0,0 @@ -/*! modern-normalize v2.0.0 | MIT License | https://github.com/sindresorhus/modern-normalize */ - -/* -Document -======== -*/ - -/** -Use a better box model (opinionated). -*/ - -*, -::before, -::after { - box-sizing: border-box; -} - -html { - /* Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3) */ - font-family: - system-ui, - 'Segoe UI', - Roboto, - Helvetica, - Arial, - sans-serif, - 'Apple Color Emoji', - 'Segoe UI Emoji'; - line-height: 1.15; /* 1. Correct the line height in all browsers. */ - -webkit-text-size-adjust: 100%; /* 2. Prevent adjustments of font size after orientation changes in iOS. */ - -moz-tab-size: 4; /* 3. Use a more readable tab size (opinionated). */ - tab-size: 4; /* 3 */ -} - -/* -Sections -======== -*/ - -body { - margin: 0; /* Remove the margin in all browsers. */ -} - -/* -Grouping content -================ -*/ - -/** -1. Add the correct height in Firefox. -2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) -*/ - -hr { - height: 0; /* 1 */ - color: inherit; /* 2 */ -} - -/* -Text-level semantics -==================== -*/ - -/** -Add the correct text decoration in Chrome, Edge, and Safari. -*/ - -abbr[title] { - text-decoration: underline dotted; -} - -/** -Add the correct font weight in Edge and Safari. -*/ - -b, -strong { - font-weight: bolder; -} - -/** -1. Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3) -2. Correct the odd 'em' font sizing in all browsers. -*/ - -code, -kbd, -samp, -pre { - font-family: - ui-monospace, - SFMono-Regular, - Consolas, - 'Liberation Mono', - Menlo, - monospace; /* 1 */ - font-size: 1em; /* 2 */ -} - -/** -Add the correct font size in all browsers. -*/ - -small { - font-size: 80%; -} - -/** -Prevent 'sub' and 'sup' elements from affecting the line height in all browsers. -*/ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* -Tabular data -============ -*/ - -/** -1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) -2. Correct table border color inheritance in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) -*/ - -table { - text-indent: 0; /* 1 */ - border-color: inherit; /* 2 */ -} - -/* -Forms -===== -*/ - -/** -1. Change the font styles in all browsers. -2. Remove the margin in Firefox and Safari. -*/ - -button, -input, -optgroup, -select, -textarea { - font-family: inherit; /* 1 */ - font-size: 100%; /* 1 */ - line-height: 1.15; /* 1 */ - margin: 0; /* 2 */ -} - -/** -Remove the inheritance of text transform in Edge and Firefox. -*/ - -button, -select { - text-transform: none; -} - -/** -Correct the inability to style clickable types in iOS and Safari. -*/ - -button, -[type='button'], -[type='reset'], -[type='submit'] { - -webkit-appearance: button; -} - -/** -Remove the inner border and padding in Firefox. -*/ - -::-moz-focus-inner { - border-style: none; - padding: 0; -} - -/** -Restore the focus styles unset by the previous rule. -*/ - -:-moz-focusring { - outline: 1px dotted ButtonText; -} - -/** -Remove the additional ':invalid' styles in Firefox. -See: https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737 -*/ - -:-moz-ui-invalid { - box-shadow: none; -} - -/** -Remove the padding so developers are not caught out when they zero out 'fieldset' elements in all browsers. -*/ - -legend { - padding: 0; -} - -/** -Add the correct vertical alignment in Chrome and Firefox. -*/ - -progress { - vertical-align: baseline; -} - -/** -Correct the cursor style of increment and decrement buttons in Safari. -*/ - -::-webkit-inner-spin-button, -::-webkit-outer-spin-button { - height: auto; -} - -/** -1. Correct the odd appearance in Chrome and Safari. -2. Correct the outline style in Safari. -*/ - -[type='search'] { - -webkit-appearance: textfield; /* 1 */ - outline-offset: -2px; /* 2 */ -} - -/** -Remove the inner padding in Chrome and Safari on macOS. -*/ - -::-webkit-search-decoration { - -webkit-appearance: none; -} - -/** -1. Correct the inability to style clickable types in iOS and Safari. -2. Change font properties to 'inherit' in Safari. -*/ - -::-webkit-file-upload-button { - -webkit-appearance: button; /* 1 */ - font: inherit; /* 2 */ -} - -/* -Interactive -=========== -*/ - -/* -Add the correct display in Chrome and Safari. -*/ - -summary { - display: list-item; -} \ No newline at end of file diff --git a/docs/css/navigation.css b/docs/css/navigation.css deleted file mode 100644 index 6150866..0000000 --- a/docs/css/navigation.css +++ /dev/null @@ -1,90 +0,0 @@ -#navigation { - margin-top: 16px; - overflow: hidden; - border-bottom: 3px solid var(--background-color-1); -} -#navigation > a { - display: block; - line-height: 0; - padding: 8px; - text-align: center; -} -#navigation > a img { - height: 44px; - width: auto; -} -#navigation nav svg { - width: 24px; - height: 24px; -} -#navigation #primary-menu-button { - display: none; -} -#navigation #primary-menu-button + label { - position: absolute; - top: 22px; - left: 0; - margin: 0; - line-height: 0; - padding: 12px; -} -#navigation #primary-menu-button + label:hover { - cursor: pointer; -} -#navigation .menu-items { - max-height: 0px; - transition: max-height .3s ease-in-out 0s; - margin-top: 6px; -} -#navigation .menu-items a { - display: block; - padding: 16px; - border-top: 0.5px solid var(--background-color-1); - color: var(--text-color); -} -#navigation .menu-items a:hover { - color: var(--link-color-hover) -} -#navigation input:checked ~ .menu-items { - max-height: 999px; -} -#navigation line { - transition: transform .3s ease-in-out 0s; - transform-origin: 50% 50%; -} -#navigation input:checked ~ label line:nth-child(1) { - transform: translateX(-50px); -} -#navigation input:checked ~ label line:nth-child(2) { - transform: rotate(45deg) translateY(6px); -} -#navigation input:checked ~ label line:nth-child(3) { - transform: rotate(-45deg) translateY(-6px); -} -@media screen and (min-width: 600px) { - #navigation { - display: grid; - grid-template-columns: auto 1fr; - } - #navigation > a { - grid-column: span 1; - } - #navigation nav { - overflow: hidden; - grid-column: span 1; - } - #primary-menu-button ~ label { - display: none; - } - #navigation nav .menu-items { - display: block; - max-height: none; - text-align: right; - padding: 8px; - } - #navigation nav .menu-items a { - padding: 8px; - display: inline-block; - border: none; - } -} diff --git a/docs/css/style.css b/docs/css/style.css deleted file mode 100644 index dee8dce..0000000 --- a/docs/css/style.css +++ /dev/null @@ -1,271 +0,0 @@ -::selection { - background: var(--selection-color); -} - -a:hover { - text-decoration: none; -} -h1 a, h2 a, h3 a, h4 a, h5 a, h6 a, -h1 a:link, h2 a:link, h3 a:link, h4 a:link, h5 a:link, h6 a:link, -h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited { - color: var(--text-color); -} -h1 a:hover, h2 a:hover, h3 a:hover, h4 a:hover, h5 a:hover, h6 a:hover { - color: var(--link-color-hover); -} -#site-container { - max-width: 960px; - margin: 0 auto; -} -#page-container { - margin: 16px 0; - padding: 16px; -} - -img.rounded { - border-radius: 50%; -} -img.small { - width: 32px; -} -img.medium { - width: 64px; -} -img.large { - width: 128px; -} - -.grid { - margin-top: 32px; -} - -.meta { - font-size: 14px; - text-transform: uppercase; - color: var(--text-color-2); -} - -.card { - position: relative; -/* border: 1px solid red;*/ -} -.card h2 { - margin-top: 0; - margin-bottom: 16px; -} -.card { - border: 3px solid var(--background-color-1); - padding: 0 16px; -} -.card .featured { - position: absolute; - top: 16px; - right: 16px; - padding: 8px 16px; - background: var(--background-color-1); - color: var(--link-color); - text-transform: uppercase; - font-size: 12px; - font-weight: bold; -} -.card a { - color: var(--text-color); -} -.card a:hover { - color: var(--link-color-hover); -} - - -.card.author, .card.tag { - padding: 32px; - text-align: center; -} -.card.author img, .card.tag img { - margin-bottom: 16px; -} -.card p, .card.author p, .card.tag p { - margin-bottom: 0; -} - -.author-list, .tag-list { - margin: 16px 0; -} -.author-list .name { - font-size: 16px; - margin-left: 4px; - line-height: 1rem; -} - -.tag-list a { - display: inline-block; - padding: 4px 12px; - margin: 4px; - margin-left: 0; - text-transform: uppercase; - font-weight: 600; - line-height: 1rem; - color: var(--text-color-2); - background: var(--background-color-1); - -} -.tag-list a:hover { - color: var(--white-color); - background: var(--link-color-hover); -} - - -.article-with-toc { - position: relative; - display: block; -} -.article-with-toc > div { - position: relative; -} -.article-with-toc article { - max-width: 600px; - margin: 0 auto; -} -.article-with-toc aside#toc ul { - margin-top: 0; - padding: 0; - list-style-type: none; - font-size: 14px; -} -.article-with-toc aside#toc ul ul { - margin-left: 16px; - margin-bottom: 0; -} -.article-with-toc li.level-3 { - margin-left: 16px; -} -.article-with-toc aside#toc ul, .article-with-toc aside#toc ul li { - margin-bottom: 0; -} -.article-with-toc aside#toc ul li.first-level { - margin-top: 8px; -} -.article-with-toc aside#toc ul li.first-level:first-child { - margin-top: 0; -} -.book { - text-align: center; - border: 3px solid var(--link-color); - margin: 32px 0; - padding: 32px; -} -.book img { - max-height: 400px; -} -.cta { - padding: 8px 16px; - color: var(--white-color) !important; - background: var(--link-color); -} -.cta:hover { - background: var(--link-color-hover); -} - -@media screen and (min-width: 900px) { - .book { - margin-top: 0; - } - .article-with-toc { - display: grid; - grid-column-gap: 32px; - grid-template-columns: 5fr 3fr; - } - - .article-with-toc > div > aside { - position: -webkit-sticky; - position: sticky; - top: 16px; - background: var(--background-color-1); - padding: 16px 32px; - font-weight: bold; - line-height: 1.5rem; - } - .article-with-toc > div > aside h4 { - margin-top: 0px; - display: none; - } - .article-with-toc > div > aside li a { - color: var(--text-color-2); - } - .article-with-toc > div > aside li a:hover { - color: var(--text-color-1); - } -} - - -.post span.featured { - color: var(--link-color); - text-transform: uppercase; - font-weight: bold; -} - - - - - - - -.pagination { - margin-top: 64px; - display: block; - text-align: center; -} -.pagination a { - font-weight: bold; - padding: 8px 16px; - border: 3px solid var(--background-color-1); -} -.pagination a.current-page { - border: 3px solid var(--link-color-hover); -} - - - -.tooltips { - position: fixed; - z-index: 2; -} -.tooltips div { - position: fixed; - display: block; - pointer-events: none; - - opacity: 0; - margin-top: 0; - margin-left: -1rem; - - background-color: var(--background-color-1); - border: 3px solid var(--background-color-2); - - padding: 8px; - margin-top: 0.25rem; - width: 30rem; -} - -.tooltips div pre { - margin: 0; - padding: 8px; -} - -.tooltips div p { - line-height: 1.4em; - margin: 1rem 0.5rem 0 0.5rem; -} - -.tooltips .visible { - opacity: 1; - z-index: 3; -} - -section.group { - margin-top: 64px; -} -section.group h4 { - color: var(--text-color-3); - text-transform: uppercase; - border-bottom: 3px solid var(--background-color-1); -} diff --git a/docs/css/variables.css b/docs/css/variables.css deleted file mode 100644 index 91083bc..0000000 --- a/docs/css/variables.css +++ /dev/null @@ -1,70 +0,0 @@ -:root { - --link-color: #de2451; - --link-color-visited: #dd0398; - --link-color-hover: #781a31; - --link-color-active: #ff2451; - - --background-color: #fff; - --background-color-1: #f5f5f5; - --background-color-2: #efefef; - --background-color-3: #e0e0e0; - --background-color-4: #afafaf; - - --text-color: #333; - --text-color-1: #000; - --text-color-2: #666; - --text-color-3: #999; - - --white-color: #ffffff; - --black-color: #000000; - --blue-color: #006fff; - --green-color: #2ec04f; - --indigo-color: #4e4cd0; - --orange-color: #ff8a00; - --pink-color: #ff284b; - --purple-color: #a648d9; - --red-color: #ff342a; - --teal-color: #50c1f9; - --yellow-color: #ffc500; - --brown-color: #987953; - --gray-color: #838388; - - --selection-color: #e0e0e0; -} -@media (prefers-color-scheme: dark) { - :root { - --link-color: #ff2451; - --link-color-visited: #dd0398; - --link-color-hover: #de2451; - --link-color-active: #ff2451; - - - - --text-color: #c9c9c9; - --text-color-1: #f0f0f0; - --text-color-2: #999; - --text-color-3: #ccc; - - --background-color: #000; - --background-color-1: #1a1a1a; - --background-color-2: #262626; - --background-color-3: #333333; - --background-color-4: #606060; - - --white-color: #ffffff; - --black-color: #000000; - --blue-color: #0d79ff; - --green-color: #2acb4e; - --indigo-color: #5352e2; - --orange-color: #ff950d; - --pink-color: #ff3054; - --purple-color: #b750f0; - --red-color: #ff3c33; - --teal-color: #59ccff; - --yellow-color: #ffd00d; - --brown-color: #a2835d; - --gray-color: #8d8d92; - - --selection-color: #333333; - } -} diff --git a/docs/custom-uiview-subclass-from-a-xib-file/index.html b/docs/custom-uiview-subclass-from-a-xib-file/index.html deleted file mode 100644 index 0a609d8..0000000 --- a/docs/custom-uiview-subclass-from-a-xib-file/index.html +++ /dev/null @@ -1,421 +0,0 @@ - - - - - - - - - - - - Custom UIView subclass from a xib file - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

-
- -

I already have a comprehensive guide about initializing views and controllers, but that one lacks a very special case: creating a custom view using interface builder. 🤷‍♂️

Loading xib files

Using the contents of a xib file is a pretty damn easy task to do. You can use the following two methods to load the contents (aka. the view hierarchy) of the file.

let view = UINib(
-    nibName: "CustomView", 
-    bundle: .main
-).instantiate(
-    withOwner: nil, 
-    options: nil
-).first as! UIView
-
-// does the same as above
-// let view = Bundle.main.loadNibNamed(
-//    "CustomView", 
-//    owner: nil, 
-//    options: nil
-// )!.first as! UIView 
-
-view.frame = self.view.bounds
-self.view.addSubview(view)
-

The snippet above will simply instantiate a view object from the xib file. You can have multiple root objects in the view hierarchy, but this time let’s just pick the first one and use that. I assume that in 99% of the cases this is what you’ll need in order to get your custom designed views. Also you can extend the UIView object with any of the solutions above to create a generic view loader. More on that later… 😊

This method is pretty simple and cheap, however there is one little drawback. You can’t get named pointers (outlets) for the views, but only for the root object. If you are putting design elements into your screen, that’s fine, but if you need to display dynamic data, you might want to reach out for the underlying views as well. 😃

Custom views with outlets & actions

So the proper way to load custom views from xib files goes something like this:

Inside your custom view object, you instantiate the xib file exactly the same way as I told you right up here. 👆 The only difference is that you don’t need to use the object array returned by the methods, but you have to connect your view objects through the interface builder, using the File’s Owner as a reference point, plus a custom container view outlet, that’ll contain everything you need. 🤨

// note: view object is from my previous tutorial, with autoresizing masks disabled
-class CustomView: View {
-
-    // this is going to be our container object
-    @IBOutlet weak var containerView: UIView!
-
-    // other usual outlets
-    @IBOutlet weak var textLabel: UILabel!
-
-    override func initialize() {
-        super.initialize()
-
-        // first: load the view hierarchy to get proper outlets
-        let name = String(describing: type(of: self))
-        let nib = UINib(nibName: name, bundle: .main)
-        nib.instantiate(withOwner: self, options: nil)
-
-        // next: append the container to our view
-        self.addSubview(self.containerView)
-        self.containerView.translatesAutoresizingMaskIntoConstraints = false
-        NSLayoutConstraint.activate([
-            self.containerView.topAnchor.constraint(equalTo: self.topAnchor),
-            self.containerView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
-            self.containerView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
-            self.containerView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
-        ])
-    }
-}
-

So the initialize method here is just loading the nib file with the owner of self. After the loading process finished, your outlet pointers are going to be filled with proper values from the xib file. There is one last thing that we need to do. Even the views from the xib file are “programmatically” connected to our custom view object, but visually they aren’t. So we have to add our container view into the view hierarchy. 🤐

IB

If you want to use your custom view object, you just have to create a new instance from it - inside a view controller - and finally feel free to add it as a subview!

One word about bounds, frames aka. springs and struts: fucking UGLY! That’s two words. They are considered as a bad practice, so please use auto layout, I have a nice tutorial about anchors, they are amazing and learning them takes about 15 minutes. 😅

class ViewController: UIViewController {
-
-    weak var customView: CustomView!
-
-    override func loadView() {
-        super.loadView()
-
-        let customView = CustomView()
-        self.view.addSubview(customView)
-        NSLayoutConstraint.activate([
-            customView.topAnchor.constraint(equalTo: self.view.topAnchor),
-            customView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
-            customView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
-            customView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
-        ])
-        self.customView = customView
-    }
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        self.customView.textLabel.text = "Lorem ipsum"
-    }
-}
-

That’s it, now you have a completely working custom UIView object that loads a xib file in order to use it’s contents. Wasn’t so bad, right? 🤪

One more extra thing. If you don’t like to handle views programmatically or you simply don’t want to mess around with the loadView method, just remove it entirely. Next put the @IBOutlet keyword right before your custom view class variable. Open your storyboard using IB, then drag & drop a new UIView element to your controller and connect the custom view outlet. It should work like magic. 💫

Storyboard

I promised outlets and actions in the heading of this section, so let’s talk a little bit about IBActions. They work exactly the same as you’d expect them with controllers. You can simply hook-up a button to your custom view and delegate the action to the custom view class. If you want to forward touches or specific actions to a controller, you should use the delegate pattern or go with a simple block. 😎

Ownership and container views

It is possible to leave out all the xib loading mechanism from the view instance. We can create a set of extensions in order to have a nice view loader with a custom view class from a xib file. This way you don’t need a container view anymore, also the owner of the file can be left out from the game, it’s more or less the same method as reusable cells for tables and collections created by Apple. 🍎

You should know that going this way you can’t use your default UIView init methods programmatically anymore, because the xib file will take care of the init process. Also if you are trying to use this kind of custom views from a storyboard or xib file, you won’t be able to use your outlets, because the correspondig xib of the view class won’t be loaded. Otherwise if you are trying to load it manyally you’ll run into an infinite loop and eventually your app will crash like hell. 😈

import UIKit
-
-extension UINib {
-    func instantiate() -> Any? {
-        return self.instantiate(withOwner: nil, options: nil).first
-    }
-}
-
-extension UIView {
-
-    static var nib: UINib {
-        return UINib(nibName: String(describing: self), bundle: nil)
-    }
-
-    static func instantiate(autolayout: Bool = true) -> Self {
-        // generic helper function
-        func instantiateUsingNib<T: UIView>(autolayout: Bool) -> T {
-            let view = self.nib.instantiate() as! T
-            view.translatesAutoresizingMaskIntoConstraints = !autolayout
-            return view
-        }
-        return instantiateUsingNib(autolayout: autolayout)
-    }
-}
-
-class CustomView: UIView {
-
-    @IBOutlet weak var textLabel: UILabel!
-}
-
-// usage (inside a view controller for example)
-// let view = CustomView.instantiate()
-

Just like with table or collection view cells this time you have to set your custom view class on the view object, instead of the File’s Owner. You have to connect your outlets and basically you’re done with everything. 🤞

ownership

From now on you should ALWAYS use the instantiate method on your custom view object. The good news is that the function is generic, returns the proper instance type and it’s highly reusable. Oh, btw. I already mentioned the bad news… 🤪

There is also one more technique by overriding awakeAfter, but I would not rely on that solution anymore. In most of the cases you can simply set the File’s Owner to your custom view, and go with a container, that’s a safe bet. If you have special needs you might need the second approach, but please be careful with that. 😉

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/custom-views-input-forms-and-mistakes/index.html b/docs/custom-views-input-forms-and-mistakes/index.html deleted file mode 100644 index 56b827e..0000000 --- a/docs/custom-views-input-forms-and-mistakes/index.html +++ /dev/null @@ -1,420 +0,0 @@ - - - - - - - - - - - - Custom views, input forms and mistakes - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 4 min read - -
-

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

-
- -

How NOT to build forms for iOS apps?

Let’s start with an honest statement: I messed up with this tutorial (a lot):

Building input forms for iOS apps

The thing is that this form building methodology only works if the cells are always visible on screen, which is quite a rare case. I discovered this issue while I was working on my current project and some fields were constantly disappearing and moving the cursor to the next input field stopped working when the cell was out of frame.

Reusability & memory efficiency is not always what you want.

Seems like UICollectionView is not the best solution for making input forms, because the constant cell reusability will mess up some of the expected behavior. It’s still good for lists with “a thousand elements”, but for an input form I would not recommend this technique anymore. Yep, my mistake, sorry about it… 😬

Learning by making mistakes

Long story short, I made a mistake and probably you’ll also make a lot during your developer career. Does this make you a bad programmer? Not at all. We’re human, we’re constantly making smaller or bigger mistakes, but…

(Remain and) turn it into strength

Your mistakes will always stay with you, but you can learn from them a lot. The problem only starts if you keep doing the same mistakes again and again, or you don’t even realize that you’re doing something wrong. It’s really hard to take one step back and see the problem from a bigger perspective. Sometimes you simply need someone else to point out the issue for you, but negative feedback can also be painful.

Anyway, I don’t want to be too much philosophical, this is a Swift developer blog ffs.

A few things that I learned:

  • my ideas are not always working, so don’t trust me 100% (haha) 🤣
  • it’s always better to code/work in pair with someone else
  • sometimes the “padawan” will teach the “master” 😉
  • a professional qa team can save you a lot of time
  • VIPER is my architectural “silver bullet”, not collection views
  • UICollectionView based form building is not working…
  • …but the collection view framework still rocks for complex interfaces
  • have some dedicated time for code cosmetics & refactor
  • use view subclasses programmatically (or SwiftUI in the future)

So the last point is the most interesting one, let me explain why.

Custom view subclasses from code only

Creating a UIView subclass programmatically is a relatively easy task. You can load a nib file or you can do it straight from code. A few weeks ago I’ve learned a new trick, that was bugging me all the time I made a new subclass in Swift:

Why the hell do I have to implement init(coder:) if I’m not using IB at all?

Also what the heck is going on with init(frame:), I don’t want to deal with these two init methods anymore, since I’m using auto layout and I’m completely trying to ignore interface builder with the messed up storyboards and nibs as well.

class View: UIView {
-
-    @available(*, unavailable)
-    override init(frame: CGRect) {
-        super.init(frame: frame)
-
-        self.initialize()
-    }
-
-    @available(*, unavailable)
-    required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-
-        self.initialize()
-    }
-
-    init() {
-        super.init(frame: .zero)
-
-        self.initialize()
-    }
-
-    func initialize() {
-        self.translatesAutoresizingMaskIntoConstraints = false
-    }
-}
-

The solution: mark these stupid init functions as unavailable, so no-one can use them anymore. The only source of truth will be your own init method, which is quite a relief if you were so annoyed about the messed up initialization process like I was. 😤

Now you have your own base class that you can use as a parent for your future views. Of course you’ll need to do the same thing for almost every UI element, like labels, buttons, text fields, etc. That’s a lot of work, but on a long term it’s totally worth it.

import UIKit
-
-class TitleLabel: Label {
-
-    override func initialize() {
-        super.initialize()
-
-        self.textAlignment = .center
-        self.font = UIFont.preferredFont(forTextStyle: .largeTitle)
-        self.textColor = .systemBlue
-    }
-
-    func constraints(in view: UIView, padding: CGFloat = 8) -> [NSLayoutConstraint] {
-        [
-            self.topAnchor.constraint(equalTo: view.topAnchor, constant: padding),
-            self.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: padding),
-            self.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -1 * padding),
-        ]
-    }
-}
-

A good practice can be to have subclass for each and every custom user interface component, like the primary button, secondary button, title label, header label, etc. This way you don’t have to configure your views in the view controller, plus you can put your frequently used constraints into the subclass using some helper methods.

Also you can have some nice extensions, those can help you with view configurations. You know, just like modifiers in SwiftUI. You can even recreate the exact same syntax. The underlying behavior won’t be the same, but that’s another story. 📚

What about the form new builder in iOS?

Oh, yeah almost forgot. I have a brand new, but still very similar solution. I’m using view subclasses instead of collection view components, plus the collection view have been replaced with a UIScrollView + UIStackView combination. 🐐

class ViewController: UIViewController {
-
-    weak var scrollView: ScrollView!
-    weak var stackView: VerticalStackView!
-
-    override func loadView() {
-        super.loadView()
-
-        let scrollView = ScrollView()
-        self.view.addSubview(scrollView)
-        self.scrollView = scrollView
-        NSLayoutConstraint.activate([/*...*/])
-
-        let stackView = VerticalStackView()
-        self.scrollView.addSubview(stackView)
-        self.stackView = stackView
-        NSLayoutConstraint.activate([/*...*/])
-    }
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        self.title = "StackForm"
-        self.navigationController?.navigationBar.prefersLargeTitles = true
-
-        let email = EmailTextField(id: "email-input", placeholder: "Email")
-        self.stackView.addArrangedSubview(email)
-
-        let password = PasswordTextField(id: "password-input", placeholder: "Password")
-        self.stackView.addArrangedSubview(password)
-
-        let submit = SubmitButton(id: "submit-button", title: "Submit")
-        .onTouch { [weak self] _ in self?.submit() }
-        self.stackView.addArrangedSubview(submit)
-    }
-
-    func submit() {
-        guard
-            let email = (self.view.view(withId: "email-input") as? UITextField)?.text,
-            let password = (self.view.view(withId: "password-input") as? UITextField)?.text
-        else {
-            return
-        }
-        print("Account: \(email) - \(password)")
-    }
-}
-

As you can see I’m still using the same view identification technique, plus I still prefer to have the SwiftUI-like .onTouch action handlers. You might ask though:

Why don’t you simply go with SwiftUI?

Well, the thing is that SwiftUI is iOS 13 only, which is only around ~55% adoption nowadays, that’s one of the main reasons, but also SwiftUI is kind of incomplete.

I’m trying to get as close as I can to SwiftUI, so the transition will be less pain in the ass when the time comes. SwiftUI will be amazing, but still it’s a giant leap forward. Sometimes I believe that Apple is rushing things just because of marketing / developer needs (yeah, we are very impatient animals). Maybe a simple wrapper framework around UIKit / AppKit without the whole declarative syntax would have been a better idea as a first step… who knows… CoreKit -> AppleKit? 🤔

Anyway, you can download a working example of my latest form building solution in Swift 5 from GitHub. Just look for the StackForm folder inside the repository.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/custom-working-directory-in-xcode/index.html b/docs/custom-working-directory-in-xcode/index.html deleted file mode 100644 index c0fb7e7..0000000 --- a/docs/custom-working-directory-in-xcode/index.html +++ /dev/null @@ -1,319 +0,0 @@ - - - - - - - - - - - - Custom working directory in Xcode - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 2 min read - -
-

Custom working directory in Xcode

-
-

Learn how to set a custom working directory in Xcode to solve one of the most common beginner issue when using Vapor.

-
- -

What is a custom working directory?

When you try to build and run your Vapor application using Xcode you might face the issue that there are some missing files, resources or Leaf templates. Don’t worry this is a very common rookie mistake, but what causes this problem exactly? 🤔

Vapor is using a place called working directory to set the current environment, locate common resources and publicly available files. This working directory usually contains a Resources folder where you can put your Leaf templates and a Public folder which is used by the FileMiddleware. The server is also trying to search for possible dotenv files to configure environmental variables.

If you run your backend application without explicitly setting a custom working directory, you should see a warning message in Xcode’s console. If you are using Feather CMS, the app will crash without a custom working directory set, because it is required to provide a working environment. 🙃

No custom working directory

If you don’t specify this custom work dir, Xcode will try to look for the resources under a random, but uniquely created place somewhere under the DerivedData directory.

This is the internal build folder for the IDE, it usually creates lots of other “garbage” files into the ~/Library/Developer/Xcode/DerivedData directory. In 99% of the cases you can safely delete its contents if you want to perform a 100% clean build. 👍

How to set a custom working directory?

First of all, open your project in Xcode by double clicking the Package.swift manifest file.

Do NOT use the swift package generate-xcodeproj command to generate a project file!!! This is a deprecated Swift Package Manager command, and it’s going to be removed soon.

✅ I repeat: always open SPM projects through the Package.swift file.

Target

Wait until the IDE loads the required Swift packages. After the dependencies are loaded, click on the target next to the stop button. The executable target is marked with a little terminal-like icon. 💡

Edit scheme

Select the “Edit Scheme…” option from the available menu items, this should open a new modal window on top of Xcode.

Custom working directory

Make sure that the Run configuration is selected on the left side of the pane. Click on the “Options” tab, and then look for the “Working directory” settings. Check the “Use custom working directory:” toggle, this will enable the input field underneath, then finally click on the little folder icon on the top right side (of the input field) and look for your desired directory using the interface. 🔍

Press the “Choose” button when you are ready. You should see the path of your choice written inside the text field. Make sure that you’ve selected the right location. Now you can click the “Close” button on the bottom right corner, then you can try to start your server by clicking the run button (play icon or you can press the CMD+R shortcut to run the app). ▶️

If you did everything right, your Vapor server application should use the custom working directory, you can confirm this by checking the logs in Xcode. The previously mentioned warning should disappear and your backend should be able to load all the necessary resources without further issues. I hope this little guide will help you to avoid this common mistake when using Vapor. 🙏

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Awesome native Xcode extensions

-
-

This is the biggest and the best collection of the currently available natively created source editor extensions for Xcode.

- -
- Xcode -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Conventions for Xcode

-
-

Learn how to organize your codebase. If you are struggling with Xcode project structure, files, naming conventions, read this.

- -
- Tooling - Xcode -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Custom working directory in Xcode

-
-

Learn how to set a custom working directory in Xcode to solve one of the most common beginner issue when using Vapor.

- -
- Tooling - Xcode -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

How to launch a macOS app at login?

-
-

In this tutorial I'll show you how to launch a completely sandboxed macOS application on system startup written in Swift.

- -
- Tooling - macOS -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/declarative-unit-tests-for-vapor/index.html b/docs/declarative-unit-tests-for-vapor/index.html deleted file mode 100644 index 53770cd..0000000 --- a/docs/declarative-unit-tests-for-vapor/index.html +++ /dev/null @@ -1,669 +0,0 @@ - - - - - - - - - - - - Declarative unit tests for Vapor - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 8 min read - -
-

Declarative unit tests for Vapor

-
-

Learn how to test your server side Swift backend app in a declarative style using a lightweight library called Spec.

-
- -

Writing tests using XCTVapor

In my previous article I showed you how to build a type safe RESTful API using Vapor. This time we’re going to extend that project a bit and write some tests using the Vapor testing tool to discover the underlying issues in the API layer. First we’re going to use XCTVapor library, then we migrate to a lightweight declarative testing framework (Spec) built on top of that.

Before we start testing our application, we have to make sure that if the app runs in testing mode we register an inMemory database instead of our local SQLite file. We can simply alter the configuration and check the environment and set the db driver based on it.

import Vapor
-import Fluent
-import FluentSQLiteDriver
-
-public func configure(_ app: Application) throws {
-
-    if app.environment == .testing {
-        app.databases.use(.sqlite(.memory), as: .sqlite, isDefault: true)
-    }
-    else {
-        app.databases.use(.sqlite(.file("Resources/db.sqlite")), as: .sqlite)
-    }
-
-    app.migrations.add(TodoMigration())
-    try app.autoMigrate().wait()
-
-    try TodoRouter().boot(routes: app.routes)
-}
-

Now we’re ready to create our very first unit test using the XCTVapor testing framework. The official docs are short, but quite useful to learn about the basics of testing Vapor endpoints. Unfortunately it won’t tell you much about testing websites or complex API calls. ✅

We’re going to make a simple test that checks the return type for our Todo list endpoint.

@testable import App
-import TodoApi
-import Fluent
-import XCTVapor
-
-final class AppTests: XCTestCase {
-
-    func testTodoList() throws {
-        let app = Application(.testing)
-        defer { app.shutdown() }
-        try configure(app)
-
-        try app.test(.GET, "/todos/", afterResponse: { res in
-            XCTAssertEqual(res.status, .ok)
-            XCTAssertEqual(res.headers.contentType, .json)
-            _ = try res.content.decode(Page<TodoListObject>.self)
-        })
-    }
-}
-

As you can see first we setup & configure our application, then we send a GET request to the /todos/ endpoint. After we have a response we can check the status code, the content type and we can try to decode the response body as a valid paginated todo list item object.

This test case was pretty simple, now let’s write a new unit test for the todo item creation.

@testable import App
-import TodoApi
-import Fluent
-import XCTVapor
-
-final class AppTests: XCTestCase {
-
-    //...
-    
-    func testCreateTodo() throws {
-        let app = Application(.testing)
-        defer { app.shutdown() }
-        try configure(app)
-
-        let title = "Write a todo tutorial"
-        
-        try app.test(.POST, "/todos/", beforeRequest: { req in
-            let input = TodoCreateObject(title: title)
-            try req.content.encode(input)
-        }, afterResponse: { res in
-            XCTAssertEqual(res.status, .created)
-            let todo = try res.content.decode(TodoGetObject.self)
-            XCTAssertEqual(todo.title, title)
-            XCTAssertEqual(todo.completed, false)
-            XCTAssertEqual(todo.order, nil)
-        })
-    }
-}
-

This time we’d like to submit a new TodoCreateObject as a POST data, fortunately XCTVapor can help us with the beforeRequest block. We can simply encode the input object as a content, then in the response handler we can check the HTTP status code (it should be created) decode the expected response object (TodoGetObject) and validate the field values.

I also updated the TodoCreateObject, since it does not make too much sense to have an optional Bool field and we can use a default nil value for the custom order. 🤓

public struct TodoCreateObject: Codable {
-    
-    public let title: String
-    public let completed: Bool
-    public let order: Int?
-    
-    public init(title: String, completed: Bool = false, order: Int? = nil) {
-        self.title = title
-        self.completed = completed
-        self.order = order
-    }
-}
-

The test will still fail, because we’re returning an .ok status instead of a .created value. We can easily fix this in the create method of the TodoController Swift file.

import Vapor
-import Fluent
-import TodoApi
-
-struct TodoController {
-
-    // ...
-
-    func create(req: Request) throws -> EventLoopFuture<Response> {
-        let input = try req.content.decode(TodoCreateObject.self)
-        let todo = TodoModel()
-        todo.create(input)
-        return todo
-            .create(on: req.db)
-            .map { todo.mapGet() }
-            .encodeResponse(status: .created, for: req)
-    }
-    
-    // ...
-}
-

Now we should try to create an invalid todo item and see what happens…

func testCreateInvalidTodo() throws {
-    let app = Application(.testing)
-    defer { app.shutdown() }
-    try configure(app)
-
-    /// title shouldn't be empty
-    let title = ""
-    
-    try app.test(.POST, "/todos/", beforeRequest: { req in
-        let input = TodoCreateObject(title: title)
-        try req.content.encode(input)
-    }, afterResponse: { res in
-        XCTAssertEqual(res.status, .created)
-        let todo = try res.content.decode(TodoGetObject.self)
-        XCTAssertEqual(todo.title, title)
-        XCTAssertEqual(todo.completed, false)
-        XCTAssertEqual(todo.order, nil)
-    })
-}
-

Well, this is bad, we shouldn’t be able to create a todo item without a title. We could use the built-in validation API to check user input, but honestly speaking that’s not the best approach.

My issue with validation is that first of all you can’t return custom error messages and the other main reason is that validation in Vapor is not async by default. Eventually you’ll face a situation when you need to validate an object based on a db call, then you can’t fit that part of the object validation process into other non-async field validation. IMHO, this should be unified. 🥲

Fort the sake of simplicity we’re going to start with a custom validation method, this time without any async logic involved, later on I’ll show you how to build a generic validation & error reporting mechanism for your JSON-based RESTful API.

import Vapor
-import TodoApi
-
-extension TodoModel {
-    
-    // ...
-    
-    func create(_ input: TodoCreateObject) {
-        title = input.title
-        completed = input.completed
-        order = input.order
-    }
-
-    static func validateCreate(_ input: TodoCreateObject) throws {
-        guard !input.title.isEmpty else {
-            throw Abort(.badRequest, reason: "Title is required")
-        }
-    }
-}
-

In the create controller we can simply call the throwing validateCreate function, if something goes wrong the Abort error will be returned as a response. It is also possible to use an async method (return with an EventLoopFuture) then await (flatMap) the call and return our newly created todo if everything was fine.

func create(req: Request) throws -> EventLoopFuture<Response> {
-    let input = try req.content.decode(TodoCreateObject.self)
-    try TodoModel.validateCreate(input)
-    let todo = TodoModel()
-    todo.create(input)
-    return todo
-        .create(on: req.db)
-        .map { todo.mapGet() }
-        .encodeResponse(status: .created, for: req)
-}
-

The last thing that we have to do is to update our test case and check for an error response.

// ...
-
-struct ErrorResponse: Content {
-    let error: Bool
-    let reason: String
-}
-
-func testCreateInvalidTodo() throws {
-    let app = Application(.testing)
-    defer { app.shutdown() }
-    try configure(app)
-    
-    try app.test(.POST, "/todos/", beforeRequest: { req in
-        let input = TodoCreateObject(title: "")
-        try req.content.encode(input)
-    }, afterResponse: { res in
-        XCTAssertEqual(res.status, .badRequest)
-        let error = try res.content.decode(ErrorResponse.self)
-        XCTAssertEqual(error.reason, "Title is required")
-    })
-}
-

Writing tests is a great way to debug our server side Swift code and double check our API endpoints. My only issue with this approach is that the code isn’t too much self-explaining.

Declarative unit tests using Spec
XCTVapor and the entire test framework works just great, but I had a small problem with it. If you ever worked with JavaScript or TypeScript you might have heard about the SuperTest library. This little npm package gives us a declarative syntactical sugar for testing HTTP requests, which I liked way too much to go back to regular XCTVapor-based test cases.

This is the reason why I’ve created the Spec “micro-framework”, which is literally one file with with an extra thin layer around Vapor’s unit testing framework to provide a declarative API. Let me show you how this works in practice, using a real-world example. 🙃

// swift-tools-version:5.3
-import PackageDescription
-
-let package = Package(
-    name: "myProject",
-    platforms: [
-       .macOS(.v10_15)
-    ],
-    products: [
-        .library(name: "TodoApi", targets: ["TodoApi"]),
-    ],
-    dependencies: [
-        .package(url: "https://github.com/vapor/vapor", from: "4.44.0"),
-        .package(url: "https://github.com/vapor/fluent", from: "4.0.0"),
-        .package(url: "https://github.com/vapor/fluent-sqlite-driver", from: "4.0.0"),
-        .package(url: "https://github.com/binarybirds/spec", from: "1.0.0"),
-    ],
-    targets: [
-        .target(name: "TodoApi"),
-        .target(
-            name: "App",
-            dependencies: [
-                .product(name: "Fluent", package: "fluent"),
-                .product(name: "FluentSQLiteDriver", package: "fluent-sqlite-driver"),
-                .product(name: "Vapor", package: "vapor"),
-                .target(name: "TodoApi")
-            ],
-            swiftSettings: [
-                .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: "Spec", package: "spec"),
-        ])
-    ]
-)
-

We had some expectations for the previous calls, right? How should we test the update todo endpoint? Well, we can create a new item, then update it and check if the results are valid.

import Spec
-
-// ...
-func testUpdateTodo() throws {
-    let app = Application(.testing)
-    defer { app.shutdown() }
-    try configure(app)
-    
-    
-    var existingTodo: TodoGetObject?
-    
-    try app
-        .describe("A valid todo object should exists after creation")
-        .post("/todos/")
-        .body(TodoCreateObject(title: "sample"))
-        .expect(.created)
-        .expect(.json)
-        .expect(TodoGetObject.self) { existingTodo = $0 }
-        .test()
-
-    XCTAssertNotNil(existingTodo)
-
-    let updatedTitle = "Item is done"
-    
-    try app
-        .describe("Todo should be updated")
-        .put("/todos/" + existingTodo!.id.uuidString)
-        .body(TodoUpdateObject(title: updatedTitle, completed: true, order: 2))
-        .expect(.ok)
-        .expect(.json)
-        .expect(TodoGetObject.self) { todo in
-            XCTAssertEqual(todo.title, updatedTitle)
-            XCTAssertTrue(todo.completed)
-            XCTAssertEqual(todo.order, 2)
-        }
-        .test()
-}
-

The very first part of the code expects that we were able to create a todo object, it is the exact same create expectation as we used to write with the help of the XCTVapor framework.

IMHO the overall code quality is way better than it was in the previous example. We described the test scenario then we set our expectations and finally we run our test. With this format it’s going to be more straightforward to understand test cases. If you compare the two versions the create case the second one is trivial to understand, but in the first one you actually have to take a deeper look at each line to understand what’s going on.

Ok, one more test before we stop, let me show you how to describe the delete endpoint. We’re going to refactor our code a bit, since there are some duplications already.

@testable import App
-import TodoApi
-import Fluent
-import Spec
-
-final class AppTests: XCTestCase {
-
-    // MARK: - helpers
-    
-    private struct ErrorResponse: Content {
-        let error: Bool
-        let reason: String
-    }
-
-    @discardableResult
-    private func createTodo(app: Application, input: TodoCreateObject) throws -> TodoGetObject {
-        var existingTodo: TodoGetObject?
-
-        try app
-            .describe("A valid todo object should exists after creation")
-            .post("/todos/")
-            .body(input)
-            .expect(.created)
-            .expect(.json)
-            .expect(TodoGetObject.self) { existingTodo = $0 }
-            .test()
-        
-        XCTAssertNotNil(existingTodo)
-
-        return existingTodo!
-    }
-    
-    // MARK: - tests
-    
-    func testTodoList() throws {
-        let app = Application(.testing)
-        defer { app.shutdown() }
-        try configure(app)
-        
-        try app
-            .describe("A valid todo list page should be returned.")
-            .get("/todos/")
-            .expect(.ok)
-            .expect(.json)
-            .expect(Page<TodoListObject>.self)
-            .test()
-    }
-    
-    func testCreateTodo() throws {
-        let app = Application(.testing)
-        defer { app.shutdown() }
-        try configure(app)
-
-        try createTodo(app: app, input: TodoCreateObject(title: "Write a todo tutorial"))
-    }
-
-    func testCreateInvalidTodo() throws {
-        let app = Application(.testing)
-        defer { app.shutdown() }
-        try configure(app)
-
-        try app
-            .describe("An invalid title response should be returned")
-            .post("/todos/")
-            .body(TodoCreateObject(title: ""))
-            .expect(.badRequest)
-            .expect(.json)
-            .expect(ErrorResponse.self) { error in
-                XCTAssertEqual(error.reason, "Title is required")
-            }
-            .test()
-    }
-
-    func testUpdateTodo() throws {
-        let app = Application(.testing)
-        defer { app.shutdown() }
-        try configure(app)
-        
-        let todo: TodoGetObject? = try createTodo(app: app, input: TodoCreateObject(title: "Write a todo tutorial"))
-
-        let updatedTitle = "Item is done"
-        
-        try app
-            .describe("Todo should be updated")
-            .put("/todos/" + todo!.id.uuidString)
-            .expect(.ok)
-            .expect(.json)
-            .body(TodoUpdateObject(title: updatedTitle, completed: true, order: 2))
-            .expect(TodoGetObject.self) { todo in
-                XCTAssertEqual(todo.title, updatedTitle)
-                XCTAssertTrue(todo.completed)
-                XCTAssertEqual(todo.order, 2)
-            }
-            .test()
-    }
-    
-    func testDeleteTodo() throws {
-        let app = Application(.testing)
-        defer { app.shutdown() }
-        try configure(app)
-        
-        let todo: TodoGetObject? = try createTodo(app: app, input: TodoCreateObject(title: "Write a todo tutorial"))
-
-        try app
-            .describe("Todo should be updated")
-            .delete("/todos/" + todo!.id.uuidString)
-            .expect(.ok)
-            .test()
-    }
-}
-

This is how you can create a complete unit test scenario for a REST API endpoint using the Spec library. Of course there are a dozen other issues that we could fix, such as better input object validation, unit test for the patch endpoint, better tests for edge cases. Well, next time. 😅

By using Spec you can build your expectations by describing the use case, then you can place your expectations on the described “specification” run the attached validators. The nice thing about this declarative approach is the clean self-explaining format that you can understand without taking too much time on investigating the underlying Swift / Vapor code.

I believe that Spec is a fun little tool that helps you to write better tests for your Swift backend apps. It has a very lightweight footprint, and the API is straightforward and easy to use. 💪

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/deep-dive-into-swift-frameworks/index.html b/docs/deep-dive-into-swift-frameworks/index.html deleted file mode 100644 index 3d3c309..0000000 --- a/docs/deep-dive-into-swift-frameworks/index.html +++ /dev/null @@ -1,498 +0,0 @@ - - - - - - - - - - - - Deep dive into Swift frameworks - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 11 min read - -
-

Deep dive into Swift frameworks

-
-

Learn everything about Swift modules, libraries, packages, closed source frameworks, command line tools and more.

-
- -

Basic definitions

First of all you should have a clear understanding about the basic terms. If you already know what’s the difference between a module, package, library or framework you can skip this section. However if you still have some mixed feelings about these things, please read ahead, you won’t regret it. 😉

Package

A package consists of Swift source files and a manifest file.

A package is a collection of Swift source files. If you are using Swift Package Manager you also have to provide a manifest file in order to make a real package. If you want to learn more about this tool, you should check my Swift Package Manager tutorial.

Example: this is your package:

Sources
-    my-source-file.swift
-Package.swift
-

You can also check out the open sourced swift-corelibs-foundation package by Apple, which is used to build the Foundation framework for Swift.

Library

Library is a packaged collection of object files that program can link against.

So a library is a bunch of compiled code. You can create two kinds of libraries:

  • static
  • dynamic

From a really simple perspective the only difference between them is the method of “integrating” aka. linking them into your project. Before I tell you more about this process, first we should talk about object files.

Mach-O file format

To create programs, developers convert source code to object files. The object files are then packaged into executable code or static libraries.

When you’re compiling the source files you are basically making object files, using the Mach-O (MachObject) file format. These files are the core building blocks of your applications, frameworks, and libraries (both dynamic and static).

Linking libraries

Linking refers to the creation of a single executable file from multiple object files.

In other words:

After the compiler has created all the object files, another program is called to bundle them into an executable program file. That program is called a linker and the process of bundling them into the executable is called linking.

Linking is just combining all your object files into an executable and resolving all the externals, so the system will be able to call all the functions inside the binary.

Static linking

The source code of the library is literally going to be copied into the application’s source. This will result in a big executable, it’ll take more time to load, so the binary will have a slower startup time. Oh, did I mention that if you are trying to link the same library more than once, the process will fail because of duplicated symbols?

Static linking

This method has advantages as well, for example the executable will always contain the correct version of the library, and only those parts will be copied into the main application that are really used, so you don’t have to load the whole stuff, but it seems like dynamic linking is going to be better in some cases.

Dynamic linking

Dynamic libraries are not embedded into the source of the binary, they are loaded at runtime. This means that apps can be smaller and startup time can significantly be faster because of the lightweight binary files. As a gratis dynamic libraries can be shared with multiple executables so they can have lower memory footprints. That’s why sometimes they’re being referred as shared libraries.

Dynamic linking

Of course if the dynamic library is not available - or it’s available but their version is incompatible - your application won’t run or it’ll crash. On the other hand this can be an advantage, because the author of the dynamic library can ship fixes and your app can benefit from these, without recompilation.

Fortunately system libraries like UIKit are always available, so you don’t have to worry too much about this issue…

Framework

A framework is a hierarchical directory that encapsulates shared resources, such as a dynamic shared library, nib files, image files, localized strings, header files, and reference documentation in a single package.

So let’s make this simple: frameworks are static or dynamic libraries packed into a bundle with some extra assets, meta description for versioning and more. UIKit is a framework which needs image assets to display some of the UI elements, also it has a version description, by the way the version of UIKit is the same as the version of iOS.

Module

Swift organizes code into modules. Each module specifies a namespace and enforces access controls on which parts of that code can be used outside of the module.

With the import keyword you are literally importing external modules into your sorce. In Swift you are always using frameworks as modules, but let’s go back in time for a while to understand why we needed modules at all.

import UIKit
-import my-awesome-module
-

Before modules you had to import framework headers directly into your code and you also had to link manually the framework’s binary within Xcode. The #import macro literally copy-pasted the whole resolved dependency structure into your code, and the compiler did the work on that huge source file.

It was a fragile system, things could go wrong with macro definitions, you could easily break other frameworks. That was the reason for defining prefixed uppercased very long macro names like: NS_MYSUPERLONGMACRONAME… 😒

There was an other issue: the copy-pasting resulted in non-scalable compile times. In order to solve this, precompiled header (PCH) files were born, but that was only a partial solution, because they polluted the namespace (you know if you import UIKit in a PCH file it gets available in everywhere), and no-one really maintained them.

Modules and module maps

The holy grail was already there, with the help of module maps (defining what kind of headers are part of a module and what’s the binary that has the implementation) we’ve got encapsulated modular frameworks. 🎉 They are separately compiled once, the header files are defining the interface (API), and the (automatically) linked dylib file contains the implementation. Hurray, no need to parse framework headers during compilation time (scalability), so local macro definitions won’t break anything. Modules can contain submodules (inheritance), and you don’t have to link them explicitly inside your (Xcode) project, because the .modulemap file has all the information that the build system needs.

End of the story, now you know what happens under the hood, when you import Foundation or import UIKit.

Command line tools

Now that you know the logic behind the whole dynamic modular framework system, we should start examining the tools that make this infrastructure possible.

Always read the man pages, aka. RTFM! If you don’t like to read that much, you can download the example project from GitLab and open the makefiles for the essence. There will be 3 main categories: C, Swift and Xcode project examples.

clang

the Clang C, C++, and Objective-C compiler

Clang is a compiler frontend for C languages (C, C++, Objective-C). If you have ever tried to compiled C code with gcc during your university years, you can imagine that clang is more or less the same as gcc, but nowadays it can do even more.

clang -c main.c -o main.o #compiles a C source file
-

LLVM: compiler backend system, which can compile and optimize the intermediate representation (IR) code generated by clang or the Swift compiler for example. It’s language independent, and it can do so many things that could fit into a book, but for now let’s say that LLVM is making the final machine code for your executable.

swiftc

The Swift compiler, there is no manual entry for this thing, but don’t worry, just fire up swiftc -h and see what can offer to you.

swiftc main.swift #compiles a Swift source file
-

As you can see this tool is what actually can compile the Swift source files into Mach-O’s or final executables. There is a short example in the attached repository, you should check on that if you’d like to learn more about the Swift compiler.

ar

The ar utility creates and maintains groups of files combined into an archive. Once an archive has been created, new files can be added and existing files can be extracted, deleted, or replaced.

So, in a nutshell you can zip Mach-O files into one file.

ar -rcs myLibrary.a *.o
-

With the help of ar you were able to create static library files, but nowadays libtool have the same functionality and even more.

ranlib

ranlib generates an index to the contents of an archive and stores it in the archive. The index lists each symbol defined by a member of an archive that is a relocatable object file.

ranlib can create an index file inside the static lib, so things are going to be faster when you’re about to use your library.

ranlib myLibrary.a
-

So ranlib & ar are tools for maintaining static libraries, usually ar takes care of the indexing, and you don’t have to run ranlib anymore. However there is a better option for managing static (and dynamic) libraries that you should learn…

libtool

create libraries

With libtool you can create dynamically linked libraries, or statically linked (archive) libraries. This tool with the -static option is intended to replace ar & ranlib.

libtool -static *.o -o myLibrary.a
-

Nowadays libtool is the main option for building up library files, you should definitely learn this tool if you’re into the topic. You can check the example project’s Makefile for more info, or as usually you can read the manuals (man libtool). 😉

ld

The ld command combines several object files and libraries, resolves references, and produces an ouput file. ld can produce a final linked image (executable, dylib, or bundle).

Let’s make it simple: this is the linker tool.

ld main.o -lSystem -LmyLibLocation -lmyLibrary -o MyApp
-

It can link multiple files into a single entity, so from the Mach-O’s you’ll be able to make an executable binary. Linking is necessary, because the system needs to resolve the addresses of each method from the linked libraries. In other words, the executable will be able to run and all of your functions will be available for calling. 📱

nm

display name list (symbol table)

With nm you can see what symbols are inside a file.

nm myLibrary.a
-# 0000000000001000 A __mh_execute_header
-#                  U _factorial
-# 0000000000001f50 T _main
-#                  U _printf
-#                  U dyld_stub_binder
-

As you can see from the output, some kind of memory addresses are associated for some of symbols. Those that have addresses are actually resolved, all the others are coming from other libraries (they’re not resolved yet). So this means that they’ll be resolved at runtime. The other option is that you have to link them. 😅

otool

object file displaying tool

With otool you can examine the contents of Mach-O files or libraries.

otool -L myLibrary.a
-otool -tV myLibrary.a
-

For example you can list the linked libraries, or see the disassembled text contents of the file. It’s a really helpful tool if you’re familiar with the Mach-O file format, also good one to use for reverse-engineer an existing application.

lipo

create or operate on universal files

With the help of the lipo tool you can create universal (multi-architecture) files. Usually this tool is used for creating universal frameworks.

lipo -create -output myFramework.framework devices.framework simulator.framework
-

Imagine the following scenario: you build your sources both for arm7 and i386. On a real device you’d need to ship the arm7 version, but for the iOS simulator you’ll need the i386 one. With the help of lipo you can combine these architectures into one, and ship that framework, so the end user don’t have to worry about this issue anymore.

Read on the article to see how it’s done. 👇

These tools can be invoked from the command line as well, but they’re much more related to Xcode than the ones before. Let’s have a quick walk-through.

xcode-select

Manages the active developer directory for Xcode and BSD tools. If you have multiple versions of Xcode on your machine this tool can easily switch between the developer tools provided by the induvidual versions.

xcode-select --switch path/to/Xcode.app
-

xcrun

Run or locate development tools and properties. With xcrun you can basically run anything that you can manage from Xcode.

xcrun simctl list #list of simulators
-

codesign

Create and manipulate code signatures

It can sign your application with the proper signature. Usually this thing failed when you were trying to sign your app before automatic signing was introduced.

codesign -s "Your Company, Inc." /path/to/MyApp.app
-codesign -v /path/to/MyApp.app
-

xcodebuild

build Xcode projects and workspaces

That’s it. It’ll parse the Xcode project or workspace file and executes the appropriate buid commands based on it.

xcodebuild -project Example.xcodeproj -target Example
-xcodebuild -list
-xcodebuild -showsdks
-

FAT frameworks

How to make a closed source universal FATtened (multi-architecture) Swift framework for iOS?

So we’re here, the whole article was made for learning the logic behind this tutorial.

First of all, I don’t want to reinvent the wheel, because there is a beautifully written article that you should read. However, I’d like to give you some more detailed explanation and a little modification for the scripts.

Thin vs. FAT frameworks

Thin frameworks contains compiled code for only one architecture. FAT frameworks on the other hand are containing “slices” for multiple architectures. Architectures are basically referred as slices, so for example the i386 or arm7 slice.

This means, if you compile a framework only for i386 and x86_64 architectures, it will work only on the simulator and horribly fail on real devices. So if you want to build a truly universal framework, you have to compile for ALL the existing architectures.

Building a FAT framework

I have a good news for you. You just need one little build phase script and an aggregate target in order to build a multi-architecture framework. Here it is, shamelessly ripped off from the source article, with some extra changes… 😁

set -e
-BUILD_PATH="${SRCROOT}/build"
-DEPLOYMENT_PATH="${SRCROOT}"
-TARGET_NAME="Console-iOS"
-FRAMEWORK_NAME="Console"
-FRAMEWORK="${FRAMEWORK_NAME}.framework"
-FRAMEWORK_PATH="${DEPLOYMENT_PATH}/${FRAMEWORK}"
-
-# clean the build folder
-if [ -d "${BUILD_PATH}" ]; then
-    rm -rf "${BUILD_PATH}"
-fi
-
-# build the framework for every architecture using xcodebuild
-xcodebuild -target "${TARGET_NAME}" -configuration Release \
-    -arch arm64 -arch armv7 -arch armv7s \
-    only_active_arch=no defines_module=yes -sdk "iphoneos"
-
-xcodebuild -target "${TARGET_NAME}" -configuration Release \
-    -arch x86_64 -arch i386 \
-    only_active_arch=no defines_module=yes -sdk "iphonesimulator"
-
-# remove previous version from the deployment path
-if [ -d "${FRAMEWORK_PATH}" ]; then
-    rm -rf "${FRAMEWORK_PATH}"
-fi
-
-# copy freshly built version to the deployment path
-cp -r "${BUILD_PATH}/Release-iphoneos/${FRAMEWORK}" "${FRAMEWORK_PATH}"
-
-# merge all the slices and create the fat framework
-lipo -create -output "${FRAMEWORK_PATH}/${FRAMEWORK_NAME}" \
-    "${BUILD_PATH}/Release-iphoneos/${FRAMEWORK}/${FRAMEWORK_NAME}" \
-    "${BUILD_PATH}/Release-iphonesimulator/${FRAMEWORK}/${FRAMEWORK_NAME}"
-
-# copy Swift module mappings for the simulator
-cp -r "${BUILD_PATH}/Release-iphonesimulator/${FRAMEWORK}/Modules/${FRAMEWORK_NAME}.swiftmodule/" \
-    "${FRAMEWORK_PATH}/Modules/${FRAMEWORK_NAME}.swiftmodule"
-
-# clean up the build folder again
-if [ -d "${BUILD_PATH}" ]; then
-    rm -rf "${BUILD_PATH}"
-fi
-

You can always examine the created framework with the lipo tool.

lipo -info Console.framework/Console
-#Architectures in the fat file: Console.framework/Console are: x86_64 i386 armv7 armv7s arm64
-

Usage

You just have to embed your brand new framework into the project that you’d like to use and set some paths. That’s it. Almost…

Build settings

Shipping to the App Store

There is only one issue with fat architectures. They contain slices for the simulator as well. If you want to submit your app to the app store, you have to cut off the simulator related codebase from the framework. The reason behind this is that no actual real device requires this chunk of code, so why submit it, right?

APP_PATH="${TARGET_BUILD_DIR}/${WRAPPER_NAME}"
-
-# remove unused architectures from embedded frameworks
-find "$APP_PATH" -name '*.framework' -type d | while read -r FRAMEWORK
-do
-    FRAMEWORK_EXECUTABLE_NAME=$(defaults read "$FRAMEWORK/Info.plist" CFBundleExecutable)
-    FRAMEWORK_EXECUTABLE_PATH="$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME"
-    echo "Executable is $FRAMEWORK_EXECUTABLE_PATH"
-
-    EXTRACTED_ARCHS=()
-
-    for ARCH in $ARCHS
-    do
-        echo "Extracting $ARCH from $FRAMEWORK_EXECUTABLE_NAME"
-        lipo -extract "$ARCH" "$FRAMEWORK_EXECUTABLE_PATH" -o "$FRAMEWORK_EXECUTABLE_PATH-$ARCH"
-        EXTRACTED_ARCHS+=("$FRAMEWORK_EXECUTABLE_PATH-$ARCH")
-    done
-
-    echo "Merging extracted architectures: ${ARCHS}"
-    lipo -o "$FRAMEWORK_EXECUTABLE_PATH-merged" -create "${EXTRACTED_ARCHS[@]}"
-    rm "${EXTRACTED_ARCHS[@]}"
-
-    echo "Replacing original executable with thinned version"
-    rm "$FRAMEWORK_EXECUTABLE_PATH"
-    mv "$FRAMEWORK_EXECUTABLE_PATH-merged" "$FRAMEWORK_EXECUTABLE_PATH"
-
-done
-

This little script will remove all the unnecessary slices from the framework, so you’ll be able to submit your app via iTunesConnect, without any issues. (ha-ha-ha. 😅)

You have to add this last script to your application’s build phases.

If you want to get familiar with the tools behind the scenes, this article will help you with the basics. I couldn’t find something like this but I wanted to dig deeper into the topic, so I made one. I hope you enjoyed the article. 😉

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/dynamic-libraries-and-code-replacements-in-swift/index.html b/docs/dynamic-libraries-and-code-replacements-in-swift/index.html deleted file mode 100644 index c50fbdb..0000000 --- a/docs/dynamic-libraries-and-code-replacements-in-swift/index.html +++ /dev/null @@ -1,458 +0,0 @@ - - - - - - - - - - - - Dynamic libraries and code replacements in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 7 min read - -
-

Dynamic libraries and code replacements in Swift

-
-

How to load a dynamic library and use native method swizzling in Swift? This article is all about the magic behind SwiftUI previews.

-
- -

Dynamic library packages

I’ve already published an article about building static and dynamic libraries using the Swift compiler, if you don’t know what is a dynamic library or you are simply interested a bit more about how the Swift compiler works, you should definitely take a look at that post first.

This time we’re going to focus a bit more on utilizing the Swift Package Manager to create our dynamic library products. The setup is going to be very similar to the one I’ve created in the loading dynamic libraries at runtime article. First we’re going to create a shared library using SPM.

// swift-tools-version:5.3
-import PackageDescription
-
-let package = Package(
-    name: "TextUI",
-    products: [
-        .library(name: "TextUI", type: .dynamic, targets: ["TextUI"]),
-    ],
-    dependencies: [
-        
-    ],
-    targets: [
-        .target(name: "TextUI", swiftSettings: [
-            .unsafeFlags(["-emit-module", "-emit-library"])
-        ]),
-    ]
-)
-

The package manifest is quite simple, although there are a few special things that we had to add. The very first thing is that we defined the product type as a dynamic library. This will ensure that the right .dylib (or .so / .dll) binary will be created when you build the target. 🎯

The second thing is that we’d like to emit our Swift module info alongside the library, we can tell this to the compiler through some unsafe flags. Don’t be afraid, these are actually not so dangerous to use, these flags will be directly passed to the Swift compiler, but that’s it.

Now the source code for our TextUI library is going to be very simple.

public struct TextUI {
-
-    public static dynamic func build() -> String {
-        "Hello, World!"
-    }
-}
-

It’s just a struct with one static function that returns a String value. Pretty simple, except one thing: the dynamic keyword. By adding the dynamic modifier to a function (or method) you tell the compiler that it should use dynamic dispatch to “resolve” the implementation when calling it.

We’re going to take advantage of the dynamic dispatch later on, but before we could move onto that part, we have to build our dynamic library and make it available for others to use. 🔨

If you run swift build (or run the project via Xcode) it’ll build all the required files and place them under the proper build folder. You can also print the build folder by running the swift build -c release --show-bin-path (-c release is for release builds, we’re going to build the library using the release configuration for obvious reasons… we’re releasing them). If you list the contents of the output directory, you should find the following files there:

  • TextUI.swiftdoc
  • TextUI.swiftmodule
  • TextUI.swiftsourceinfo
  • libTextUI.dylib
  • libTextUI.dylib.dSYM

So, what can we do with this build folder and the output files? We’re going to need them under a location where the build tools can access the related files, for the sake of simplicity we’re going to put everything into the /usr/local/lib folder using a Makefile.

PRODUCT_NAME := "TextUI"
-DEST_DIR := "/usr/local/lib/"
-BUILD_DIR := $(shell swift build -c release --show-bin-path)
-
-install: clean
-    @swift build -c release
-    @install "$(BUILD_DIR)/lib$(PRODUCT_NAME).dylib" $(DEST_DIR)
-    @cp -R "$(BUILD_DIR)/lib$(PRODUCT_NAME).dylib.dSYM" $(DEST_DIR)
-    @install "$(BUILD_DIR)/$(PRODUCT_NAME).swiftdoc" $(DEST_DIR)
-    @install "$(BUILD_DIR)/$(PRODUCT_NAME).swiftmodule" $(DEST_DIR)
-    @install "$(BUILD_DIR)/$(PRODUCT_NAME).swiftsourceinfo" $(DEST_DIR)
-    @rm ./lib$(PRODUCT_NAME).dylib
-    @rm -r ./lib$(PRODUCT_NAME).dylib.dSYM
-
-uninstall: clean
-    
-    @rm $(DEST_DIR)lib$(PRODUCT_NAME).dylib
-    @rm -r $(DEST_DIR)lib$(PRODUCT_NAME).dylib.dSYM
-    @rm $(DEST_DIR)$(PRODUCT_NAME).swiftdoc
-    @rm $(DEST_DIR)$(PRODUCT_NAME).swiftmodule
-    @rm $(DEST_DIR)$(PRODUCT_NAME).swiftsourceinfo
-
-clean:
-    @swift package clean
-

Now if you run make or make install all the required files will be placed under the right location. Our dynamic library package is now ready to use. The only question is how do we consume this shared binary library using another Swift Package target? 🤔

Linking against shared libraries

We’re going to build a brand new executable application called TextApp using the Swift Package Manager. This package will use our previously created and installed shared dynamic library.

// swift-tools-version:5.3
-import PackageDescription
-
-let package = Package(
-    name: "TextApp",
-    targets: [
-        .target(name: "TextApp", swiftSettings: [
-            .unsafeFlags(["-L", "/usr/local/lib/"]),
-            .unsafeFlags(["-I", "/usr/local/lib/"]),
-            .unsafeFlags(["-lTextUI"]),
-        ], linkerSettings: [
-            .unsafeFlags(["-L", "/usr/local/lib/"]),
-            .unsafeFlags(["-I", "/usr/local/lib/"]),
-            .unsafeFlags(["-lTextUI"]),
-        ]),
-    ]
-)
-

The trick is that we can add some flags to the Swift compiler and the linker, so they’ll know that we’ve prepared some special library and header (modulemap) files under the /usr/local/lib/ folder. We’d also like to link the TextUI framework with our application, in order to do this we have to pass the name of the module as a flag. I’ve already explained these flags (-L, -I, -l) in my previous posts so I suppose you’re familiar with them, if not please read the linked articles. 🤓

import TextUI
-
-print(TextUI.build())
-

Our main.swift file is pretty straightforward, we just print the result of the build method, the default implementation should return the famous “Hello, World!” text.

Are you ready to replace the build function using native method swizzling in Swift?

Dynamic method replacement

After publishing my original plugin system related article, I’ve got an email from one of my readers. First of all thank you for letting me know about the @_dynamicReplacement attribute Corey. 🙏

The thing is that Swift supports dynamic method swizzling out of the box, although it is through a private attribute (starts with an underscore), which means it is not ready for public use yet (yeah… just like @_exported, @_functionBuilder and the others), but eventually it will be finalized.

You can read the original dynamic method replacement pitch on the Swift forums, there’s also this great little snippet that contains a minimal showcase about the @_dynamicReplacement attribute.

Long story short, you can use this attribute to override a custom dynamic method with your own implementation (even if it comes from a dynamically loaded library). In our case we’ve already prepared a dynamic build method, so if we try we can override that the following snippet.

import TextUI
-
-extension TextUI {
-
-    @_dynamicReplacement(for: build())
-    static func _customBuild() -> String {
-        "It just works."
-    }
-}
-
-print(TextUI.build()) // It just works.
-

If you alter the main.swift file and run the project you should see that even we’re calling the build method, it is going to be dispatched dynamically and our _customBuild() method will be called under the hood, hence the new return value.

It works like a charm, but can we make this even more dynamic? Is it possible to build one more dynamic library and load that at runtime, then replace the original build implementation with the dynamically loaded lib code? The answer is yes, let me show you how to do this. 🤩

// swift-tools-version:5.3
-import PackageDescription
-
-let package = Package(
-    name: "TextView",
-    products: [
-        .library(name: "TextView", type: .dynamic, targets: ["TextView"]),
-    ],
-    targets: [
-        .target(name: "TextView", swiftSettings: [
-            .unsafeFlags(["-L", "/usr/local/lib/"]),
-            .unsafeFlags(["-I", "/usr/local/lib/"]),
-            .unsafeFlags(["-lTextUI"]),
-        ], linkerSettings: [
-            .unsafeFlags(["-L", "/usr/local/lib/"]),
-            .unsafeFlags(["-I", "/usr/local/lib/"]),
-            .unsafeFlags(["-lTextUI"]),
-        ]),
-    ]
-)
-

Same SPM pattern, we’ve just created a dynamic library and we’ve used the TextUI as a shared library so we can place our TextUI extension into this library instead of the TextApp target.

So far we’ve created 3 separated Swift packages shared the TextUI module between the TextApp and the TextView packages as a pre-built dynamic library (using unsafe build flags). Now we’re going to extend the TextUI struct inside our TextView package and build it as a dynamic library.

import TextUI
-
-extension TextUI {
-
-    @_dynamicReplacement(for: build())
-    static func _customBuild() -> String {
-        "It just works."
-    }
-}
-

We can use a similar makefile (to the previous one) or simply run the swift build -c release command and copy the libTextView.dylib file from the build directory by hand.

If you run this code using Linux or Windows, the dynamic library file will be called libTextView.so under Linux and libTextView.dll on Windows.

So just place this file under your home directory we’re going to need the full path to access it using the TextApp’s main file. We’re going to use the dlopen call to load the dylib, this will replace our build method, then we close it using dlclose (on the supported platforms, more on this later…).

import Foundation
-import TextUI
-
-print(TextUI.build())
-
-let dylibPath = "/Users/tib/libTextView.dylib"
-guard let dylibReference = dlopen(dylibPath, RTLD_LAZY) else {
-    if let err = dlerror() {
-        fatalError(String(format: "dlopen error - %s", err))
-    }
-    else {
-        fatalError("unknown dlopen error")
-    }
-}
-defer {
-    dlclose(dylibReference)
-}
-
-
-print(TextUI.build())
-
-// Output:
-//
-// Hello, World!
-// It just works.
-

The great thing about this approach is that you don’t have to mess around with additional dlsym calls and unsafe C pointers. There is also a nice and detailed article about Swift and native method swizzling, this focuses a bit more on the emitted replacements code, but I found it a very great read.

Unfortunately there is one more thing that we have to talk about…

Drawbacks & conclusion

Dynamic method replacement works nice, this approach is behind SwiftUI live previews (or dlsym with some pointer magic, but who knows this for sure..). Anyway, everything looks great, until you start involving Swift classes under macOS. What’s wrong with classes?

Turns out that the Objective-C runtime gets involved under macOS if you compile a native Swift class. Just compile the following example source and take a look at it using the nm tool.

// a.swift
-class A {}
-
-// swiftc a.swift -emit-library
-// nm liba.dylib|grep -i objc
-

Under macOS the output of nm will contain traces of the Objective-C runtime and that is more than enough to cause some troubles during the dylib close process. Turns out if your library contains the ObjC runtime you won’t be able to actually close the dylib, no matter what. ⚠️

Prior to Mac OS X 10.5, only bundles could be unloaded. Starting in Mac OS X 10.5, dynamic libraries may also be unloaded. There are a couple of cases in which a dynamic library will never be unloaded: 1) the main executable links against it, 2) an API that does not support unloading (e.g. NSAddImage()) was used to load it or some other dynamic library that depends on it, 3) the dynamic library is in dyld’s shared cache.

If you take a look at man 3 dlclose you can get a few more hints about the reasons, plus you can also check the source code of the Objective-C runtime, if you want to see more details.

Anyway I thought this should be mentioned, because it can cause some trouble (only on macOS), but everything works just great under Linux, so if you are planning to use this approach on the server side, then I’d say it’ll work just fine. It’s not safe, but it should work. 😈

Oh, I almost forget the hot-reload functionality. Well, you can add a directory or file watcher that can monitor your source codes and if something changes you can re-build the TextView dynamic library then load the dylib again and call the build method if needed. It’s relatively easy after you’ve tackled the dylib part, once you figure out the smaller details, it works like magic. 🥳

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/easy-multipart-file-upload-for-swift/index.html b/docs/easy-multipart-file-upload-for-swift/index.html deleted file mode 100644 index 0056950..0000000 --- a/docs/easy-multipart-file-upload-for-swift/index.html +++ /dev/null @@ -1,402 +0,0 @@ - - - - - - - - - - - - Easy multipart file upload for Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 2 min read - -
-

Easy multipart file upload for Swift

-
-

Let me show you how to create HTTP requests using multipart (form data) body without a third party library. Simple solution.

-
- -

I believe that you’ve already heard about the famous multipart-data upload technique that everyone loves to upload files and submit form data, but if not, hopefully this article will help you a little bit to understand these things better.

Let’s start with some theory. Don’t worry, it’s just one link, about the multipart/form-data content type specification. To quickly summarize it first I’d like to tell you a few words about how the HTTP layer works. In a nutshell, you send some data with some headers (think about it as a key-value user info object) to a given URL using a method and as a response you’ll get back a status code, some headers and maybe some sort of response data too. 🥜

  • HTTP request = Method + URL + Headers + Body (request data)
  • HTTP response = Status code + Headers + Body (response data)

The request method & URL is pretty straightforward, the interesting part is when you specify the Content-Type HTTP header, in our case the multipart/form-data;boundary="xxx" value means, that we’re going to send a request body using multiple parts and we’re going to use the “xxx” boundary string as a separator between the parts. Oh, by the way each part can have it’s own type and name, we’ll use the Content-Disposition: form-data; name="field1" line to let the server know about these fields, before we actually send the actual content value.

That’s more than enough theory for now, let me snow you how we can implement all of this using Swift 5. First of all, we would like to be able to append string values to a Data object, so we’re going to extend Data type with an ‘append string using encoding’ method:

import Foundation
-
-public extension Data {
-
-    mutating func append(
-        _ string: String,
-        encoding: String.Encoding = .utf8
-    ) {
-        guard let data = string.data(using: encoding) else {
-            return
-        }
-        append(data)
-    }
-}
-

Next, we need something that can construct the HTTP multipart body data, for this purpose we’re going to build a MultipartRequest object. We can set the boundary when we init this object and we’re going to append the parts needed to construct the HTTP body data.

The private methods will help to assemble everything, we simply append string values to the private data object that holds our data structure. The public API only consists of two add functions that you can use to append a key-value based form field or an entire file using its data. 👍

public struct MultipartRequest {
-    
-    public let boundary: String
-    
-    private let separator: String = "\r\n"
-    private var data: Data
-
-    public init(boundary: String = UUID().uuidString) {
-        self.boundary = boundary
-        self.data = .init()
-    }
-    
-    private mutating func appendBoundarySeparator() {
-        data.append("--\(boundary)\(separator)")
-    }
-    
-    private mutating func appendSeparator() {
-        data.append(separator)
-    }
-
-    private func disposition(_ key: String) -> String {
-        "Content-Disposition: form-data; name=\"\(key)\""
-    }
-
-    public mutating func add(
-        key: String,
-        value: String
-    ) {
-        appendBoundarySeparator()
-        data.append(disposition(key) + separator)
-        appendSeparator()
-        data.append(value + separator)
-    }
-
-    public mutating func add(
-        key: String,
-        fileName: String,
-        fileMimeType: String,
-        fileData: Data
-    ) {
-        appendBoundarySeparator()
-        data.append(disposition(key) + "; filename=\"\(fileName)\"" + separator)
-        data.append("Content-Type: \(fileMimeType)" + separator + separator)
-        data.append(fileData)
-        appendSeparator()
-    }
-
-    public var httpContentTypeHeadeValue: String {
-        "multipart/form-data; boundary=\(boundary)"
-    }
-
-    public var httpBody: Data {
-        var bodyData = data
-        bodyData.append("--\(boundary)--")
-        return bodyData
-    }
-}
-

The last remaining two public variables are helpers to easily get back the HTTP related content type header value using the proper boundary and the complete data object that you should to send to the server. Here’s how you can construct the HTTP URLRequest using the multipart struct.

var multipart = MultipartRequest()
-for field in [
-    "firstName": "John",
-    "lastName": "Doe"
-] {
-    multipart.add(key: field.key, value: field.value)
-}
-
-multipart.add(
-    key: "file",
-    fileName: "pic.jpg",
-    fileMimeType: "image/png",
-    fileData: "fake-image-data".data(using: .utf8)!
-)
-
-/// Create a regular HTTP URL request & use multipart components
-let url = URL(string: "https://httpbin.org/post")!
-var request = URLRequest(url: url)
-request.httpMethod = "POST"
-request.setValue(multipart.httpContentTypeHeadeValue, forHTTPHeaderField: "Content-Type")
-request.httpBody = multipart.httpBody
-
-/// Fire the request using URL sesson or anything else...
-let (data, response) = try await URLSession.shared.data(for: request)
-
-print((response as! HTTPURLResponse).statusCode)
-print(String(data: data, encoding: .utf8)!)
-

As you can see it’s relatively straightforward, you just add the form fields and the files that you want to upload, and get back the HTTP related values using the helper API. I hope this article will help you to simulate form submissions using multipart requests without hassle. 😊

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - -
-
- -
- - - -
- - - - diff --git a/docs/encoding-and-decoding-data-using-the-hummingbird-framework/index.html b/docs/encoding-and-decoding-data-using-the-hummingbird-framework/index.html deleted file mode 100644 index 2d0846e..0000000 --- a/docs/encoding-and-decoding-data-using-the-hummingbird-framework/index.html +++ /dev/null @@ -1,506 +0,0 @@ - - - - - - - - - - - - Encoding and decoding data using the Hummingbird framework - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 6 min read - · featured -
-

Encoding and decoding data using the Hummingbird framework

-
-

URL encoded requests over multipart form data? Maybe JSON and raw HTTP post body types? Let me explain all of this.

-
- -

HTTP is all about sending and receiving data over the network. Originally it was only utilized to transfer HTML documents, but nowadays we use HTTP to transfer CSS, JavaScript, JSON and many other data types. According to the standards, the Content-Type and Content-Length headers can be used to have a better understanding about the data inside the body of the HTTP request.

Modern web servers can automatically send back these headers based on the object you return in a request handler function. This is the case with Hummingbird, it has built-in encoding and decoding support, which makes the data transformation process really simple.

For example if we setup the following route handler and call the hello endpoint using cURL with the -i flag, the output will contain a bit more information about the response. ℹ️

router.get("hello") { _ in "hello" }
-
-//
-// curl -i http://localhost:8080/hello
-//
-// HTTP/1.1 200 OK
-// content-type: text/plain; charset=utf-8
-// Date: Mon, 20 Mar 2023 14:45:41 GMT
-// connection: keep-alive
-// content-length: 5
-// server: Hummingbird
-//
-// hello
-//
-

There are some basic headers in the response, the content-type header contains the type of the body, which is currently a plain text with an UTF-8 encoded string, since we’ve returned a String type using our Swift code. The content-length is 5, because the character count of hello is 5.

There are some other headers, but ignore those, the interesting part for us is the content-type header, and how it is injected into the response. Every Hummingbird application has an encoder and a decoder property. The default values for these are NullEncoder and NullDecoder. The encoders can magically add the proper content type header to the response and encode some object into a HTTP response data. Not everything is response encodable and decodable by default, but you can encode String objects in Hummingbird by default. 👍

Encoding and decoding JSON objects

Many of the server-side Swift systems are used to create JSON-based RESTful API backends for mobile frontends. Hummingbird can help you with this, since it has built-in encoding and decoding support for JSON objects through the Codable protocol.

First you have to import the HummingbirdFoundation library, since it is a standalone helper tool built around the Foundation framework, and that package contains the Codable type extensions. Next you have to setup the encoder and decoder using a JSONEncoder and JSONDecoder instance. After this, you can easily transform incoming HTTP body objects into Swift data structures and return with them as well. Let me show you a quick example. ⤵️

import Hummingbird
-import HummingbirdFoundation
-
-struct Foo: Codable {
-    let bar: String
-    let baz: Int
-}
-
-extension Foo: HBResponseCodable {}
-
-
-extension HBApplication {
-
-    func configure(_ args: AppArguments) throws {
-
-        decoder = JSONDecoder()
-        encoder = JSONEncoder()
-
-        router.post("foo") { req async throws -> Foo in
-            guard let foo = try? req.decode(as: Foo.self) else {
-                throw HBHTTPError(.badRequest, message: "Invalid request body.")
-            }
-            return foo
-        }
-    }
-
-    //
-    // curl -i -X POST http://localhost:8080/foo \
-    //     -H "Content-Type: application/json" \
-    //     -H "Accept: application/json" \
-    //     --data-raw '{"bar": "bar", "baz": 42}'
-    //
-    // HTTP/1.1 200 OK
-    // content-type: application/json; charset=utf-8
-    // Date: Mon, 20 Mar 2023 15:15:06 GMT
-    // connection: keep-alive
-    // content-length: 22
-    // server: Hummingbird
-    //
-    // {"bar":"bar","baz":42}
-    //
-}
-

As you can see the type of the returned content is now properly set to application/json and the length is also provided by default. We were also able to decode the Foo object from the request body and automatically encode the object after we returned with it.

Codable routing works like magic and nowadays it’s a pretty standard approach if it comes to server-side Swift frameworks. Fun fact: this approach was originally ‘invented’ for Swift by the developers of the Kitura framework. Thank you. 🙏

The HBResponseCodable and the HBResponseEncodable protocols are the basic building blocks and the HBRequestDecoder and the HBResponseEncoder are responsible for this magic. They make it possible to decode a Decodable object from a HBRequest and encode things into a HBResponse object and also provide additional headers. If you would like to know more, I highly recommend to take a look at the JSONCoding.swift file inside the framework. 😉

Encoding and decoding HTML forms

I don’t want to get too much into the details of building forms using HTML code, by the way there is a better way using SwiftHtml, but I’d like to focus more on the underlying data transfer mechanism and the enctype attribute. There are 3 possible, but only two useful values of the encoding type:

  • application/x-www-form-urlencoded
  • multipart/form-data

URL encoding and decoding is supported out of the box when using HummingbirdFoundation, this is a simple wrapper around the URL encoding mechanism to easily support data transformation.

decoder = URLEncodedFormDecoder()
-encoder = URLEncodedFormEncoder()
-
-//
-// curl -i -X POST http://localhost:8080/foo \
-//     -H "Content-Type: application/x-www-form-urlencoded" \
-//     -H "Accept: application/x-www-form-urlencoded" \
-//     --data-raw 'bar=bar&baz=42'
-//
-// HTTP/1.1 200 OK
-// content-type: application/x-www-form-urlencoded
-// Date: Mon, 20 Mar 2023 15:54:54 GMT
-// connection: keep-alive
-// content-length: 14
-// server: Hummingbird
-//
-// bar=bar&baz=42
-//
-

So that’s one way to process a URL encoded form, the other version is based on the multipart approach, which has no built-in support in Hummingbird, but you can use the multipart-kit library from the Vapor framework to process such forms. You can find a working example here. I also have an article about how to upload files using multipart form data requests. So there are plenty of resources out there, that’s why I won’t include an example in this article. 😅

Header based encoding and decoding

First we have to implement a custom request decoder and a response encoder. In the decoder, we’re going to check the Content-Type header for a given request and decode the HTTP body based on that. The encoder will do the exact same thing, but the response body output is going to depend on the Accept header field. Here’s how you can implement it:

struct AppDecoder: HBRequestDecoder {
-
-    func decode<T>(
-        _ type: T.Type,
-        from req: HBRequest
-    ) throws -> T where T: Decodable {
-        switch req.headers["content-type"].first {
-        case "application/json", "application/json; charset=utf-8":
-            return try JSONDecoder().decode(type, from: req)
-        case "application/x-www-form-urlencoded":
-            return try URLEncodedFormDecoder().decode(type, from: req)
-        default:
-            throw HBHTTPError(.badRequest)
-        }
-    }
-}
-
-struct AppEncoder: HBResponseEncoder {
-
-    func encode<T>(
-        _ value: T,
-        from req: HBRequest
-    ) throws -> HBResponse where T: Encodable {
-        switch req.headers["accept"].first {
-        case "application/json":
-            return try JSONEncoder().encode(value, from: req)
-        case "application/x-www-form-urlencoded":
-            return try URLEncodedFormEncoder().encode(value, from: req)
-        default:
-            throw HBHTTPError(.badRequest)
-        }
-    }
-}
-

Now if you change the configuration and use the AppEncoder & AppDecoder you should be able to respond based on the Accept header and process the input based on the Content-Type header.

import Hummingbird
-import HummingbirdFoundation
-
-struct Foo: Codable {
-    let bar: String
-    let baz: Int
-}
-
-extension Foo: HBResponseEncodable {}
-extension Foo: HBResponseCodable {}
-
-extension HBApplication {
-
-    func configure(_ args: AppArguments) throws {
-
-        decoder = AppDecoder()
-        encoder = AppEncoder()
-
-        router.post("foo") { req async throws -> Foo in
-            guard let foo = try? req.decode(as: Foo.self) else {
-                throw HBHTTPError(.badRequest, message: "Invalid request body.")
-            }
-            return foo
-        }
-    }
-}
-

Feel free to play around with some cURL snippets… 👾

# should return JSON encoded data
-curl -i -X POST http://localhost:8080/foo \
-    -H "Content-Type: application/x-www-form-urlencoded" \
-    -H "Accept: application/json" \
-    --data-raw 'bar=bar&baz=42'
-
-# should return URL encoded data
-curl -i -X POST http://localhost:8080/foo \
-    -H "Content-Type: application/json" \
-    -H "Accept: application/x-www-form-urlencoded" \
-    --data-raw '{"bar": "bar", "baz": 42}'
-
-# should return with a 400 status code
-curl -i -X POST http://localhost:8080/foo \
-    -H "Content-Type: application/json" \
-    -H "Accept: multipart/form-data" \
-    --data-raw '{"bar": "bar", "baz": 42}'
-

So, based on this article you should be able to implement support to even more content types by simply extending the app encoder and decoder. Of course you might have to import some additional package dependencies, but that’s fine.

Raw requests and responses

One more little thing, before I end this article: you can access the raw request body data and send back a raw response using the HBResponse object like this:

router.post("foo") { req async throws -> HBResponse in
-    // get raw request body
-    if let buffer = req.body.buffer {
-        let rawInputData = buffer.getData(
-            at: 0,
-            length: buffer.readableBytes
-        )
-        print(rawInputData)
-    }
-
-    // streaming input body chunk-by-chunk
-    if let sequence = req.body.stream?.sequence {
-        for try await chunk in sequence {
-            print(chunk)
-        }
-    }
-
-    guard let data = "hello".data(using: .utf8) else {
-        throw HBHTTPError(.internalServerError)
-    }
-
-    return .init(
-        status: .ok,
-        headers: .init(),
-        body: .byteBuffer(.init(data: data))
-    )
-}
-

For smaller requests, you can use the req.body.buffer property and turn it into a Data type if needed. Hummingbird has great support for the new Swift Concurreny API, so you can use the sequence on the body stream if you need chunked reads. Now only one question left:

What types should I support?

The answer is simple: it depends. Like really. Nowadays I started to ditch multipart encoding and I prefer to communicate with my API using REST (JSON) and upload files as raw HTTP body. I never really had to support URL encoding, because if you submit HTML forms, you’ll eventually face the need of file upload and that won’t work with URL encoded forms, but only with multipart.

In conclusion I’d say that the good news is that we have plenty of opportunities and if you want to provide support for most of these types you don’t have to reinvent the wheel at all. The multipart-kit library is built into Vapor 4, but that’s one of the reasons I started to like Hummingbird a bit more, because I can only include what I really need. Anyway, competition is a good thing to have in this case, because hopefully both frameworks will evolve for good… 🙃

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/event-driven-generic-hooks-for-swift/index.html b/docs/event-driven-generic-hooks-for-swift/index.html deleted file mode 100644 index decbe54..0000000 --- a/docs/event-driven-generic-hooks-for-swift/index.html +++ /dev/null @@ -1,438 +0,0 @@ - - - - - - - - - - - - Event-driven generic hooks for Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 6 min read - -
-

Event-driven generic hooks for Swift

-
-

In this article I am going to show you how to implement a basic event processing system for your modular Swift application.

-
- -

Dependencies, protocols and types

When we write Swift, we can import frameworks and other third party libraries. It’s quite natural, just think about Foundation, UIKit or nowadays it’s more likely SwiftUI, but there are many other dependencies that we can use. Even when we don’t import anything we usually create separate structures or classes to build smaller components instead of one gigantic spaghetti-like file, function or whatever. Consider the following example:

struct NameProvider {
-    func getName() -> String { "John Doe" }
-}
-
-
-struct App {
-    let provider = NameProvider()
-    
-    func run() {
-        let name = provider.getName()
-        print("Hello \(name)!")
-    }
-}
-
-let app = App()
-app.run()
-

It shows us the basics of the separation of concerns principle. The App struct the representation of our main application, which is a simple “Hello World!” app, with a twist. The name is not hardcoded into the App object, but it’s coming from a NameProvider struct.

The thing that you should notice is that we’ve created a static dependency between the App and the NameProvider object here. We don’t have to import a framework to create a dependency, these objects are in the same namespace, but still the application will always require the NameProvider type at compilation time. This is not bad, but sometimes it’s not what we really want.

How can we solve this? Wait I have an idea, let’s create a protocol! 😃

import Foundation
-
-struct MyNameProvider: NameProvider {
-    func getName() -> String { "John Doe" }
-}
-
-
-protocol NameProvider {
-    func getName() -> String
-}
-
-struct App {
-    let provider: NameProvider
-    
-    func run() {
-        let name = provider.getName()
-        print("Hello \(name)!")
-    }
-}
-
-let provider = MyNameProvider()
-let app = App(provider: provider)
-app.run()
-

Oh no, this just made our entire codebase a bit harder to understand, also didn’t really solved anything, because we still can’t compile our application without the MyNameProvider dependency. That class must be part of the package no matter how many protocols we create. Of course we could move the NameProvider protocol into a standalone Swift package, then we could create another package for the protocol implementation that relies on that one, then use both as a dependency when we build our application, but hey isn’t this getting a little bit complicated? 🤔

What did we gain here? First of all we overcomplicated a really simple thing. On the other hand, we eliminated an actual dependency from the App struct itself. That’s a great thing, because now we could create a mock name provider and test our application instance with that, we can inject any kind of Swift object into the app that conforms to the NameProvider protocol.

Can we change the provider at runtime? Well, yes, that’s also possible we could define the provider as a variable and alter its value later on, but there’s one thing that we can’t solve with this approach.

We can’t move out the provider reference from the application itself. 😳

Event-driven architecture

The EDA design pattern allows us to create loosely coupled software components and services without forming an actual dependency between the participants. Consider the following alternative:

struct MyNameProvider {
-    func getName(_: HookArguments) -> String { "John Doe" }
-}
-
-struct App {
-
-    func run() {
-        guard let name: String = hooks.invoke("name-event") else {
-            fatalError("Someone must provide a name-event handler.")
-        }
-        print("Hello \(name)!")
-    }
-}
-
-let hooks = HookStorage()
-
-let provider = MyNameProvider()
-hooks.register("name-event", use: provider.getName)
-
-let app = App()
-app.run()
-

Don’t try to compile this yet, there are some additional things that we’ll need to implement, but first I am going to explain this snippet step-by-step. The MyNameProvider struct getName function signature changed a bit, because in an event-driven world we need a unified function signature to handle all kind of scenarios. Fortunately we don’t have to erease the return type to Any thanks to the amazing generic support in Swift. This HookArguments type will be just an alias for a dictionary that has String keys and it can have Any value.

Now inside the App struct we call-out for the hook system and invoke an event with the “name-event” name. The invoke method is a function with a generic return type, it actually returns an optional generic value, hence the guard statement with the explicit String type. Long story short, we call something that can return us a String value, in other words we fire the name event. 🔥

The very last part is the setup, first we need to initialize our hook system that will store all the references for the event handlers. Next we create a provider and register our handler for the given event, finally we make the app and run everything.

I’m not saying that this approach is less complicated than the protocol oriented version, but it’s very different for sure. Unfortunately we still have to build our event handler system, so let’s get started.

public typealias HookArguments = [String: Any]
-
-/// a hook function is something that can be invoked with a given arguments
-public protocol HookFunction {
-    func invoke(_: HookArguments) -> Any
-}
-
-/// a hook function signature with a generic return type
-public typealias HookFunctionSignature<T> = (HookArguments) -> T
-

As I mentioned this before, the HookArguments is just a typealias for the [String:Any] type, this way we are going to be able to pass around any kind of values under given keys for the hook functions. Next we define a protocol for invoking these functions, and finally we build up a function signature for our hooks, this is going to be used during the registration process. 🤓

public struct AnonymousHookFunction: HookFunction {
-
-    private let functionBlock: HookFunctionSignature<Any>
-
-    /// anonymous hooks can be initialized using a function block
-    public init(_ functionBlock: @escaping HookFunctionSignature<Any>) {
-        self.functionBlock = functionBlock
-    }
-
-    /// since they are hook functions they can be invoked with a given argument
-    public func invoke(_ args: HookArguments) -> Any {
-        functionBlock(args)
-    }
-}
-

The AnonymousHookFunction is a helper that we can use to pass around blocks instead of object pointers when we register a new hook function. It can be quite handy sometimes to write an event handler without creating additional classes or structs. We are going to also need to associate these hook function pointers with an event name and an actual a return type…

public final class HookFunctionPointer {
-
-    public var name: String
-    public var pointer: HookFunction
-    public var returnType: Any.Type
-    
-    public init(name: String, function: HookFunction, returnType: Any.Type) {
-        self.name = name
-        self.pointer = function
-        self.returnType = returnType
-    }
-}
-

The HookFunctionPointer is used inside the hook storage, that’s the core building block for this entire system. The hook storage is the place where all your event handlers live and you can call these events through this storage pointer when you need to trigger an event. 🔫

public final class HookStorage {
-    
-    private var pointers: [HookFunctionPointer]
-
-    public init() {
-        self.pointers = []
-    }
-
-    public func register<ReturnType>(_ name: String, use block: @escaping HookFunctionSignature<ReturnType>) {
-        let function = AnonymousHookFunction { args -> Any in
-            block(args)
-        }
-        let pointer = HookFunctionPointer(name: name, function: function, returnType: ReturnType.self)
-        pointers.append(pointer)
-    }
-
-    /// invokes the first hook function with a given name and the provided arguments
-    public func invoke<ReturnType>(_ name: String, args: HookArguments = [:]) -> ReturnType? {
-        pointers.first { $0.name == name && $0.returnType == ReturnType.self }?.pointer.invoke(args) as? ReturnType
-    }
-
-    /// invokes all the available hook functions with a given name
-    public func invokeAll<ReturnType>(_ name: String, args: HookArguments = [:]) -> [ReturnType] {
-        pointers.filter { $0.name == name && $0.returnType == ReturnType.self }.compactMap { $0.pointer.invoke(args) as? ReturnType }
-    }
-}
-

I know, this seems like quite complicated at first sight, but when you start playing around with these methods it’ll all make sense. I’m still not sure about the naming conventions, for example the HookStorage is also a global event storage so maybe it’d be better to call it something related to the event term. If you have a better idea, feel free to tweet me.

Oh, I almost forgot that I wanted to show you how to register an anonymous hook function. 😅

hooks.register("name-event") { _ in "John Doe" }
-

That’s it you don’t event have to write the return type, the Swift compiler this time is smart enough to figure out the final function signature. This magic only works with one-liners I suppose… ✨

This article was a follow-up on the modules and hooks in Swift, also heavily inspired by the my old Entropy framework, Drupal and the Wordpress hook systems. The code implementation idea comes from the Vapor’s routing abstraction, but it’s slightly changed to match my needs.

The event-driven design approach is a very nice architecture and I really hope that we’ll see the long term benefit of using this pattern inside Feather. I can’t wait to tell you more about it… 🪶

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/everything-about-public-and-private-swift-attributes/index.html b/docs/everything-about-public-and-private-swift-attributes/index.html deleted file mode 100644 index 97b9b8d..0000000 --- a/docs/everything-about-public-and-private-swift-attributes/index.html +++ /dev/null @@ -1,499 +0,0 @@ - - - - - - - - - - - - Everything about public and private Swift attributes - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 6 min read - -
-

Everything about public and private Swift attributes

-
-

Have you ever heard about Swift language attributes? In this article I'm trying to gather all the @ annotations and their meanings.

-
- -

Public attributes

Public Swift language attributes are marked with the @ symbol, they are (more or less) well documented and ready for use. Here is the complete list of all the public Swift language attributes. Most of them will seem very familiar… 😉

@IBOutlet

If you mark a property with the @IBOutlet attribute, the Interface Builder (IB) will recognize that variable and you’ll be able to connect your source with your visuals through the provided “outlet” mechanism.

@IBOutlet weak var textLabel: UILabel!
-

@IBAction

Similarly, @IBAction is an attribute that makes possible connecting actions sent from Interface Builder. So the marked method will directly receive the event fired up by the user interface. 🔥

@IBaction func buttonTouchedAction(_ sender: UIButton) {}
-

@IBInspectable, @GKInspectable

Marking an NSCodable property with the @IBInspectable attribute will make it easily editable from the Interface Builder’s inspector panel. Using @GKInspectable has the same behavior as @IBInspectable, but the property will be exposed for the SpriteKit editor UI instead of IB. 🎮

@IBInspectable var borderColor: UIColor = .black
-@GKInspectable var mass: Float = 1.21
-

@IBDesignable

When applied to a UIView or NSView subclass, the @IBDesignable attribute lets Interface Builder know that it should display the exact view hierarchy. So basically anything that you draw inside your view will be rendered into the IB canvas.

@IBDesignable class MyCustomView: UIView { /*...*/ }
-

@UIApplicationMain, @NSApplicationMain

With this attribute you can mark a class as the application’s delegate. Usually this is already there in every AppDelegate.swift file that you’ll ever create, however you can provide a main.swift file and call the [UI|NS]ApplicationMain method by hand. #pleasedontdoit 😅

@available

With the @available attribute you can mark types available, deprecated, unavailable, etc. for specific platforms. I’m not going into the details there are some great posts about how to use the attribute with availability checkings in Swift.

@available(swift 4.1)
-@available(iOS 11, *)
-func avaialbleMethod() { /*...*/ }
-

@NSCopying

You can mark a property with this attribute to make a copy of it instead of the value of the property iself. Obviously this can be really helpful when you copy reference types.

class Example: NSOBject {
-    @NSCopying var objectToCopy: NSObject
-}
-

@NSManaged

If you are using Core Data entities (usually NSManagedObject subclasses), you can mark stored variables or instance methods as @NSManaged to indicate that the Core Data framework will dynamically provide the implementation at runtime.

class Person: NSManagedObject {
-    @NSManaged var name: NSString
-}
-

@objcMembers

It’s basically a convenience attribute for marking multiple attributes available for Objective-C. It’s legacy stuff for Objective-C dinosaurs, with performance caveats. 🦕

@objcMembers class Person {
-    var firstName: String?
-    var lastName: String?
-}
-

@escaping

You can mark closure parameters as @escaping, if you want to indicate that the value can be stored for later execution, so in other words the value is allowed to outlive the lifetime of the call. 💀

var completionHandlers: [() -> Void] = []
-
-func add(_ completionHandler: @escaping () -> Void) {
-    completionHandlers.append(completionHandler)
-}
-

@discardableResult

By default the compiler raises a warning when a function returns with something, but that returned value is never used. You can suppress the warning by marking the return value discardable with this Swift language attribute. ⚠️

@discardableResult func logAdd(_ a: Int, _ b: Int) -> Int {
-    let c = a + b
-    print(c)
-    return c
-}
-logAdd(1, 2)
-

@autoclosure

This attribute can magically turn a function with a closure parameter that has no arguments, but a return type, into a function with a parameter type of that original closure return type, so you can call it much more easy. 🤓

func log(_ closure: @autoclosure () -> String) {
-    print(closure())
-}
-
-log("b") // it's like func log(_ value: String) { print(value) }
-

@testable

If you mark an imported module with the @testable attribute all the internal access-level entities will be visible (available) for testing purposes. 👍

@testable import CoreKit
-

@objc

This attribute tells the compiler that a declaration is available to use in Objective-C code. Optionally you can provide a single identifier that’ll be the name of the Objective-C representation of the original entity. 🦖

@objc(LegacyClass)
-class ExampleClass: NSObject {
-
-    @objc private var store: Bool = false
-
-    @objc var enabled: Bool {
-        @objc(isEnabled) get {
-            return self.store
-        }
-        @objc(setEnabled:) set {
-            self.store = newValue
-        }
-    }
-
-    @objc(setLegacyEnabled:)
-    func set(enabled: Bool) {
-        self.enabled = enabled
-    }
-}
-

@nonobjc

Use this attribute to supress an implicit objc attribute. The @nonobjc attribute tells the compiler to make the declaration unavailable in Objective-C code, even though it’s possible to represent it in Objective-C. 😎

@nonobjc static let test = "test"
-

@convention

This attribute indicate function calling conventions. It can have one parameter which indicates Swift function reference (swift), Objective-C compatible block reference (block) or C function reference (c).

// private let sysBind: @convention(c) (CInt, UnsafePointer<sockaddr>?, socklen_t) -> CInt = bind
-
-// typealias LeagcyBlock = @convention(block) () -> Void
-// let objcBlock: AnyObject = ... // get real ObjC object from somewhere
-// let block: (() -> Void) = unsafeBitCast(objcBlock, to: LeagcyBlock.self)
-// block()
-
-func a(a: Int) -> Int {
-    return a
-}
-let exampleSwift: @convention(swift) (Int) -> Int = a
-exampleSwift(10)
-

Private attributes

Private Swift language attributes should only be used by the creators of the language, or hardcore developers. They usually provide extra (compiler) functionality that is still work in progress, so please be very careful… 😳

Please do not use private attributes in production code, unless you really know what you are doing!!! 😅

@_exported

If you want to import an external module for your whole module you can use the @_exported keyword before your import. From now the imported module will be available everywhere. Remember PCH files? 🙃

@_exported import UIKit
-

@inline

With the @inline attribute you explicitly tell the compiler the function inlining behavior. For example if a function is small enough or it’s only getting called a few times the compiler is maybe going to inline it, unless you disallow it explicitly.

@inline(never) func a() -> Int {
-    return 1
-}
-
-@inline(__always) func b() -> Int {
-    return 2
-}
-
-@_inlineable public func c() {
-    print("c")
-}
-c()
-

@inlinable is the future (@_inlineable) by Marcin Krzyzanowskim 👏

@effects

The @effects attribute describes how a function affects “the state of the world”. More practically how the optimizer can modify the program based on information that is provided by the attribute.
You can find the corresponding docs here.

@effects(readonly) func foo() { /*...*/ }
-

@_transparent

Basically you can force inlining with the @_transparent attribute, but please read the unofficial documentation for more info.

@_transparent
-func example() {
-    print("example")
-}
-

@_specialize

With the @_specialize Swift attribute you can give hints for the compiler by listing concrete types for the generic signature. More detailed docs are here.

struct S<T> {
-  var x: T
-  @_specialize(where T == Int, U == Float)
-  mutating func exchangeSecond<U>(_ u: U, _ t: T) -> (U, T) {
-    x = t
-    return (u, x)
-  }
-}
-
-// Substitutes: <T, U> with <Int, Float> producing:
-// S<Int>::exchangeSecond<Float>(u: Float, t: Int) -> (Float, Int)
-

@_semantics

The Swift optimizer can detect code in the standard library if it is marked with special attributes @_semantics, that identifies the functions.
You can read about semantics here and here, or inside this concurrency proposal.

@_semantics("array.count")
-func getCount() -> Int {
-    return _buffer.count
-}
-

@silgenname

This attribute specifies the name that a declaration will have at link time.
You can read about it inside the Standard Librery Programmers Manual.

@_silgen_name("_destroyTLS")
-internal func _destroyTLS(_ ptr: UnsafeMutableRawPointer?) {
-  // ... implementation ...
-}
-

@_cdecl

Swift compiler comes with a built-in libFuzzer integration, which you can use with the help of the @_cdecl annotation. You can learn more about libFuzzer here.

@_cdecl("LLVMFuzzerTestOneInput") 
-public func fuzzMe(Data: UnsafePointer<CChar>, Size: CInt) -> CInt{
-    // Test our code using provided Data.
-  }
-}
-

Unavailable, undocumented, unknown

As you can see this is already quite a list, but there is even more. Inside the official Swift repository you can find the attr tests. If you need more info about the remaining Swift annotations you can go directly there and check the source code comments. If you could help me writing about the leftovers, please drop me a few lines, I’d really appreciate any help. 😉👍

  • @requiresstoredproperty_inits
  • @warnunqualifiedaccess
  • @fixedlayout
  • @_versioned
  • @showin_interface
  • @_alignment
  • @objcnonlazy_realization
  • @_frozen
  • @_optimize(none|speed|size)
  • @_weakLinked
  • @consuming
  • @_restatedObjCConformance
  • @_staticInitializeObjCMetadata
  • @setterAccess
  • @rawdoccomment
  • @objc_bridged
  • @noescape -> removed, see @escaping
  • @noreturn -> removed, see Never type
  • @downgradeexhaustivity_check -> no effect on switch case anymore?
  • @_implements(…) - @implements(Equatable, ==(:_:))
  • @swiftnativeobjcruntime_base(class)

The @_implements attribute, which treats a decl as the implementation for some named protocol requirement (but otherwise not-visible by that name).

This attribute indicates a class that should be treated semantically as a native Swift root class, but which inherits a specific Objective-C class at runtime. For most classes this is the runtime’s “SwiftObject” root class. The compiler does not need to know about the class; it’s the build system’s responsibility to link against the ObjC code that implements the root class, and the ObjC implementation’s responsibility to ensure instances begin with a Swift-refcounting-compatible object header and override all the necessary NSObject refcounting methods.

This allows us to subclass an Objective-C class and use the fast Swift memory allocator.
If you want to add some notes about these attributes, please contact me.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/file-upload-api-server-in-vapor-4/index.html b/docs/file-upload-api-server-in-vapor-4/index.html deleted file mode 100644 index 177b9e0..0000000 --- a/docs/file-upload-api-server-in-vapor-4/index.html +++ /dev/null @@ -1,446 +0,0 @@ - - - - - - - - - - - - File upload API server in Vapor 4 - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

File upload API server in Vapor 4

-
-

Learn how to build a very simple file upload API server using Vapor 4 and URLSession upload task on the client side.

-
- -

A simple file upload server written in Swift

For this simple file upload tutorial we’ll only use the Vapor Swift package as a dependency. 📦

// swift-tools-version:5.3
-import PackageDescription
-
-let package = Package(
-    name: "myProject",
-    platforms: [
-       .macOS(.v10_15)
-    ],
-    dependencies: [
-        .package(url: "https://github.com/vapor/vapor", from: "4.35.0"),
-    ],
-    targets: [
-        .target(
-            name: "App",
-            dependencies: [
-                .product(name: "Vapor", package: "vapor"),
-            ],
-            swiftSettings: [
-                .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"),
-        ])
-    ]
-)
-

You can setup the project with the required files using the Vapor toolbox, alternatively you can create everything by hand using the Swift Package Manager, long story short, we just need a starter Vapor project without additional dependencies. Now if you open the Package.swift file using Xcode, we can setup our routes by altering the configure.swift file.

import Vapor
-
-public func configure(_ app: Application) throws {
-
-    /// enable file middleware
-    app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
-
-    /// set max body size
-    app.routes.defaultMaxBodySize = "10mb"
-
-    /// setup the upload handler
-    app.post("upload") { req -> EventLoopFuture<String> in
-        let key = try req.query.get(String.self, at: "key")
-        let path = req.application.directory.publicDirectory + key
-        return req.body.collect()
-            .unwrap(or: Abort(.noContent))
-            .flatMap { req.fileio.writeFile($0, at: path) }
-            .map { key }
-    }
-}
-

First we use the FileMiddleware, this will allow us to server files using the Public directory inside our project folder. If you don’t have a directory named Public, please create one, since the file upload server will need that. Don’t forget to give proper file system permissions if necessary, otherwise we won’t be able to write our data inside the directory. 📁

The next thing that we set is the default maximum body size. This property can limit the amount of data that our server can accept, you don’t really want to use this method for large files because uploaded files will be stored in the system memory before we write them to the disk.

If you want to upload large files to the server you should consider streaming the file instead of collecting the file data from the HTTP body. The streaming setup will require a bit more work, but it’s not that complicated, if you are interested in that solution, you should read the Files API and the body streaming section using official Vapor docs site.

This time we just want a dead simple file upload API endpoint, that collects the incoming data using the HTTP body into a byte buffer object, then we simply write this buffer using the fileio to the disk, using the given key from the URL query parameters. If everything was done without errors, we can return the key for the uploaded file.

File upload tasks using the URLSession API
The Foundation frameworks gives us a nice API layer for common networking tasks. We can use the URLSession uploadTask method to send a new URLRequest with a data object to a given server, but IMHO this API is quite strange, because the URLRequest object already has a httpBody property, but you have to explicitly pass a “from: Data?” argument when you construct the task. But why? 🤔

import Foundation
-
-extension URLSession {
-
-    func uploadTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionUploadTask {
-        uploadTask(with: request, from: request.httpBody, completionHandler: completionHandler)
-    }
-}
-

Anyway, I made a little extension method, so when I create the URLRequest I can set the httpBody property of it and safely pass it before the completion block and use the contents as the from parameter. Very strange API design choice from Apple… 🤐

We can put this little snippet into a simple executable Swift package (or of course we can create an entire application) to test our upload server. In our case I’ll place everything into a main.swift file.

import Foundation
-import Dispatch
-
-extension URLSession {
-
-    func uploadTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionUploadTask {
-        uploadTask(with: request, from: request.httpBody, completionHandler: completionHandler)
-    }
-}
-
-
-let fileData = try Data(contentsOf: URL(fileURLWithPath: "/Users/[user]]/[file].png"))
-var request = URLRequest(url: URL(string: "http://localhost:8080/upload?key=\(UUID().uuidString).png")!)
-request.httpMethod = "POST"
-request.httpBody = fileData
-
-let task = URLSession.shared.uploadTask(with: request) { data, response, error in
-    guard error == nil else {
-        fatalError(error!.localizedDescription)
-    }
-    guard let response = response as? HTTPURLResponse else {
-        fatalError("Invalid response")
-    }
-    guard response.statusCode == 200 else {
-        fatalError("HTTP status error: \(response.statusCode)")
-    }
-    guard let data = data, let result = String(data: data, encoding: .utf8) else {
-        fatalError("Invalid or missing HTTP data")
-    }
-    print(result)
-    exit(0)
-}
-
-task.resume()
-dispatchMain()
-

The above example uses the Dispatch framework to wait until the asynchronous file upload finishes. You should change the location (and the extension) of the file if necessary before you run this script. Since we defined the upload route as a POST endpoint, we have to set the httpMethod property to match this, also we store the file data in the httpBody variable before we create our task. The upload URL should contain a key, that the server can use as a name for the file. You can add more properties of course or use header values to check if the user has proper authorization to perform the upload operation. Then we call the upload task extension method on the shared URLSession property. The nice thing about uploadTask is that you can run them on the background if needed, this is quite handy if it comes to iOS development. 📱

Inside the completion handler we have to check for a few things. First of all if there was an error, the upload must have failed, so we call the fatalError method to break execution. If the response was not a valid HTTP response, or the status code was not ok (200) we also stop. Finally we want to retrieve the key from the response body so we check the data object and convert it to a UTF8 string if possible. Now we can use the key combined with the domain of the server to access the uploaded file, this time I just printed out the result, but hey, this is just a demo, in a real world application you might want to return a JSON response with additional data. 😅

Vanilla JavaScript file uploader

One more thing… you can use Leaf and some Vanilla JavaScript to upload files using the newly created upload endpoint. Actually it’s really easy to implement a new endpoint and render a Leaf template that does the magic. You’ll need some basic HTML and a few lines of JS code to submit the contents of the file as an array buffer. This is a basic example.

<!DOCTYPE html>
-<html>
-  <head>
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <title>File upload</title>
-  </head>
-  <body>
-      <h1>File upload</h1>
-      <input type="file" id="file" name="file" accept="image/*" /><br><br>
-      <img id="preview" src="https://theswiftdev.com/images/logos/logo.png" width="256px">
-      <script>
-        document.getElementById('file').addEventListener("change", uploadImage);
-
-        function uploadImage() {
-            var xhr = new XMLHttpRequest();
-            xhr.open("POST", "/upload?key=test.png", true);
-            xhr.onreadystatechange = function() {
-                if(xhr.readyState == 4 && xhr.status == 200) {
-                    document.getElementById('preview').src = "/" + this.responseText;
-                }
-            };
-
-            var file = document.getElementById('file').files[0];
-            if (file) {
-                var reader = new FileReader();
-                reader.onload = function() {
-                    xhr.send(reader.result);
-                }
-                reader.readAsArrayBuffer(file);
-            }
-        }
-      </script>
-  </body>
-</html>
-

As you can see it’s a standard XHR request combined with the FileReader JavaScript API. We use the FileReader to convert our input to a binary data, this way our server can write it to the file system in the expected format. In most cases people are using a multipart-encoded form to access files on the server, but when you have to work with an API you can also transfer raw file data. If you want to learn more about XHR requests and AJAX calls, you should read my previous article.

I also have a post about different file upload methods using standard HTML forms and a Vapor 4 server as a backend. I hope you’ll find the right solution that you need for your application. 👍

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/file-upload-using-vapor-4/index.html b/docs/file-upload-using-vapor-4/index.html deleted file mode 100644 index 29d96ea..0000000 --- a/docs/file-upload-using-vapor-4/index.html +++ /dev/null @@ -1,546 +0,0 @@ - - - - - - - - - - - - File upload using Vapor 4 - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 6 min read - -
-

File upload using Vapor 4

-
-

Learn how to implement a basic HTML file upload form using the Leaf template engine and Vapor, all written in Swift of course.

-
- -

Building a file upload form

Let’s start with a basic Vapor project, we’re going to use Leaf (the Tau release) for rendering our HTML files. You should note that Tau was an experimental release, the changes were reverted from the final 4.0.0 Leaf release, but you can still use Tau if you pin the exact version in your manifest file. Tau will be published later on in a standalone repository… 🤫

// swift-tools-version:5.3
-import PackageDescription
-
-let package = Package(
-    name: "myProject",
-    platforms: [
-       .macOS(.v10_15)
-    ],
-    dependencies: [
-        .package(url: "https://github.com/vapor/vapor", from: "4.35.0"),
-        .package(url: "https://github.com/vapor/leaf", .exact("4.0.0-tau.1")),
-        .package(url: "https://github.com/vapor/leaf-kit", .exact("1.0.0-tau.1.1")),
-    ],
-    targets: [
-        .target(
-            name: "App",
-            dependencies: [
-                .product(name: "Leaf", package: "leaf"),
-                .product(name: "LeafKit", package: "leaf-kit"),
-                .product(name: "Vapor", package: "vapor"),
-            ],
-            swiftSettings: [
-                .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"),
-        ])
-    ]
-)
-

Now if you open the project with Xcode, don’t forget to setup a custom working directory first, because we’re going to create templates and Leaf will look for those view files under the current working directory by default. We are going to build a very simple index.leaf file, you can place it into the Resources/Views directory.

<!DOCTYPE html>
-<html>
-  <head>
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <title>File upload example</title>
-  </head>
-  <body>
-    <h1>File upload example</h1>
-
-    <form action="/upload" method="post" enctype="multipart/form-data">
-        <input type="file" name="file"><br><br>
-        <input type="submit" value="Submit">
-    </form>
-  </body>
-</html>
-

As you can see, it’s a standard file upload form, when you want to upload files using the browser you always have to use the multipart/form-data encryption type. The browser will pack every field in the form (including the file data with the original file name and some meta info) using a special format and the server application can parse the contents of this. Fortunately Vapor has built-in support for easy decoding multipart form data values. We are going to use the POST /upload route to save the file, let’s setup the router first so we can render our main page and we are going to prepare our upload path as well, but we will respond with a dummy message for now.

import Vapor
-import Leaf
-
-public func configure(_ app: Application) throws {
-
-    /// config max upload file size
-    app.routes.defaultMaxBodySize = "10mb"
-    
-    /// setup public file middleware (for hosting our uploaded files)
-    app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
-    
-    /// setup Leaf template engine
-    LeafRenderer.Option.caching = .bypass
-    app.views.use(.leaf)
-
-    /// index route
-    app.get { req in
-        req.leaf.render(template: "index")
-    }
-    
-    /// upload handler
-    app.post("upload") { req in
-        "Upload file..."
-    }
-}
-

You can put the snippet above into your configure.swift file then you can try to build and run your server and visit http://localhost:8080, then try to upload any file. It won’t actually upload the file, but at least we are prepared to write our server side Swift code to process the incoming form data. ⬆️

File upload handler in Vapor

Now that we have a working uploader form we should parse the incoming data, get the contents of the file and place it under our Public directory. You can actually move the file anywhere on your server, but for this example we are going to use the Public directory so we can simply test if everthing works by using the FileMiddleware. If you don’t know, the file middleware serves everything (publicly available) that is located inside your Public folder. Let’s code.

app.post("upload") { req -> EventLoopFuture<String> in
-    struct Input: Content {
-        var file: File
-    }
-    let input = try req.content.decode(Input.self)
-    
-    let path = app.directory.publicDirectory + input.file.filename
-    
-    return req.application.fileio.openFile(path: path,
-                                           mode: .write,
-                                           flags: .allowFileCreation(posixMode: 0x744),
-                                           eventLoop: req.eventLoop)
-        .flatMap { handle in
-            req.application.fileio.write(fileHandle: handle,
-                                         buffer: input.file.data,
-                                         eventLoop: req.eventLoop)
-                .flatMapThrowing { _ in
-                    try handle.close()
-                    return input.file.filename
-                }
-        }
-}
-

So, let me explain what just happened here. First we define a new Input type that will contain our file data. There is a File type in Vapor that helps us decoding multipart file upload forms. We can use the content of the request and decode this type. We gave the file name to the file input form previously in our leaf template, but of course you can change it, but if you do so you also have to align the property name inside the Input struct.

After we have an input (please note that we don’t validate the submitted request yet) we can start uploading our file. We ask for the location of the public directory, we append the incoming file name (to keep the original name, but you can generate a new name for the uploaded file as well) and we use the non-blocking file I/O API to create a file handler and write the contents of the file into the disk. The fileio API is part of SwiftNIO, which is great because it’s a non-blocking API, so our server will be more performant if we use this instead of the regular FileManager from the Foundation framework. After we opened the file, we write the file data (which is a ByteBuffer object, bad naming…) and finally we close the opened file handler and return the uploaded file name as a future string. If you haven’t heard about futures and promises you should read about them, because they are everywhere on the server side Swift world. Can’t wait for async / awake support, right? 😅

We will enhance the upload result page just a little bit. Create a new result.leaf file inside the views directory.

<!DOCTYPE html>
-<html>
-  <head>
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <title>File uploaded</title>
-  </head>
-  <body>
-    <h1>File uploaded</h1>
-
-    #if(isImage):
-        <img src="#(fileUrl)" width="256px"><br><br>
-    #else:
-    <a href="#(fileUrl)" target="_blank">Show me!</a><br><br>
-    #endif
-    
-    <a href="/">Upload new one</a>
-  </body>
-</html>
-

So we’re going to check if the uploaded file has an image extension and pass an isImage parameter to the template engine, so we can display it if we can assume that the file is an image, otherwise we’re going to render a simple link to view the file. Inside the post upload handler method we are going to add a date prefix to the uploaded file so we will be able to upload multiple files even with the same name.

app.post("upload") { req -> EventLoopFuture<View> in
-    struct Input: Content {
-        var file: File
-    }
-    let input = try req.content.decode(Input.self)
-
-    guard input.file.data.readableBytes > 0 else {
-        throw Abort(.badRequest)
-    }
-
-    let formatter = DateFormatter()
-    formatter.dateFormat = "y-m-d-HH-MM-SS-"
-    let prefix = formatter.string(from: .init())
-    let fileName = prefix + input.file.filename
-    let path = app.directory.publicDirectory + fileName
-    let isImage = ["png", "jpeg", "jpg", "gif"].contains(input.file.extension?.lowercased())
-
-    return req.application.fileio.openFile(path: path,
-                                           mode: .write,
-                                           flags: .allowFileCreation(posixMode: 0x744),
-                                           eventLoop: req.eventLoop)
-        .flatMap { handle in
-            req.application.fileio.write(fileHandle: handle,
-                                         buffer: input.file.data,
-                                         eventLoop: req.eventLoop)
-                .flatMapThrowing { _ in
-                    try handle.close()
-                }
-                .flatMap {
-                    req.leaf.render(template: "result", context: [
-                        "fileUrl": .string(fileName),
-                        "isImage": .bool(isImage),
-                    ])
-                }
-        }
-}
-

If you run this example you should be able to view the image or the file straight from the result page.

Multiple file upload using Vapor

By the way, you can also upload multiple files at once if you add the multiple attribute to the HTML file input field and use the files[] value as name.

<input type="file" name="files[]" multiple><br><br>
-

To support this we have to alter our upload method, don’t worry it’s not that complicated as it looks at first sight. 😜

app.post("upload") { req -> EventLoopFuture<View> in
-    struct Input: Content {
-        var files: [File]
-    }
-    let input = try req.content.decode(Input.self)
-
-    let formatter = DateFormatter()
-    formatter.dateFormat = "y-m-d-HH-MM-SS-"
-    let prefix = formatter.string(from: .init())
-    
-    struct UploadedFile: LeafDataRepresentable {
-        let url: String
-        let isImage: Bool
-        
-        var leafData: LeafData {
-            .dictionary([
-                "url": url,
-                "isImage": isImage,
-            ])
-        }
-    }
-    
-    let uploadFutures = input.files
-        .filter { $0.data.readableBytes > 0 }
-        .map { file -> EventLoopFuture<UploadedFile> in
-            let fileName = prefix + file.filename
-            let path = app.directory.publicDirectory + fileName
-            let isImage = ["png", "jpeg", "jpg", "gif"].contains(file.extension?.lowercased())
-            
-            return req.application.fileio.openFile(path: path,
-                                                   mode: .write,
-                                                   flags: .allowFileCreation(posixMode: 0x744),
-                                                   eventLoop: req.eventLoop)
-                .flatMap { handle in
-                    req.application.fileio.write(fileHandle: handle,
-                                                 buffer: file.data,
-                                                 eventLoop: req.eventLoop)
-                        .flatMapThrowing { _ in
-                            try handle.close()
-                            return UploadedFile(url: fileName, isImage: isImage)
-                        }
-                    
-                }
-        }
-
-    return req.eventLoop.flatten(uploadFutures).flatMap { files in
-        req.leaf.render(template: "result", context: [
-            "files": .array(files.map(\.leafData))
-        ])
-    }
-}
-

The trick is that we have to parse the input as an array of files and turn every possible upload into a future upload operation. We can filter the upload candidates by readable byte size, then we map the files into futures and return an UploadedFile result with the proper file URL and is image flag. This structure is a LeafDataRepresentable object, because we want to pass it as a context variable to our result template. We also have to change that view once again.

<!DOCTYPE html>
-<html>
-  <head>
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <title>Files uploaded</title>
-  </head>
-  <body>
-    <h1>Files uploaded</h1>
-
-    #for(file in files):
-        #if(file.isImage):
-        <img src="#(file.url)" width="256px"><br><br>
-        #else:
-        <a href="#(file.url)" target="_blank">#(file.url)</a><br><br>
-        #endif
-    #endfor
-    
-    <a href="/">Upload new files</a>
-  </body>
-</html>
-

Well, I know this is a dead simple implementation, but it’s great if you want to practice or learn how to implement file uploads using server side Swift and the Vapor framework. You can also upload files directly to a cloud service using this technique, there is a library called Liquid, which is similar to Fluent, but for file storages. Currently you can use Liquid to upload files to the local storage or you can use an AWS S3 bucket or you can write your own driver using LiquidKit. The API is pretty simple to use, after you configure the driver you can upload files with just a few lines of code.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/generating-random-numbers-in-swift/index.html b/docs/generating-random-numbers-in-swift/index.html deleted file mode 100644 index c8703b8..0000000 --- a/docs/generating-random-numbers-in-swift/index.html +++ /dev/null @@ -1,502 +0,0 @@ - - - - - - - - - - - - Generating random numbers in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 6 min read - -
-

Generating random numbers in Swift

-
-

Learn everything what you'll ever need to generate random values in Swift using the latest methods and covering some old techniques.

-
- -

How to generate random numbers using Swift?

Fortunately random number generation has been unified since Swift 4.2. This means that you don’t have to mess around with imported C APIs anymore, you can simply generate random values by using native Swift methods on all platforms! 😍

let randomBool = Bool.random()
-let randomInt = Int.random(in: 1...6) //dice roll
-let randomFloat = Float.random(in: 0...1)
-let randomDouble = Double.random(in: 1..<100)
-

As you can see generating a dice roll is now super easy, thanks to the cryptographically secure randomizer that’s built into the Swift language. The new random generator API also better at distributing the numbers. The old arc4random function had some issues, because the generated values were not uniformly distributed for example in between 1 and 6 due to the modulo bias side effect. 🎲

Random Number Generator (RNG)

These examples above are implicitly using the default random number generator (SystemRandomNumberGenerator) provided by the Swift standard library. There is a second parameter for every method, so you can use a different RNG if you want. You can also implement your own RNG or extend the built-in generator, if you’d like to alter the behavior of distribution (or just give it some more “entropy”! 🤪).

var rng = SystemRandomNumberGenerator()
-let randomBool = Bool.random(using: &rng)
-let randomInt = Int.random(in: 1...6, using: &rng) //dice roll
-let randomFloat = Float.random(in: 0...1, using: &rng)
-let randomDouble = Double.random(in: 1..<100, using: &rng)
-

Collections, random elements, shuffle

The new random API introduced some nice extensions for collection types. Picking a random element and mixing up the order of elements inside a collection is now ridiculously easy and performant (with custom RNG support as well). 😉

let array = ["🐶", "🐱", "🐮", "🐷", "🐔", "🐵"]
-let randomArrayElement = array.randomElement()
-let shuffledArray = array.shuffled()
-
-let dictionary = [
-    "🐵": "🍌",
-    "🐱": "🥛",
-    "🐶": "🍖",
-]
-let randomDictionaryElement = dictionary.randomElement()
-let shuffledDictionary = dictionary.shuffled()
-
-let sequence = 1..<10
-let randomSequenceElement = sequence.randomElement()
-let shuffledSequence = sequence.shuffled()
-
-let set = Set<String>(arrayLiteral: "🐶", "🐱", "🐮", "🐷", "🐔", "🐵")
-let randomSetElement = set.randomElement()
-let shuffledSet = set.shuffled()
-

Randomizing custom types

You can implement random functions on your custom types as well. There are two simple things that you should keep in mind in order to follow the Swift standard library pattern:

  • provide a static method that has a (inout) parameter for the custom RNG
  • make a random() method that uses the SystemRandomNumberGenerator

-enum Animal: String, CaseIterable {
-    case dog = "🐶"
-    case cat = "🐱"
-    case cow = "🐮"
-    case pig = "🐷"
-    case chicken = "🐔"
-    case monkey = "🐵"
-}
-
-extension Animal {
-
-    static func random<T: RandomNumberGenerator>(using generator: inout T) -> Animal {
-        return self.allCases.randomElement(using: &generator)!
-    }
-
-    static func random() -> Animal {
-        var rng = SystemRandomNumberGenerator()
-        return Animal.random(using: &rng)
-    }
-}
-
-let random: Animal = .random()
-random.rawValue
-

Generating random values using GameplayKit

The GameplayKit provides lots of things to help you dealing with random number generation. Various random sources and distributions are available inside the framework, let’s have a quick look at them.

Random sources in GameplayKit

GameplayKit has three random source algorithms implemented, the reason behind it is that random number generation is hard, but usually you’re going to go with arc4 random source. You should note that Apple recommends resetting the first 769 values (just round it up to 1024 to make it look good) before you’re using it for something important, otherwise it will generate sequences that can be guessed. 🔑

  • GKARC4RandomSource - okay performance and randomness
  • GKLinearCongruentialRandomSource - fast, less random
  • GKMersenneTwisterRandomSource - good randomness, but slow

You can simply generate a random number from int min to int max by using the nextInt() method on any of the sources mentioned above or from 0 to upper bound by using the nextInt(upperBound:) method.

import GameplayKit
-
-let arc4 = GKARC4RandomSource()
-arc4.dropValues(1024) //drop first 1024 values first
-arc4.nextInt(upperBound: 20)
-let linearCongruential = GKLinearCongruentialRandomSource()
-linearCongruential.nextInt(upperBound: 20)
-let mersenneTwister = GKMersenneTwisterRandomSource()
-mersenneTwister.nextInt(upperBound: 20)
-

Random distribution algorithms

GKRandomDistribution - A generator for random numbers that fall within a specific range and that exhibit a specific distribution over multiple samplings.

Basically we can say that this implementation is trying to provide randomly distributed values for us. It’s the default value for shared random source. 🤨

GKGaussianDistribution - A generator for random numbers that follow a Gaussian distribution (also known as a normal distribution) across multiple samplings.

The gaussian distribution is a shaped random number generator, so it’s more likely that the numbers near the middle are more frequent. In other words elements in the middle are going to occure significantly more, so if you are going to simulate dice rolling, 3 is going to more likely happen than 1 or 6. Feels like the real world, huh? 😅

GKShuffledDistribution - A generator for random numbers that are uniformly distributed across many samplings, but where short sequences of similar values are unlikely.

A fair random number generator or shuffled distribution is one that generates each of its possible values in equal amounts evenly distributed. If we keep the dice rolling example with 6 rolls, you might get 6, 2, 1, 3, 4, 5 but you would never get 6 6 6 1 2 6.

// 6 sided dice
-let randomD6 = GKRandomDistribution.d6()
-let shuffledD6 = GKShuffledDistribution.d6()
-let gaussianD6 = GKGaussianDistribution.d6()
-randomD6.nextInt()   // completely random
-shuffledD6.nextInt() // see below... // eg. 1
-gaussianD6.nextInt() // mostly 3, most likely 2, 4 less likely 1, 6
-
-//goes through all the possible values again and again...
-shuffledD6.nextInt() // eg. 3
-shuffledD6.nextInt() // eg. 5
-shuffledD6.nextInt() // eg. 2
-shuffledD6.nextInt() // eg. 6
-shuffledD6.nextInt() // eg. 4
-
-// 20 sided dice
-let randomD20 = GKRandomDistribution.d20()
-let shuffledD20 = GKShuffledDistribution.d20()
-let gaussianD20 = GKGaussianDistribution.d20()
-randomD20.nextInt()
-shuffledD20.nextInt()
-gaussianD20.nextInt()
-
-// using custom random source, by default it uses arc4
-let mersenneTwister = GKMersenneTwisterRandomSource()
-let mersoneTwisterRandomD6 = GKRandomDistribution(randomSource: mersenneTwister, lowestValue: 1, highestValue: 6)
-mersoneTwisterRandomD6.nextInt()
-mersoneTwisterRandomD6.nextInt(upperBound: 3) //limiting upper bound
-

How to shuffle arrays using GameplayKit?

You can use the arrayByShufflingObjects(in:) method to mix up elements inside an array. Also you can use a seed value in order to shuffle elements identically. It’s going to be a random order, but it can be predicted. This comes handy if you need to sync two random arrays between multiple devices. 📱

let dice = [Int](1...6)
-
-let random = GKRandomSource.sharedRandom()
-let randomRolls = random.arrayByShufflingObjects(in: dice)
-
-let mersenneTwister = GKMersenneTwisterRandomSource()
-let mersenneTwisterRolls = mersenneTwister.arrayByShufflingObjects(in: dice)
-
-let fixedSeed = GKMersenneTwisterRandomSource(seed: 1001)
-let fixed1 = fixedSeed.arrayByShufflingObjects(in: dice) // always the same order
-

GameplayKit best practice to generate random values

There is also a shared random source that you can use to generate random numbers. This is ideal if you don’t want to mess around with distributions or sources. This shared random object uses arc4 as a source and random distribution. 😉

let sharedRandomSource = GKRandomSource.sharedRandom()
-sharedRandomSource.nextBool() // true / false
-sharedRandomSource.nextInt() //from int min - to int max
-sharedRandomSource.nextInt(upperBound: 6) //dice roll
-sharedRandomSource.nextUniform() //float between 0 - 1
-

Please note that none of these random number generation solutions provided by the GameplayKit framework are recommended for cryptography purposes!

Pre-Swift 4.2 random generation methods

I’ll leave this section here for historical reasons. 😅

arc4random

arc4random() % 6 + 1 // dice roll
-

This C function was very common to generate a dice roll, but it’s also dangerous, because it can lead to a modulo bias (or pigenhole principle), that means some numbers are generated more frequently than others. Please don’t use it. 😅

arc4random_uniform

This method will return a uniformly distributed random numbers. It was the best / recommended way of generating random numbers before Swift 4.2, because it avoids the modulo bias problem, if the upper bound is not a power of two.

func rndm(min: Int, max: Int) -> Int {
-    if max < min {
-        fatalError("The max value should be greater than the min value.")
-    }
-    if min == max {
-        return min
-    }
-    return Int(arc4random_uniform(UInt32((max - min) + 1))) + min
-}
-rndm(min: 1, max: 6) //dice roll
-

drand48

The drand48 function returns a random floating point number between of 0 and 1. It was really useful for generating color values for random UIColor objects. One minor side note that it generates a pseudo-random number sequence, and you have to provide a seed value by using srand48 and usually a time parameter. 🤷‍♂️

let red = CGFloat(drand48())
-let green = CGFloat(drand48())
-let blue = CGFloat(drand48())
-

Linux support, glibc and the rand method

I was using this snippet below in order to generate random numbers on both appleOS and Linux platform. I know it’s not perfect, but it did the job for me. 🤐

#!/usr/bin/env swift
-
-#if os(iOS) || os(tvOS) || os(macOS) || os(watchOS)
-    import Darwin
-#endif
-#if os(Linux)
-    import Glibc
-#endif
-
-public func rndm(to max: Int, from min: Int = 0) -> Int {
-    #if os(iOS) || os(tvOS) || os(macOS) || os(watchOS)
-        let scale = Double(arc4random()) / Double(UInt32.max)
-    #endif
-    #if os(Linux)
-        let scale = Double(rand()) / Double(RAND_MAX)
-    #endif
-    var value = max - min
-    let maximum = value.addingReportingOverflow(1)
-    if maximum.overflow {
-        value = Int.max
-    }
-    else {
-        value = maximum.partialValue
-    }
-    let partial = Int(Double(value) * scale)
-    let result = partial.addingReportingOverflow(min)
-    if result.overflow {
-        return partial
-    }
-    return result.partialValue
-}
-
-rndm(to: 6)
-

Now that we have Swift 4.2 just around the corner I’d like to encourage everyone to adapt the new random number generation API methods. I’m really glad that Apple and the community tackled down this issue so well, the results are amazing! 👏

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/get-started-with-the-fluent-orm-framework-in-vapor-4/index.html b/docs/get-started-with-the-fluent-orm-framework-in-vapor-4/index.html deleted file mode 100644 index e088d7c..0000000 --- a/docs/get-started-with-the-fluent-orm-framework-in-vapor-4/index.html +++ /dev/null @@ -1,880 +0,0 @@ - - - - - - - - - - - - Get started with the Fluent ORM framework in Vapor 4 - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 15 min read - -
-

Get started with the Fluent ORM framework in Vapor 4

-
-

Learn how to use the Fluent ORM framework. Migrations, schemas, relations powered by PostgreSQL, written in Swift.

-
- -

If you want to learn Fluent, but you don’t have a working PostgreSQL installation, you should check my tutorial about how to install and use pgSQL before you start reading this one.

Using the Fluent ORM framework

The beauty of an ORM framework is that it hides the complexity of the underlying database layer. Fluent 4 comes with multiple database driver implementations, this means that you can easily replace the recommended PostgreSQL driver with SQLite, MySQL or MongoDB if you want. MariaDB is also supported through the MySQL driver.

If you are using the SQLite database driver you might have to install the corresponding package (brew install sqlite) if you run into the following error: “missing required module ‘CSQLite’”. 😊

In this tutorial we’ll use PostgreSQL, since that’s the new default driver in Vapor 4. First you have to create a database, next we can start a new Vapor project & write some Swift code using Fluent. If you create a new project using the toolbox (vapor new myProject) you’ll be asked which database driver to use. If you are creating a project from scratch you can alter the Package.swift file:

// swift-tools-version:5.2
-import PackageDescription
-
-let package = Package(
-    name: "pgtut",
-    platforms: [
-       .macOS(.v10_15)
-    ],
-    dependencies: [
-        // 💧 A server-side Swift web framework.
-        .package(url: "https://github.com/vapor/vapor.git", from: "4.3.0"),
-        .package(url: "https://github.com/vapor/fluent.git", from: "4.0.0-rc"),
-        .package(url: "https://github.com/vapor/fluent-postgres-driver.git", from: "2.0.0-rc")
-    ],
-    targets: [
-        .target(name: "App", dependencies: [
-            .product(name: "Fluent", package: "fluent"),
-            .product(name: "FluentPostgresDriver", package: "fluent-postgres-driver"),
-            .product(name: "Vapor", package: "vapor")
-        ]),
-        .target(name: "Run", dependencies: ["App"]),
-        .testTarget(name: "AppTests", dependencies: [
-            .target(name: "App"),
-            .product(name: "XCTVapor", package: "vapor"),
-        ])
-    ]
-)
-

Open the Package.swift file in Xcode, wait until all the dependencies are loaded.

Let’s configure the psql database driver in the configure.swift file. We’re going to use a database URL string to provide the connection details, loaded from the local environment.

import Vapor
-import Fluent
-import FluentPostgresDriver
-
-extension Application {
-    static let databaseUrl = URL(string: Environment.get("DB_URL")!)!
-}
-
-public func configure(_ app: Application) throws {
-    
-    try app.databases.use(.postgres(url: Application.databaseUrl), as: .psql)
-    
-    //...
-}
-

Create a new .env.development file in the project directory with the following contents:

DB_URL=postgres://myuser:mypass@localhost:5432/mydb
-

You can also configure the driver using other methods, but I personally prefer this approach, since it’s very easy and you can also put other specific environmental variables right next to the DB_URL.

You can also use the .env file in production mode to set your environmental variables.

Run the application, but first make sure that the current working directory is set properly, read more about this in my previous tutorial about the leaf templating engine.

Well done, you have a working project that connects to the pgSQL server using Fluent. 🚀

Model definition

The official documentation pretty much covers all the important concepts, so it’s definitely worth a read. In this section, I’m only going to focus on some of the “missing parts”.

The API template sample code comes with a Todo model which is pretty much a good starting point for us.

Field keys

Field keys are available from the 5th major beta version of Fluent 4. Long story short, you don’t have to repeat yourself anymore, but you can define a key for each and every database field. As a gratis you never have to do the same for id fields, since fluent has built-in support for identifiers.

extension FieldKey {
-    static var title: Self { "title" }
-}
-
-// model definition
-@ID() var id: UUID?
-@Field(key: .title) var title: String
-
-// migration
-.id()
-.field(.title, .string, .required)
-

Identifiers are now UUID types by default

Using the new @ID property wrapper and the .id() migration function will automatically require your models to have a UUID value by default. This is a great change, because I don’t really like serial identifiers. If you want to go use integers as identifiers you can still do it. Also you can define UUID fields with the old-school syntax, but if you go so you can have some troubles with switching to the new MongoDB driver, so please don’t do it. 🥺

// custom int identifier (won't work with MongoDB driver)
-@ID(custom: "todo_id")
-var id: Int?
-
-// custom id type & field name (you have to generate it)
-@ID(custom: "todo_identifier", generatedBy: .user)
-var id: String?
-
-// old-school uuid field migration
-.field("id", .uuid, .identifier(auto: false))
-

How to store native database enums?

If you want to store enums using Fluent you have two options now. The first one is that you simply save your enums as native values (int, string, etc.), if you do so you just need an enum with a new field of the given type, plus you have to conform the enum to the Codable protocol.

// model definition
-enum Status: String, Codable {
-    case pending
-    case completed
-}
-
-@Field(key: "status") var status: Status
-
-// migration (you can use the .int or .string type)
-.field("status", .string, .required)
-

The second option is to use the new @Enum field type and migrate everything using the enum builder. This method requires more setup, but I think it’s going to worth it on the long term.

// model definition
-extension FieldKey {
-    static var status: Self { "status" }
-}
-
-enum Status: String, Codable, CaseIterable {
-    static var name: FieldKey { .status }
-
-    case pending
-    case completed
-}
-
-@Enum(key: .status) var status: Status
-
-// migration
-struct CreateTodo: Migration {
-    func prepare(on database: Database) -> EventLoopFuture<Void> {
-        var enumBuilder = database.enum(Todo.Status.name.description)
-        for option in Todo.Status.allCases {
-            enumBuilder = enumBuilder.case(option.rawValue)
-        }
-        return enumBuilder.create()
-        .flatMap { enumType in
-            database.schema(Todo.schema)
-                .id()
-                .field(.title, .string, .required)
-                .field(.status, enumType, .required)
-                .create()
-        }
-    }
-
-    func revert(on database: Database) -> EventLoopFuture<Void> {
-        return database.schema(Todo.schema).delete().flatMap {
-            database.enum(Todo.Status.name.description).delete()
-        }
-    }
-}
-

The main advantage of this approach that Fluent can take advantage of the database driver’s built-in enum type support. Also if you want to store native enums you have to migrate the fields if you introduce a new case. You can read more about this in the beta release notes. I can’t tell you which one is the best way, since this is a brand new feature, I have to run some tests. ✅

Saving option sets in Fluent

There is a great post written by Bastian Inuk about managing user roles using option sets in Fluent. You should definitely take a look if you want to use an OptionSet as a Fluent property. Anyway, I’ll show you how to create this type, so we’ll be able to flag our todo items. 🔴🟣🟠🟡🟢🔵⚪️

// model definition
-extension FieldKey {
-    static var labels: Self { "labels" }
-}
-
-struct Labels: OptionSet, Codable {
-    var rawValue: Int
-    
-    static let red = Labels(rawValue: 1 << 0)
-    static let purple = Labels(rawValue: 1 << 1)
-    static let orange = Labels(rawValue: 1 << 2)
-    static let yellow = Labels(rawValue: 1 << 3)
-    static let green = Labels(rawValue: 1 << 4)
-    static let blue = Labels(rawValue: 1 << 5)
-    static let gray = Labels(rawValue: 1 << 6)
-    
-    static let all: Labels = [.red, .purple, .orange, .yellow, .green, .blue, .gray]
-}
-
-@Field(key: .labels) var labels: Labels
-
-// migration
-.field(.labels, .int, .required)
-

There is a nice Option protocol OptionSet

Storing dates

Fluent can also store dates and times and convert them back-and-forth using the built-in Date object from Foundation. You just have to choose between the .date or .datetime storage types. You should go with the first one if you don’t care about the hours, minutes or seconds. The second one is good if you simply want to save the day, month and year. 💾

You should always go with the exact same TimeZone when you save / fetch dates from the database. When you save a date object that is in UTC, next time if you want to filter those objects and you use a different time zone (e.g. PDT), you’ll get back a bad set of results.

Here is the final example of our Todo model including the migration script:

// model definition
-final class Todo: Model, Content {
-
-    static let schema = "todos"
-    
-    enum Status: String, Codable {
-        case pending
-        case completed
-    }
-
-    struct Labels: OptionSet, Codable {
-        var rawValue: Int
-        
-        static let red = Labels(rawValue: 1 << 0)
-        static let purple = Labels(rawValue: 1 << 1)
-        static let orange = Labels(rawValue: 1 << 2)
-        static let yellow = Labels(rawValue: 1 << 3)
-        static let green = Labels(rawValue: 1 << 4)
-        static let blue = Labels(rawValue: 1 << 5)
-        static let gray = Labels(rawValue: 1 << 6)
-        
-        static let all: Labels = [
-            .red,
-            .purple,
-            .orange,
-            .yellow,
-            .green,
-            .blue,
-            .gray
-        ]
-    }
-
-    @ID() var id: UUID?
-    @Field(key: .title) var title: String
-    @Field(key: .status) var status: Status
-    @Field(key: .labels) var labels: Labels
-    @Field(key: .due) var due: Date?
-
-    init() { }
-
-    init(id: UUID? = nil,
-         title: String,
-         status: Status = .pending,
-         labels: Labels = [],
-         due: Date? = nil)
-    {
-        self.id = id
-        self.title = title
-        self.status = status
-        self.labels = labels
-        self.due = due
-    }
-}
-
-// migration
-struct CreateTodo: Migration {
-    func prepare(on database: Database) -> EventLoopFuture<Void> {
-        return database.schema(Todo.schema)
-            .id()
-            .field(.title, .string, .required)
-            .field(.status, .string, .required)
-            .field(.labels, .int, .required)
-            .field(.due, .datetime)
-            .create()
-    }
-
-    func revert(on database: Database) -> EventLoopFuture<Void> {
-        return database.schema(Todo.schema).delete()
-    }
-}
-

One more thing…

Nested fields & compound fields

Sometimes you might need to save additional structured data, but you don’t want to introduce a relation (e.g. attributes with different keys, values). This is when the @NestedField property wrapper comes extremely handy. I won’t include here an example, since I had no time to try this feature yet, but you can read more about it here with a working sample code.

The difference between a @CompoundField and a @NestedField is that a compound field is stored as a flat top level field in the database, but the other will be stored as a nested object.

Sets are now compatible with the array database type, you can use them like this: .field(.mySetField, .array(of: .string), .required)

I think we pretty much covered everything that you’ll need in order to create DB entities. We’ll have a quick detour here before we get into relations. 🚧

Schemas & migrations

The Todo object is more or less ready to use, but this is just one part of the whole story. We still need to create the actual database table that can store our objects in PostgreSQL. In order to create the DB schema based on our Swift code, we have to run the migration command.

Migration is the process of creating, updating or deleting one or more database tables. In other words, everything that alters the database schema is a migration. You should know that you can register multiple migration scripts and Vapor will run them always in the order they were added.

The name of your database table & the fields are declared in your model. The schema is the name of the table, and the property wrappers are containing the name of each field.

Nowadays I prefer to use a semantic version suffix for all my migration objects, this is really handy because I don’t have to think too much about the naming conventions, migration_v1_0_0 is always the create operation, everything comes after this version is just an altering the schema.

You can implement a var name: String { "custom-migration-name" } property inside the migration struct / class, so you don’t have to put special characters into your object’s name

You should be careful with relations! If you are trying to use a table with a field as a foreign key you have to make sure that the referenced object already exists, otherwise it’ll fail.

During the first migration Fluent will create an internal lookup table named _fluent_migrations. The migration system is using this table to detect which migrations were already performed and what needs to be done next time you run the migrate command.

In order to perform a migration you can launch the Run target with the migrate argument. If you pass the --auto-migrate flag you don’t have to confirm the migration process. Be careful. 😳

swift run Run migrate
-

You can revert the last batch of migrations by running the command with the –revert flag.

swift run Run migrate --revert
-

Here is a quick example how to run multiple schema updates by using flatten function. This migration simply removes the existing title field, and creates new unique name field.

extension FieldKey {
-    static var name: Self { "name" }
-}
-
-struct UpdateTodo: Migration {
-
-    func prepare(on database: Database) -> EventLoopFuture<Void> {
-        database.eventLoop.flatten([
-            database.schema(Todo.schema)
-                .deleteField(.title)
-                .update(),
-            database.schema(Todo.schema)
-                .field(.name, .string, .required)
-                .unique(on: .name)
-                .update(),
-            // you can also create objects in migration scripts
-            Todo(name: "Hello world").save(on: database),
-        ])
-    }
-    
-    func revert(on database: Database) -> EventLoopFuture<Void> {
-        database.eventLoop.flatten([
-            database.schema(Todo.schema)
-                .deleteField(.name)
-                .update(),
-            database.schema(Todo.schema)
-                .field(.title, .string, .required)
-                .update(),
-        ])
-    }
-}
-

Feel free to go ahead, migrate the Todo scheme so we can write some queries.

Querying

Again I have to refer to the official 4.0 Fluent docs. Please go ahead read the querying section carefully, and come back to this article. The TodoController also provides a basic Swift sample code. IMHO a controller is an interactor, nowadays I’m using VIPER on the backend side as well (article coming soon). Here are a few CRUD practices. 😅

Creating multiple records at once

This one is simple, please note that the save method in Fluent behaves like an upsert command. If your model exists, it’ll update otherwise it calls the create function. Anyway you can always call create on a bunch of models to perform a batch insert.

let todos = [
-    Todo(title: "Publish new article tomorrow"),
-    Todo(title: "Finish Fluent tutorial"),
-    Todo(title: "Write more blog posts"),
-]
-todos.create(on: req.db)
-

Batch delete records

You can query all the required records using filters and call the .delete() method on them.

Todo.query(on: req.db)
-        .filter(\.$status == .completed)
-        .delete()
-

How to update or delete a single record?

If you know the object identifier it’s pretty simple, the Model protocol has a find method for this purpose. Otherwise you can query the required object and request the first one.

Fluent is asynchronous by default, this means that you have to work a lot with Futures and Promises. You can read my tutorial for beginners about promises in Swift.

You can use the .map or .flatMap methods to perform the necessary actions & return a proper response. The .unwrap function is quite handy, since you don’t have to unwrap optionals by hand in the other blocks. Block based syntax = you have to deal with memory management. 💩

// update an existing record (find by uuid)
-_ = Todo.find(uuid, on: req.db)
-.unwrap(or: Abort(.notFound))
-.flatMap { todo -> EventLoopFuture<Void> in
-    todo.title = ""
-    return todo.save(on: req.db)
-}
-
-//delete an existing record (find first using filters)
-_ = Todo.query(on: req.db)
-    .filter(\.$title == "Hello world")
-    .first()
-    .unwrap(or: Abort(.notFound))
-    .flatMap { $0.delete(on: req.db) }
-

That’s it about creating, requesting, updating and deleting entities.

Relations

Sometimes you want to store some additional information in a separate database. In our case for example we could make a dynamic tagging system for the todo items. These tags can be stored in a separate table and they can be connected to the todos by using a relation. A relation is nothing more than a foreign key somewhere in the other table or inside a pivot.

One-to-one relations

Fluent supports one-to-many relations out of the box. The documentation clearly explains everything about them, but I’d like to add a few notes, time to build a one-to-many relation.

If you want to model a one-to-one relation the foreign key should be unique for the related table. Let’s add a detail table to our todo items with a separately stored description field.

extension FieldKey {
-    static var todoId: Self { "todo_id" }
-    static var description: Self { "description" }
-}
-
-final class Detail: Model, Content {
-
-    static let schema = "details"
-
-    @ID() var id: UUID?
-    @Parent(key: .todoId) var todo: Todo
-    @Field(key: .description) var description: String
-
-    init() { }
-
-    init(id: UUID? = nil, description: String, todoId: UUID) {
-        self.id = id
-        self.description = description
-        self.$todo.id = todoId
-    }
-}
-

The model above has a parent relation to a Todo object through a todo_id field. In other words, we simply store the original todo identifier in this table. Later on we’ll be able to query the associated descriptions by using this foreign key. Let me show you the migration:

struct CreateTodo: Migration {
-    
-    func prepare(on database: Database) -> EventLoopFuture<Void> {
-        database.eventLoop.flatten([
-            database.schema(Todo.schema)
-                .id()
-                .field(.title, .string, .required)
-                .field(.status, .string, .required)
-                .field(.labels, .int, .required)
-                .field(.due, .datetime)
-                .create(),
-            database.schema(Detail.schema)
-                .id()
-                .field(. todoId, .uuid, .required)
-                .foreignKey(.todoId, references: Todo.schema, .id, onDelete: .cascade, onUpdate: .noAction)
-                .field(.description, .string, .required)
-                .unique(on: .todoId)
-                .create(),
-        ])
-    }
-
-    func revert(on database: Database) -> EventLoopFuture<Void> {
-        database.eventLoop.flatten([
-            database.schema(Detail.schema).delete(),
-            database.schema(Todo.schema).delete(),
-        ])
-    }
-}
-

The final step here is to extend the Todo model with the child reference.

@Children(for: \.$todo) var details: [Detail]
-

Creating a relation only takes a few lines of Swift code

let todo = Todo(title: "Finish the Fluent article already")
-todo.create(on: app.db)
-.flatMap { _ in
-    Detail(description: "write some cool things about Fluent relations",
-           todoId: todo.id!).create(on: req.db)
-}
-

Now if you try to add multiple details to the same todo object the you won’t be able to perform that DB query, since the todo_id has a unique constraint, so you must be extremely carful with these kind of operations. Apart from this limitation (that comes alongside with a one-to-one relation) you use both objects as usual (find by id, eager load the details from the todo object, etc.). 🤓

One-to-many relations

A one-to-many relation is just like a one-to-one, except that you can associate multiple objects with the parent. You can even use the same code from above, you just have to remove the unique constraint from the migration script. I’ll add some grouping feature to this todo example.

// todo group model
-final class Group: Model, Content {
-
-    static let schema = "groups"
-
-    @ID() var id: UUID?
-    @Field(key: .name) var name: String
-    @Children(for: \.$group) var todos: [Todo]
-
-    init() { }
-
-    init(id: UUID? = nil, name: String) {
-        self.id = id
-        self.name = name
-    }
-}
-
-// extended todo model
-final class Todo: Model, Content {
-    //...other fields
-    @Parent(key: .groupId) var group: Group
-    @Children(for: \.$todo) var details: [Detail]
-
-    init() { }
-
-    init(id: UUID? = nil,
-         title: String,
-         status: Status = .pending,
-         labels: Labels = [],
-         due: Date? = nil,
-         groupId: UUID)
-    {
-        self.id = id
-        self.title = title
-        self.status = status
-        self.labels = labels
-        self.due = due
-        self.$group.id = groupId
-    }
-}
-
-// migration
-struct CreateTodo: Migration {
-    
-    func prepare(on database: Database) -> EventLoopFuture<Void> {
-        database.eventLoop.flatten([
-            database.schema(Group.schema)
-                .id()
-                .field(.name, .string, .required)
-                .create(),
-            database.schema(Todo.schema)
-                .id()
-                .field(.title, .string, .required)
-                .field(.status, .string, .required)
-                .field(.labels, .int, .required)
-                .field(.due, .datetime)
-                .field(. groupId, .uuid, .required)
-                .foreignKey(.groupId, references: Group.schema, .id)
-                .create(),
-            database.schema(Detail.schema)
-                .id()
-                .field(. todoId, .uuid, .required)
-                .foreignKey(.todoId, references: Todo.schema, .id, onDelete: .cascade, onUpdate: .noAction)
-                .field(.description, .string, .required)
-                .unique(on: .todoId) //enforce a one-to-one relation
-                .create(),
-            Group(name: "Default").create(on: database),
-        ])
-    }
-
-    func revert(on database: Database) -> EventLoopFuture<Void> {
-        database.eventLoop.flatten([
-            database.schema(Detail.schema).delete(),
-            database.schema(Todo.schema).delete(),
-            database.schema(Group.shcema).delete(),
-        ])
-    }
-}
-

From now on, you’ll have to insert the todos into a group. It’s ok to create a default one in the migration script, so later on it’s possible to get the id reference of the pre-existing group.

// fetch default group & add a new todo
-Group.query(on: req.db)
-.first()
-.flatMap { group in
-    Todo(title: "This belongs to a group", groupId: group!.id!).create(on: app.db)
-}
-// eager load todos in the group
-Group.query(on: req.db)
-    .with(\.$todos)
-    .all()
-.whenSuccess { groups in
-    for group in groups {
-        print(group.name)
-        print(group.todos.map { "- \($0.title)" }.joined(separator: "\n"))
-    }
-}
-

If you want to change a parent, you can simply set the new identifier using the .$id syntax. Don’t forget to call update or save on the object, since it’s not enough just to update the relation in memory, but you have to persist everything back to the database. 💡

Many-to-many relations

You can create an association between two tables by using a third one that stores foreign keys from both of the original tables. Sounds fun? Welcome to the world of many-to-many relations. They are useful if you want to build a tagging system or a recipe book with ingredients.

Again, Bastian Inuk has a great post about how to use siblings in Fluent 4. I just want to add one extra thing here: you can store additional information on the pivot table. I’m not going to show you this time how to associate ingredients with recipes & amounts, but I’ll put some tags on the todo items with an important flag option. Thanks buddy! 😜

extension FieldKey {
-    static var name: Self { "name" }
-    static var todoId: Self { "todo_id" }
-    static var tagId: Self { "tag_id" }
-    static var important: Self { "important" }
-}
-
-// Tag.swift
-final class Tag: Model, Content {
-
-    static let schema = "tags"
-
-    @ID() var id: UUID?
-    @Field(key: .name) var name: String
-    @Siblings(through: TodoTags.self, from: \.$tag, to: \.$todo) var todos: [Todo]
-    
-    init() { }
-
-    init(id: UUID? = nil, name: String) {
-        self.id = id
-        self.name = name
-    }
-}
-
-// a cross table for the relation between the todos and the tags
-final class TodoTags: Model {
-
-    static let schema = "todo_tags"
-    
-    @ID() var id: UUID?
-    @Parent(key: .todoId) var todo: Todo
-    @Parent(key: .tagId) var tag: Tag
-    @Field(key: .important) var important: Bool
-    
-    init() {}
-    
-    init(todoId: UUID, tagId: UUID, important: Bool) {
-        self.$todo.id = todoId
-        self.$tag.id = tagId
-        self.important = important
-    }
-}
-
-// Todo.swift property extension
-//...
-@Siblings(through: TodoTags.self, from: \.$todo, to: \.$tag) var tags: [Tag]
-//...
-
-// the migration script extension
-//...
-database.schema(Tag.schema)
-    .id()
-    .field(.name, .string, .required)
-    .create(),
-database.schema(TodoTags.schema)
-    .id()
-    .field(.todoId, .uuid, .required)
-    .field(.tagId, .uuid, .required)
-    .field(.important, .bool, .required)
-    .create(),
-//...
-database.schema(Tag.schema).delete(),
-database.schema(TodoTags.schema).delete(),
-//...
-

The only new thing here is the siblings property wrapper which defines the connection between the two tables. It’s awesome that Fluent can handle these complex relations in such a nice way.

The code snippet below is for educational purposes only, you should never use the .wait() method in a real-world application, use futures & promises instead.

Finally we’re able to tag our todo items, plus we can mark some of them as important. 🎊

let defaultGroup = try Group.query(on: app.db).first().wait()!
-
-let shoplist = Group(name: "Shoplist")
-let project = Group(name: "Awesome Fluent project")
-try [shoplist, project].create(on: app.db).wait()
-
-let family = Tag(name: "family")
-let work = Tag(name: "family")
-try [family, work].create(on: app.db).wait()
-
-let smoothie = Todo(title: "Make a smoothie",
-                    status: .pending,
-                    labels: [.purple],
-                    due: Date(timeIntervalSinceNow: 3600),
-                    groupId: defaultGroup.id!)
-
-let apples = Todo(title: "Apples", groupId: shoplist.id!)
-let bananas = Todo(title: "Bananas", groupId: shoplist.id!)
-let mango = Todo(title: "Mango", groupId: shoplist.id!)
-
-let kickoff = Todo(title: "Kickoff meeting",
-                   status: .completed,
-                   groupId: project.id!)
-
-let code = Todo(title: "Code in Swift",
-                labels: [.green],
-                groupId: project.id!)
-
-let deadline = Todo(title: "Project deadline",
-                    labels: [.red],
-                    due: Date(timeIntervalSinceNow: 86400 * 7),
-                    groupId: project.id!)
-
-try [smoothie, apples, bananas, mango, kickoff, code, deadline].create(on: app.db).wait()
-
-let familySmoothie = TodoTags(todoId: smoothie.id!, tagId: family.id!, important: true)
-let workDeadline = TodoTags(todoId: deadline.id!, tagId: work.id!, important: false)
-
-try [familySmoothie, workDeadline].create(on: app.db).wait()
-

That’s it, now we’re ready with our awesome todo application. 😎

Conclusion

Fluent is a crazy powerful tool. You can easily make the switch between the available drivers. You don’t even have to write SQL if you are using an ORM tool, but only Swift code, which is nice.

Server side Swift and all the related tools are evolving fast. The whole Vapor community is doing such a great job. I hope this article will help you to understand Fluent way better. 💧

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/getting-started-with-swiftio/index.html b/docs/getting-started-with-swiftio/index.html deleted file mode 100644 index 4eb482e..0000000 --- a/docs/getting-started-with-swiftio/index.html +++ /dev/null @@ -1,409 +0,0 @@ - - - - - - - - - - - - Getting started with SwiftIO - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 9 min read - -
-

Getting started with SwiftIO

-
-

SwiftIO is an electronic circuit board that runs Swift on the bare metal. It can control sensors, displays, lights, motors and more.

-
- -

The SwiftIO board

MadMachine’s SwiftIO board is an Arduino-like system, but it can run Swift code natively on the hardware. Swift is a great programming language for education purposes, it is a modern language with a JavaScript-like syntax that is easy to learn and understand. Swift is safe and efficient, this combination makes it a perfect candidate for embedded systems. The SwiftIO board enables us regular Swift / iOS developers to build IoT projects using our favorite programming language.

SwiftIO board

The board itself has a small footprint, it’s just about 1,57” x 2,36” (4cm x 6cm) big.

It’s quite a powerful microcontroller. It is definitely the most affordable Swift language learning device. It can be ideal for STEM education purposes for students who want to see how to build electronic circuits and get some basic Swift programming skills at the same time.

The Maker Kit box contains both a 7 segment and an LCD display, a servo and a DC motor with drivers, a humiture (temperature & humidity) sensor, a buzzer module a potentiometer and a light sensor and many more other things (breadboard, resistors, LEDs, buttons and cables, etc.) that you can use to build the starter projects. You will have pretty much everything in the box that you need as a newcomer to get started and have some fun time using SwiftIO. 📦

Unfortunately you won’t find a working GPS, WiFi or Bluetooth module for the SwiftIO board yet, but hopefully these kind of accessories are just a few months away. There is a new shield and a brand new screen module in the works. Oh by the way a shield is something that you can plug into your board and connect external accessories using “special” colorful cables. 🌈

Shields are boards that can be plugged on top of the Arduino PCB extending its capabilities. The different shields follow the same philosophy as the original toolkit: they are easy to mount, and cheap to produce. - ArduinoShields

The board initially went on sale in July, 2020 and many people brought it from all around the world. New plug-ins, extensions and modules are still being developed by the makers, but it is important to emphasize that the SwiftIO board hardware is in a stable state. 💪

Technical specifications:

  • i.MX RT1052 Crossover Processor with Arm® Cortex®-M7 core @600MHz
  • Micro SD card slot, supporting standard and high capacity SD cards
  • Micro USB connector for power
  • On-board USB to UART for serial communication
  • 46 GPIO on left and right edges
  • On-board GRB LED
  • 12x 12-bit analog to digital (ADC) converters
  • 4 UART, 2 CAN, 2 IIC, and 2 SPI
  • 14 PWM pins
  • Many additional advanced features to meet the needs of advanced users

Ports, communication, architecture

You can connect the SwiftIO board to your computer via a USB connector, the two devices can communicate through a serial port. The USB cable will provide the necessary power support, but alternatively you can use an adapter or an external battery through a shield.

SwiftIO ports

You can wire up additional components using the General Purpose Input/Output pins (GPIO). You can see the exact specification on the picture above, but honestly I barely understand this diagram.

Confession time: I don’t know shit about electronics (yet). 💩

The board can be used with a shield and fortunately the MakersKit arrives with a handy instruction manual for absolute beginners. I felt lucky, because I was able to approach this little gadget with my programmer mindset and I could focus more on Swift instead of building working circuits.

So what’s actually under the hood? How can I build & run applications for SwiftIO?

SwiftIO architecture

The gray area is the hardware itself, on top of that in the pink-ish/purple boxes there is this Zephyr layer which I’ve never heard before. I’m just scratching the surface here, but that’s fine, since as a Swift developer we only care about the orange stuff. Long story short, there is a custom built Swift toolchain for this device that allows us to build and run Swift applications on the board. 😅

If there is a toolchain, then we can run Swift binaries, but how do we communicate with the ports and stuff like that? Fortunately the SwiftIO framework is here for us. It provides an easy access to communicate with external hardware accessories. You can read or write digital and analog signals using the communication protocol. Are you ready for some Swift code?

The SwiftIO environment

Although the hardware is stable, the software is not finished yet. There are two options available, but if you are on a Windows machine, you should grab the official MadMachine IDE and use that. The reason is that the Swift Package Manager is not ready for that platform yet, so you won’t be able to work with the tools that I’ll show you next. Sorry Windows, no offense. 😅

So option A, is to go with the IDE, it’s really easy to use and provides a great DX. Option B, learn a bit more about the underlying toolkit and follow my guide, it’s a bit more complicated, but you’ll know more about the technical infrastructure if you choose this path. 🤔

Installing the MadMachine SDK & CLI

As a macOS or Linux user you should know that you can install the unofficial MadMachine SDK alongside a command line tool called mm. As you can see this MadMachine SDK is written in Swift, but you should know that I ported a python script from the original mm-sdk project with the help of Marcus Kida. First, he made an Xcode template by invoking the original script inside the official MadMachine IDE, then I thought, it would be cool to get rid of the IDE for good, and now, here we go, we have a standalone Swift version of the necessary build tools. You can install it like this:

git clone https://github.com/EmbeddedSwift/MadMachine.git
-cd MadMachine
-make install
-

Now you should be ready to use the mm cli app, you can check the available commands inside the README file on GitHub. I have not tested this on Linux yet, so if you find any issues, please don’t hesitate to report or submit a PR. This is an experimental project, so keep this in mind. 😅

The MadMachine toolchain

In order to use the MadMachine SDK you will need a working toolchain installed on your computer. You can grab the latest one by running the following command:

mm toolchain --upgrade
-

This will grab the latest release from the unofficial MadMachine toolchain repository, and place it under your home folder inside the .MadMachine directory. There is one additional thing that now you have to do before you could start building SwiftIO apps. Currently there is one extra python script that was not ported yet, because it will be completely eliminated in the future. For now you still have to download the official MadMachine SDK from the GitHub releases page and place the entire contents of the mm-sdk/tools_[platform]/scripts/dist/gen_isr_tables folder into the ~/.MadMachine/legacy directory. You might have to create a legacy folder. 🙈

The SwiftIO framework

The SwiftIO framework is going to be linked with the application binary, we have to install it (with the help of the mm-cli tool) as a system library first. There’s an unofficial repo with a makefile for this:

git clone https://github.com/EmbeddedSwift/SwiftIO
-cd SwiftIO
-make install
-

You can find the library reference for the SwiftIO framework, but we’ll see you can make it work, in just a few moments. Before we go further you should note that the custom MadMachine toolchain is a modified version of the Swift 5.1 toolchain. This means that you can’t use Swift 5.3 on the board yet, but hopefully the creators of SwiftIO will release new software components real soon. 🤞

Using Xcode

The SwiftIO framework can be compiled on your local machine with the local Swift toolchain (using Xcode), so it is possible to build applications without targeting the board, and later on you can re-compile the source files with the mm-cli command, sign the final binary and deploy it to the SwiftIO board after you’ve pressed the download button. This is the current workflow in a nutshell.

There is an existing Xcode template created by @kidmar that you can use as a starting point.

Using SPM

Nowadays I prefer to create a Swift package for almost everything. You can use SPM with a Makefile and your favorite editor to create SwiftIO apps. You just have to initialize a new executable package with the necessary dependencies, for example:

// swift-tools-version:5.3
-import PackageDescription
-
-let package = Package(
-    name: "myProject",
-    products: [
-        .executable(name: "myProject", targets: ["myProject"]),
-    ],
-    dependencies: [
-        .package(url: "https://github.com/EmbeddedSwift/SwiftIO", .branch("main")),
-        .package(url: "https://github.com/EmbeddedSwift/SHT3x", .branch("main")),
-        .package(url: "https://github.com/EmbeddedSwift/LCD1602", .branch("main")),
-    ],
-    targets: [
-        .target(name: "myProject", dependencies: [
-            .product(name: "SwiftIO", package: "SwiftIO"),
-            .product(name: "SHT3x", package: "SHT3x"),
-            .product(name: "LCD1602", package: "LCD1602"),
-        ]),
-    ]
-)
-

Inside the main.swift file now you can write a simple Humiture sensor app that displays the current humidity & temperature on a 16x2 LCD display like this:

import SwiftIO
-import SHT3x
-import LCD1602
-
-// Get a number with one decimal place.
-extension Float {
-    func format(_ f: Int) -> Float {
-        guard f > 0 else {return self}
-        var mul = 10
-        for _ in 1..<f {
-            mul *= 10
-        }
-        let data = Int(self * Float(mul))
-        return Float(data) / Float(mul)
-    }
-}
-
-// Initialize the LCD and sensor to use the I2C communication.
-let i2c = I2C(Id.I2C0)
-let lcd = LCD1602(i2c)
-let sht = SHT3x(i2c)
-
-while true{
-    // Read and display the temperature on the LCD and update the value every 1s.
-    let temp = sht.readTemperature()
-    lcd.write(x: 0, y: 0, "Temperature:")
-    lcd.write(x: 0, y: 1, String(temp.format(1)))
-    lcd.write(x: 4, y: 1, " ")
-    lcd.write(x: 5, y: 1, "C")
-    sleep(ms: 1000)
-}
-

Now if you open the Package.swift file using Xcode you can even build the project locally using the CMD+B shortcut, but don’t try to run it, since your Mac is not a SwiftIO board… 😅

If you want to build and run this project you have to target the SwiftIO board. The mm-cli can’t resolve package dependencies (yet) so you have to install the necessary dependencies (SHT3x, LCD1602) the same way as we did with the SwiftIO library. You have to clone both repositories and use the makefile to install them as local MadMachine system libraries. This will move the required files under the ~/.MadMachine/libraries folder, all the available libraries “live” there. 🔨

After you’ve installed the libraries, you can use the following Makefile for the actual binary builds:

build:
-    mm build --name myProject --binary-type executable --input . --output ./myProject
-
-run: build
-    mm board --run ./myProject/swiftio.bin
-
-clean:
-    rm -r ./myProject
-

The mm build command invokes the swiftc compiler from the SwiftIO toolchain with the right search paths and library search paths. The mm board --run [binary] command will simply copy the necessary files to the MadMachine board if it is in download mode. You have to press the download button on the board before you run this command. Pressing the download button will mount the SD card so we can deploy the signed binary to the device, then eject the storage, which will restart the board and the new application will be loaded & executed right away. 🚀

The MadMachine IDE

If you don’t like to play with command line utilities, you can always use the MadMachine IDE to build and deliver your projects. It uses a mmp file to describe dependencies, so it can resolve them on the fly, but I don’t like the fact that I have to use a new package manager and editor to work with Swift. I’m in a love and hate relationship with Xcode (plus I’m an old-school Sublime Text fan). ❤️

Anyway, you can find really great examples with a dedicated GitHub repository and many video tutorials on the official YouTube channel. You can access all the MakerKit sample codes, they are both on GitHub and you can clone a template with a click using the MadMachine IDE.

Conclusion

For me, the SwiftIO board was a pleasant surprise. I always wanted to play with embedded systems, know a little more about hardware and low level stuff, but the Raspberry PI and the Arduino felt like alien planet. Once I’ve tried to build an app for a Raspberry PI at a hackaton, but it was a complete disaster, I was disappointed, because the tools and the developer experience was really bad.

The promise of MadMachine is that you can easily use Swift on such a device and I believe that Andy Liu created something that can have a bright future on the long term. I felt in love with SwiftIO right after I’ve assembled my first circuit and deployed my very first Swift source. It was a seamless (pain free) experience and I believe that’s a really important factor for developers who never used such devices before. This can be the beginning and the future of Swift on embedded systems. 😍

SwiftIO

You can build some really fun stuff with SwiftIO, it’s a perfect tool for learning how electric circuits work. I can’t wait to see more and more sensors and libraries available for MadMachine. If you have an idea or a question feel free to join the official Discord server.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/how-to-build-better-command-line-apps-and-tools-using-swift/index.html b/docs/how-to-build-better-command-line-apps-and-tools-using-swift/index.html deleted file mode 100644 index 95d4c30..0000000 --- a/docs/how-to-build-better-command-line-apps-and-tools-using-swift/index.html +++ /dev/null @@ -1,509 +0,0 @@ - - - - - - - - - - - - How to build better command line apps and tools using Swift? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

How to build better command line apps and tools using Swift?

-
-

These tips will help you to create amazing CLI tools, utility apps, server side projects or terminal scripts using the Swift language.

-
- -

Running Swift files as scripts

It is possible to run a Swift file straight from the command line if you add a hashbang to the beginning of the file. This way you don’t have to manually compile the code using the swiftc command. You can simply give the file the executable permission flag and the system will call the Swift REPL under the hood, so our app can be evaluated automatically. 🔨

#!/usr/bin/env swift
-
-print("Hello, world!")
-

For example this main.swift file above can be marked as an executable file, and we can simply call it via the ./main.swift command later on (you just have to use chmod only one time).

chmod +x main.swift 
-./main.swift  
-# Hello, world!
-

The beauty of this method is that you can rapidly test your Swift command line snippets. You can even place the finished Swift scripts under the /usr/local/bin/ directory without the swift file extension to make them available “globally” for your operating system user. 💪

Using command line arguments in Swift

The CommandLine enum makes it very easy to fetch the arguments passed to our Swift application or script. You can access every argument using the arguments variable as an array of Strings, but it is also possible to get the raw data using the argc and unsafeArgv properties.

#!/usr/bin/env swift
-
-/// the very first element is the current script
-let script = CommandLine.arguments[0]
-print("Script:", script)
-
-/// you can get the input arguments by dropping the first element
-let inputArgs = CommandLine.arguments.dropFirst()
-print("Number of arguments:", inputArgs.count)
-
-print("Arguments:")
-for arg in inputArgs {
-    print("-", arg)
-}
-

You should note that the first argument is always the path of the current script, so if you are only looking for the input arguments you can use the dropFirst() method to return a subset of the input strings. Usually each argument is separated by a space character.

./main.swift hello world
-# Script: main.swift
-# Number of arguments: 2
-# Arguments:
-# - hello
-# - world
-

In Xcode you can add custom arguments under the Edit Scheme… menu item when you click on the current scheme, look for the Arguments tab and use the Arguments Passed On Launch section.

Process info and environment in Swift
Just like we can access command line arguments, it is possible to examine the current process including some hardware information and environment variables.

#!/usr/bin/env swift
-import Foundation
-
-let info = ProcessInfo.processInfo
-
-print("Process info")
-print("Process identifier:", info.processIdentifier)
-print("System uptime:", info.systemUptime)
-print("Globally unique process id string:", info.globallyUniqueString)
-print("Process name:", info.processName)
-
-print("Software info")
-print("Host name:", info.hostName)
-print("OS major version:", info.operatingSystemVersion.majorVersion)
-print("OS version string", info.operatingSystemVersionString)
-
-print("Hardware info")
-print("Active processor count:", info.activeProcessorCount)
-print("Physical memory (bytes)", info.physicalMemory)
-
-/// same as CommandLine.arguments
-print("Arguments")
-print(ProcessInfo.processInfo.arguments)
-
-print("Environment")
-/// print available environment variables
-print(info.environment)
-

The environment variables property is a Dictionary where both the keys and the values are available as strings, so you might have to parse them if you are looking for different value types. You can set up environment custom variables in Xcode just like arguments, or you can pass them via the command line before you execute the Swift script using the export command.

Standard input and output in Swift

You can use the print function to write text to the standard output, but you should note that the print function has a variadic items definition, so you can pass around multiple arguments and a custom separator & terminator parameter to display more advanced outputs.

There is also a standard error stream, which is part of the standard streams of course, but what’s interesting about it is that you can also write to this channel through the FileHandle.standardError property there is quite an elegant solution on a Stack Overflow thread originally created by Rob Napier, I’m going to include that one here as well. 🙏

Another great feature of the print function is the to parameter, which can accept a custom TextOutputStream so you can wrap the stderr stream in a custom object or you can also create custom output handlers and separate your print statements e.g. by context if you need.

#!/usr/bin/env swift
-import Foundation
-
-/// print using custom separator & terminator
-print("This", "is", "fun", separator: "-", terminator: "!")
-
-/// write to the standard output
-"This goes to the standard error output"
-    .data(using: .utf8)
-    .map(FileHandle.standardError.write)
-
-/// print to the standard output using a custom stream
-final class StandardErrorOutputStream: TextOutputStream {
-    func write(_ string: String) {
-        FileHandle.standardError.write(Data(string.utf8))
-    }
-}
-
-var outputStream = StandardErrorOutputStream()
-print("This is also an error", to: &outputStream)
-
-
-/// clears the console (@NOTE: won't work in Xcode)
-func clear() {
-    print("\u{1B}[2J")
-    print("\u{1B}[\(1);\(0)H", terminator: "")
-}
-
-print("foooooooooooooooooooooo")
-clear()
-print("Hello, world!")
-
-
-/// print colorful text using ANSI escape codes
-print("\u{1b}[31;1m\u{1b}[40;1m\("Hello, world!")\u{1b}[m")
-print("\u{1b}[32;1m\("Hello, world!")\u{1b}[m")
-
-/// reading lines from the standard input
-print("Please enter your input:")
-guard let input = readLine(strippingNewline: true) else {
-    fatalError("Missing input")
-}
-print(input)
-

The second half of the snippet is full of ANSI escape codes which I like quite a lot, because it can make our terminal output quite beautiful. The only problem is that they don’t work in Xcode at all (come-on Apple, please support this…). You can clear the console or change the background / foreground color of the output by using these codes.

There are quite a lot of libraries on GitHub that you can use to print colorful output, for example ColorizeSwift, ANSITerminal, ANSIEscapeCode and many more cool ones.

The very last thing that I’d like to show you is the readLine function, which you can use to read a line from the standard input. This comes handy if you need to get user input from the command line.

Use an argument parser library

If you are looking for a type-safe argument parser written in Swift, you should definitely take a look at the Swift Argument Parser library. It is created and maintained by Apple, so it’s kind of an official solution for this particular issue, but IMHO it lacks some advanced features.

This is the main reason why I prefer the Vapor command API built on top of the ConsoleKit library. Both libraries can parse arguments, options and flags, but ConsoleKit is also capable of displaying progress indicators, it features multiple command groups, secure input, auto-completion, multiple log levels and many more.

/// HelloCommand.swift
-import Foundation
-import ConsoleKit
-
-final class HelloCommand: Command {
-        
-    struct Signature: CommandSignature {
-
-        @Argument(name: "name", help: "The name to say hello")
-        var name: String
-
-        @Option(name: "greeting", short: "g", help: "Greeting used")
-        var greeting: String?
-
-        @Flag(name: "capitalize", short: "c", help: "Capitalizes the name")
-        var capitalize: Bool
-    }
-
-    static var name = "hello"
-    let help = "This command will say hello to a given name."
-
-    func run(using context: CommandContext, signature: Signature) throws {
-        let greeting = signature.greeting ?? "Hello"
-        var name = signature.name
-        if signature.capitalize {
-            name = name.capitalized
-        }
-        print("\(greeting) \(name)!")
-        
-        /// progress bar
-        let bar = context.console.progressBar(title: "Hello")
-        bar.start()
-        /// perform some work...
-        // bar.fail()
-        bar.succeed()
-        
-        /// input
-        let foo = context.console.ask("What?")
-        print(foo)
-        
-        /// secure input
-        let baz = context.console.ask("Secure what?", isSecure: true)
-        print(baz)
-        
-        /// choice
-        let c = context.console.choose("Make a choice", from: ["foo", "bar", "baz"])
-        print(c)
-
-        /// @Tip: look for more options under the context.console property.
-    }
-}
-
-/// main.swift
-import Foundation
-import ConsoleKit
-
-let console: Console = Terminal()
-var input = CommandInput(arguments: CommandLine.arguments)
-var context = CommandContext(console: console, input: input)
-
-var commands = Commands(enableAutocomplete: true)
-commands.use(HelloCommand(), as: HelloCommand.name, isDefault: false)
-
-do {
-    let group = commands.group(help: "Using ConsoleKit without Vapor.")
-    try console.run(group, input: input)
-}
-catch {
-    console.error("\(error)")
-    exit(1)
-}
-

You can use both solution through the Swift Package Manager, the setup process is quite easy, you’ll find more tutorials about the Swift Argument Parser and I think that it is harder to find proper docs for ConsoleKit, so yeah… anyway, they’re great libraries you won’t regret using them. 😉

Take advantage of the Swift Package Manager

The Swift Package Manager is one of the best thing about the Swift programming language. I really love it and I use it almost every day. The fact that the package manifest file is defined using Swift itself makes it easy to use & understand.

// swift-tools-version:5.5
-import PackageDescription
-
-let package = Package(
-    name: "myProject",
-    platforms: [
-        .macOS(.v10_15)
-    ],
-    dependencies: [
-        .package(url: "https://github.com/vapor/console-kit", from: "4.1.0"),
-    ],
-    targets: [
-        .executableTarget(name: "myProject",dependencies: [
-            .product(name: "ConsoleKit", package: "console-kit"),
-        ]),
-        .testTarget(name: "myProjectTests", dependencies: ["myProject"]),
-    ]
-)
-

The package manager evolved quite a lot during the past few months, if you take a look at the Swift Evolution dashboard you can track these changes, the most recent update was the introduction of custom, user-defined Package Collections, but if you are looking for packages you can always take a look at the Swift Package Index website. 👍

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/how-to-build-macos-apps-using-only-the-swift-package-manager/index.html b/docs/how-to-build-macos-apps-using-only-the-swift-package-manager/index.html deleted file mode 100644 index dc67272..0000000 --- a/docs/how-to-build-macos-apps-using-only-the-swift-package-manager/index.html +++ /dev/null @@ -1,478 +0,0 @@ - - - - - - - - - - - - How to build macOS apps using only the Swift Package Manager? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

How to build macOS apps using only the Swift Package Manager?

-
-

In this article we're going to create a macOS application without ever touching an Xcode project file, but only working with SPM.

-
- -

Swift scripts and macOS apps

Swift compiler 101, you can create, build and run a Swift file using the swiftc command. Consider the most simple Swift program that we can all imagine in a main.swift file:

print("Hello world!")
-

In Swift if we want to print something, we don’t even have to import the Foundation framework, we can simply compile and run this piece of code by running the following:

swiftc main.swift   # compile main.swift
-chmod +x main       # add the executable permission
-./main          # run the binary
-

The good news that we can take this one step further by auto-invoking the Swift compiler under the hood with a shebang.

#! /usr/bin/swift
-
-print("Hello world!")
-

Now if you simply run the ./main.swift file it’ll print out the famous “Hello world!” text. 👋

Thanks to the program-loader mechanism and of course the Swift interpreter we can skip an extra step and run our single-source Swift code as easy as a regular shell script. The good news is that we can import all sort of system frameworks that are part of the Swift toolchain. With the help of Foundation we can build quite useful or completely useless command line utilities.

#!/usr/bin/env swift
-
-import Foundation
-import Dispatch
-
-guard CommandLine.arguments.count == 2 else {
-    fatalError("Invalid arguments")
-}
-let urlString =  CommandLine.arguments[1]
-guard let url = URL(string: urlString) else {
-    fatalError("Invalid URL")   
-}
-
-struct Todo: Codable {
-    let title: String
-    let completed: Bool
-}
-
-let task = URLSession.shared.dataTask(with: url) { data, response, error in 
-    if let error = error {
-        fatalError("Error: \(error.localizedDescription)")
-    }
-    guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
-        fatalError("Error: invalid HTTP response code")
-    }
-    guard let data = data else {
-        fatalError("Error: missing response data")
-    }
-
-    do {
-        let decoder = JSONDecoder()
-        let todos = try decoder.decode([Todo].self, from: data)
-        print("List of todos:")
-        print(todos.map { " - [" + ($0.completed ? "✅" : "❌") + "] \($0.title)" }.joined(separator: "\n"))
-        exit(0)
-    }
-    catch {
-        fatalError("Error: \(error.localizedDescription)")
-    }
-}
-task.resume()
-dispatchMain()
-

If you call this example with a URL that can return a list of todos it’ll print a nice list of the items.

./main.swift https://jsonplaceholder.typicode.com/todos
-

Yes, you can say that this script is completely useless, but in my opinion it’s an amazing demo app, since it covers how to check command line arguments (CommandLine.arguments), it also shows you how to wait (dispatchMain) for an async task, such as a HTTP call through the network using the URLSession API to finish and exit using the right method when something fails (fatalError) or if you reach the end of execution (exit(0)). Just a few lines of code, but it contains so much info.

Have you noticed the new shebang? If you have multiple Swift versions installed on your system, you can use the env shebang to go with the first one that’s available in your PATH.

It’s not just Foundation, but you can import AppKit or even SwiftUI. Well, not under Linux of course, since those frameworks are only available for macOS plus you will need Xcode installed on your system, since some stuff in Swift the toolchain is still tied to the IDE, but why? 😢

Anyway, back to the topic, here’s the boilerplate code for a macOS application Swift script that can be started from the Terminal with one simple ./main.swift command and nothing more.

#!/usr/bin/env swift
-
-import AppKit
-import SwiftUI
-
-@available(macOS 10.15, *)
-struct HelloView: View {
-    var body: some View {
-        Text("Hello world!")
-    }
-}
-
-@available(macOS 10.15, *)
-class WindowDelegate: NSObject, NSWindowDelegate {
-
-    func windowWillClose(_ notification: Notification) {
-        NSApplication.shared.terminate(0)
-    }
-}
-
-
-@available(macOS 10.15, *)
-class AppDelegate: NSObject, NSApplicationDelegate {
-    let window = NSWindow()
-    let windowDelegate = WindowDelegate()
-
-    func applicationDidFinishLaunching(_ notification: Notification) {
-        let appMenu = NSMenuItem()
-        appMenu.submenu = NSMenu()
-        appMenu.submenu?.addItem(NSMenuItem(title: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q"))
-        let mainMenu = NSMenu(title: "My Swift Script")
-        mainMenu.addItem(appMenu)
-        NSApplication.shared.mainMenu = mainMenu
-        
-        let size = CGSize(width: 480, height: 270)
-        window.setContentSize(size)
-        window.styleMask = [.closable, .miniaturizable, .resizable, .titled]
-        window.delegate = windowDelegate
-        window.title = "My Swift Script"
-
-        let view = NSHostingView(rootView: HelloView())
-        view.frame = CGRect(origin: .zero, size: size)
-        view.autoresizingMask = [.height, .width]
-        window.contentView!.addSubview(view)
-        window.center()
-        window.makeKeyAndOrderFront(window)
-        
-        NSApp.setActivationPolicy(.regular)
-        NSApp.activate(ignoringOtherApps: true)
-    }
-}
-
-let app = NSApplication.shared
-let delegate = AppDelegate()
-app.delegate = delegate
-app.run()
-

Special thanks goes to karwa for the original gist. Also if you are into Storyboard-less macOS app development, you should definitely take a look at this article by @kicsipixel. These resources helped me a lot to put together what I needed. I still had to extend the gist with a proper menu setup and the activation policy, but now this version acts like a real-world macOS application that works like a charm. There is only one issue here… the script file is getting crowded. 🙈

Swift Package Manager and macOS apps

So, if we follow the same logic, that means we can build an executable package that can invoke AppKit related stuff using the Swift Package Manager. Easy as a pie. 🥧

mkdir MyApp
-cd MyApp 
-swift package init --type=executable
-

Now we can separate the components into standalone files, we can also remove the availability checking, since we’re going to add a platform constraint using our Package.swift manifest file. If you don’t know much about how the Swift Package Manager works, please read my SPM tutorial, or if you are simply curious about the structure of a Package.swift file, you can read my article about the Swift Package manifest file. Let’s start with the manifest updates.

// swift-tools-version:5.3
-import PackageDescription
-
-let package = Package(
-    name: "MyApp",
-    platforms: [
-        .macOS(.v10_15)
-    ],
-    dependencies: [
-        // .package(url: /* package url */, from: "1.0.0"),
-    ],
-    targets: [
-        .target(name: "MyApp", dependencies: []),
-        .testTarget(name: "MyAppTests", dependencies: ["MyApp"]),
-    ]
-)
-

Now we can place the HelloView struct into a new HelloView.swift file.

import SwiftUI
-
-struct HelloView: View {
-    var body: some View {
-        Text("Hello world!")
-    }
-}
-

The window delegate can have its own place inside a WindowDelegate.swift file.

import AppKit
-
-class WindowDelegate: NSObject, NSWindowDelegate {
-
-    func windowWillClose(_ notification: Notification) {
-        NSApplication.shared.terminate(0)
-    }
-}
-

We can apply the same thing to the AppDelegate class.

import AppKit
-import SwiftUI
-
-class AppDelegate: NSObject, NSApplicationDelegate {
-    let window = NSWindow()
-    let windowDelegate = WindowDelegate()
-
-    func applicationDidFinishLaunching(_ notification: Notification) {
-        let appMenu = NSMenuItem()
-        appMenu.submenu = NSMenu()
-        appMenu.submenu?.addItem(NSMenuItem(title: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q"))
-        let mainMenu = NSMenu(title: "My Swift Script")
-        mainMenu.addItem(appMenu)
-        NSApplication.shared.mainMenu = mainMenu
-        
-        let size = CGSize(width: 480, height: 270)
-        window.setContentSize(size)
-        window.styleMask = [.closable, .miniaturizable, .resizable, .titled]
-        window.delegate = windowDelegate
-        window.title = "My Swift Script"
-
-        let view = NSHostingView(rootView: HelloView())
-        view.frame = CGRect(origin: .zero, size: size)
-        view.autoresizingMask = [.height, .width]
-        window.contentView!.addSubview(view)
-        window.center()
-        window.makeKeyAndOrderFront(window)
-        
-        NSApp.setActivationPolicy(.regular)
-        NSApp.activate(ignoringOtherApps: true)
-    }
-}
-

Finally we can update the main.swift file and initiate everything that needs to be done.

import AppKit
-
-let app = NSApplication.shared
-let delegate = AppDelegate()
-app.delegate = delegate
-app.run()
-

The good news is that this approach works, so you can develop, build and run apps locally, but unfortunately you can’t submit them to the Mac App Store, since the final application package won’t look like a real macOS bundle. The binary is not code signed, plus you’ll need a real macOS target in Xcode to submit the application. Then why bother with this approach?

Well, just because it is fun and I can even avoid using Xcode with the help of SourceKit-LSP and some Editor configuration. The best part is that SourceKit-LSP is now part of Xcode, so you don’t have to install anything special, just configure your favorite IDE and start coding.

You can also bundle resources, since this feature is available from Swift 5.3, and use them through the Bundle.module variable if needed. I already tried this, works pretty well, and it is so much fun to develop apps for the mac without the extra overhead that Xcode comes with. 🥳

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

How to build macOS apps using only the Swift Package Manager?

-
-

In this article we're going to create a macOS application without ever touching an Xcode project file, but only working with SPM.

- -
- macOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

How to launch a macOS app at login?

-
-

In this tutorial I'll show you how to launch a completely sandboxed macOS application on system startup written in Swift.

- -
- Tooling - macOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Networking examples for appleOS

-
-

Learn how to use Bonjour, with UDP/TCP sockets, streams and how to communicate through CoreBluetooth or the watch APIs.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/how-to-build-swiftui-apps-using-viper/index.html b/docs/how-to-build-swiftui-apps-using-viper/index.html deleted file mode 100644 index c42fa66..0000000 --- a/docs/how-to-build-swiftui-apps-using-viper/index.html +++ /dev/null @@ -1,666 +0,0 @@ - - - - - - - - - - - - How to build SwiftUI apps using VIPER? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 10 min read - -
-

How to build SwiftUI apps using VIPER?

-
-

In this tutorial I'll show you how to combine SwiftUI with the VIPER architecture in a real world iOS application example.

-
- -

SwiftUI - the new kid on the block

There are literally hundreds of SwiftUI tutorials around the web, but I was only able to find just one or two that focuses on real world use cases instead of the smaller details like how to configure / make X in SwiftUI. Nice tutorials @mecid keep it up!

I also had my own “struggle” with SwiftUI, because my collection view framework is structured exactly the same way as you write SwiftUI code. After WWDC I was like, hell no! I’m doing the same method for months now, so why should I care? I started to believe that some Apple engineers are reading my blog. 😂

Anyway I knew at day zero that a crazy amount of new SwiftUI tutorials will arrive and everyone will be hyped about the new declarative UI framework, but honestly I already had my universal toolkit for this purpose. That’s why I don’t wanted to write about it. Honestly I still love Combine much more than SwiftUI. I’m also quite disappointed since CollectionView is completely missing from the framework.

Finally, just because what the heck lets try new things and I was curious about how SwiftUI can fit into my app building methodology I started to create a new VIPER template based on those kind of views. I also wanted to make a useful, scalable, modular real world application example using the new framework, which is up-to-date. A lot has changed in SwiftUI during the Xcode 11 beta period, so that’s why I’m only publishing this tutorial now. Enough chatter, shouldn’t we code already? 😛

Learn a modern VIPER architecture

I’ve spent my last two years using the VIPER architecture. Some people say “it’s way too complex” or “it’s not a good fit for small teams”. I can only tell them one word:

Bullshit!

I believe that I’ve created a modern & relatively simple pattern that can be used for literally anything. Learning VIPER will definitely improve your code quality thanks to the clean architecture and the SOLID principles. You’ll have a better understanding of how smaller pieces can work together and communicate with each other.

Isolated smaller components can speed up development, because you just have to work on a little piece at once, plus you can create tests for that particular thing, which is a huge win for testability & code coverage (you don’t have to run your app all the time if you want to test something, you can work on the module you just need).

I’m usually working with a really simple code generator to fire up new modules, this way I can save a lot of time. If you have to work alone on a project the module generator and the predefined structure can even save you some more time. Also you really can’t mess up things or end up with massive files if you are following the basic VIPER rules. I’ll teach you my method in about 10 minutes. ⏰

What the heck is VIPER anyway?

If you never heard about VIPER before, the first thing you should know is that a VIPER module contains the following components:

  • View = UIViewController subclass or SwiftUI View
  • Interactor = Provides the required data in the proper format
  • Presenter = UI independent business logic (what to do exactly)
  • Entity = Data objects (sometimes it’s missing from the module)
  • Router = Builds up the view controller hierarchy (show, present, dismiss, etc)

I always have a module file next to these ones where I define a module builder which builds up the whole thing from the components above and in that file I also define the module specific protocols. I usually name these protocols as interfaces they make it possible that any of the components can be replaced using dependency injection. This way we can test anything by using mocked objects in our unit tests.

Some say that a VIPER module with a Builder is called VIPER/B. I think the module file is an ideal place to store your module builder object, the module interfaces and the module delegate if you need one.

Protocol oriented VIPER architecture

So the key is the 6 main protocol that connects View-Interactor-Presenter-Router. These protocols ensure that none of the VIPER components can see more than it’s required. If you go back to my first tutorial you’ll see that I made a mistake there. The thing is that you can call a method on the router through the presenter from the view, which is bad. This new approach fixes that issue. 🐛

View-to-Presenter
-Presenter-to-View
-
-Router-to-Presenter
-Presenter-to-Router
-
-Interactor-to-Presenter
-Presenter-to-Interactor
-
-
-Module
-# ---
-builds up pointers and returns a UIViewController
-
-
-View implements View-to-Presenter
-# ---
-strong presenter as Presenter-to-View-interface
-
-
-Presenter implements Presenter-to-Router, Presenter-to-Interactor, Presenter-to-View
-# ---
-strong router as Router-to-Presenter-interface
-strong interactor as Interactor-to-Presenter-interface
-weak view as View-to-Presenter-interface
-
-
-Interactor implements Interactor-to-Presenter
-# ---
-weak presenter as Presenter-to-Interactor-interface
-
-
-Router implemenents Presenter-to-Router
-# ---
-weak presenter as Presenter-to-Router-interface
-

As you can see the view (which can be a UIViewController subclass) holds the presenter strongly and the presenter will retain the interactor and router classes. Everything else is a weak pointer, because we don’t like retain cycles. It might seems a little bit complicated at first sight, but after writing your first few modules you’ll see how nice is to separate logical components from each other. 🐍

Please note that not everything is a VIPER module. Don’t try to write your API communication layer or a CoreLocation service as a module, because those kind of stuff are standalone let’s say: services. I’ll write about them in the next one, but for now let’s just focus on the anatomy of a VIPER module.

Generic VIPER implementation in Swift 5

Are you ready to write some Swift code? All right, let’s create some generic VIPER interfaces that can be extended later on, don’t be afraid won’t be that hard. 😉

// MARK: - interfaces
-
-public protocol RouterPresenterInterface: class {
-
-}
-
-public protocol InteractorPresenterInterface: class {
-
-}
-
-public protocol PresenterRouterInterface: class {
-
-}
-
-public protocol PresenterInteractorInterface: class {
-
-}
-
-public protocol PresenterViewInterface: class {
-
-}
-
-public protocol ViewPresenterInterface: class {
-
-}
-
-// MARK: - viper
-
-public protocol RouterInterface: RouterPresenterInterface {
-    associatedtype PresenterRouter
-
-    var presenter: PresenterRouter! { get set }
-}
-
-public protocol InteractorInterface: InteractorPresenterInterface {
-    associatedtype PresenterInteractor
-
-    var presenter: PresenterInteractor! { get set }
-}
-
-public protocol PresenterInterface: PresenterRouterInterface & PresenterInteractorInterface & PresenterViewInterface {
-    associatedtype RouterPresenter
-    associatedtype InteractorPresenter
-    associatedtype ViewPresenter
-
-    var router: RouterPresenter! { get set }
-    var interactor: InteractorPresenter! { get set }
-    var view: ViewPresenter! { get set }
-}
-
-public protocol ViewInterface: ViewPresenterInterface {
-    associatedtype PresenterView
-
-    var presenter: PresenterView! { get set }
-}
-
-public protocol EntityInterface {
-
-}
-
-// MARK: - module
-
-public protocol ModuleInterface {
-
-    associatedtype View where View: ViewInterface
-    associatedtype Presenter where Presenter: PresenterInterface
-    associatedtype Router where Router: RouterInterface
-    associatedtype Interactor where Interactor: InteractorInterface
-
-    func assemble(view: View, presenter: Presenter, router: Router, interactor: Interactor)
-}
-
-public extension ModuleInterface {
-
-    func assemble(view: View, presenter: Presenter, router: Router, interactor: Interactor) {
-        view.presenter = (presenter as! Self.View.PresenterView)
-
-        presenter.view = (view as! Self.Presenter.ViewPresenter)
-        presenter.interactor = (interactor as! Self.Presenter.InteractorPresenter)
-        presenter.router = (router as! Self.Presenter.RouterPresenter)
-
-        interactor.presenter = (presenter as! Self.Interactor.PresenterInteractor)
-
-        router.presenter = (presenter as! Self.Router.PresenterRouter)
-    }
-}
-

Associated types are just placeholders for specific types, by using a generic interface design I can assemble my modules with a generic module interface extension and if some protocol is missing the app will crash just as I try to initialize the bad module.

I love this approach, because it saves me from a lot of boilerplate module builder code. Also this way everything will have a base protocol, so I can extend anything in a really neat protocol oriented way. Anyway if you don’t understand generics that’s not a big deal, in the actual module implementation you will barely meet them.

So how does an actual module looks like?

// TodoModule.swift
-
-// MARK: - router
-
-protocol TodoRouterPresenterInterface: RouterPresenterInterface {
-
-}
-
-// MARK: - presenter
-
-protocol TodoPresenterRouterInterface: PresenterRouterInterface {
-
-}
-
-protocol TodoPresenterInteractorInterface: PresenterInteractorInterface {
-
-}
-
-protocol TodoPresenterViewInterface: PresenterViewInterface {
-
-}
-
-// MARK: - interactor
-
-protocol TodoInteractorPresenterInterface: InteractorPresenterInterface {
-
-}
-
-// MARK: - view
-
-protocol TodoViewPresenterInterface: ViewPresenterInterface {
-
-}
-
-
-// MARK: - module builder
-
-final class TodoModule: ModuleInterface {
-
-    typealias View = TodoView
-    typealias Presenter = TodoPresenter
-    typealias Router = TodoRouter
-    typealias Interactor = TodoInteractor
-
-    func build() -> UIViewController {
-        let view = View()
-        let interactor = Interactor()
-        let presenter = Presenter()
-        let router = Router()
-
-        self.assemble(view: view, presenter: presenter, router: router, interactor: interactor)
-
-        router.viewController = view
-
-        return view
-    }
-}
-
-
-// TodoPresenter.swift
-
-final class TodoPresenter: PresenterInterface {
-    var router: TodoRouterPresenterInterface!
-    var interactor: TodoInteractorPresenterInterface!
-    weak var view: TodoViewPresenterInterface!
-}
-
-extension TodoPresenter: TodoPresenterRouterInterface {
-
-}
-
-extension TodoPresenter: TodoPresenterInteractorInterface {
-
-}
-
-extension TodoPresenter: TodoPresenterViewInterface {
-
-}
-
-// TodoInteractor.swift
-
-final class TodoInteractor: InteractorInterface {
-    weak var presenter: TodoPresenterInteractorInterface!
-}
-
-extension TodoInteractor: TodoInteractorPresenterInterface {
-
-}
-
-// TodoRouter.swift
-
-final class TodoRouter: RouterInterface {
-    weak var presenter: TodoPresenterRouterInterface!
-    weak var viewController: UIViewController?
-}
-
-extension TodoRouter: TodoRouterPresenterInterface {
-
-}
-
-// TodoView.swift
-
-final class TodoView: UIViewController, ViewInterface {
-    var presenter: TodoPresenterViewInterface!
-}
-
-extension TodoView: TodoViewPresenterInterface {
-
-}
-

A VIPER module is made from five files, which is a huge improvement compared to my old method (I used 9 files for a single module, which is still better than a 2000 lines of code massive view controller, but yeah it was quite many files… 😂 ).

You can use my VIPER protocol library if you want or simply copy & paste these interfaces to your project. I also have a module generator written entirely in Swift that can generate a module based on this template (or you can make your own).

How to build VIPER interfaces?

Let me explain a sample flow real quick, consider the following example:

protocol TodoRouterPresenterInterface: RouterPresenterInterface {
-    func dismiss()
-}
-
-// MARK: - presenter
-
-protocol TodoPresenterRouterInterface: PresenterRouterInterface {
-
-}
-
-protocol TodoPresenterInteractorInterface: PresenterInteractorInterface {
-    func didLoadWelcomeText(_ text: String)
-}
-
-protocol TodoPresenterViewInterface: PresenterViewInterface {
-    func ready()
-    func close()
-}
-
-// MARK: - interactor
-
-protocol TodoInteractorPresenterInterface: InteractorPresenterInterface {
-    func startLoadingWelcomeText()
-}
-
-// MARK: - view
-
-protocol TodoViewPresenterInterface: ViewPresenterInterface {
-    func setLoadingIndicator(visible: Bool)
-    func setWelcomeText(_ text: String)
-}
-

The view calls ready() on the presenter at some point in time viewDidLoad(), so the presenter can kick off. First it tells the view to show the loading indicator by calling setLoadingIndicator(visible: true), next asks the interactor to load the welcome text asynchronously startLoadingWelcomeText(). After the data arrives back to the interactor it can notify the presenter by using the didLoadWelcomeText("") method. The presenter can now tell the view to hide the loading indicator using the same method setLoadingIndicator(visible: false) this time with a false parameter and to display the welcome text by using setWelcomeText("").

Another use case is that someone taps a button on the view in order to close the controller. The view calls close() on the presenter, and the presenter can simply call dismiss() on the router. The presenter can also do some other stuff (like cleaning up some resources) before it asks the router to dismiss the view controller.

I hope that you get the example, feel fee to implement everything by your own, it’s quite a nice task to practice. Of course you can utilize blocks, promises or the brand new Combine framework to make your live more easy. You can for example auto-notify the presenter if some async data loading have finished. 😉

So now that you have a basic understanding about a modern VIPER architecture lets talk about how to replace the traditional ViewController subclass with SwiftUI.

How to design a VIPER based SwiftUI application?

SwiftUI is quite a unique beast. View are structs so our generic VIPER protocol needs some alterations in order to make everything work.

The first thing you have to do is to get rid of the ViewPresenterInterface protocol. Next you can remove the view property from the PresenterInterface since we’re going to use an observable view-model pattern to auto-update the view with data. The last modification is that you have to remove the view parameter from the default implementation of the assemble function inside the ModuleInterface extension.

So I mentioned a view-model, let’s make one. For the sake of simplicity I’m going to use an error Bool to indicate if something went wrong, but you could use another view, or a standalone VIPER module that presents an alert message.

import Combine
-import SwiftUI
-
-final class TodoViewModel: ObservableObject {
-
-    let objectWillChange = ObservableObjectPublisher()
-
-    @Published var error: Bool = false {
-        willSet {
-            self.objectWillChange.send()
-        }
-    }
-
-    @Published var todos: [TodoEntity] = [] {
-       willSet {
-            self.objectWillChange.send()
-        }
-    }
-}
-

This class conforms to the ObservableObject which makes SwiftUI possible to check for updates & re-render the view hierarchy if something changed. You just need a property with the ObservableObjectPublisher type and literally send() a message if something will change this trigger the auto-update in your views. 🔥

The TodoEntity is just a basic struct that conforms to a bunch of protocols like the new Identifiable from SwiftUI, because we’d like to display entities in a list.

import Foundation
-import SwiftUI
-
-struct TodoEntity: EntityInterface, Codable, Identifiable {
-    let id: Int
-    let title: String
-    let completed: Bool
-}
-

A basic SwiftUI view will still implement the ViewInterface and it’ll have a reference to the presenter. Our view-model property is also going to be used here marked with an @ObservedObject property wrapper. This is how it looks like in code so far:

import SwiftUI
-
-struct TodoView: ViewInterface, View {
-
-    var presenter: TodoPresenterViewInterface!
-
-    @ObservedObject var viewModel: TodoViewModel
-
-    var body: some View {
-        Text("SwiftUI ❤️ VIPER")
-    }
-}
-

The presenter will also have a weak var viewModel: TodoViewModel! reference to be able to update the the view-model. Seems like we have a two-way communication flow between the view and the presenter by using a view-model. Looks good to me. 👍

We can also utilize the brand new @EnvironmentObject if we want to pass around some data in the view hierarchy. You just have to implement the same observation protocol in your environment object that we did for the view-model. For example:

import Foundation
-import Combine
-
-final class TodoEnvironment: ObservableObject {
-
-    let objectWillChange = ObservableObjectPublisher()
-
-    @Published var title: String = "Todo list" {
-       willSet {
-            self.objectWillChange.send()
-        }
-    }
-}
-

Finally let me show you how to implement the module builder, because that’s quite tricky. You have to use the new generic UIHostingController, which is thankfully an UIViewController subclass so you can return it after you finish module building.

final class TodoModule: ModuleInterface {
-    typealias View = TodoView
-    typealias Presenter = TodoPresenter
-    typealias Router = TodoRouter
-    typealias Interactor = TodoInteractor
-
-    func build() -> UIViewController {
-        let presenter = Presenter()
-        let interactor = Interactor()
-        let router = Router()
-
-        let viewModel = TodoViewModel()
-        let view = View(presenter: presenter, viewModel: viewModel)
-            .environmentObject(TodoEnvironment())
-        presenter.viewModel = viewModel
-
-        self.assemble(presenter: presenter, router: router, interactor: interactor)
-
-        let viewController = UIHostingController(rootView: view)
-        router.viewController = viewController
-        return viewController
-    }
-}
-

Putting together the pieces from now is just a piece of cake. If you want, you can challenge yourself to build something without downloading the final project. 🍰

Well, if you’re not into challenges that’s fine too, feel free to grab the example code from The.Swift.Dev tutorials on GitHub. It contains a nice interactor with some cool networking stuff using URLSession and the Combine framework. The final SwiftUI code is just a rough implementation, because as I told you in the beginning there are really good tutorials about SwiftUI with examples.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

How to build SwiftUI apps using VIPER?

-
-

In this tutorial I'll show you how to combine SwiftUI with the VIPER architecture in a real world iOS application example.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

How to write services for VIPER?

-
-

Not everything is a VIPER module. In this article I'll show you how do I separate the service layer from the modules, using Swift.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Mastering the VIPER architecture

-
-

Learn how to master the VIPER architectural design pattern, with some protocol oriented programming techniques using Swift.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

The ultimate VIPER architecture tutorial

-
-

Learn how to write scalable iOS code using the VIPER architecture with some MVVM and MVC tricks and coordinators in mind.

- -
- VIPER -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/how-to-call-c-code-from-swift/index.html b/docs/how-to-call-c-code-from-swift/index.html deleted file mode 100644 index 60726be..0000000 --- a/docs/how-to-call-c-code-from-swift/index.html +++ /dev/null @@ -1,389 +0,0 @@ - - - - - - - - - - - - How to call C code from Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 3 min read - -
-

How to call C code from Swift

-
-

Interacting with C libraries from the Swift language is really amazing, from this post can learn the most of C interoperability.

-
- -

From Swift 4 there is native support for wrapping C libraries in Swift system module packages. This means that you can easily ship your own system modules, you just have to learn how to use the Swift Package Manager.😅

Bridging header inside Xcode

Let’s fire up Xcode and start a brand new single view app iOS project. Fill out the required fields, and of course choose the Swift language. Next, add a new file and choose the C file template.

After you enter the name and check the also create header file box, Xcode will ask you about the Objective-C bridging header file. Just create it. The name of this file is tricky, because it also supports other C family languages, like pure C or C++, Objective-C and plus-plus. 😉

Let’s create a public header for the C code (factorial.h):

#ifndef factorial_h
-#define factorial_h
-
-#include <stdio.h>
-
-long factorial(int n);
-
-#endif /* factorial_h */
-

This is gona be the implementation of the method (factorial.c):

#include "factorial.h"
-
-long factorial(int n) {
-    if (n == 0 || n == 1) return 1;
-    return n * factorial(n-1);
-}
-

Inside the bridging header, simply import the C header file:

#include "factorial.h"
-

Somewhere inside a Swift file you can use the factorial method:

print("Hello \(factorial(5))!")
-// it actually prints out "Hello 120!" ;)
-

Compile and run. 🔨 It just works. 🌟 Magic! 🌟

You can do the exact same thing to use Objective-C classes inside your Swift projects. Apple has great docs about this technique, you should read that if you want to know more about mix and match.

Shipping C code with SPM

The real fun begins when you start using the Swift Package Manager to build C family based sources. From Swift 3.0 you can build C language targets with SPM. If you don’t know how to use the SPM tool, you should read my comprehensive tutorial about the Swift Package Manager first.

The only thing that you’ll need to do this is a proper directory structure (plus you’ll need the package description file), and the package manager will take care all the rest. Here is everything what you need to build the factorial example with SPM.

// swift-tools-version:4.0
-
-import PackageDescription
-
-let package = Package(
-    name: "cfactorial",
-    products: [
-        .library(name: "cfactorial", targets: ["cfactorial"]),
-    ],
-    targets: [
-        .target(
-            name: "cfactorial",
-            path: "./Sources/factorial"
-        ),
-    ]
-)
-

The directory structure should be something like this.

Sources
-    factorial
-        include
-            factorial.h
-        factorial.c
-

You should also change the #include "factorial.h" line inside the factorial.c file to #include "include/factorial.h" because we made a new include directory. This is NOT necessary, but if you don’t put your umbrella header into the include directory, you’ll need to provide a modulemap file, and provide the correct location of your header. If you use the include structure SPM will generate everything for you.

With this technique you can import your cfactorial module from any other Swift package and call the factorial method, like we did through Xcode. You just have to add this module as a dependency, oh by the way you can even call this module from another C project made with SPM! 💥

.package(url: "https://gitlab.com/theswiftdev/cfactorial", .branch("master")),
-

Congratulations, you just shipped your first C code with Swift Package Manager. This setup also works with C, C++, Objective-C, Objective-C++ code.

Wrapping C [system] modules with SPM

If you want to wrap a C [system] library and call it directly from Swift you can crete a brand new wrapper package with the help of the Swift Package Manager. To start you can use the swift package init --type system-module command, this will create a generic template project.

These are special packages according to Apple, you just have to ship your own modulemap and a header file to expose the needed APIs, but first - obviously - you’ll need the usual package definition file:

// swift-tools-version:4.0
-
-import PackageDescription
-
-let package = Package(
-    name: "ccurl",
-    providers: [
-        .brew(["curl"]),
-        .apt(["libcurl4-openssl-dev"])
-    ]
-)
-

Inside the Package.swift file you can set the providers for the library (like brew on macOS or aptitude for Ubuntu / Debian and the others). Here is a good advice for you: sudo apt-get install pkg-config under Linux to make things work, because the system will search for package header files with the help of the pkgConfig property. For example if you want to use libxml2 and pkg-config is not installed, you won’t be able to compile / use your system module.

Next you’ll need a module.modulemap file, which is pretty straightforward.

module ccurl [system] {
-    header "shim.h"
-    link "curl"
-    export *
-}
-

About the link property see the Xcode release notes search for “auto-linking”

Finally add an extra shim.h header file to import all the required APIs. Usually I don’t like to import directly the required header files from the modulemap file that’s why I am using this shim.h - name it like you want - you’ll see in a second why am I preferring this method, but here is a basic one.

#ifndef CLIB_SWIFT_CURL
-#define CLIB_SWIFT_CURL
-
-#import <curl/curl.h>;
-
-#endif
-

Let’s talk about why I like importing the shim file. If you have platform differences you can use a neat trick with the help of using macros, for example you can import header files from different locations if you check for the __APPLE__ platform macro.

#ifndef CLIB_SWIFT_EXAMPLE
-#define CLIB_SWIFT_EXAMPLE
-
-#ifdef __APPLE__
-    #include "/usr/local/include/example.h"
-#else
-    #include "/usr/include/example.h"
-#endif
-
-#endif
-

Cool, huh? 🍎 + 🔨 = ❤️

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/how-to-create-a-swift-package-collection/index.html b/docs/how-to-create-a-swift-package-collection/index.html deleted file mode 100644 index e0d701d..0000000 --- a/docs/how-to-create-a-swift-package-collection/index.html +++ /dev/null @@ -1,365 +0,0 @@ - - - - - - - - - - - - How to create a Swift package collection? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 4 min read - -
-

How to create a Swift package collection?

-
-

In this tutorial I'm going to show you how to create your own package collection from your favorite Swift libraries.

-
- -

What is a Swift package collection?

A Swift package collection is a curated list of packages. Swift Package Manager users can subscribe to these collections, this way they are going to be able to search libraries and discover new ones. A collection is not equal with a package index or registry service, but usually means a smaller (somehow related) group of Swift packages.

For example if you take a look at the Swift Package Index website, it’s all about discovering new Swift packages, but each author can have its own collection, so if we go to the Vapor page, there you can see the URL of the Swift package collection link. This collection only contains those packages that are authored by Vapor. It’s just a small, curated subset of the entire content of the Swift Package Index website.

So we can say that a package registry service is focused on hosting and serving package sources, a package index service is all about discovering and searching packages and a package collection is usually a smaller curated list that can be easily shared with others. It can be a list of your preferred Swift dependencies that your company uses for building new projects. 💡

Preparing the environment

In order to create a package collection, you’ll have to install a tool called Swift package collection generator. It was created by Apple and it was introduced in this WWDC session in 2021.

You can install the package collection generator by running these commands:

git clone https://github.com/apple/swift-package-collection-generator
-cd swift-package-collection-generator 
-swift build --configuration release
-
-sudo install .build/release/package-collection-generate /usr/local/bin/package-collection-generate
-sudo install .build/release/package-collection-diff /usr/local/bin/package-collection-diff
-sudo install .build/release/package-collection-sign /usr/local/bin/package-collection-sign
-sudo install .build/release/package-collection-validate /usr/local/bin/package-collection-validate
-

You’ll also need a certificate and a key in order to sign a package collection. Signing packages are not required, but it is recommended. The signature can be added with the package-collection-sign command, but first of all you’ll need a developer certificate from the Apple developer portal. 🔨

Before you go to the dev portal, simply launch the Keychain Access app and use the Keychain Access > Certificate Assitant > Request a Certificate from a Certificate Authority menu item to generate a new CertificateSigningRequest.certSigningRequest file. Double check your email address and select the Saved to disk option and press the Continue button to generate the file.

Now you can use the CSR file to generate a new certificate using the Apple dev portal. Press the plus icon next to the Certificates text and scroll down to the Services section, there you should see a Swift Package Collection Certificate option, select that one and press the Continue button. Upload your CSR file and press Continue again, now you should be able to download the certificate that can be used to properly sign your Swift package collections. 🖊

We still have to export the private key that’s behind the certificate and we also have to convert it to the right format before we can start dealing with the contents of the package collection itself. Double click the downloaded certificate file, this will add it to your keychain. Find the certificate (click My Certificates on the top), right click on it and choose the Export menu item, save the Certificates.p12 file somewhere on your disk. Don’t forget to add password protection to the exported file, otherwise the key extraction won’t work.

Now we should use the openssl to extract the private key from the p12 file using an RSA format.

openssl pkcs12 -nocerts -in Certificates.p12 -out key.pem && openssl rsa -in key.pem -out rsa_key.pem
-

Run the command and enter the password that you’ve used to export the p12 file. This command should extract the required key using the proper format for the package collection sign command. You’ll need both the downloaded certificate and the RSA key file during the package creation. 📦

Building a Swift package collection

It is time to create a brand new Swift package collection. I’m going to build one for my Swift repositories located under the Binary Birds organization. Everything starts with a JSON file.

{
-    "name": "Binary Birds packages",
-    "overview": "This collection contains the our favorite Swift packages.",
-    "author": {
-        "name": "Tibor Bödecs"
-    },
-    "keywords": [
-        "favorite"
-    ],
-    "packages": [
-        {
-            "url": "https://github.com/binarybirds/swift-html"
-        },
-        {
-            "url": "https://github.com/BinaryBirds/liquid"
-        },
-        {
-            "url": "https://github.com/BinaryBirds/liquid-kit"
-        },
-        {
-            "url": "https://github.com/BinaryBirds/liquid-local-driver"
-        },
-        {
-            "url": "https://github.com/BinaryBirds/liquid-aws-s3-driver"
-        },
-        {
-            "url": "https://github.com/BinaryBirds/spec"
-        }
-    ]
-}
-

You can read more about the Package Collection format file on GitHub, but if you want to stick with the basics, it is pretty much self-explanatory. You can give a name and a short overview description to your collection, set the author, add some related keywords to improve the search experience and finally define the included packages via URLs.

Save this file using the input.json name. If you run the generate command with this input file it’ll try to fetch the repositories listed inside the JSON file. In order to get more metadata information about the GitHub repositories you can also provide an -auth-token parameter with your personal access token, you can read more about the available options by running the command with the -h or –help flag (package-collection-generate -h).

package-collection-generate input.json ./output.json
-

The generated output file will contain the required package collection metadata, but we still have to sign the output file if we want to properly use it as a collection file. Of course the sign step is optional, but it is recommend to work with signed collections. 😇

package-collection-sign output.json collection.json rsa_key.pem swift_package.cer
-

Finally you should upload your collection.json file to a public hosting service. For example I’ve created a simple SPM repository under my organization and I can use the raw file URL of the collection JSON file to use it with SPM or Xcode.

If you prefer the command line you have several options to manipulate Swift Package Collections. For more info you can read the related Swift Package Manager documentation, but here are some example commands that you can use to add, list, refresh search or remove a collection:

swift package-collection list
-swift package-collection add https://raw.githubusercontent.com/BinaryBirds/SPM/main/collection.json
-swift package-collection refresh
-swift package-collection search --keywords html
-
-swift package-collection remove https://raw.githubusercontent.com/BinaryBirds/SPM/main/collection.json
-

If you are developing apps using Xcode, you can use the Package Dependencies menu under your project settings to manage your package dependencies and use package collections.

Swift Package Collections are great if you want to organize your Swift libraries and you want to share them with others. If you are a heavy Xcode user you’ll enjoy using collections for sure. ☺️

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginner's guide to Swift package manager command plugins

-
-

Learn how to create command plugins for the Swift Package Manager to execute custom actions using SPM and other tools.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

How to create a Swift package collection?

-
-

In this tutorial I'm going to show you how to create your own package collection from your favorite Swift libraries.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Introduction to SPM artifact bundles

-
-

In this tutorial I'm going to show you how to use the new binary target related artifact bundle using the Swift package manager.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Swift Package Manager tutorial

-
-

Learn how to use the Swift Package Manager to handle external dependencies, create your library or app on macOS and Linux.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/how-to-create-reusable-views-for-modern-collection-views/index.html b/docs/how-to-create-reusable-views-for-modern-collection-views/index.html deleted file mode 100644 index e9f373c..0000000 --- a/docs/how-to-create-reusable-views-for-modern-collection-views/index.html +++ /dev/null @@ -1,507 +0,0 @@ - - - - - - - - - - - - How to create reusable views for modern collection views? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 6 min read - -
-

How to create reusable views for modern collection views?

-
-

A quick intro to modern collection views using compositional layout, diffable data source and reusable view components.

-
- -

Reusable views inside a generic cell

We all love to create custom views for building various user interface elements, right? We also love to use collection views to display data using a grid or a list layout. Collection view cells are custom views, but what if you’d like to use the exact same cell as a view?

Turns out that you can provide your own UIContentConfiguration, just like the built-in ones that you can use to setup cells to look like list items. If you take a look at the modern collection views sample code, which I highly recommend, you’ll see how to implement custom content configurations in order to create your own cell types. There are a few things that I don’t like about this approach. 😕

First of all, your view has to conform to the UIContentView protocol, so you have to handle additional config related stuff inside the view. I prefer the MVVM pattern, so this feels a bit strange. The second thing that you need is a custom cell subclass, where you also have to take care of the configuration updates. What if there was some other way?

Let’s start our setup by creating a new subclass for our future cell object, we’re simply going to provide the usual initialize method that I always use for my subclasses. Apple often calls this method configure in their samples, but they’re more or less the same. 😅

import UIKit
-
-open class CollectionViewCell: UICollectionViewCell {
-        
-    @available(*, unavailable)
-    private override init(frame: CGRect) {
-        super.init(frame: frame)
-        
-        self.initialize()
-    }
-
-    @available(*, unavailable)
-    public required init?(coder aDecoder: NSCoder) {
-        fatalError("init(coder) isn not available")
-    }
-    
-    open func initialize() {
-        
-    }
-
-}
-

All right, this is just a basic subclass so we don’t have to deal with the init methods anymore. Let’s create one more subclass based on this object. The ReusableCell type is going to be a generic type, it’s going to have a view property, which is going to be added as a subview to the contentView and we also pin the constraints to the content view.

import UIKit
-
-open class ReusableCell<View: UIView>: CollectionViewCell {
-    
-    var view: View!
-
-    open override func initialize() {
-        super.initialize()
-
-        let view = View()
-        view.translatesAutoresizingMaskIntoConstraints = false
-        contentView.addSubview(view)
-        self.view = view
-        
-        NSLayoutConstraint.activate([
-            view.topAnchor.constraint(equalTo: contentView.topAnchor),
-            view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
-            view.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
-            view.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
-        ])
-    }
-}
-

By using this reusable cell type, it’s going to be possible to add a custom view to the cell. We just need to create a new custom view, but that’s quite an easy task to do. ✅

import UIKit
-
-extension UIColor {
-
-    static var random: UIColor {
-        .init(red: .random(in: 0...1),
-              green: .random(in: 0...1),
-              blue: .random(in: 0...1),
-              alpha: 1)
-    }
-}
-
-class CustomView: View {
-
-    let label = UILabel(frame: .zero)
-
-    override func initialize() {
-        label.translatesAutoresizingMaskIntoConstraints = false
-        label.numberOfLines = 0
-        addSubview(label)
-        
-        // If you want to set a fixed height for the cell you can use this constraint...
-        // let fixedHeightConstraint = heightAnchor.constraint(equalToConstant: 120)
-        // fixedHeightConstraint.priority = .defaultHigh
-        backgroundColor = .random
-
-        NSLayoutConstraint.activate([
-            // fixedHeightConstraint,
-            label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8),
-            label.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8),
-            label.topAnchor.constraint(equalTo: topAnchor, constant: 8),
-            label.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8),
-        ])
-    }
-}
-

This custom view has a label, which we can pin to the superview with some extra padding. You can store all your subviews as strong properties, since Apple is going to take care of the deinit, even though the addSubview creates a strong reference, you don’t have to worry about it anymore.

If you want to create a cell that supports dynamic height, you should simply pin the edge layout constraints, but if you’d like to use a fixed height cell you can add your own height anchor constraint with a constant value. You have to set a custom priority for the height constraint this way the auto layout system won’t break and it’s going to be able to satisfy all the necessary constraints.

Compositional layout basics

The UICollectionViewCompositionalLayout class is a highly adaptive and flexible layout tool that you can use to build modern collection view layouts. It has three main components that you can configure to display your custom user interface elements in many different ways.

You combine the components by building up from items into a group, from groups into a section, and finally into a full layout, like in this example of a basic list layout:

There are plenty of great resources and tutorials about this topic, so I won’t get too much into the details now, but we’re going to create a simple layout that can display full width (fractional layout dimension) items in a full width group, by using and estimated height to support dynamic cell sizes. I suppose this is quite a common use-case for many of us. We can create an extension on the UICollectionViewLayout object to instantiate a new list layout. 🙉

extension UICollectionViewLayout {
-    static func createListLayout() -> UICollectionViewLayout {
-        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(44))
-        let item = NSCollectionLayoutItem(layoutSize: itemSize)
-      
-        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(44))
-        let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
-        let section = NSCollectionLayoutSection(group: group)
-
-        let layout = UICollectionViewCompositionalLayout(section: section)
-        return layout
-    }
-}
-

Now it is possible to add a collectionView to our view hierarchy inside the view controller.

class ViewController: UIViewController {
-
-    let collectionView = UICollectionView(frame: .zero, collectionViewLayout: .createListLayout())
-
-    override func loadView() {
-        super.loadView()
-
-        collectionView.translatesAutoresizingMaskIntoConstraints = false
-        view.addSubview(collectionView)
-        NSLayoutConstraint.activate([
-            view.topAnchor.constraint(equalTo: collectionView.topAnchor),
-            view.bottomAnchor.constraint(equalTo: collectionView.bottomAnchor),
-            view.leadingAnchor.constraint(equalTo: collectionView.leadingAnchor),
-            view.trailingAnchor.constraint(equalTo: collectionView.trailingAnchor),
-        ])
-    }
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-    }
-}
-

You can also create your own auto layout helper extensions, or use SnapKit to quickly setup your layout constraints. It is relatively easy to work with anchors, you should read my other tutorial about mastering auto layout anchors if you don’t know much about them.

Cell registration and diffable data source

Apple has a new set of APIs to register and dequeue cells for modern collection views. It is worth to mention that almost everything we talk about this tutorials is only available on iOS14+ so if you are planning to support an older version you won’t be able to use these features.

If you want to learn more about the topic, I’d like to recommend an article by Donny Wals and there is a great, but a bit longer post by John Sundell about modern collection views. I’m using the same helper extension to get a cell provider using a cell registration object, to make the process more simple, plus we’re going to need some random sentences, so let’s add a few helpers. 💡

extension String {
-    static func randomWord() -> String {
-        (0..<Int.random(in: 1...10)).map { _ in String(format: "%c", Int.random(in: 97..<123)) }.joined(separator: "")
-    }
-
-    static func randomSentence() -> String {
-        (0...50).map { _ in randomWord() }.joined(separator: " ")
-    }
-}
-
-extension UICollectionView.CellRegistration {
-
-    var cellProvider: (UICollectionView, IndexPath, Item) -> Cell {
-        { collectionView, indexPath, product in
-            collectionView.dequeueConfiguredReusableCell(using: self, for: indexPath, item: product)
-        }
-    }
-}
-

Now we can use the new UICollectionViewDiffableData class to specify our sections and items inside the collection view. You can define your sections as an enum, and in this case we’re going to use a String type as our items. There is a great tutorial by AppCoda about diffable data sources.

Long story short, you should make a new cell configuration where now you can use the ReusableCell with a CustomView, then it is possible to setup the diffable data source with the cellProvider on the cellRegistration object. Finally we can apply an initial snapshot by appending a new section and our items to the snapshot. You can update the data source with the snapshot and the nice thing about is it that you can also animate the changes if you want. 😍

enum Section {
-    case `default`
-}
-
-class ViewController: UIViewController {
-
-    let collectionView = UICollectionView(frame: .zero, collectionViewLayout: .createListLayout())
-    var dataSource: UICollectionViewDiffableDataSource<Section, String>!
-    let data: [String] = (0..<10).map { _ in String.randomSentence() }
-
-    override func loadView() {
-        super.loadView()
-
-        collectionView.translatesAutoresizingMaskIntoConstraints = false
-        view.addSubview(collectionView)
-        NSLayoutConstraint.activate([
-            view.topAnchor.constraint(equalTo: collectionView.topAnchor),
-            view.bottomAnchor.constraint(equalTo: collectionView.bottomAnchor),
-            view.leadingAnchor.constraint(equalTo: collectionView.leadingAnchor),
-            view.trailingAnchor.constraint(equalTo: collectionView.trailingAnchor),
-        ])
-    }
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        collectionView.delegate = self
-
-        createDataSource()
-        applyInitialSnapshot()
-    }
-
-    func createDataSource() {
-        let cellRegistration = UICollectionView.CellRegistration<ReusableCell<CustomView>, String> { cell, indexPath, model in
-            cell.view.label.text = model
-        }
-
-        dataSource = UICollectionViewDiffableDataSource<Section, String>(collectionView: collectionView,
-                                                                         cellProvider: cellRegistration.cellProvider)
-    }
-    
-    func applyInitialSnapshot() {
-        var snapshot = NSDiffableDataSourceSnapshot<Section, String>()
-        snapshot.appendSections([.default])
-        snapshot.appendItems(data)
-        dataSource.apply(snapshot, animatingDifferences: true)
-    }
-}
-
-extension ViewController: UICollectionViewDelegate {
-
-    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
-        let item = dataSource.itemIdentifier(for: indexPath)
-
-        print(item ?? "n/a")
-    }
-}
-

You still have to implement a delegate method if you’d like to handle cell selection, but fortunately the diffable data source has an itemIdentifier method to look up elements inside the data source.

As you can see it’s pretty easy to come up with a generic cell that can be used to render a custom view inside a collection view. I believe that the “official” cell configuration based approach is a bit more complicated, plus you have to write quite a lot of code if it comes to modern collection views.

I’m going to update my original collection view framework with these new techniques for sure. The new compositional layout is way more powerful compared to regular flow layouts, diffable data sources are also amazing and the new cell registration API is also nice. I believe that the collection view team at Apple did an amazing job during the years, it’s still one of my favorite components if it comes to UIKit development. I highly recommend learning these modern techniques. 👍

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/how-to-create-your-first-website-using-vapor-4-and-leaf/index.html b/docs/how-to-create-your-first-website-using-vapor-4-and-leaf/index.html deleted file mode 100644 index 0026378..0000000 --- a/docs/how-to-create-your-first-website-using-vapor-4-and-leaf/index.html +++ /dev/null @@ -1,484 +0,0 @@ - - - - - - - - - - - - How to create your first website using Vapor 4 and Leaf? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 9 min read - -
-

How to create your first website using Vapor 4 and Leaf?

-
-

Let's build a web page in Swift. Learn how to use the brand new template engine of the most popular server side Swift framework.

-
- -

Project setup

Start a brand new project by using the Vapor toolbox. If you don’t know what’s the toolbox or how to install it, you should read my beginner’s guide about Vapor 4 first.

// swift-tools-version:5.3
-import PackageDescription
-
-let package = Package(
-    name: "myProject",
-    platforms: [
-       .macOS(.v10_15)
-    ],
-    dependencies: [
-        // 💧 A server-side Swift web framework.
-        .package(url: "https://github.com/vapor/vapor", from: "4.32.0"),
-        .package(url: "https://github.com/vapor/leaf", .exact("4.0.0-tau.1")),
-        .package(url: "https://github.com/vapor/leaf-kit", .exact("1.0.0-tau.1.1")),
-    ],
-    targets: [
-        .target(name: "App", dependencies: [
-            .product(name: "Leaf", package: "leaf"),
-            .product(name: "Vapor", package: "vapor"),
-        ]),
-        .target(name: "Run", dependencies: ["App"]),
-        .testTarget(name: "AppTests", dependencies: [
-            .target(name: "App"),
-            .product(name: "XCTVapor", package: "vapor"),
-        ])
-    ]
-)
-

Open the project by double clicking the Package.swift file. Xcode will download all the required package dependencies first, then you’ll be ready to run your app (you might have to select the Run target & the proper device) and write some server side Swift code.

Getting started with Leaf 4

Leaf is a powerful templating language with Swift-inspired syntax. You can use it to generate dynamic HTML pages for a front-end website or generate rich emails to send from an API.

If you choose a domain-specific language (DSL) for writing type-safe HTML (such as Plot) you’ll have to rebuild your entire backend application if you want to change your templates. Leaf is a dynamic template engine, this means that you can change templates on the fly without recompiling your Swift codebase. Let me show you how to setup Leaf.

import Vapor
-import Leaf
-
-public func configure(_ app: Application) throws {
-
-    app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
-
-    if !app.environment.isRelease {
-        LeafRenderer.Option.caching = .bypass
-    }
-
-    app.views.use(.leaf)
-
-    try routes(app)
-}
-

With just a few lines of code you are ready to use Leaf. If you build & run your app you’ll be able to modify your templates and see the changes instantly if reload your browser, that’s because we’ve bypassed the cache mechanism using the LeafRenderer.Option.caching property. If you build your backend application in release mode the Leaf cache will be enabled, so you need to restart your server after you edit a template.

Your templates should have a .leaf extension and they should be placed under the Resources/Views folder inside your working directory by default. You can change this behavior through the LeafEngine.rootDirectory configuration and you can also alter the default file extension with the help of the NIOLeafFiles source object.

import Vapor
-import Leaf
-    
-public func configure(_ app: Application) throws {
-
-    app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
-
-    if !app.environment.isRelease {
-        LeafRenderer.Option.caching = .bypass
-    }
-    
-    let detected = LeafEngine.rootDirectory ?? app.directory.viewsDirectory
-    LeafEngine.rootDirectory = detected
-
-    LeafEngine.sources = .singleSource(NIOLeafFiles(fileio: app.fileio,
-                                                    limits: .default,
-                                                    sandboxDirectory: detected,
-                                                    viewDirectory: detected,
-                                                    defaultExtension: "html"))
-    
-    app.views.use(.leaf)
-
-    try routes(app)
-
-}
-

The LeafEngine uses sources to look up template locations when you call your render function with a given template name. You can also use multiple locations or build your own lookup source if you implement the LeafSource protocol if needed.

import Vapor
-import Leaf
-    
-public func configure(_ app: Application) throws {
-
-    app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
-
-    if !app.environment.isRelease {
-        LeafRenderer.Option.caching = .bypass
-    }
-    
-    let detected = LeafEngine.rootDirectory ?? app.directory.viewsDirectory
-    LeafEngine.rootDirectory = detected
-
-    let defaultSource = NIOLeafFiles(fileio: app.fileio,
-                                     limits: .default,
-                                     sandboxDirectory: detected,
-                                     viewDirectory: detected,
-                                     defaultExtension: "leaf")
-
-    let customSource = CustomSource()
-
-    let multipleSources = LeafSources()
-    try multipleSources.register(using: defaultSource)
-    try multipleSources.register(source: "custom-source-key", using: customSource)
-
-    LeafEngine.sources = multipleSources
-    
-    app.views.use(.leaf)
-
-    try routes(app)
-}
-
-struct CustomSource: LeafSource {
-
-    func file(template: String, escape: Bool, on eventLoop: EventLoop) -> EventLoopFuture<ByteBuffer> {
-        /// Your custom lookup method comes here...
-        return eventLoop.future(error: LeafError(.noTemplateExists(template)))
-    }
-}
-

Anyway, this is a more advanced topic, we’re good to go with a single source, also I highly recommend using a .html extension instead of leaf, so Xcode can give us partial syntax highlight for our Leaf files. Now we are going to make our very first Leaf template file. 🍃

You can enable basic syntax highlighting for .leaf files in Xcode by choosing the Editor ▸ Syntax Coloring ▸ HTML menu item. Unfortunately if you close Xcode you have to do this again and again for every single Leaf file.

Create a new file under the Resources/Views directory called index.html.

<!DOCTYPE html>
-<html>
-  <head>
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <title>#(title)</title>
-  </head>
-  <body>
-    <h1>#(body)</h1>
-  </body>
-</html>
-

Leaf gives you the ability to put specific building blocks into your HTML code. These blocks (or tags) are always starting with the # symbol. You can think of these as preprocessor macros (if you are familiar with those). The Leaf renderer will process the template file and print the #() placeholders with actual values. In this case both the body and the title key is a placeholder for a context variable. We’re going to set these up using Swift. 😉

After the template file has been processed it’ll be rendered as a HTML output string. Let me show you how this works in practice. First we need to respond some HTTP request, we can use a router to register a handler function, then we tell our template engine to render a template file, we send this rendered HTML string with the appropriate Content-Type HTTP header value as a response, all of this happens under the hood automatically, we just need to write a few lines of Swift code.

import Vapor
-import Leaf
-
-func routes(_ app: Application) throws {
-
-    app.get { req in
-        req.leaf.render(template: "index", context: [
-            "title": "Hi",
-            "body": "Hello world!"
-        ])
-    }
-}
-

The snippet above goes to your routes.swift file. Routing is all about responding to HTTP requests. In this example using the .get you can respond to the / path. In other words if you run the app and enter http://localhost:8080 into your browser, you should be able to see the rendered view as a response.

The first parameter of the render method is the name of the template file (without the file extension). As a second parameter you can pass anything that can represent a context variable. This is usually in a key-value format, and you can use almost every native Swift type including arrays and dictionaries. 🤓

When you run the app using Xcode, don’t forget to set a custom working directory, otherwise Leaf won’t find your templates. You can also run the server using the command line: swift run Run.

Xcode custom working directory

Congratulations! You just made your very first webpage. 🎉

Inlining, evaluation and block definitions

Leaf is a lightweight, but very powerful template engine. If you learn the basic principles, you’ll be able to completely separate the view layer from the business logic. If you are familiar with HTML, you’ll find that Leaf is easy to learn & use. I’ll show you some handy tips real quick.

Splitting up templates is going to be essential if you are planning to build a multi-page website. You can create reusable leaf templates as components that you can inline later on.

We are going to update our index template and give an opportunity for other templates to set a custom title & description variable and define a bodyBlock that we can evaluate (or call) inside the index template. Don’t worry, you’ll understand this entire thing when you look at the final code.

<!DOCTYPE html>
-<html>
-  <head>
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <title>#(title)</title>
-    <meta name="description" content="#(description)">
-  </head>
-  <body>
-    <main>
-        #bodyBlock()
-    </main>
-  </body>
-</html>
-

The example above is a really good starting point. We could render the index template and pass the title & description properties using Swift, of course the bodyBlock would be still missing, but let me show you how can we define that using a different Leaf file called home.html.

#let(description = "This is the description of our home page.")
-#define(bodyBlock):
-<section class="wrapper">
-    <h2>#(header)</h2>
-</section>
-<section class="wrapper">
-    <p>#(message)</p>
-</section>
-#enddefine
-#inline("index")
-

Our home template starts with a constant declaration using the #let syntax (you can also use #var to define variables), then in the next line we build a new reusable block with a multi-line content. Inside the body we can also print out variables combined with HTML code, every single context variable is also available inside definition blocks. In the very last line we tell the system that it should inline the contents of our index template. This means that we’re literally copy & paste the contents of that file here. Think of it like this:

#let(description = "This is the description of our home page.")
-#define(bodyBlock):
-<section class="wrapper">
-    <h2>#(header)</h2>
-</section>
-<section class="wrapper">
-    <p>#(message)</p>
-</section>
-#enddefine
-<!DOCTYPE html>
-<html>
-  <head>
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <title>#(title)</title>
-    <meta name="description" content="#(description)">
-  </head>
-  <body>
-    <main>
-        #bodyBlock()
-    </main>
-  </body>
-</html>
-

As you can see we still need values for the title, header and message variables. We don’t have to deal with the bodyBlock anymore, the renderer will evaluate that block and simply replace the contents of the block with the defined body, this is how you can imagine the template before the variable replacement:

#let(description = "This is the description of our home page.")
-<!DOCTYPE html>
-<html>
-  <head>
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <title>#(title)</title>
-    <meta name="description" content="#(description)">
-  </head>
-  <body>
-    <main>
-        <section class="wrapper">
-            <h2>#(header)</h2>
-        </section>
-        <section class="wrapper">
-            <p>#(message)</p>
-        </section>
-    </main>
-  </body>
-</html>
-

Now that’s not the most accurate representation of how the LeafRenderer works, but I hope that it’ll help you to understand this whole define / evaluate syntax thing.

You can also use the #evaluate tag instead of calling the block (bodyBlock() vs #evaluate(bodyBlock), these two snippets are essentially the same).

It’s time to render the page template. Again, we don’t have to deal with the bodyBlock, since it’s already defined in the home template, the description value also exists, because we created a new constant using the #let tag. We only have to pass around the title, header and message keys with proper values as context variables for the renderer.

app.get { req in
-    req.leaf.render(template: "home", context: [
-        "title": "My Page",
-        "header": "This is my own page.",
-        "message": "Welcome to my page!"
-    ])
-}
-

It’s possible to inline multiple Leaf files, so for example you can create a hierarchy of templates such as: index ▸ page ▸ welcome, just follow the same pattern that I introduced above. Worth to mention that you can inline files as raw files (#inline("my-file", as: raw)), but this way they won’t be processed during rendering. 😊

LeafData, loops and conditions

Passing some custom data to the view is not that hard, you just have to conform to the LeafDataRepresentable protocol. Let’s build a new list.html template first, so I can show you a few other practical things as well.

#let(title = "My custom list")
-#let(description = "This is the description of our list page.")
-#var(heading = nil)
-#define(bodyBlock):
-<h1>#(heading ?? "Todo list")</h1>
-<ul>
-#for(todo in todos):
-    <li>#if(todo.isCompleted):✅#else:❌#endif #(todo.name)</p></li>
-#endfor
-</ul>
-#enddefine
-#inline("index")
-

We declare two constants so we don’t have to pass around the title and description using the same keys as context variables. Next we use the variable syntax to override our heading and set it to a nil value, we’re doing this so I can show you that we can use the coalescing (??) operator to chain optional values. Next we use the #for block to iterate through our list. The todos variable will be a context variable that we setup using Swift later on. We can also use conditions to check values or expressions, the syntax is pretty much straightforward.

Now we just have to create a data structure to represent our Todo items.

import Vapor
-import Leaf
-
-struct Todo {
-    let name: String
-    let isCompleted: Bool
-}
-
-extension Todo: LeafDataRepresentable {
-
-    var leafData: LeafData {
-        .dictionary([
-            "name": name,
-            "isCompleted": isCompleted,
-        ])
-    }
-}
-

I made a new Todo struct and extended it so it can be used as a LeafData value during the template rendering process. You can extend Fluent models just like this, usually you will have to return a LeafData.dictionary type with your object properties as specific values under given keys. You can extend the dictionary with computed properties, but this is a great way to hide sensitive data from the views. Just completely ignore the password fields. 😅

Time to render a list of todos, this is one possible approach:

func routes(_ app: Application) throws {
-
-    app.get { req -> EventLoopFuture<View> in
-        let todos = [
-            Todo(name: "Update Leaf 4 articles", isCompleted: true),
-            Todo(name: "Write a brand new article", isCompleted: false),
-            Todo(name: "Fix a bug", isCompleted: true),
-            Todo(name: "Have fun", isCompleted: true),
-            Todo(name: "Sleep more", isCompleted: false),
-        ]
-        return req.leaf.render(template: "list", context: [
-            "heading": "Lorem ipsum",
-            "todos": .array(todos),
-        ])
-    }
-}
-

The only difference is that we have to be more explicit about types. This means that we have to tell the Swift compiler that the request handler function returns a generic EventLoopFuture object with an associated View type. The Leaf renderer works asynchronously so that’s why we have to work with a future value here. If you don’t how how they work, please read about them, futures and promises are quite essential building blocks in Vapor.

The very last thing I want to talk about is the context argument. We return a [String: LeafData] type, that’s why we have to put an additional .array initializer around the todos variable so the renderer will know the exact type here. Now if you run the app you should be able to see our todos.

Summary

I hope that this tutorial will help you to build better templates using Leaf. If you understand the basic building blocks, such as inlines, definitions and evaluations, it’s going to be really easy to compose your template hierarchies. If you want to learn more about Leaf or Vapor you should check for more tutorials in the articles section or you can purchase my Practical Server Side Swift book.

- -
- - - -
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/how-to-define-strings-use-escaping-sequences-and-interpolations/index.html b/docs/how-to-define-strings-use-escaping-sequences-and-interpolations/index.html deleted file mode 100644 index 589f4a4..0000000 --- a/docs/how-to-define-strings-use-escaping-sequences-and-interpolations/index.html +++ /dev/null @@ -1,393 +0,0 @@ - - - - - - - - - - - - How to define strings, use escaping sequences and interpolations? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 6 min read - -
-

How to define strings, use escaping sequences and interpolations?

-
-

As a beginner it can be hard to understand String interpolation and escaping sequences, in this tutorial I'll teach you the basics.

-
- -

What is a string?

According to swift.org and Wikipedia we can simply say that:

A string is a series of characters

It’s dead simple. This sentence for example is a string. When you write computer programs, you usually have to mark the beginning and the end of your strings with a special character, these surrounding characters are sometimes called as delimiters. Most of the programming languages use single or double quotation marks or backticks to specify the boundaries of a string. 💀

Constants, literals, variables and escaping

In Swift you can define string literals (constants) by using the let keyword, or string variables through the var keyword. If you don’t want to change the value in the future at all you can use a string constant, but if you need a more dynamically changing value you should use a variable.

let message = "Hello World!"
-print(message)
-

As you can see we are using double quotation marks " as delimiters and we gave a name to our string literal (or string constant, which is literally just a non-changing string, hence the name), in this example we can simply call the literal as message.

Now here comes the interesting part, how can I put a double quotation mark inside a string literal if that always represents the beginning and / or the end of a string? Well, for this reason the creators of many programming languages introduced escaping characters. 😱

let quote = "\"One more thing...\" - Steve Jobs"
-

The backslash (\) character is a very special one if it comes to the Swift programming language. We can also use it to write an actual backslash by escaping one (\\), but the newline (\n), tab (\t) and return (\r), characters are also created by using a backslash. It is also possible to write unicode characters using the \u{CODE} pattern. Here is how it works…

let newline = "\n"
-let tab = "\t"
-let `return` = "\r"
-let unicode = "\u{2023}"
-
-print(unicode) // ‣
-

Okay, okay, I know, why the backticks around the return keyword? Well, in Swift you can define a constant or variable name with almost any given name that is not a language keyword, you can even use emojis as names, but if you want to define a variable by using a reserved keyword, you have to escape it, aka. put it in between backticks. In our case the return was an already taken word, so we had to escape it. Now let’s get back to the more interesting part.

If you take a look at a unicode character chart you’ll see that the 2023 belongs to the play symbol. Unicode has so many characters and the list is constantly growing. Fortunately Swift can handle them very well, you can print unicode characters straight ahead or you can use the escape sequence by providing the hexa code of the unicode character.

// old Hungarian letter p
-let p1 = "𐳠"
-let p2 = "\u{10CE0}"
-
-// smiling face emoji
-let s1 = "😊"
-let s2 = "\u{1F60A}"
-

You can play around with emojis and look up unicode character codes for them on the Emojipedia website. Since we were talking about escaping quite a lot, let me show you a few more things that you can do with the backslash character in Swift.

String interpolation

So we’ve already seen how to put special characters into strings, what if I want to put another constant or variable in a string? This is a perfectly valid use case and we can actually use the following syntax to place variables into strings in Swift.

let name = "World"
-let message = "Hello \(name)!"
-
-print(message)
-

Long story short, this escape format (\(VARIABLE)) is called string interpolation and it’s a really convenient & powerful tool for every beginner Swift programmer. You know in some other languages you have to use format strings to put variables into other strings, which can be extremely painful in some circumstances, but in Swift, you can simply interpolate almost anything. 🎉

Since we’re talking about interpolations, I’d like to show how to concatenate two strings in Swift.

let welcome = "Hello"
-let name = "World"
-
-let m1 = welcome + " " + name + "!"
-let m2 = "\(welcome) \(name)!"
-
-print(m1)
-print(m2)
-

The two final message strings will be identical, the only difference is the way we joined the parts together. In the first scenario we used the + sign to combine the strings, but in the second version we’ve simply used interpolation to construct a new string using the previously defined constants.

Custom String interpolation

This is a more advanced topic, but I believe that not so many people are aware of this function in Swift, so let’s talk a little bit about it. The main idea here is that you can create your own custom interpolation methods to format strings. I’ll show you a working example real quick.

extension String.StringInterpolation {
-    mutating func appendInterpolation(sayHelloTo value: String) {
-        appendLiteral("Hello " + value + "!")
-    }
-}
-
-let message = "\(sayHelloTo: "World")"
-print(message)
-

This way you can put your string formatter code into a custom String.StringInterpolation extension and you don’t have to deal with the rest when you create your variable. The appendInterpolation function can have multiple parameters and you have to use them inside the interpolation brackets when using it. No worries if this is too much, this topic is quite an advanced one, just remember that something like this exists and come back later. 💡

I highly recommend reading Paul Hudson’s article about super-powered string interpolation.

Multi-line string literals interpolation

Back to a relatively simple issue, what about multi-line strings? Do I have to concatenate everything line by line to construct such a thing? The answer is no. Multi-Line String Literals were introduced in Swift 4 and it was a really great addition to the language.

let p1 = """
-    Please, remain calm, the end has arrived
-    We cannot save you, enjoy the ride
-    This is the moment you've been waiting for
-    Don't call it a warning, this is a war
-
-    It's the parasite eve
-    Got a feeling in your stomach 'cause you know that it's coming for ya
-    Leave your flowers and grieve
-    Don't forget what they told ya, ayy ayy
-    When we forget the infection
-    Will we remember the lesson?
-    If the suspense doesn't kill you
-    Something else will, ayy ayy
-    Move
-    """
-

You can use three double quotes (""") as a delimiter if you want to define long strings. These kind of string literals can contain newlines and individual double quote characters without the need of escaping. It is also good to know that if the closing delimiter alignment matters, so if you place a tab or a few spaces before that you also have to align everything before to the same column, this way those hidden space / tab characters will be ignored. Fell free to try it out. 🔨

Newline escape in strings interpolation
There is one problem with really long one-liner strings. They are hard to read, because… those strings are freaking long. Consider the following example.

let p1 = """
-    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
-    """
-

Wouldn’t be cool if we could break this mess into some little pieces somehow? Yes or course, you can use string concatenation, but fortunately there is a more elegant solution.

// Shorter lines that are easier to read, but represent the same long line 
-let text2 = """ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod \ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, \ quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. """
-

The String Newline Escaping Swift evolution proposal was also implemented a long time ago so we can use the backslash character to work with shorter lines and escape the newline marker from the end of every single line. It’s a pretty small but nice to have feature that can make our life more pleasant when we have to work with multi-line string literals. No more: \n\n\n. 👍

Raw String escaping

The very last thing I want to show you is based on the Enhancing String Literals Delimiters to Support Raw Text proposal. The motivation behind this one was that there are some cases when you have to escape too much in a string and we should be able to avoid this somehow.

let regex1 = "\\\\[A-Z]+[A-Za-z]+\\.[a-z]+"
-let regex2 = #"\\[A-Z]+[A-Za-z]+\.[a-z]+"#
-

In my opinion the regular expression above is a very good example for this case. By defining a custom delimiter (#" and "#) we can avoid further escaping inside our string definition. The only downside is that now we can’t simply interpolate substrings, but we have to place a a delimiter string there as well. Here, let me show you another example.

let name = "Word"
-let message  = #"Hello "\#(name)"!"#
-
-print(message)
-

As you can see it makes quite a big difference, but don’t worry you won’t have to use this format that much. Honestly I only used this feature like one or two times so far. 😅

Summary

Strings in Swift are easy to learn, but don’t get fooled: they are extremely complicated under the hood. In this article we’ve learned about unicode characters, encoding, escaping, literals and many more. I hope this will help you to understand Strings just a little bit better.

We’ve also examined a few Swift evolution proposals, but you can find a complete list of them on the Swift evolution dashboard. These proposals are open source and they help us to make Swift an even better programming language through the help of the community. ❤️

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/how-to-design-type-safe-restful-apis-using-swift-and-vapor/index.html b/docs/how-to-design-type-safe-restful-apis-using-swift-and-vapor/index.html deleted file mode 100644 index fd665a1..0000000 --- a/docs/how-to-design-type-safe-restful-apis-using-swift-and-vapor/index.html +++ /dev/null @@ -1,592 +0,0 @@ - - - - - - - - - - - - How to design type safe RESTful APIs using Swift & Vapor? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 9 min read - -
-

How to design type safe RESTful APIs using Swift & Vapor?

-
-

Learn to make proper data transfer objects for CRUD operations and integrate them both into the client and server side API layer.

-
- -

Full stack Swift & BFF

A little more than a year have passed since I published my article about A generic CRUD solution for Vapor 4. Quite a lot happened in a year, and I’ve learned so much about Vapor and server side Swift in general. I believe that it is time to polish this article a bit and share the new ideas that I’m using lately to design and build backend APIs.

Swift is on the server side, and last 2020 was definitely a HUGE milestone. Vapor 4 alpha release started in May 2019, then a year later in April 2020, the very first stable version of the framework arrived. Lots of new server side libraries were open sourced, there is a great integration with AWS services, including a native Swift AWS library (Soto) and Lambda support for Swift.

More and more people are asking: “is Vapor / server side Swift ready for production?” and I truly believe that the anser is definitely: yes it is. If you are an iOS developer and you are looking for an API service, I belive Swift can be a great choice for you.

Of course you still have to learn a lot about how to build a backend service, including the basic understanding of the HTTP protocol and many more other stuff, but no matter which tech stack you choose, you can’t avoid learning these things if you want to be a backend developer.

The good news is that if you choose Swift and you are planning to build a client application for an Apple platform, you can reuse most of your data objects and create a shared Swift library for your backend and client applications. Tim Condon is a huge full-stack Swift / Vapor advocate (also member of the Vapor core team), he has some nice presentation videos on YouTube about Backend For Frontend (BFF) systems and full-stack development with Swift and Vapor.

Anyway, in this article I’m going to show you how to design a shared Swift package including an API service that can be a good starting point for your next Swift client & Vapor server application. You should know that I’ve created Feather CMS to simplify this process and if you are looking for a real full-stack Swift CMS solution you should definitely take a look.

Project setup

As a starting point you can generate a new project using the default template and the Vapor toolbox, alternatively you can re-reate the same structure by hand using the Swift Package Manager. We’re going to add one new target to our project, this new TodoApi is going to be a public library product and we have to use it as a dependency in our App target.

// swift-tools-version:5.3
-import PackageDescription
-
-let package = Package(
-    name: "myProject",
-    platforms: [
-       .macOS(.v10_15)
-    ],
-    products: [
-        .library(name: "TodoApi", targets: ["TodoApi"]),
-    ],
-    dependencies: [
-        .package(url: "https://github.com/vapor/vapor", from: "4.44.0"),
-        .package(url: "https://github.com/vapor/fluent", from: "4.0.0"),
-        .package(url: "https://github.com/vapor/fluent-sqlite-driver", from: "4.0.0"),
-    ],
-    targets: [
-        .target(name: "TodoApi"),
-        .target(
-            name: "App",
-            dependencies: [
-                .product(name: "Fluent", package: "fluent"),
-                .product(name: "FluentSQLiteDriver", package: "fluent-sqlite-driver"),
-                .product(name: "Vapor", package: "vapor"),
-                .target(name: "TodoApi")
-            ],
-            swiftSettings: [
-                .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"),
-        ])
-    ]
-)
-

You should note that if you choose to use Fluent when using the vapor toolbox, then the generated Vapor project will contain a basic Todo example. Christian Weinberger has a great tutorial about how to create a Vapor 4 todo backend if you are interested more in the todobackend.com project, you should definitely read it. In our case we’re going to build our todo API, in a very similar way.

First, we need a Todo model in the App target, that’s for sure, because we’d like to model our database entities. The Fluent ORM framework is quite handy, because you can choose a database driver and switch between database provides, but unfortunately the framework is stuffing too much responsibilities into the models. Models always have to be classes and property wrappers can be annyoing sometimes, but it’s more or less easy to use and that’s also a huge benefit.

import Vapor
-import Fluent
-
-final class Todo: Model {
-    static let schema = "todos"
-   
-    struct FieldKeys {
-        static let title: FieldKey = "title"
-        static let completed: FieldKey = "completed"
-        static let order: FieldKey = "order"
-        
-    }
-    
-    @ID(key: .id) var id: UUID?
-    @Field(key: FieldKeys.title) var title: String
-    @Field(key: FieldKeys.completed) var completed: Bool
-    @Field(key: FieldKeys.order) var order: Int?
-    
-    init() { }
-    
-    init(id: UUID? = nil, title: String, completed: Bool = false, order: Int? = nil) {
-        self.id = id
-        self.title = title
-        self.completed = completed
-        self.order = order
-    }
-}
-

A model represents a line in your database, but you can also query db rows using the model entity, so there is no separate repository that you can use for this purpose. You also have to define a migration object that defines the database schema / table that you’d like to create before you could operate with models. Here’s how to create one for our Todo models.

import Fluent
-
-struct TodoMigration: Migration {
-
-    func prepare(on db: Database) -> EventLoopFuture<Void> {
-        db.schema(Todo.schema)
-            .id()
-            .field(Todo.FieldKeys.title, .string, .required)
-            .field(Todo.FieldKeys.completed, .bool, .required)
-            .field(Todo.FieldKeys.order, .int)
-            .create()
-    }
-
-    func revert(on db: Database) -> EventLoopFuture<Void> {
-        db.schema(Todo.schema).delete()
-    }
-}
-

Now we’re mostly ready with the database configuration, we just have to configure the selected db driver, register the migration and call the autoMigrate() method so Vapor can take care of the rest.

import Vapor
-import Fluent
-import FluentSQLiteDriver
-
-public func configure(_ app: Application) throws {
-
-    app.databases.use(.sqlite(.file("Resources/db.sqlite")), as: .sqlite)
-
-    app.migrations.add(TodoMigration())
-    try app.autoMigrate().wait()
-}
-

That’s it, we have a working SQLite database with a TodoModel that is ready to persist and retreive entities. In my old CRUD article I mentioned that Models and Contents should be separated. I still believe in clean architectures, but back in the days I was only focusing on the I/O (input, output) and the few endpoints (list, get, create, update, delete) that I implemented used the same input and output objects. I was so wrong. 😅

A response to a list request is usually quite different from a get (detail) request, also the create, update and patch inputs can be differentiated quite well if you take a closer look at the components. In most of the cases ignoring this observation is causing so much trouble with APIs. You should NEVER use the same object for creating and entity and updating the same one. That’s a bad practice, but only a few people notice this. We are talking about JSON based RESTful APIs, but come on, every company is trying to re-invent the wheel if it comes to APIs. 🔄

But why? Because developers are lazy ass creatures. They don’t like to repeat themselves and unfortunately creating a proper API structure is a repetative task. Most of the participating objects look like the same, and no in Swift you don’t want to use inheritance to model these Data Transfer Objects. The DTO layer is your literal communication interface, still we use unsafe crappy tools to model our most important part of our projects. Then we wonder when an app crashes because of a change in the backend API, but that’s a different story, I’ll stop right here… 🔥

Anyway, Swift is a nice way to model the communication interface. It’s simple, type safe, secure, reusable, and it can be converted back and forth to JSON with a single line of code. Looking back to our case, I imagine an RESTful API something like this:

  • GET /todos/ => () -> Page<[TodoListObject]>
  • GET /todos/:id/ => () -> TodoGetObject
  • POST /todos/ => (TodoCreateObject) -> TodoGetObject
  • PUT /todos/:id/ => (TodoUpdateObject) -> TodoGetObject
  • PATCH /todos/:id/ => (TodoPatchObject) -> TodoGetObject
  • DELETE /todos/:id/ => () -> ()

As you can see we always have a HTTP method that represents an CRUD action. The endpoint always contains the referred object and the object identifier if you are going to alter a single instance. The input parameter is always submitted as a JSON encoded HTTP body, and the respone status code (200, 400, etc.) indicates the outcome of the call, plus we can return additional JSON object or some description of the error if necessary. Let’s create the shared API objects for our TodoModel, we’re going to put these under the TodoApi target, and we only import the Foundation framework, so this library can be used everywhere (backend, frontend).

import Foundation
-
-struct TodoListObject: Codable {
-    let id: UUID
-    let title: String
-    let order: Int?
-}
-
-struct TodoGetObject: Codable {
-    let id: UUID
-    let title: String
-    let completed: Bool
-    let order: Int?
-}
-
-struct TodoCreateObject: Codable {
-    let title: String
-    let completed: Bool
-    let order: Int?
-}
-
-struct TodoUpdateObject: Codable {
-    let title: String
-    let completed: Bool
-    let order: Int?
-}
-
-struct TodoPatchObject: Codable {
-    let title: String?
-    let completed: Bool?
-    let order: Int?
-}
-

The next step is to extend these objects so we can use them with Vapor (as a Content type) and furthermore we should be able to map our TodoModel to these entities. This time we are not going to take care about validation or relations, that’s a topic for a different day, for the sake of simplicity we’re only going to create basic map methods that can do the job and hope just for valid data. 🤞

import Vapor
-import TodoApi
-
-extension TodoListObject: Content {}
-extension TodoGetObject: Content {}
-extension TodoCreateObject: Content {}
-extension TodoUpdateObject: Content {}
-extension TodoPatchObject: Content {}
-
-extension TodoModel {
-    
-    func mapList() -> TodoListObject {
-        .init(id: id!, title: title, order: order)
-    }
-
-    func mapGet() -> TodoGetObject {
-        .init(id: id!, title: title, completed: completed, order: order)
-    }
-    
-    func create(_ input: TodoCreateObject) {
-        title = input.title
-        completed = input.completed ?? false
-        order = input.order
-    }
-    
-    func update(_ input: TodoUpdateObject) {
-        title = input.title
-        completed = input.completed
-        order = input.order
-    }
-    
-    func patch(_ input: TodoPatchObject) {
-        title = input.title ?? title
-        completed = input.completed ?? completed
-        order = input.order ?? order
-    }
-}
-

There are only a few differences between these map methods and of course we could re-use one single type with optional property values everywhere, but that wouldn’t describe the purpose and if something changes in the model data or in an endpoint, then you’ll be ended up with side effects no matter what. FYI: in Feather CMS most of this model creation process will be automated through a generator and there is a web-based admin interface (with permission control) to manage db entries.

So we have our API, now we should build our TodoController that represents the API endpoints. Here’s one possible implementation based on the CRUD function requirements above.

import Vapor
-import Fluent
-import TodoApi
-
-struct TodoController {
-
-    private func getTodoIdParam(_ req: Request) throws -> UUID {
-        guard let rawId = req.parameters.get(TodoModel.idParamKey), let id = UUID(rawId) else {
-            throw Abort(.badRequest, reason: "Invalid parameter `\(TodoModel.idParamKey)`")
-        }
-        return id
-    }
-
-    private func findTodoByIdParam(_ req: Request) throws -> EventLoopFuture<TodoModel> {
-        TodoModel
-            .find(try getTodoIdParam(req), on: req.db)
-            .unwrap(or: Abort(.notFound))
-    }
-
-    // MARK: - endpoints
-    
-    func list(req: Request) throws -> EventLoopFuture<Page<TodoListObject>> {
-        TodoModel.query(on: req.db).paginate(for: req).map { $0.map { $0.mapList() } }
-    }
-    
-    func get(req: Request) throws -> EventLoopFuture<TodoGetObject> {
-        try findTodoByIdParam(req).map { $0.mapGet() }
-    }
-
-    func create(req: Request) throws -> EventLoopFuture<TodoGetObject> {
-        let input = try req.content.decode(TodoCreateObject.self)
-        let todo = TodoModel()
-        todo.create(input)
-        return todo.create(on: req.db).map { todo.mapGet() }
-    }
-    
-    func update(req: Request) throws -> EventLoopFuture<TodoGetObject> {
-        let input = try req.content.decode(TodoUpdateObject.self)
-
-        return try findTodoByIdParam(req)
-            .flatMap { todo in
-                todo.update(input)
-                return todo.update(on: req.db).map { todo.mapGet() }
-            }
-    }
-    
-    func patch(req: Request) throws -> EventLoopFuture<TodoGetObject> {
-        let input = try req.content.decode(TodoPatchObject.self)
-
-        return try findTodoByIdParam(req)
-            .flatMap { todo in
-                todo.patch(input)
-                return todo.update(on: req.db).map { todo.mapGet() }
-            }
-    }
-
-    func delete(req: Request) throws -> EventLoopFuture<HTTPStatus> {
-        try findTodoByIdParam(req)
-            .flatMap { $0.delete(on: req.db) }
-            .map { .ok }
-    }
-}
-

The very last step is to attach these endpoints to Vapor routes, we can create a RouteCollection object for this purpose.

import Vapor
-
-struct TodoRouter: RouteCollection {
-
-    func boot(routes: RoutesBuilder) throws {
-
-        let todoController = TodoController()
-        
-        let id = PathComponent(stringLiteral: ":" + TodoModel.idParamKey)
-        let todoRoutes = routes.grouped("todos")
-        
-        todoRoutes.get(use: todoController.list)
-        todoRoutes.post(use: todoController.create)
-        
-        todoRoutes.get(id, use: todoController.get)
-        todoRoutes.put(id, use: todoController.update)
-        todoRoutes.patch(id, use: todoController.patch)
-        todoRoutes.delete(id, use: todoController.delete)
-    }
-}
-

Now inside the configuration we just have to boot the router, you can place the following snippet right after the auto migration call: try TodoRouter().boot(routes: app.routes). Just build and run the project, you can try the API using some basic cURL commands.

# list
-curl -X GET "http://localhost:8080/todos/"
-# {"items":[],"metadata":{"per":10,"total":0,"page":1}}
-
-# create
-curl -X POST "http://localhost:8080/todos/" \
-    -H "Content-Type: application/json" \
-    -d '{"title": "Write a tutorial"}'
-# {"id":"9EEBD3BB-77AC-4511-AFC9-A052D62E4713","title":"Write a tutorial","completed":false}
-    
-#get
-curl -X GET "http://localhost:8080/todos/9EEBD3BB-77AC-4511-AFC9-A052D62E4713"
-# {"id":"9EEBD3BB-77AC-4511-AFC9-A052D62E4713","title":"Write a tutorial","completed":false}
-
-# update
-curl -X PUT "http://localhost:8080/todos/9EEBD3BB-77AC-4511-AFC9-A052D62E4713" \
-    -H "Content-Type: application/json" \
-    -d '{"title": "Write a tutorial", "completed": true, "order": 1}'
-# {"id":"9EEBD3BB-77AC-4511-AFC9-A052D62E4713","title":"Write a tutorial","order":1,"completed":true}
-
-# patch
-curl -X PATCH "http://localhost:8080/todos/9EEBD3BB-77AC-4511-AFC9-A052D62E4713" \
-    -H "Content-Type: application/json" \
-    -d '{"title": "Write a Swift tutorial"}'
-# {"id":"9EEBD3BB-77AC-4511-AFC9-A052D62E4713","title":"Write a Swift tutorial","order":1,"completed":true}
-
-# delete
-curl -i -X DELETE "http://localhost:8080/todos/9EEBD3BB-77AC-4511-AFC9-A052D62E4713"
-# 200 OK
-

Of course you can use any other helper tool to perform these HTTP requests, but I prefer cURL because of simplicity. The nice thing is that you can even build a Swift package to battle test your API endpoints. It can be an advanced type-safe SDK for your future iOS / macOS client app with a test target that you can run as a standalone product on a CI service.

I hope you liked this tutorial, next time I’ll show you how to validate the endpoints and build some test cases both for the backend and client side. Sorry for the huge delay in the articles, but I was busy with building Feather CMS, which is by the way amazing… more news are coming soon. 🤓

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/how-to-download-files-with-urlsession-using-combine-publishers-and-subscribers/index.html b/docs/how-to-download-files-with-urlsession-using-combine-publishers-and-subscribers/index.html deleted file mode 100644 index d3b3c43..0000000 --- a/docs/how-to-download-files-with-urlsession-using-combine-publishers-and-subscribers/index.html +++ /dev/null @@ -1,602 +0,0 @@ - - - - - - - - - - - - How to download files with URLSession using Combine Publishers and Subscribers? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 12 min read - -
-

How to download files with URLSession using Combine Publishers and Subscribers?

-
-

Learn how to load a remote image into an UIImageView asynchronously using URLSessionDownloadTask and the Combine framework in Swift.

-
- -

A simple image downloader

Downloading a resource from an URL seems like a trivial task, but is it really that easy? Well, it depends. If you have to download and parse a JSON file which is just a few KB, then you can go with the classical way or you can use the new dataTaskPublisher method on the URLSession object from the Combine framework.

Bad practices ⚠️

There are some quick & dirty approaches that you can use to get some smaller data from the internet. The problem with these methods is that you have to deal a lot with threads and queues. Fortunately using the Dispatch framework helps a lot, so you can turn your blocking functions into non-blocking ones. 🚧

let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")!
-
-// Synchronous download using Data & String
-do {
-    // get the content as String synchronously
-    let content = try String(contentsOf: url)
-    print(content)
-
-    // get the content of the url as Data synchronously
-    let data = try Data(contentsOf: url)
-}
-catch {
-    print(error.localizedDescription)
-}
-
-
-// Turning sync to async
-DispatchQueue.global().async { [weak self] in
-    //this is happening on a background thread
-    do {
-        let content = try String(contentsOf: url)
-        DispatchQueue.main.async {
-            //this is happening on the main thread
-            print(content)
-        }
-    }
-    catch {
-        print(error.localizedDescription)
-    }
-}
-

Apple made an important note on their official Data documentation, that you should NOT use these methods for downloading non-file URLs, but still people are teaching / using these bad practices, but why? 😥

Don’t use this synchronous method to request network-based URLs.

My advice here: always use the URLSession to perform network related data-transfers. Creating a data task is simple, it’s an asynchronous operation by default, the callback runs on a background thread, so nothing will be blocked by default. Modern networking APIs are real good on iOS, in 99% of the cases you won’t need Alamofire anymore for these kind of tasks. Say no to dependencies! 🚫

// The best approach without using Combine
-URLSession.shared.dataTask(with: url) { data, response, error in
-    // do your stuff here...
-    DispatchQueue.main.async {
-        // do something on the main queue
-    }
-}.resume()
-

It’s also worth to mention if you need to use a different HTTP method (other than GET), send special headers (credentials, accept policies, etc.) or provide extra data in the body, you need to construct an URLRequest object first. You can only send these custom requests using the URLSession APIs.

On Apple platforms you are not allowed to use the insecure HTTP protocol anymore. If you want to reach a URL without the secure layer (HTTPS) you have to disable App Transport Security.

The problem with data tasks

What about big files, such as images? Let me show you a few tutorials before we dive in:

With all due respect, I think all of these links above are really bad examples of loading remote images. Sure they do the job, they’re also very easy to implement, but maybe we should cover the whole story… 🤐

For small interactions with remote servers, you can use the URLSessionDataTask class to receive response data into memory (as opposed to using the URLSessionDownloadTask class, which stores the data directly to the file system). A data task is ideal for uses like calling a web service endpoint.

What is difference between URLSessionDataTask vs URLSessionDownloadTask?

If we read the docs carefully, it becomes clear that data task is NOT the right candidate for downloading big assets. That class is designed to request only smaller objects, since the underlying data is going to be loaded into memory. On the other hand the download task saves the content of the response on the disk (instead of memory) and you will receive a local file URL instead of a Data object. Turns out that moving from data tasks to download tasks will have a HUGE impact on your memory consumption. I have some numbers. 📈

I downloaded the following image file (6000x4000px 💾 13,1MB) using both methods. I made a brand new storyboard based Swift 5.1 project. The basic RAM usage was ~52MB, when I fetched the image using the URLSessionDataTask class, the memory usage jumped to ~82MB. Turning the data task into a download task only increased the base memory size by ~4MB (to a total ~56MB), which is a significant improvement.

let url = URL(string: "https://images.unsplash.com/photo-1554773228-1f38662139db")!
-
-// data task
-URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
-    guard let data = data else {
-        return
-    }
-    DispatchQueue.main.async {
-        self?.imageView.image = UIImage(data: data)
-    }
-}.resume()
-
-
-// download task
-URLSession.shared.downloadTask(with: url) { [weak self] url, response, error in
-    guard
-        let cache = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first,
-        let url = url
-    else {
-        return
-    }
-
-    do {
-        let file = cache.appendingPathComponent("\(UUID().uuidString).jpg")
-        try FileManager.default.moveItem(atPath: url.path,
-                                         toPath: file.path)
-        DispatchQueue.main.async {
-            self?.imageView.image = UIImage(contentsOfFile: file.path)
-        }
-    }
-    catch {
-        print(error.localizedDescription)
-    }
-}.resume()
-

When I rendered the image using an UIImageView the memory footprint was ~118MB (total: ~170MB) for the data task, and ~93MB (total: ~145MB) for the download task. Here’s a quick summary:

  • Data task: ~30MB
  • Data task with rendering: ~118MB
  • Download task: ~4MB
  • Download task with rendering: ~93MB

I hope you get my point. Please don’t forget that the Foundation networking layer comes with four types of session tasks. You should always use the right one that fits the job. We can say that the difference between URLSessionDataTask vs URLSessionDownloadTask is: a lot of memory (in this case about 25MB of RAM).

You can use Kingfisher or SDWebImage to download & manipulate remote images..

You might say that this is an edge case since most of the images (even HD ones) are maximum a few hundred kilobytes. Still, my takeaway here is that we can do better, and we should always do so if possible. 🤓

Downloading images using Combine

WWDC19, Apple announced the Combine framework, which brings us a few new extensions for some Foundation objects. Modern times require modern APIs, right? If you are already familiar with the new SDK that’s good, but if you don’t know what the heck is this declarative functional reactive madness, you should read my comprehensive tutorial about the Combine framework.

The first version of Combine shipped with a nice dataTaskPublisher extension method for the URLSession class. Wait, where are the others? No download task publisher? What should we do now? 🤔

How to write a custom Publisher?

SwiftLee has a nice tutorial about Combine that can help you a lot with UIControl events. Another great read (even better than the first one) by Donny Wals is about understanding Publishers and Subscribers. It’s a really well-written article, you should definitely check this one, I highly recommend it. 🤘🏻

Now let’s start creating our own DownloadTaskPublisher. If you command + click on the dataTaskPublisher method in Xcode, you can see the corresponding interface. There is also a DataTaskPublisher struct, right below. Based on that template we can create our own extension. There are two variants of the same data task method, we’ll replicate this behavior. The other thing we need is a DownloadTaskPublisher struct, I’ll show you the Swift code first, then we’ll discuss the implementation details.

extension URLSession {
-
-    public func downloadTaskPublisher(for url: URL) -> URLSession.DownloadTaskPublisher {
-        self.downloadTaskPublisher(for: .init(url: url))
-    }
-
-    public func downloadTaskPublisher(for request: URLRequest) -> URLSession.DownloadTaskPublisher {
-        .init(request: request, session: self)
-    }
-
-    public struct DownloadTaskPublisher: Publisher {
-
-        public typealias Output = (url: URL, response: URLResponse)
-        public typealias Failure = URLError
-
-        public let request: URLRequest
-        public let session: URLSession
-
-        public init(request: URLRequest, session: URLSession) {
-            self.request = request
-            self.session = session
-        }
-
-        public func receive<S>(subscriber: S) where S: Subscriber,
-            DownloadTaskPublisher.Failure == S.Failure,
-            DownloadTaskPublisher.Output == S.Input
-        {
-            let subscription = DownloadTaskSubscription(subscriber: subscriber, session: self.session, request: self.request)
-            subscriber.receive(subscription: subscription)
-        }
-    }
-}
-

A Publisher can send an Output or a Failure message to an attached subscriber. You have to create a new typealias for each type, since they both are generic constraints defined on the protocol level. Next, we’ll store the session and the request objects for later use. The last part of the protocol conformance is that you have to implement the receive(subscriber: S) generic method. This method is responsible for attaching a new subscriber through a subscription object. Ummm… what? 🤨

A publisher/subscriber relationship in Combine is solidified in a third object, the subscription. When a subscriber is created and subscribes to a publisher, the publisher will create a subscription object and it passes a reference to the subscription to the subscriber. The subscriber will then request a number of values from the subscription in order to begin receiving those values.

A Publisher and a Subscriber is connected through a Subscription. The Publisher only creates the Subscription and passes it to the subscriber. The Subscription contains the logic that’ll fetch new data for the Subscriber. The Subscriber receives the Subscription, the values and the completion (success or failure).

  • The Subscriber subscribes to a Publisher
  • The Publisher creates a Subscription
  • The Publisher gives this Subscription to the Subscriber
  • The Subscriber demands some values from the Subscription
  • The Subscription tries to collect the values (success or failure)
  • The Subscription sends the values to the Subscriber based on the demand policy
  • The Subscription sends a Failure completion to the Subscriber if an error happens
  • The Subscription sends completion if no more values are available

How to make a custom Subscription?

Ok, time to create our subscription for our little Combine based downloader, I think that you will understand the relationship between these three objects if we put together the final pieces of the code. 🧩

extension URLSession {
-
-    final class DownloadTaskSubscription<SubscriberType: Subscriber>: Subscription where
-        SubscriberType.Input == (url: URL, response: URLResponse),
-        SubscriberType.Failure == URLError
-    {
-        private var subscriber: SubscriberType?
-        private weak var session: URLSession!
-        private var request: URLRequest!
-        private var task: URLSessionDownloadTask!
-
-        init(subscriber: SubscriberType, session: URLSession, request: URLRequest) {
-            self.subscriber = subscriber
-            self.session = session
-            self.request = request
-        }
-
-        func request(_ demand: Subscribers.Demand) {
-            guard demand > 0 else {
-                return
-            }
-            self.task = self.session.downloadTask(with: request) { [weak self] url, response, error in
-                if let error = error as? URLError {
-                    self?.subscriber?.receive(completion: .failure(error))
-                    return
-                }
-                guard let response = response else {
-                    self?.subscriber?.receive(completion: .failure(URLError(.badServerResponse)))
-                    return
-                }
-                guard let url = url else {
-                    self?.subscriber?.receive(completion: .failure(URLError(.badURL)))
-                    return
-                }
-                do {
-                    let cacheDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
-                    let fileUrl = cacheDir.appendingPathComponent((UUID().uuidString))
-                    try FileManager.default.moveItem(atPath: url.path, toPath: fileUrl.path)
-                    _ = self?.subscriber?.receive((url: fileUrl, response: response))
-                    self?.subscriber?.receive(completion: .finished)
-                }
-                catch {
-                    self?.subscriber?.receive(completion: .failure(URLError(.cannotCreateFile)))
-                }
-            }
-            self.task.resume()
-        }
-
-        func cancel() {
-            self.task.cancel()
-        }
-    }
-}
-

A Subscriber has an Input and a Failure type. A subscriber can only subscribe to a publisher with the same types. The Publisher’s Output & Failure types have to be identical with the Subscription Input and Failure types. This time we can’t go with an associatedType, but we have to create a generic value that has a constraint on these requirements by using a where clause. The reason behind this is that we don’t know what kind of Subscriber will subscribe to this subscription. It can be either a class A or B, who knows… 🤷‍♂️

We have to pass a few properties in the init method, store them as instance variables (be careful with classes, you should use weak if applicable). Lastly we implement the value request method, by respecting the demand policy. The demand is just a number. It tells us how many values can we send back to the subscriber at maximum. In our case we’ll have max 1 value, so if the demand is greater than zero, we’re good to go. You can send messages to the subscriber by calling various receive methods on it.

You have to manually send the completion event with the .finished or the .failure(T) value. Also we have to move the downloaded temporary file before the completion block returns otherwise we’ll completely lose it. This time I’m going to simply move the file to the application cache directory. As a gratis cancellation is a great way to end battery draining operations. You just need to implement a custom cancel() method. In our case, we can call the same method on the underlying URLSessionDownloadTask.

That’s it. We’re ready with the custom publisher & subscription. Wanna try them out?

How to create a custom Subscriber?

Let’s say that there are 4 kinds of subscriptions. You can use the .sink or the .assign method to make a new subscription, there is also a thing called Subject, which can be subscribed for publisher events or you can build your very own Subscriber object. If you choose this path you can use the .subscribe method to associate the publisher and the subscriber. You can also subscribe a subject.

final class DownloadTaskSubscriber: Subscriber {
-    typealias Input = (url: URL, response: URLResponse)
-    typealias Failure = URLError
-
-    var subscription: Subscription?
-
-    func receive(subscription: Subscription) {
-        self.subscription = subscription
-        self.subscription?.request(.unlimited)
-    }
-
-    func receive(_ input: Input) -> Subscribers.Demand {
-        print("Subscriber value \(input.url)")
-        return .unlimited
-    }
-
-    func receive(completion: Subscribers.Completion<Failure>) {
-        print("Subscriber completion \(completion)")
-        self.subscription?.cancel()
-        self.subscription = nil
-    }
-}
-

The subscriber above will simply print out the incoming values. We have to be extremely careful with memory management. The received subscription will be stored as a strong property, but when the publisher sends a completion event we should cancel the subscription and remove the reference.

When a value arrives we have to return a demand. In our case it really doesn’t matter since we’ll only have 1 incoming value, but if you’d like to limit your publisher, you can use e.g. .max(1) as a demand.

Here is a quick sample code for all the Combine subscriber types written in Swift 5.1:

class ViewController: UIViewController {
-
-    @IBOutlet weak var imageView: UIImageView!
-
-    static let url = URL(string: "https://images.unsplash.com/photo-1554773228-1f38662139db")!
-
-    static var defaultValue: (url: URL, response: URLResponse) = {
-        let fallbackUrl = URL(fileURLWithPath: "fallback-image-path")
-        let fallbackResponse = URLResponse(url: fallbackUrl, mimeType: "foo", expectedContentLength: 1, textEncodingName: "bar")
-        return (url: fallbackUrl, response: fallbackResponse)
-    }()
-
-    @Published var value: (url: URL, response: URLResponse) = ViewController.defaultValue
-    let subject = PassthroughSubject<(url: URL, response: URLResponse), URLError>()
-    let subscriber = DownloadTaskSubscriber()
-
-    var sinkOperation: AnyCancellable?
-
-    var assignOperation: AnyCancellable?
-    var assignSinkOperation: AnyCancellable?
-
-    var subjectOperation: AnyCancellable?
-    var subjectSinkOperation: AnyCancellable?
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        self.sinkExample()
-        self.assignExample()
-        self.subjectExample()
-        self.subscriberExample()
-    }
-
-    func sinkExample() {
-        self.sinkOperation = URLSession.shared
-            .downloadTaskPublisher(for: ViewController.url)
-            .sink(receiveCompletion: { completion in
-                print("Sink completion: \(completion)")
-            }) { value in
-                print("Sink value: \(value.url)")
-            }
-    }
-
-    func assignExample() {
-        self.assignSinkOperation = self.$value.sink { value in
-            print("Assign value: \(value.url)")
-        }
-
-        self.assignOperation = URLSession.shared
-            .downloadTaskPublisher(for: ViewController.url)
-            .replaceError(with: ViewController.defaultValue)
-            .assign(to: \.value, on: self)
-    }
-
-    func subjectExample() {
-        self.subjectSinkOperation = self.subject.sink(receiveCompletion: { completion in
-            print("Subject completion: \(completion)")
-        }) { value in
-            print("Subject value: \(value.url)")
-        }
-
-        self.subjectOperation = URLSession.shared
-            .downloadTaskPublisher(for: ViewController.url)
-            .subscribe(self.subject)
-    }
-
-    func subscriberExample() {
-        URLSession.shared
-            .downloadTaskPublisher(for: ViewController.url)
-            .subscribe(DownloadTaskSubscriber())
-    }
-}
-

This is really nice. We can download a file using our custom Combine based URLSession extension.

Don’t forget to store the AnyCancellable pointer otherwise the entire Combine operation will be deallocated way before you could receive anything from the chain / stream.

Putting everything together

I promised a working image downloader, so let me explain the whole flow. We have a custom download task publisher that’ll save our remove image file locally and returns a tuple with the file URL and the response. ✅

Next I’m going to simply assume that there was a valid image behind the URL, and the server returned a valid response, so I’m going to map the publisher’s output to an UIImage object. I’m also going to replace any kind of error with a fallback image value. In a real-world application, you should always do some extra checkings on the URLResponse object, but for the sake of simplicity I’ll skip that for now.

The last thing is to update our image view with the returned image. Since this is a UI task it should happen on the main thread, so we have to use the receive(on:) operation to switch context. If you want to learn more about schedulers in the Combine framework you should read Vadim Bulavin’s article. It’s a gem. 💎

If you are not receiving values on certain appleOS versions, that’s might because there was a change in Combine around December, 2019. You should check these links: link1, link2

Anyway, here’s the final Swift code for a possible image download operation, simple & declarative. 👍

class ViewController: UIViewController {
-
-    @IBOutlet weak var imageView: UIImageView!
-
-    var operation: AnyCancellable?
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        let url = URL(string: "https://images.unsplash.com/photo-1554773228-1f38662139db")!
-
-        self.operation = URLSession.shared
-            .downloadTaskPublisher(for: url)
-            .map { UIImage(contentsOfFile: $0.url.path)! }
-            .replaceError(with: UIImage(named: "fallback"))
-            .receive(on: DispatchQueue.main)
-            .assign(to: \.image, on: self.imageView)
-    }
-}
-

Finally, we can display our image. Ouch, but wait… there is still room for improvements. What about caching? Plus a 6000x4000px picture is quite huge for a small display, shouldn’t we resize / scale the image first? What happens if I want to use the image in a list, shouldn’t I cancel the download tasks when the user scrolls? 😳

Maybe I’ll write about these issues in an upcoming tutorial, but I think this is the point where I should end this article. Feel free to play around with my solution and please share your ideas & thoughts with me on Twitter.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/how-to-launch-a-macos-app-at-login/index.html b/docs/how-to-launch-a-macos-app-at-login/index.html deleted file mode 100644 index 091de0d..0000000 --- a/docs/how-to-launch-a-macos-app-at-login/index.html +++ /dev/null @@ -1,402 +0,0 @@ - - - - - - - - - - - - How to launch a macOS app at login? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 2 min read - -
-

How to launch a macOS app at login?

-
-

In this tutorial I'll show you how to launch a completely sandboxed macOS application on system startup written in Swift.

-
- -

Update: you should simply add the LaunchAtLogin library to your project. It’ll take care of everything and it has some other cool utility features.

Project setup

Let’s start this tutorial by creating a new Xcode project with a macOS app template. Name it for example MainApplication, use storyboards and of course select Swift as the default language, we don’t need tests for this project at all.

Now that we have the main application target, there is this nice little function available called SMLoginItemSetEnabled. With that function you can register an application bundle identifier to auto start when the user logs in, but you can not register your own app identifier. Sounds crazy, huh? 😜

You can register a bundle identifier embedded into your main application to get auto-launched by the system. To do this you will have to create a new launcher application which will be launched later by your main application.

You also have to code sign your application with your Developer ID, otherwise it won’t start after you log in to macOS. Sandboxing is a crucial part of the process, so make sure that you follow every instruction carefully.

Targets & configurations

Create a new target inside your current project. Name this new target for example LauncherApplication. Enable sandbox and code signing for both targets (main and launcher apps) under the Signing & Capabilities tab. For the LauncherApplication target in the build settings set skip install to yes.

Skip install

For the launcher app add a new entry to the Info.plist file: Application is background only with the value: yes. This will set your application as a background app, we don’t really need user interface for a launcher tool, right?

Background only

Add a new copy file build phase to your main application target to copy your launcher application into the bundle. The destination should be wrapper and the subpath should be Contents/Library/LoginItems.

Copy files

Link the ServiceManagement.framework to your main application and double check that the launcher app is embedded into your main application.

Frameworks

From the LauncherApplication‘s storyboard file delete your window and your view controller, also you can remove the ViewController.swift file from this target. This is a background app after all, so we don’t need these stupid things to lay around.

Creating the launcher programmatically

Somewhere in your main application you have to register your launcher application’s identifier. When your main application starts you have to kill the launcher application if it’s still running. You can do this by sending a notification to that specific app with the NSDistributedNotificationCenter class.

import Cocoa
-import ServiceManagement
-
-extension Notification.Name {
-    static let killLauncher = Notification.Name("killLauncher")
-}
-
-@NSApplicationMain
-class AppDelegate: NSObject {}
-
-
-extension AppDelegate: NSApplicationDelegate {
-
-    func applicationDidFinishLaunching(_ aNotification: Notification) {
-
-        let launcherAppId = "com.tiborbodecs.LauncherApplication"
-        let runningApps = NSWorkspace.shared.runningApplications
-        let isRunning = !runningApps.filter { 
-            $0.bundleIdentifier == launcherAppId 
-        }.isEmpty
-
-        SMLoginItemSetEnabled(launcherAppId as CFString, true)
-
-        if isRunning {
-            DistributedNotificationCenter.default().post(
-                name: .killLauncher, 
-                object: Bundle.main.bundleIdentifier!
-            )
-        }
-    }
-}
-

In the launcher application you have to start your main application if it’s not running already. That’s it. You should also subscribe for the notifications from the main app to terminate if the launcher is not needed anymore.

import Cocoa
-
-extension Notification.Name {
-    static let killLauncher = Notification.Name("killLauncher")
-}
-
-@NSApplicationMain
-class AppDelegate: NSObject {
-
-    @objc func terminate() {
-        NSApp.terminate(nil)
-    }
-}
-
-extension AppDelegate: NSApplicationDelegate {
-
-    func applicationDidFinishLaunching(_ aNotification: Notification) {
-
-        let mainAppIdentifier = "com.tiborbodecs.MainApplication"
-        let runningApps = NSWorkspace.shared.runningApplications
-        let isRunning = !runningApps.filter { 
-            $0.bundleIdentifier == mainAppIdentifier 
-        }.isEmpty
-
-        if !isRunning {
-            DistributedNotificationCenter.default().addObserver(
-                self, 
-                selector: #selector(self.terminate), 
-                name: .killLauncher, 
-                object: mainAppIdentifier
-            )
-
-            let path = Bundle.main.bundlePath as NSString
-            var components = path.pathComponents
-            components.removeLast()
-            components.removeLast()
-            components.removeLast()
-            components.append("MacOS")
-            components.append("MainApplication") //main app name
-
-            let newPath = NSString.path(withComponents: components)
-
-            NSWorkspace.shared.launchApplication(newPath)
-        }
-        else {
-            self.terminate()
-        }
-    }
-}
-

That’s it, we’re ready to launch. Export your main application and here is the most important thing: code sign it with your Developer ID. Start it, close it, log out and back into the system. Hopefully your main application will be running again.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Conventions for Xcode

-
-

Learn how to organize your codebase. If you are struggling with Xcode project structure, files, naming conventions, read this.

- -
- Tooling - Xcode -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Custom working directory in Xcode

-
-

Learn how to set a custom working directory in Xcode to solve one of the most common beginner issue when using Vapor.

- -
- Tooling - Xcode -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

How to build macOS apps using only the Swift Package Manager?

-
-

In this article we're going to create a macOS application without ever touching an Xcode project file, but only working with SPM.

- -
- macOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

How to launch a macOS app at login?

-
-

In this tutorial I'll show you how to launch a completely sandboxed macOS application on system startup written in Swift.

- -
- Tooling - macOS -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/how-to-make-a-swift-framework/index.html b/docs/how-to-make-a-swift-framework/index.html deleted file mode 100644 index 05becff..0000000 --- a/docs/how-to-make-a-swift-framework/index.html +++ /dev/null @@ -1,327 +0,0 @@ - - - - - - - - - - - - How to make a Swift framework? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 2 min read - -
-

How to make a Swift framework?

-
-

Creating a Swift framework shouldn't be hard. This tutorial will help you making a universal framework for complex projects.

-
- -

What is a framework?

A framework is a hierarchical directory that encapsulates shared resources, such as a dynamic shared library, nib files, image files, localized strings, header files, and reference documentation in a single package.

So in a nutshell, a framework is a highly reusable component for your apps.

How to make it?

There is an article about Xcode conventions which will help you organize your projects, you should check that first, if you haven’t before.

Traditional way

There is a traditional way to make a framework in Xcode. I’m going to create a shared framework for all the apple platforms (iOS, macOS, tvOS, watchOS), which is going to be capable of logging things to the standard console.

Let’s make the project:

  • Create a new project using one of the framework targets
  • Follow the instructions fill & name all the fields
  • Add all the other platform framework targets
  • Rename all targets according to the platform names

Now in Finder:

  • Create a Sources folder and move all the Swift and header files there
  • Create an Assets folder with platforms subfolders
  • Move all the Info.plist files into the correct platfrom subdirectory
  • Create a Tests folder and move test files there

Back to Xcode:

  • Remove every group and add the new Folders from Finder
  • Check that every target has the correct files (framework & tests)
  • Inside the header file, replace UIKit depencency with Foundation

The goal is to achieve a structure somewhat like this:

Xcode project framework setup

Project settings:

  • Select the correct plist files for the targets
  • Set your bundle identifiers (use my conventions)
  • Setup platform versions (advice: support 1 older version too)
  • Setup the plist files for the tests from the build settings pane
  • Set the product name (Console) in your framework build settings
  • Check your build phases and add the public header file.

Scheme settings:

  • Go to the scheme settings and setup shared schemes for the frameworks
  • Gather coverage data if you need it
  • Write your framework you can use Swift “macros” to detect platforms

There is a flag in Xcode to allow app extension API only, if you are embedding your framework inside an application extension it should be enabled!

Congratulations, now you have your brand new Swift framework made in the traditional way. Let’s continue with a neat trick.

Universal cross platform framework

It is possible to create a multiplatform single scheme Xcode project with cross platform support for every platform, but it’s not recommended because it’s a hack. However multiple open source libraries do the same way, so why shouldn’t we.

  • Delete all the targets, schemes, except macOS!!!
  • Rename the remaining target, scheme (we don’t need platform names)
  • Use the project configuration file, set the xcconfig on the project
  • Delete Info.plist files, use one for the framework and one for the tests
  • Rename bundle identifier (we don’t need platform names there too)

States can be mixed up if you are building for multiple platforms, however this is a nice clean way to support every platforms, without duplications.

How to use a Swift framework?

Embedding your framework is the most straightforward thing to do. You can simply drag the framework project to another Xcode project, the only thing left to do is to the embedded the framework into the application. You can go to the embedded binaries section inside the general project info tab and add the framework as a dependency.

Swift Package Manager

With SPM, you have to make a Package.swift file first, then you’ll be able to build your targets with the swift build command. Now that Xcode supports the Swift Package Manager, it’s really easy to integrate third party frameworks by using it.

You can download the final framework examples from GitHub.

Make sure that you don’t miss out my deep dive into swift frameworks post.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/how-to-parse-json-in-swift-using-codable-protocol/index.html b/docs/how-to-parse-json-in-swift-using-codable-protocol/index.html deleted file mode 100644 index d03efe9..0000000 --- a/docs/how-to-parse-json-in-swift-using-codable-protocol/index.html +++ /dev/null @@ -1,372 +0,0 @@ - - - - - - - - - - - - How to parse JSON in Swift using Codable protocol? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 1 min read - -
-

How to parse JSON in Swift using Codable protocol?

-
-

In this Swift tutorial, I'd like to give you an example about getting and parsing JSON data using URLSession and Codable protocol.

-
- -

Dependencies

First of all just a few words about dependencies. From Swift 4 you don’t need any dependency to parse JSON data, because there are built-in protocols to take care of everything. If you are still using some kind of 3rd-party you should definitely ditch it for the sake of simplicity. By the way before you add any external dependency into your project, please think twice. 🤔

Networking

If your task is simply to load some kind of JSON document through HTTP from around the web, - surprise - you won’t need Alamofire at all. You can use the built-in URLSession class to make the request, and get back everything that you’ll need. The Foundation networking stack is already a complex and very useful stack, don’t make things even more complicated with extra layers.

JSON parsing

Now, after the short intro, let’s dive in and get some real fake JSON data from the JSONPlaceholder web service. I’m going to place the whole thing right here, you can select it, copy and paste into a Swift playground file.

import Foundation
-import PlaygroundSupport
-
-PlaygroundPage.current.needsIndefiniteExecution = true
-
-struct Post: Codable {
-
-    enum CodingKeys: String, CodingKey {
-        case id
-        case title
-        case body
-        case userIdentifier = "userId"
-    }
-
-    let id: Int
-    let title: String
-    let body: String
-    let userIdentifier: Int
-}
-
-let url = URL(string: "https://jsonplaceholder.typicode.com/posts")!
-
-URLSession.shared.dataTask(with: url) { data, response, error in
-    if let error = error {
-        print("Error: \(error.localizedDescription)")
-        PlaygroundPage.current.finishExecution()
-    }
-    guard 
-        let httpResponse = response as? HTTPURLResponse, 
-        httpResponse.statusCode == 200 
-    else {
-        print("Error: invalid HTTP response code")
-        PlaygroundPage.current.finishExecution()
-    }
-    guard let data = data else {
-        print("Error: missing data")
-        PlaygroundPage.current.finishExecution()
-    }
-
-    // feel free to uncomment this for debugging data
-    // print(String(data: data, encoding: .utf8))
-
-    do {
-        let decoder = JSONDecoder()
-        let posts = try decoder.decode([Post].self, from: data)
-
-        print(posts.map { $0.title })
-        PlaygroundPage.current.finishExecution()
-    }
-    catch {
-        print("Error: \(error.localizedDescription)")
-        PlaygroundPage.current.finishExecution()
-    }
-}.resume()
-

As you can see downloading and parsing JSON from the web is a really easy task. This whole code snippet is around 50 lines of code. Of course it’s just a proof of concept, but it works and you don’t need any dependency. It’s pure Swift and Foundation.

To save some typing, you can also generate the final objects directly from the JSON structure with these amazing Xcode extensions.

The Codable protocol - which is actually a compound typealias from Encodable & Decodable protocols - makes the process of parsing JSON data in Swift magical. 💫

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/how-to-set-up-pgsql-for-fluent-4/index.html b/docs/how-to-set-up-pgsql-for-fluent-4/index.html deleted file mode 100644 index 4ae191f..0000000 --- a/docs/how-to-set-up-pgsql-for-fluent-4/index.html +++ /dev/null @@ -1,397 +0,0 @@ - - - - - - - - - - - - How to set up pgSQL for Fluent 4? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 2 min read - -
-

How to set up pgSQL for Fluent 4?

-
-

This is a tutorial for beginners about using PostgreSQL. I'll show you how to automatically backup and restore the database.

-
- -

If you are already familiar with PostgreSQL, but you don’t know much about how to use databases in Vapor, you should read my other tutorial about Fluent for beginners.

A quick intro to PostgreSQL

PostgreSQL is an open source database, it’s available for macOS, Linux and some other operating systems. You can install it by using the de-facto package manager on every platform. 📦

# Linux
-sudo apt-get install postgresql postgresql-contrib
-sudo service postgresql start
-# check service status
-sudo service --status-all
-sudo service postgresql status
-
-# macOS
-brew install postgresql
-brew services start postgresql
-# check service status
-brew services list
-

You’ll also need to set a proper password for the postgres user, which is the admin user by default with godlike permissions. You can change the root password, you just have to log in as a root & alter the postgres user record with the new pass. 🔑

# Linux
-sudo -u postgres psql postgres
-# macOS
-psql -U postgres
-
-# psql (12.1)
-# Type "help" for help.
-#
-# postgres=#
-
-# ALTER ROLE
-alter user postgres with password 'mypassword';
-
-# exit
-\q
-

From now on you’ll be able to access pgSQL as root on both platforms like this:

psql -h localhost -U postgres
-

It is recommended to use a dedicated user for every single database that you create instead of working with a shared root user. Let me show you how to create a new DB with an associated user.

# List of databases
-\l
-# Show current database
-select current_database();
-# Create new database
-create database mydb;
-# Change database
-\c mydb
-# Create user
-create user myuser with encrypted password 'mypassword';
-# Grant privileges for user on the database
-grant all privileges on database mydb to myuser;
-# Quit from psql console
-\q
-

That’s it, you can manage your database by using the newly created myuser account.

# Log in back to psql console with myuser using mydb
-psql -h localhost -U myuser mydb
-# List all tables
-\dt
-# Describe table structure (will be useful later on)
-\d+ <table>
-

You can learn more about SQL commands using this pgSQL tutorial site.

The command below can completely wipe your database, be extremely careful!

Now you are ready to play around with Fluent, but before we start I’d like to show you some more tips & tricks. During development, things can go wrong and you might need a fresh start for your DB. Here’s how to drop & reinitiate everything. 😱

# Reset database
-\c mydb
-drop schema public cascade;
-create schema public;
-grant all on schema public to postgres;
-grant all on schema public to myuser;
-grant all on schema public to public;
-

The snippet above will delete the public schema, next it’ll recreate it and add all the necessary permissions for the required users. It’s pretty straightforward but still dangerous. ⚠️

NOTE : You can execute SQL scripts straight from the terminal by using the following command: psql -h localhost -U myuser mydb -c "select * from mytable;"

You can wipe everything from the command line using this “one-liner”:

# Run psql command from the command line
-psql -h localhost -U postgres mydb\
-    -c "drop schema public cascade; \
-    create schema public; \
-    grant all on schema public to postgres; \
-    grant all on schema public to myuser; \
-    grant all on schema public to public;"
-

I prefer to have daily backups from all my databases, this little shell script can do the job.

#!/bin/bash
-
-# Backup database
-BACKUP_DIR=/Users/tib/backups
-FILE_SUFFIX=_pg_backup.sql
-OUTPUT_FILE=${BACKUP_DIR}/`date +"%Y_%m_%d__%H_%M"`${FILE_SUFFIX}
-PGPASSWORD="mypass" pg_dump -U myuser -h localhost mydb -F p -f ${OUTPUT_FILE}
-gzip $OUTPUT_FILE
-
-# Remove old backups
-DAYS_TO_KEEP=30
-find $BACKUP_DIR -maxdepth 1 -mtime +$DAYS_TO_KEEP -name "*${FILE_SUFFIX}.gz" -exec rm -rf '{}' ';'
-

You can easily restore a database from a backup by entering the following lines to the terminal:

# Restore database
-gunzip -k file.gz
-psql -U myuser -d mydb -1 -f mybackup.sql
-

Sometimes after I restarted my mac it happened to me that the PostgreSQL stopped working. I had to run the snippet below to fix the issue. The first line stops the service, the second initialize a new database, and the third will start the service again. Alternatively, you can start the database again with the brew services start postgresql command.

pg_ctl -D /usr/local/var/postgres stop -s -m fast
-initdb /usr/local/var/postgres
-pg_ctl -D /usr/local/var/postgres -l /usr/local/var/postgres/server.log start
-

I’m not a DevOps guru, feel free to tweet me if you know why this happened to me. 😅

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/how-to-store-keys-in-env-files/index.html b/docs/how-to-store-keys-in-env-files/index.html deleted file mode 100644 index 691f65f..0000000 --- a/docs/how-to-store-keys-in-env-files/index.html +++ /dev/null @@ -1,360 +0,0 @@ - - - - - - - - - - - - How to store keys in env files? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 2 min read - -
-

How to store keys in env files?

-
-

In this tutorial I'll show you how to save and load secret keys as base64 encoded strings using dotenv files in Vapor 4.

-
- -

Using the Environment in Vapor 4

Just like many popular server side frameworks, your Vapor based backend application can load a file called .env. It is possible to store key-value based (secret) configuration values inside this file. When you run the app, one of the following file will be loaded, based on the current environment:

  • Production (.env)
  • Development (.env.development)
  • Testing (.env.testing)

When you execute your tests the .env.testing file will be used. If you start the app using the serve Vapor command you can also change the environment using the --env or -e flag. The available options are production and development, and the corresponding .env file will be loaded. It is possible to create a custom environment, you can read more about this in the official Vapor docs. The .env file usually contains one key and value per line, now the problem starts when you want to store a multiline secret key in the file. So what can we do about this? 🤔

Base64 encoded secret keys

Yes, we can encode the secret key using a base64 encoding. No, I don’t want to copy my secrets into an online base64 encoder, because there is a pretty simple shell command that I can use.

echo "<my-secret-key>" | base64
-

If you don’t like unix commands, we can always put together a little Swift script and use an extension on the String type to encode keys. Just save the snippet from below into a base64.swift file, put your key into the key section, give the file some executable permission & run it using the chmod o+x && ./base64.swift one-liner command and voilá…

#! /usr/bin/swift
-
-import Foundation
-
-extension String {
-
-    func base64Encoded() -> String? {
-        return data(using: .utf8)?.base64EncodedString()
-    }
-}
-
-let key = """
-    <my-secret-key-comes-here>
-"""
-
-print(key.base64Encoded()!)
-

You can copy & paste the encoded value of the secret key into your own .env.* file, replace the asterix symbol with your current environment of course, before you do it. 🙈

//e.g. .env.development
-SECRET_KEY="<base64-encoded-secret-key>"
-

Now we just have to decode this key somehow, before we can start using it…

Decoding the secret key

You can implement a base64 decoder as a String extension with just a few lines of Swift code.

import Foundation
-
-extension String {
-
-    func base64Decoded() -> String? {
-        guard let data = Data(base64Encoded: self) else { return nil }
-        return String(data: data, encoding: .utf8)
-    }
-}
-

Now in my projects I like to extend the Environment object and place all my custom variables there as static constants, this way I can access them in a really convenient way, plus if something goes wrong (usually when I don’t re-create the .env file after a git reset or I don’t have all the variables present in the dotenv file) the app will crash because of the forced unwraps, and I’ll know for sure that something is wrong with my environment. It’s a crash for my own safety. 💥

import Vapor
-
-extension Environment {
-    static let secretKey = Self.get("SECRET_KEY")!.base64Decoded()!
-}
-
-// usage:
-Environment.secretKey
-

I think this approach is very useful. Of course you should place the .env.* pattern into your .gitignore file, otherwise if you place some secrets into the dotenv file and you push that into the remote… well, everyone else will know your keys, passwords, etc. You don’t want that, right? ⚠️

Feel free to use this method when you have to implement a Sign in With Apple workflow, or a Apple Push Notification service (APNs). In those cases you’ll definitely have to pass one ore more secret keys to your Vapor based backend application. That’s it for now, thanks for reading.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/how-to-use-a-swift-library-in-c/index.html b/docs/how-to-use-a-swift-library-in-c/index.html deleted file mode 100644 index 0a723b7..0000000 --- a/docs/how-to-use-a-swift-library-in-c/index.html +++ /dev/null @@ -1,407 +0,0 @@ - - - - - - - - - - - - How to use a Swift library in C - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 4 min read - -
-

How to use a Swift library in C

-
-

In this tutorial, we're going to build a C app by importing a Swift library and talk a bit about the Swift / C Interoperability in general.

-
- -

How to build a C compatible Swift library?

In order to create a Swift library that’s going to work with C, we have to play around with unsafe memory pointers to create a C compatible interface. Fortunately I was able to find a nice example, which served me as a good starting point, on the Swift forums created by Cory Benfield, so that’s what we’re going to use in this case. Thanks you. 🙏

final class MyType {
-    var count: Int = 69
-}
-
-@_cdecl("mytype_create")
-public func mytype_create() -> OpaquePointer {
-    let type = MyType()
-    let retained = Unmanaged.passRetained(type).toOpaque()
-    return OpaquePointer(retained)
-}
-
-@_cdecl("mytype_get_count")
-public func mytype_get_count(_ type: OpaquePointer) -> CInt {
-    let type = Unmanaged<MyType>.fromOpaque(UnsafeRawPointer(type)).takeUnretainedValue()
-    return CInt(type.count)
-}
-
-@_cdecl("mytype_destroy")
-public func mytype_destroy(_ type: OpaquePointer) {
-    _ = Unmanaged<MyType>.fromOpaque(UnsafeRawPointer(type)).takeRetainedValue()
-}
-

The good news is that we don’t necessary have to create a separate header file for our interfaces, but the Swift compiler can generate it for us if we provide the -emit-objc-header flag.

I have an article about the swiftc command for beginners and I also wrote some things about the Swift compiler, where I talk about the available flags. This time we’re going to use the -module-name option to specify our module name, we’re going to generate the required files using the -emit-dependencies flag, parse the source files as a library (-parse-as-library), since we’d like to generate a Swift library provide the necessary target and version information and emit a header file.

# macOS
-swiftc \
-        -module-name mytype \
-        -emit-dependencies \
-        -parse-as-library \
-        -c mytype.swift \
-        -target arm64-apple-macosx12.0 \
-        -swift-version 5 \
-        -emit-objc-header \
-        -emit-objc-header-path mytype.h
-
-# Linux (without the target option)
-swiftc \
-    -module-name mytype \
-    -emit-dependencies \
-    -parse-as-library \
-    -c mytype.swift \
-    -swift-version 5 \
-    -emit-objc-header \
-    -emit-objc-header-path mytype.h
-

This should generate a mytype.h and a mytype.o file plus some additional Swift module related output files. We’re going to use these files to build our final executable, but there are a few more additional things I’d like to mention.

Under Linux the header file won’t work. It contains a line #include Foundation/Foundation.h and of course there is no such header file for Linux. It is possible to install the GNUstep package (e.g. via yum: sudo yum install gnustep-base gnustep-base-devel gcc-objc, but for me the clang command still complained about the location of the objc.h file. Anyway, I just removed the include Foundation statement from the header file and I was good to go. 😅

The second thing I’d like to mention is that if you want to export a class for Swift, that’s going to be a bit harder, because classes won’t be included in the generated header file. You have two options in this case. The first one is to turn them into Objective-C classes, but this will lead to problems when using Linux, anyway, this is how you can do it:

import Foundation
-
-@objc public final class MyType: NSObject {
-    public var count: Int = 69
-}
-

I prefer the second option, when you don’t change the Swift file, but you create a separate header file and define your object type as a struct with a custom type (mytype_struct.h).

typedef struct mytype mytype_t;
-

We’re going to need this type (with the corresponding header file), because the mytype_create function returns a pointer that we can use to call the other mytype_get_count method. 🤔

Compiling C sources using Swift libraries
So how do we use these exposed Swift objects in C? In the C programming language you just have to import the headers and then voilá you can use everything defined in those headers.

#include <stdio.h>
-#include "mytype.h"
-
-int main() {
-    mytype_t *item = mytype_create();
-
-    int i = mytype_get_count(item);
- 
-    printf("Hello, World! %d\n", i);
-
-    return 0;
-}
-

We can use clang to compile the main.c file into an object file using the necessary header files.

# macOS
-clang -x objective-c -include mytype.h -include mytype_struct.h -c main.c
-
-# Linux
-clang -include mytype.h -include mytype_struct.h -c main.c
-

This command will build a main.o file, which we can use to create the final executable. 💪

Linking the final executable

This was the hardest part to figure out, but I was able to link the two object files together after a few hours of struggling with the ld command and other framework tools I decided to give it up and let swiftc take care of the job, since it can build and link both C and Swift-based executables.

We’re going to need a list of the object files that we’re going to link together.

ls *.o > LinkFileList
-

Then we can call swiftc to do the job for us. I suppose it’ll invoke the ld command under the hood, but I’m not a linker expert, so if you know more about this, feel free to reach out and provide me more info about the process. I have to read this book for sure. 📚

# macOS
-swiftc \
-        -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.1.sdk \
-        -F /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks \
-        -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/lib \
-        -L /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/lib \
-        -L /Users/tib/swiftfromc/ \
-        -module-name Example \
-        -emit-executable \
-        -Xlinker -rpath \
-        -Xlinker @loader_path @/Users/tib/swiftfromc/LinkFileList \
-        -Xlinker -rpath \
-        -Xlinker /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx \
-        -Xlinker -rpath \
-        -Xlinker /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/macosx \
-        -target arm64-apple-macosx12.1 \
-        -Xlinker -add_ast_path \
-        -Xlinker /Users/tib/swiftfromc/mytype.swiftmodule \
-        -L /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib
-
-# Linux
-swiftc \
-    -L /home/ec2-user/swiftfromc \
-    -module-name Example \
-    -emit-executable \
-    -Xlinker -rpath \
-    -Xlinker @loader_path @/home/ec2-user/swiftfromc/LinkFileList
-

The command above will produce the final linked executable file that you can run by using the ./Example snippet and hopefully you’ll see the “Hello, World! 69” message. 🙈

If you want to know more about the rpath linker flag, I highly recommend reading the article by Marcin Krzyzanowski. If you want to read more about Swift / Objective-C interoperability and using the swiftc command, you should check out this article by RDerik. Finally if you want to call C code from Swift and go the other way, you should take a look at my other blog post.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/how-to-use-c-libraries-in-swift/index.html b/docs/how-to-use-c-libraries-in-swift/index.html deleted file mode 100644 index bf35d0c..0000000 --- a/docs/how-to-use-c-libraries-in-swift/index.html +++ /dev/null @@ -1,439 +0,0 @@ - - - - - - - - - - - - How to use C libraries in Swift? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 6 min read - -
-

How to use C libraries in Swift?

-
-

Learn how to use system libraries and call C code from Swift. Interoperability between the Swift language and C for beginners.

-
- -

Building a custom C library using SPM

You can use the Swift Package Manager to create C family based source files (C, C++, Objective-C and Objective-C++) and ship them as standalone components. If you don’t know much about the Swift Package Manager, you should read my comprehensive tutorial about how SPM works. 📦

The only thing that you need to setup a library is a standard Package.swift manifest file with a slightly altered directory structure to support header files. Let’s make a MyPoint library.

// swift-tools-version:5.3
-import PackageDescription
-
-let package = Package(
-    name: "MyPoint",
-    products: [
-        .library(name: "MyPoint", targets: ["MyPoint"]),
-    ],
-    targets: [
-        .target(name: "MyPoint"),
-    ]
-)
-

Everything that you put into the header file will be publicly available for other developers to use, the implementation details are going to be located directly under the Sources/[target]/ directory, but you have to create an additional include folder for your headers. Let’s make a MyPoint.h file under the Sources/MyPoint/include path with the following contents.

struct MyPoint {
-   int x;
-   int y;
-};
-

We’ve just defined the public interface for our library. Now if you try to compile it through the swift build command, it’ll complain that the project is missing some source files. We can easily fix this by creating an empty MyPoint.c file under the Sources/MyPoint directory.

When you import a local header file to use in your implementation code, you can skip the “include” path and simply write #include “MyPoint.h”. You could also put all kinds of C family components into this project, this method works with C++, Objective-C and even Objective-C++ files.

You could also place header files next to the implementation source code, but in that case the system won’t be able to auto-locate your public (umbrella) header files, so you also have to create a modulemap file and provide the correct location of your headers explicitly. If you use the structure with the include directory SPM will generate everything for you automatically.

Congratulations, you just shipped your first C code with Swift Package Manager. 🥳

Interacting with C libraries using Swift

We’re going to create a brand new Swift package to build an executable application based on the previously created C library. In order to use a local package you can simply specify it as with the path argument under the dependencies in your Package.swift manifest file.

// swift-tools-version:5.3
-import PackageDescription
-
-let package = Package(
-    name: "Sample",
-    products: [
-        .executable(name: "Sample", targets: ["Sample"]),
-    ],
-    dependencies: [
-        .package(path: "../MyPoint")
-    ],
-    targets: [
-        .target(name: "Sample", dependencies: [
-            .product(name: "MyPoint", package: "MyPoint"),
-        ]),
-    ]
-)
-

This time we are going to use the MyPoint library as a local dependency, but of course you can manage and publish your own libraries using a git repository somewhere in the cloud. Next we should create our Sources/Sample/main.swift file, import the library and write some code.

import MyPoint
-
-let p = MyPoint(x: 4, y: 20)
-print("Hello, world!", p.x, p.y)
-

If both packages are available locally, make sure you place them next to each other, then everything should work like a charm. You can open the Sample project manifest file using Xcode as well, the IDE can resolve package dependencies automatically for you, but if you prefer the command line, you can use the swift run command to compile & run the executable target.

With this technique you can import the MyPoint module from any other Swift package and use the available public components from it. You just have to add this module as a dependency, by the way you can even call this module from another C (C++, ObjC, Objc++) project made with SPM. 😎

How to use C system libraries from Swift?

There are thousands of available tools that you can install on your operating system (Linux, macOS) with a package manager (apt, brew). For example there is the famous curl command line tool and library, that can be used for transferring data from or to a server. In other words, you can make HTTP requests with it, just type curl "https://www.apple.com/" into a terminal window.

These system components are usually built around libraries. In our case curl comes with libcurl, the multiprotocol file transfer library. Sometimes you might want to use these low level components (usually written in C) in your application, but how do we add them as a dependency? 🤔

The answer is simple, we can define a new systemLibrary target in our package manifest file.

// swift-tools-version:5.3
-import PackageDescription
-
-let package = Package(
-    name: "Sample",
-    products: [
-        .executable(name: "Sample", targets: ["Sample"]),
-    ],
-    dependencies: [
-        .package(path: "../MyPoint")
-    ],
-    targets: [
-
-        .systemLibrary(
-            name: "libcurl",
-            providers: [
-                .apt(["libcurl4-openssl-dev"]),
-                .brew(["curl"])
-            ]
-        ),
-
-        .target(name: "Sample", dependencies: [
-            .product(name: "MyPoint", package: "MyPoint"),
-            .target(name: "libcurl"),
-        ]),
-    ]
-)
-

Inside the Package.swift file you can set the providers for the library (such as brew for macOS or aptitude for many Linux distributions). Unfortunately you still have to manually install these packages, because SPM won’t do this for you, think of it as “just a reminder” for now… 😅

This will allow us to create a custom modulemap file with additional headers (regular or umbrella) and linker flags inside our project folder. First, we should add the following modulemap definition to the Sources/libcurl/module.modulemap file. Please create the libcurl directory, if needed.

module libcurl [system] {
-    header "libcurl.h"
-    link "curl"
-    export *
-}
-

The concept of modules are coming from (clang) LLVM, I highly recommend checking the linked article if you want to know more about modulemaps. This way we tell the compiler that we want to build a module based on the curl library, hence we link curl. We also want to provide our custom header file to make some additional stuff available or more convenient. People usually call these header files shims, umbrella headers or bridging headers.

An umberlla header is the main header file for a framework or library. A bridging header allows us to use two languages in the same application. The shim header works around the limitation that module maps must contain absolute or local paths. They all exposes APIs from a library or language to another, they are very similar, but they are not the same concept. 🙄

In our case we’re going to create a libcurl.h header file inside the Sources/libcurl folder. The module map simply refers to this header file. Here’s what we’re going to place inside of it.

#include <stdbool.h>
-#include <curl/curl.h>
-
-typedef size_t (*curl_func)(void * ptr, size_t size, size_t num, void * ud);
-
-CURLcode curl_easy_setopt_string(CURL *curl, CURLoption option, const char *param) {
-    return curl_easy_setopt(curl, option, param);
-}
-
-CURLcode curl_easy_setopt_func(CURL *handle, CURLoption option, curl_func param) {
-    return curl_easy_setopt(handle, option, param);
-}
-
-CURLcode curl_easy_setopt_pointer(CURL *handle, CURLoption option, void* param) {
-    return curl_easy_setopt(handle, option, param);
-}
-

This code comes from the archived SoTS/CCurl repository, but if you check the shim file inside the Kitura/CCurl package, you’ll find a pretty much similar approach with even more convenient helpers.

The main reason why we need these functions is that variadic functions can’t be imported by Swift (yet), so we have to wrap the curl_easy_setopt calls, so we’ll be able to use it from Swift.

Ok, let me show you how to write a low-level curl call using the libcurl & Swift.

import Foundation
-import MyPoint
-import libcurl
-
-class Response {
-    var data = Data()
-
-    var body: String { String(data: data, encoding: .ascii)! }
-}
-
-var response = Response()
-
-let handle = curl_easy_init()
-curl_easy_setopt_string(handle, CURLOPT_URL, "http://www.google.com")
-
-let pointerResult = curl_easy_setopt_pointer(handle, CURLOPT_WRITEDATA, &response)
-guard pointerResult == CURLE_OK else {
-    fatalError("Could not set response pointer")
-}
-curl_easy_setopt_func(handle, CURLOPT_WRITEFUNCTION) { buffer, size, n, reference in
-    let length = size * n
-    let data = buffer!.assumingMemoryBound(to: UInt8.self)
-    let p = reference?.assumingMemoryBound(to: Response.self).pointee
-    p?.data.append(data, count: length)
-    return length
-}
-
-let ret = curl_easy_perform(handle)
-guard ret == CURLE_OK else {
-//    let error = curl_easy_strerror(ret)
-//    print("error: ", error)
-    fatalError("Something went wrong with the request")
-}
-curl_easy_cleanup(handle)
-
-print(response.body)
-

I know, I know. This looks terrible for the first sight, but unfortunately C interoperability is all about dealing with pointers, unfamiliar types and memory addresses. Anyway, here’s what happens in the code snippet. First we have to define a response object that can hold the data coming from the server as a response. Next we call the system functions from the curl library to create a handle and set the options on it. We simply provide the request URL as a string, we pass the result pointer and a write function that can append the incoming data to the storage when something arrives from the server. Finally we perform the request, check for errors and cleanup the handle.

It is not so bad, but still it looks nothing like you’d expect from Swift. It’s just a basic example I hope it’ll help you to understand what’s going on under the hood and how low level C-like APIs can work in Swift. If you want to practice you should try to take a look at the Kanna library and parse the response using a custom libxml2 wrapper (or you can read about a SQLite3 wrapper). 🤓

The system library target feature is a nice way of wrapping C [system] modules with SPM. You can read more about it on the official Swift forums. If you are still using the old system library package type format, please migrate, since it’s deprecated and it’ll be completely removed later on.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/how-to-use-icloud-drive-documents/index.html b/docs/how-to-use-icloud-drive-documents/index.html deleted file mode 100644 index 4e4af95..0000000 --- a/docs/how-to-use-icloud-drive-documents/index.html +++ /dev/null @@ -1,398 +0,0 @@ - - - - - - - - - - - - How to use iCloud drive documents? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 3 min read - -
-

How to use iCloud drive documents?

-
-

Learn how to sync files and data through a shared iCloud drive folder using the latest version of Swift programming language.

-
- -

iCloud drive project setup tutorial

Let’s start by creating a new project for iOS. You can select the single view application template, don’t worry too much about document based apps, because in this tutorial we’re not going to touch the UIDocument class at all. 🤷‍♂️

Capabilities

The first step is to enable iCloud capabilities, which will generate a new entitlements file for you. Also you’ll have to enable the iCloud application service for the app id on the Apple developer portal. You should also assign the iCloud container that’s going to be used to store data. Just a few clicks, but you have to do this manually. 💩

You need a valid Apple Developer Program membership in order to set advanced app capabilities like iCloud support. So you have to pay $99/year. #greed 🤑

iCloud

So I believe that now you have a proper iOS app identifier with iCloud capabilities and application services enabled. One last step is ahead, you have to add these few lines to your Info.plist file in order to define the iCloud drive container (folder name) that you’re going to use. Note that you can have multiple containers for one app.

<key>NSUbiquitousContainers</key>
-<dict>
-    <key>iCloud.com.tiborbodecs.teszt</key>
-    <dict>
-        <key>NSUbiquitousContainerIsDocumentScopePublic</key>
-        <true/>
-        <key>NSUbiquitousContainerName</key>
-        <string>Teszt</string>
-        <key>NSUbiquitousContainerSupportedFolderLevels</key>
-        <string>Any</string>
-    </dict>
-</dict>
-

Finally we’re ready to move forward with some actual coding. 💻

Files inside iCloud drive containers

Working with iCloud files using Swift is relatively easy. Basically you just have to get the base URL of your iCloud drive container, and you can do whatever you want. 🤔 However I’ll show you some best practices & tricks.

First you have to check if your container folder already exists, if not you should create it by hand using the FileManager class. I’ve also made a “shortcut” variable for the container base URL, so I don’t have to write all those long words again. 😅

var containerUrl: URL? {
-    FileManager.default.url(
-        forUbiquityContainerIdentifier: nil
-    )?.appendingPathComponent("Documents")
-}
-// check for container existence
-if 
-    let url = self.containerUrl, 
-    !FileManager.default.fileExists(
-        atPath: url.path, 
-        isDirectory: nil
-    ) {
-    do {
-        try FileManager.default.createDirectory(
-            at: url, withIntermediateDirectories: true, 
-            attributes: nil
-        )
-    }
-    catch {
-        print(error.localizedDescription)
-    }
-}
-

Working with paths inside the iCloud drive container is simple, you can append path components to the base URL and use that exact location URL as you want.

let myDocumentUrl = self.containerUrl?
-    .appendingPathComponent(subDirectory)
-    .appendingPathComponent(fileName)
-    .appendingPathExtension(fileExtension)
-

Picking existing files is also quite straightforward. You can use the built-in document picker class from UIKit. There are only two catches here. 🤦‍♂️

First one is that you need to provide the type of the documents that you’d like to access. Have you ever heard about UTI’s? No? Maybe yes…? The thing is that you have to find the proper uniform type identifier for every file type, instead of providing an extension or mime-type or something commonly used thing. Smart one, huh? 🧠

let picker = UIDocumentPickerViewController(
-    documentTypes: ["public.json"], 
-    in: .open
-)
-picker.delegate = self
-picker.modalPresentationStyle = .fullScreen
-self.present(picker, animated: true, completion: nil)
-

The second catch is that you have to “unlock” the picked file before you start reading it. That can be done by calling the startAccessingSecurityScopedResource method. Don’t forget to call the stopAccessingSecurityScopedResource method, or things are going to be out of balance. You don’t want that, trust me! #snap 🧤

func documentPicker(
-    _ controller: UIDocumentPickerViewController, 
-    didPickDocumentsAt urls: [URL]
-) {
-    guard
-        controller.documentPickerMode == .open,
-        let url = urls.first,
-        url.startAccessingSecurityScopedResource()
-    else {
-        return
-    }
-    defer {
-        url.stopAccessingSecurityScopedResource()
-    }
-    // do some work with the url
-}
-

Everything else works as you’d expect. You can save files directly into the container through file APIs or by using the UIDocumentPickerViewController instance. Here are some of the most common api calls, that you can use to manipulate files.

// string
-try string.write(to: url, atomically: true, encoding: .utf8)
-try String(contentsOf: url)
-
-// data
-try data.write(to: url, options: [.atomic])
-try Data(contentsOf: url)
-
-// file manager
-FileManager.default.copyItem(at: local, to: url)
-FileManager.default.removeItem(at: url)
-

You can read and write any kind of string, data. By using the FileManager you can copy, move, delete items or change file attributes. All your documents stored inside iCloud drive will be magically available on every device. Obviously you have to be logged in with your iCloud account, and have enough free storage. 💰

Debugging

If you alter something in your settings you might want to increment your build number as well in order to notify the operating system about the changes. 💡

On the mac all the iCloud drive files / containers are located under the user’s Library folder inside the Mobile Documents directory. You can simply use the Terminal or Finder to go there and list all the files. Pro tip: look for hidden ones as well! 😉

cd ~/Library/Mobile\ Documents
-ls -la
-# ls -la|grep tiborbodecs
-

You can also monitor the activity of the CloudDocs daemon, by using this command:

# man brctl
-brctl log --wait --shorten
-

The output will tell you what’s actually happening during the sync.

Debug

I encourage you to check the manual entry for the brctl command, because there are a few more flags that can make troubleshooting more easy. 🤐

This article was heavily inspired by Marcin Krzyzanowski’s really old blog post. 🍺

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

How to use iCloud drive documents?

-
-

Learn how to sync files and data through a shared iCloud drive folder using the latest version of Swift programming language.

- -
- iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

iOS Auto Layout tutorial programmatically

-
-

In this great iOS Auto Layout tutorial I'll teach you how to support rotation, use constraints, work with layers, animate corner radius.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

iOS custom transition tutorial in Swift

-
-

In this tutorial, you'll learn how to replace the push, pop and modal animations with custom transitions & percent driven interactions.

- -
- UIKit - iOS -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/how-to-use-middlewares-in-vapor-4/index.html b/docs/how-to-use-middlewares-in-vapor-4/index.html deleted file mode 100644 index 42fd9a7..0000000 --- a/docs/how-to-use-middlewares-in-vapor-4/index.html +++ /dev/null @@ -1,418 +0,0 @@ - - - - - - - - - - - - How to use middlewares in Vapor 4? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 4 min read - -
-

How to use middlewares in Vapor 4?

-
-

Learn how to create middlewares for a Vapor based server side Swift application to handle common routing functionalities.

-
- -

What is a middleware?

A middleware is basically a function that will be executed every time before the request handler. This way you can hook up special functionalities, such as altering the request before your handler gets the chance to respond to it. Let me show you a real-world example real quick.

import Vapor
-
-final class ExtendPathMiddleware: Middleware {
-
-    public func respond(to request: Request, chainingTo next: Responder) -> EventLoopFuture<Response> {
-        if !request.url.path.hasSuffix("/") {
-            let response = request.redirect(to: request.url.path + "/", type: .permanent)
-            return request.eventLoop.makeSucceededFuture(response)
-        }
-        return next.respond(to: request)
-    }
-}
-

I’m using this middleware to always extend my paths with a trailing slash character. Just try to delete the last char from the URL here on my site & press enter, you’ll be redirected to the original path with a “/” suffix, since the middleware is doing its job. 👨‍💻

A middleware function has two input parameters. The first one is the Request object that you can check or even alter its properties. The second one is the next reference in the Responder chain, so you can respond as usual (with your route handlers) if the middleware has nothing to do with the incoming request. You should always call the next.respond(to: request) method.

Using a middleware

In order to use the middleware from above you have to register it first. It is possible to use a middleware globally, you can hook up your middleware using the app.middleware.use(_) method. This way the registered middleware will be applided for every single route in your Vapor server.

import Vapor
-
-public func configure(_ app: Application) throws {
-    // ...
-    app.middleware.use(ExtendPathMiddleware())
-}
-

The other option is to apply a middleware to specific subset of routes.

let middlewareRoutes = app.grouped(ExtendPathMiddleware())
-middlewareRoutes.get("hello") { req in
-    return "hello"
-}
-

You can read more about routing in the official Vapor 4 docs. I also prefer to have a dedicated router class for my modules (I’m using kind of a VIPER architecture on the server side). 😜

final class MyRouter: RouteCollection {
-
-    func boot(routes: RoutesBuilder) throws {
-        routes.grouped(ExtendPathMiddleware()).get("hello", use: self.hello)
-    }
-    
-    func hello(req: Request) -> String {
-        return "hello"
-    }
-}
-// config
-try app.routes.register(collection: routes)
-

That’s how I utilize middlewares in my Vapor apps. Honestly I don’t have that much custom middlewares, but the ones I implemented helps me a lot to solve common problems.

Built-in middlewares

There are some useful middlewares built right into Vapor.

File middleware

The FileMiddleware allows you to serve static assets from a given folder. This comes handy if you are using Vapor without an nginx server, so you can serve images, stylesheets, javascript files with the client (browser). You can setup the middleware like this:

import Vapor
-
-public func configure(_ app: Application) throws {
-    // ...
-
-    app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
-}
-

You can configure the path of your resources by passing the publicDirectory input parameter.

CORS middleware

In short, CORS allows you to share resources between multiple domains.

Cross-origin resource sharing (CORS) is a mechanism that allows restricted resources on a web page to be requested from another domain outside the domain from which the first resource was served.

This comes handy if you are developing frontend apps by using Leaf & Vapor. This middleware will replace or add the necessary CORS headerss to the response. You can use the default config or initialize a custom one, here is the Swift code for using the CORS middleware:

import Vapor
-
-public func configure(_ app: Application) throws {
-    // ...
-    
-    // using default configuration
-    app.middleware.use(CORSMiddleware(configuration: .default()))
-    
-    // using custom configuration
-    app.middleware.use(CORSMiddleware(configuration: .init(
-        allowedOrigin: .originBased,
-        allowedMethods: [.GET, .POST, .PUT, .OPTIONS, .DELETE, .PATCH],
-        allowedHeaders: [.accept, .authorization, .contentType, .origin, .xRequestedWith]
-    )))
-}
-

If you want to learn more about how these middlewares work you should option+click on the name of the middleware in Xcode. This way you can browse the source files directly. 🔍

Error middleware

Route handlers can throw erros. You can catch those by using the ErrorMiddlware and turn them into proper HTTP responses if necessary. Here is how to setup the middleware:

import Vapor
-
-public func configure(_ app: Application) throws {
-    // ...
-    // using the default error handler
-    app.middleware.use(ErrorMiddleware.default(environment: app.environment))
-    
-    // using a custom error handler
-    app.middleware.use(ErrorMiddleware { req, error -> Response in
-        // implement custom response...
-        .init(status: .internalServerError, version: req.version, headers: .init(), body: .empty)
-    })
-}
-

If you are developing an API service, this middleware is kind of an essential component. 💥

The Authenticator protocol conforms to the Middleware protocol, so we can register anything that implements any of the Authenticator protocols. You can read more about how the auth layer works in Vapor 4 from my authentication tutorial.

The Authenticatable protocol has two static methods, they returns middlewares too. The first one is the guard middleware, which will throw an error if the user is not logged in. The second one is the redirect middleware, that redirects unauthenticated requests to the supplied path.

// The UserModelAuthenticator is an Authenticator
-app.routes.grouped(UserModelAuthenticator())
-
-// The UserModel object is Authenticatable
-app.routes.grouped([
-    UserModel.guardMiddleware(),
-    UserModel.redirectMiddleware(path: "/"),
-])
-

Multiple middlewares can be registered at once using an array.

Middlewares vs route handlers

Sometimes it’s useful to write a middleware, but in other cases a simple route handler can be more than enough. I’m not against middlewares at all, but you should consider which approach is the best for your needs. I usually go with simple handlers and blocks in 95% of the cases.

Middlwares are good for solving global problems, for example if you want to add a new header to every request it’s safe to use a middleware. Checking user permission levels? Not necessary, but yeah if you want to simplify things a middleware could work here as well. 🤔

Fun fact

This URL: https://www.google.com/////search?????client=safari&&&&&q=swift+vapor still works, despite the fact that it contains 5 slashes, question marks and ampersands. I don’t know why, but most of the websites are not checking for duplicates. Try with other domains as well.

If you want to learn how to build a custom middleware I think it’s a good practice to solve this issue. Write one that removes the unnecessary characters and redirects to the “right” URL.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/how-to-use-the-result-type-to-handle-errors-in-swift/index.html b/docs/how-to-use-the-result-type-to-handle-errors-in-swift/index.html deleted file mode 100644 index bac98b8..0000000 --- a/docs/how-to-use-the-result-type-to-handle-errors-in-swift/index.html +++ /dev/null @@ -1,484 +0,0 @@ - - - - - - - - - - - - How to use the result type to handle errors in Swift 5? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 7 min read - -
-

How to use the result type to handle errors in Swift 5?

-
-

From this tutorial you can learn how to utilize the do-try-catch syntax with the brand new result type to handle errors in Swift.

-
- -

Error handling basics in Swift

The way of handling errors changed a lot since the first version of Swift. The first big milestone happened in Swift 2, where Apple completely revamped error management. Nowadays you can use the do, try, catch, throw, throws, rethrows keywords instead of dealing with nasty NSError pointers, so this was a warmly welcomed addition for the language. Now in Swift 5 we take another giant leap forward by introducing the Result type as a built-in generic. First, let me show you all the best practices of error handling in the Swift programming language, next I’ll show you some cool stuff by using results to deal with errors. 🚧

Optionals as error indicators

For simple scenarios you can always use optional values, to indicate that something bad happened. Also the guard statement is extremely helpful for situations like this.

let zeroValue = Int("0")! // Int
-let nilValue = Int("not a number") // Int?
-
-guard let number = Int("6") else {
-    fatalError("Ooops... this should always work, so we crash.")
-}
-print(number)
-

If you don’t really care about the underlying type of the error, this approach is fine, but sometimes things can get more complicated, so you might need some details about the problem. Anyway, you can always stop the execution by calling the fatalError method, but if you do so, well… your app will crash. 💥

There are also a couple other ways of stop execution process, but this could be a topic of a standalone post, so here is just a quick cheat sheet of available methods:

precondition(false, "ouch")
-preconditionFailure("ouch")
-assert(false, "ouch")
-assertionFailure("ouch")
-fatalError("ouch")
-exit(-1)
-

The key difference between precondition and assertion is that assert will work only in debug builds, but precondition is evaluated always (even in release builds). Both methods will trigger a fatal error if the condition fails aka. is false. ⚠️

Throwing errors by using the Error protocol

You can define your own error types by simply confirming to the built-in Error protocol. Usually most developers use an enum in order to define different reasons. You can also have a custom error message if you conform to the LocalizedError protocol. Now you’re ready to throw custom errors, just use the throw keyword if you’d like to raise an error of your type, but if you do so in a function, you have to mark that function as a throwing function with the throws keywords. 🤮

enum DivisionError: Error {
-    case zeroDivisor
-}
-
-extension DivisionError: LocalizedError {
-    public var errorDescription: String? {
-        switch self {
-        case .zeroDivisor:
-            return "Division by zero is quite problematic. " +
-                   "(https://en.wikipedia.org/wiki/Division_by_zero)"
-        }
-    }
-}
-
-func divide(_ x: Int, by y: Int) throws -> Int {
-    guard y != 0 else {
-        throw DivisionError.zeroDivisor
-    }
-    return x / y
-}
-

Great, so the divide function above can generate a custom error message. If the divisor is zero it’ll throw the zeroDivision error case. Now imagine the following scenario: you are trying to read the contents of a file from the disk. There could be multiple types of errors related to permission or file existence, etc.

Rethrowing Functions and Methods A function or method can be declared with the rethrows keyword to indicate that it throws an error only if one of it’s function parameters throws an error. These functions and methods are known as rethrowing functions and rethrowing methods. Rethrowing functions and methods must have at least one throwing function parameter.

Ok, so a throwing function can emit different error types, also it can propagate all the parameter errors, but how do we handle (or should I say: catch) these errors?

The do-try-catch syntax

You just simply have to try to execute do a throwing function. So don’t trust the master, there is definitely room for trying out things! Bad joke, right? 😅

do {
-    let number = try divide(10, by: 0)
-    print(number)
-}
-catch let error as DivisionError {
-    print("Division error handler block")
-    print(error.localizedDescription)
-}
-catch {
-    print("Generic error handler block")
-    print(error.localizedDescription)
-}
-

As you can see the syntax is pretty simple, you have a do block, where you can try to execute your throwing functions, if something goes wrong, you can handle the errors in different catch blocks. By default an error property is available inside every catch block, so you don’t have to define one yourself by hand. You can however have catch blocks for specific error types by casting them using the let error as MyType sytnax right next to the catch keyword. So always try first, don’t just do! 🤪

Differences between try, try? and try!

As we’ve seen before you can simply try to call a function that throws an error inside a do-catch block. If the function triggers some kind of error, you can put your error handling logic inside the catch block. That’s very simple & straightforward.

Sometimes if you don’t really care about the underlying error, you can simply convert your throwing function result into an optional by using try?. With this approach you’ll get a nil result if something bad happens, otherwise you’ll get back your regular value as it is expected. Here is the example from above by using try?:

guard let number = try? divide(10, by: 2) else {
-    fatalError("This should work!")
-}
-print(number) // 5
-

Another technique is to prevent error propagation by using try!, but you have to be extremely careful with this approach, because if the execution of the “tried function” fails, your application will simply crash. So use only if you’re absolutely sure that the function won’t throw an error. ⚠️

let number = try! divide(10, by: 2) // This will work for sure!
-print(number)
-

There are a few places where it’s accepted to use force try, but in most of the cases you should go on an alternate path with proper error handlers.

Swift errors are not exceptions

The Swift compiler always requires you to catch all thrown errors, so a situation of unhandled error will never occur. I’m not talking about empty catch blocks, but unhandled throwing functions, so you can’t try without the do-catch companions. This is one key difference when comparing to exceptions. Also when an error is raised, the execution will just exit the current scope. Exceptions will usually unwind the stack, that can lead to memory leaks, but that’s not the case with Swift errors. 👍

Introducing the result type

Swift 5 introduces a long-awaited generic result type. This means that error handling can be even more simple, without adding your own result implementation. Let me show you our previous divide function by using Result.

func divide(_ x: Int, by y: Int) -> Result<Int, DivisionError> {
-    guard y != 0 else {
-        return .failure(.zeroDivisor)
-    }
-    return .success(x / y)
-}
-
-let result = divide(10, by: 2)
-switch result {
-case .success(let number):
-    print(number)
-case .failure(let error):
-    print(error.localizedDescription)
-}
-

The result type in Swift is basically a generic enum with a .success and a .failure case. You can pass a generic value if your call succeeds or an Error if it fails.

One major advantage here is that the error given back by result is type safe. Throwing functions can throw any kind of errors, but here you can see from the implementation that a DivisionError is coming back if something bad happens. Another benefit is that you can use exhaustive switch blocks to “iterate through” all the possible error cases, even without a default case. So the compiler can keep you safe, e.g. if you are going to introduce a new error type inside your enum declaration.

So by using the Result type it’s clear that we’re getting back either result data or a strongly typed error. It’s not possible to get both or neither of them, but is this better than using throwing functions? Well, let’s get asynchrounous!

func divide(_ x: Int, by y: Int, completion: ((() throws -> Int) -> Void)) {
-    guard y != 0 else {
-        completion { throw DivisionError.zeroDivisor }
-        return
-    }
-    completion { return x / y }
-}
-
-divide(10, by: 0) { calculate in
-    do {
-        let number = try calculate()
-        print(number)
-    }
-    catch {
-        print(error.localizedDescription)
-    }
-}
-

Oh, my dear… an inner closure! A completion handler that accepts a throwing function, so we can propagate the error thrown to the outer handler? I’m out! 🤬

Another option is that we eliminate the throwing error completely and use an optional as a result, but in this case we’re back to square one. No underlying error type.

func divide(_ x: Int, by y: Int, completion: (Int?) -> Void) {
-    guard y != 0 else {
-        return completion(nil)
-    }
-    completion(x / y)
-}
-
-divide(10, by: 0) { result in
-    guard let number = result else {
-        fatalError("nil")
-    }
-    print(number)
-}
-

Finally we’re getting somewhere here, but this time let’s add our error as a closure parameter as well. You should note that both parameters need to be optionals.

func divide(_ x: Int, by y: Int, completion: (Int?, Error?) -> Void) {
-    guard y != 0 else {
-        return completion(nil, DivisionError.zeroDivisor)
-    }
-    completion(x / y, nil)
-}
-
-divide(10, by: 0) { result, error in
-    guard error == nil else {
-        fatalError(error!.localizedDescription)
-    }
-    guard let number = result else {
-        fatalError("Empty result.")
-    }
-    print(number)
-}
-

Finally let’s introduce result, so we can eliminate optionals from our previous code.

func divide(_ x: Int, by y: Int, completion: (Result<Int, DivisionError>) -> Void) {
-    guard y != 0 else {
-        return completion(.failure(.zeroDivisor))
-    }
-    completion(.success(x / y))
-}
-
-divide(10, by: 0) { result in
-    switch result {
-    case .success(let number):
-        print(number)
-    case .failure(let error):
-        print(error.localizedDescription)
-    }
-}
-

See? Strongly typed errors, without optionals. Handling errors in asynchronous function is way better by using the Result type. If you consider that most of the apps are doing some kind of networking, and the result is usually a JSON response, there you already have to work with optionals (response, data, error) plus you have a throwing JSONDecoder method… can’t wait the new APIs! ❤️

Working with the Result type in Swift 5

We already know that the result type is basically an enum with a generic .succes(T) and a .failure(Error) cases, but there is more that I’d like to show you here. For example you can create a result type with a throwing function like this:

let result = Result {
-    return try divide(10, by: 2)
-}
-

It is also possible to convert back the result value by invoking the get function.

do {
-    let number = try result.get()
-    print(number)
-}
-catch {
-    print(error.localizedDescription)
-}
-

Also there are map, flatMap for transforming success values plus you can also use the mapError or flatMapError methods if you’d like to transform failures. 😎

// Result<Int, DivisionError>
-let result = divide(10, by: 2) 
-
-// Result<Result<Int, DivisionError>, DivisionError>
-let mapSuccess = result.map { divide($0, by: 2) } 
-
-// Result<Int, DivisionError>
-let flatMapSuccess = result.flatMap { divide($0, by: 2) } 
-let mapFailure = result.mapError { 
-    NSError(domain: $0.localizedDescription, code: 0, userInfo: nil)
-}
-
-let flatMapFailure = result.flatMapError { 
-    .failure(NSError(domain: $0.localizedDescription, code: 0, userInfo: nil)) 
-}
-

That’s it about the Result type in Swift 5. As you can see it’s extremely powerful to have a generic implementation built directly into the language. Now that we have result, I just wish for higher kinded types or an async / await implementation. 👍

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/how-to-write-html-in-swift/index.html b/docs/how-to-write-html-in-swift/index.html deleted file mode 100644 index 2723a3b..0000000 --- a/docs/how-to-write-html-in-swift/index.html +++ /dev/null @@ -1,500 +0,0 @@ - - - - - - - - - - - - How to write HTML in Swift? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

How to write HTML in Swift?

-
-

This tutorial is all about rendering HTML docs using a brand new DSL library called SwiftHtml and the Vapor web framework.

-
- -

Introducing SwiftHtml

This time we’re going to start everything from scratch. In the first section of this article I’m going to show you how to setup the SwiftHtml as a package dependency and how to generate HTML output based on a template file. Let’s start by creating a brand new executable Swift package.

mkdir Example
-cd "$_"
-swift package init --type=executable
-open Package.swift
-

You can also start with a macOS Command Line Tool from Xcode if you wish, but nowadays I prefer Swift Packages. Anyway, we should add SwiftHtml as a dependency to our package right away.

// swift-tools-version:5.5
-import PackageDescription
-
-let package = Package(
-    name: "Example",
-    platforms: [
-        .macOS(.v12)
-    ],
-    dependencies: [
-        .package(url: "https://github.com/binarybirds/swift-html", from: "1.2.0"),
-    ],
-    targets: [
-        .executableTarget(name: "Example", dependencies: [
-            .product(name: "SwiftHtml", package: "swift-html"),
-        ]),
-        .testTarget(name: "ExampleTests", dependencies: ["Example"]),
-    ]
-)
-

All right, now we’re ready to write some Swift DSL code. We’re going to start with a really basic example to get to know with SwiftHtml. In the main.swift file we should create a new HTML document, then we can use SwiftHtml’s built-in renderer to print the html source. 🖨

import SwiftHtml
-
-let doc = Document(.html) {
-    Html {
-        Head {
-            Title("Hello, World!")
-            
-            Meta().charset("utf-8")
-            Meta().name(.viewport).content("width=device-width, initial-scale=1")
-        }
-        Body {
-            Main {
-                Div {
-                    H1("Hello, World!")
-                    P("This page was generated by the SwiftHtml library.")
-                }
-            }
-            .class("container")
-        }
-    }
-}
-
-let html = DocumentRenderer(minify: false, indent: 2).render(doc)
-print(html)
-

As you can see the code is pretty straightforward, especially if you know a bit about HTML. The SwiftHtml library tries to follow the naming conventions as closely as possible, so if you’ve written HTML before this syntax should be very familiar, except that you don’t have to write opening and closing tags, but we can utilize the Swift compiler to do the boring repetative tasks instead of us.

Since we’re using a domain specific language in Swift, the compiler can type-check everything at build-time, this way it’s 100% sure that our HTML code won’t have syntax issues. Of course you can still make semantic mistakes, but that’s also possible if you’re not using a DSL. 😅

The main advantage here is that you won’t be able to mistype or misspell tags, and you don’t even have to think about closing tags, but you can use result builders to construct the HTML node tree. SwiftHtml uses tags and it’ll build a tree from them, this way it is possible to efficiently render the entire structure with proper indentation or minification if it is needed.

The DocumentRenderer object can render a document, it is also possible to create all sorts of SGML-based document types, because the SwiftHtml package comes with an abstraction layer. If you take a look at the package structure you should see that inside the Sources directory there are several other directories, the core of the package is the SwiftSgml component, which allows developers to create other domain specific languages on top of the base components. 🤔
For example, if you take a look at the SwiftRss package you will see that it’s a simple extension over the SwiftSgml library. You can subclass the Tag object to create a new (domain specific) tag with an underlying Node object to represent a custom item for your document.

The SwiftSgml library is very lightweight. The Node struct is a representation of a given SGML node with a custom type, name and attributes. The Tag class is all about building a hierarchy in between the nodes. The Document struct is a special object which is responsible for rendering the doctype declaration before the root tag if needed, also of course the document contains the root tag, which is the beginning of everything. 😅

SwiftSgml also contains the DocumentRenderer and a simple TagBuilder enum, which is a result builder and it allows us to define our structure in a SwiftUI-like style.

So the SwiftHtml package is just a set of HTML rules on top of the SwiftSgml library and it follows the W3C HTML reference guides. You can use the output string to save a HTML file, this way you can generate static websites by using the SwiftHtml library.

import Foundation
-import SwiftHtml
-
-let doc = Document(.html) {
-    Html {
-        Head {
-            Title("Hello, World!")
-            
-            Meta().charset("utf-8")
-            Meta().name(.viewport).content("width=device-width, initial-scale=1")
-        }
-        Body {
-            Main {
-                Div {
-                    H1("Hello, World!")
-                    P("This page was generated by the SwiftHtml library.")
-                }
-            }
-            .class("container")
-        }
-    }
-}
-
-do {
-    let dir = FileManager.default.homeDirectoryForCurrentUser
-    let file = dir.appendingPathComponent("index.html")
-    let html = DocumentRenderer(minify: false, indent: 2).render(doc)
-    try html.write(to: file, atomically: true, encoding: .utf8)
-}
-catch {
-    fatalError(error.localizedDescription)
-}
-

This is just one way to use SwiftHtml, in my opinion static site generators are fine, but the real fun begins when you can render websites based on some kind of dynamic data. 🙃

Using SwiftHtml with Vapor

Vapor has an official template engine called Leaf plus the community also created a type-safe HTML DSL library called HTMLKit, so why create something very similar?

Well, I tried all the available Swift HTML DSL libraries that I was able to find on GitHub, but I was not entirely satisfied with the currently available solutions. Many of them was outdated, incomplete or I simply didn’t like the flavor of the DSL. I wanted to have a library which is freakin’ lightweight and follows the standards, that’s the reason why I’ve built SwiftHtml. 🤐

How can we integrate SwiftHtml with Vapor? Well, it’s pretty simple, let’s add Vapor as a dependency to our project first.

// swift-tools-version:5.5
-import PackageDescription
-
-let package = Package(
-    name: "Example",
-    platforms: [
-        .macOS(.v12)
-    ],
-    dependencies: [
-        .package(url: "https://github.com/binarybirds/swift-html", from: "1.2.0"),
-        .package(url: "https://github.com/vapor/vapor", from: "4.54.0"),
-    ],
-    targets: [
-        .executableTarget(name: "Example", dependencies: [
-            .product(name: "SwiftHtml", package: "swift-html"),
-            .product(name: "Vapor", package: "vapor"),
-        ]),
-        .testTarget(name: "ExampleTests", dependencies: ["Example"]),
-    ]
-)
-

We’re going to need a new protocol, which we can use construct a Tag, this is going to represent a template file, so let’s call it TemplateRepresentable.

import Vapor
-import SwiftSgml
-
-public protocol TemplateRepresentable {
-    
-    @TagBuilder
-    func render(_ req: Request) -> Tag
-}
-

Next, we need something that can render a template file and return with a Response object, that we can use inside a request handler when we setup the route handlers in Vapor. Since we’re going to return a HTML string, it is necessary to set the proper response headers too.

import Vapor
-import SwiftHtml
-
-public struct TemplateRenderer {
-    
-    var req: Request
-    
-    init(_ req: Request) {
-        self.req = req
-    }
-
-    public func renderHtml(_ template: TemplateRepresentable, minify: Bool = false, indent: Int = 4) -> Response {
-        let doc = Document(.html) { template.render(req) }
-        let body = DocumentRenderer(minify: minify, indent: indent).render(doc)
-        return Response(status: .ok, headers: ["content-type": "text/html"], body: .init(string: body))
-    }
-}
-

Finally we can extend the built-in Request object to return a new template renderer if we need it.

import Vapor
-
-public extension Request {
-    var templates: TemplateRenderer { .init(self) }
-}
-

Now we just have to create a HTML template file. I’m usually creating a context object right next to the template this way I’m going to be able to pass around contextual variables for each template file. I’m quite happy with this approach so far. ☺️

import Vapor
-import SwiftHtml
-
-struct IndexContext {
-    let title: String
-    let message: String
-}
-
-struct IndexTemplate: TemplateRepresentable {
-    
-    let context: IndexContext
-    
-    init(_ context: IndexContext) {
-        self.context = context
-    }
-    
-    func render(_ req: Request) -> Tag {
-        Html {
-            Head {
-                Title(context.title)
-                
-                Meta().charset("utf-8")
-                Meta().name(.viewport).content("width=device-width, initial-scale=1")
-            }
-            Body {
-                Main {
-                    Div {
-                        H1(context.title)
-                        P(context.message)
-                    }
-                }
-                .class("container")
-            }
-        }
-    }
-}
-

Finally we just have to write some boilerplate code to start up our Vapor web server, we can use the app instance and set a get request handler and render our template using the newly created template renderer extension on the Request object.

import Vapor
-import SwiftHtml
-
-var env = try Environment.detect()
-try LoggingSystem.bootstrap(from: &env)
-let app = Application(env)
-defer { app.shutdown() }
-
-app.get { req -> Response in
-    let template = IndexTemplate(.init(title: "Hello, World!",
-                                    message: "This page was generated by the SwiftHtml library."))
-    
-    return req.templates.renderHtml(template)
-}
-
-try app.run()
-

More or less that’s it, you should be able to run the server and hopefully you should see the rendered HTML document if you open the http://localhost:8080/ address using your browser.

It is also possible to use one template inside another, since you can call the render method on a template and that template will return a Tag. The beauty of this approach is that you can compose smaller templates together, this way you can come up with a nice project structure with reusable HTML templates written entirely in Swift. I’m more than happy with this simple solution and seems like, for me, there is no turning back to Leaf or Tau… 🤓

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/how-to-write-services-for-viper/index.html b/docs/how-to-write-services-for-viper/index.html deleted file mode 100644 index 0ad77b0..0000000 --- a/docs/how-to-write-services-for-viper/index.html +++ /dev/null @@ -1,462 +0,0 @@ - - - - - - - - - - - - How to write services for VIPER? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 7 min read - -
-

How to write services for VIPER?

-
-

Not everything is a VIPER module. In this article I'll show you how do I separate the service layer from the modules, using Swift.

-
- -

I can imagine that you just started to write your first VIPER module and you might wonder: where should I put all my network communication, CoreLocation, CoreData or “whatever service” code, that’s not related to the user interface at all?

To the service layer!

I usually call these the API, location, storage as a service, because they serve your modules with some kind of information. Plus they can encapsulate the underlying layer, providing a well-defined API interface for your VIPER modules. 😅

Ok, but what about interactors? Shouldn’t I implement this kind of stuff there?

Well, my answer is no, because there is a major difference between services and interactors. While a service is just a “dummy” wrapper around e.g. a RESTful API, another one around the CoreData storage, an interactor however could use both of them to request some kind of data though the API, and save it locally using the storage service. Interactors can also do sorting, filtering, transformation between Data Transfer Objects (DTOs) and entities, more about them later.

Enough theory for now, let’s create a new service.

Service interfaces

This time as the Protocol Objective Programming paradigm says:

We start designing our system by defining protocols.

Our first one is going to be a really simple one for all the services:

protocol ServiceInterface: class {
-    func setup()
-}
-
-extension ServiceInterface {
-
-    func setup() {
-        // do nothing...
-    }
-}
-

The setup will be called for each service during the service initialization process. We can extend the base service so we don’t have to implement this method, but only if we really have to do something, like setting up our CoreData stack.

Next we can come up with our API service, in this case I’m going to implement a dummy endpoint that loads some data using the new Combine framework with URLSession, but of course you can go with completion blocks or Promises as well.

protocol ApiServiceInterface: ServiceInterface {
-
-    func todos() -> AnyPublisher<[TodoObject], HTTP.Error>
-}
-

Nowadays I’m using a HTTP namespace for all my network related stuff, like request methods, responses, errors, etc. Feel free to extend it based on your needs.

enum HTTP {
-
-    enum Method: String {
-        case get
-        //...
-    }
-    enum Error: LocalizedError {
-        case invalidResponse
-        case statusCode(Int)
-        case unknown(Swift.Error)
-    }
-}
-

As you can see it’s quite lightweight, but it’s extremely handy. We haven’t talked about the TodoObject yet. That’s going to be our very first DTO. 😱

Data Transfer Objects

A data transfer object (DTO) is an object that carries data between processes. - Wikipedia

In this case we’re not talking about processes, but services & VIPER modules. They exists so we can decouple our service layer from our modules. The interactor can transform the DTO into a module entity, so all other parts of the VIPER module will be completely independent from the service. Worth to mention that a DTO is usually really simple, in a RESTful API service, a DTO can implement the Codable interface and nothing more or for CoreData it can be just a NSManagedObject subclass.

struct TodoObject: Codable {
-    let id: Int
-    let title: String
-    let completed: Bool
-}
-

You can also use a simple DTO to wrap your request parameters. For example you can use a TodoRequestObject which can contain some filter or sorting parameters. You might noticed that I always use the Object suffix for my DTO’s, that’s a personal preference, but it helps me differentiate them from entities.

Going a little bit further this way: you can publish your entire service layer as an encapsulated Swift package using SPM, from Xcode 11 these packages are natively supported so if you’re still using CocoaPods, you should consider migrating to the Swift Package Manager as soon as possible.

Service implementations

Before we start building our real service implementation, it’s good to have a fake one for demos or testing purposes. I call this fake, because we’re going to return a fixed amount of fake data, but it’s close to our real-world implementation. If our request would include filtering or sorting, then this fake implementation service should filter or sort our response like the final one would do it.

final class FakeApiService: ApiServiceInterface {
-
-    var delay: TimeInterval
-
-    init(delay: TimeInterval = 1) {
-        self.delay = delay
-    }
-
-    private func fakeRequest<T>(response: T) -> AnyPublisher<T, HTTP.Error> {
-        return Future<T, HTTP.Error> { promise in
-            promise(.success(response))
-        }
-        .delay(for: .init(self.delay), scheduler: RunLoop.main)
-        .eraseToAnyPublisher()
-    }
-
-    func todos() -> AnyPublisher<[TodoObject], HTTP.Error> {
-        let todos = [
-            TodoObject(id: 1, title: "first", completed: false),
-            TodoObject(id: 2, title: "second", completed: false),
-            TodoObject(id: 3, title: "third", completed: false),
-        ]
-        return self.fakeRequest(response: todos)
-    }
-}
-

I like to add some delay to my fake objects, because it helps me testing the UI stack. I’m a big fan of Scott’s how to fix a bad user interface article. You should definitely read it, because it’s amazing and it will help you to design better products. 👍

Moving forward, here is the actual “real-world” implementation of the service:

final class MyApiService: ApiServiceInterface {
-
-    let baseUrl: String
-
-    init(baseUrl: String) {
-        self.baseUrl = baseUrl
-    }
-
-    func todos() -> AnyPublisher<[TodoObject], HTTP.Error> {
-        let url = URL(string: self.baseUrl + "todos")!
-        var request = URLRequest(url: url)
-        request.httpMethod = HTTP.Method.get.rawValue.uppercased()
-
-        return URLSession.shared.dataTaskPublisher(for: request)
-        .tryMap { data, response in
-            guard let httpResponse = response as? HTTPURLResponse else {
-                throw HTTP.Error.invalidResponse
-            }
-            guard httpResponse.statusCode == 200 else {
-                throw HTTP.Error.statusCode(httpResponse.statusCode)
-            }
-            return data
-        }
-        .decode(type: [TodoObject].self, decoder: JSONDecoder())
-        .mapError { error -> HTTP.Error in
-            if let httpError = error as? HTTP.Error {
-                return httpError
-            }
-            return HTTP.Error.unknown(error)
-        }
-        .eraseToAnyPublisher()
-    }
-}
-

The thing is that we could make this even better, but for the sake of simplicity I’m going to “hack-together” the implementation. I don’t like the implicitly unwrapped url, and many more little details, but for learning purposes it’s totally fine. 😛

So the big question is now, how to put things togehter? I mean we have a working service implementation, a fake service implementation, but how the hell should we put everything into a real Xcode project, without shipping fake code into production?

Target environments

Usually you will have a live production environment, a development environment, maybe a staging environment and some more for QA, UAT, or demo purposes. Things can vary for these environments such as the final API url or the app icon, etc.

This time I’m going to set up a project with 3 separate environments:

  • Production
  • Development
  • Fake

If you start with a new project you’ll have one primary (non-test) target by default. You can duplicate a target by right-clicking on it. Let’s do this two times.

I usually go with a suffix for the target and scheme names, except for the production environment, where I use the “base name” without the -Production postfix.

As you can see on the screenshot I have a basic folder structure for the environments. There has to be a separate Info.plist file for every target, so I put them into the proper Assets folder. The FakeApiService.swift is only part of the fake target, and every other file is shared. Wait, what the heck is a ServiceBuilder?

Dependency injection

Multiple environment means that we have to use the right service (or configuration) for every build target. I’m using the dependency injection design pattern for this purpose. A service builder is just a protocol that helps to achieve this goal. It defines how to setup services based on the environment. Let me show you how it works.

protocol ServiceBuilderInterface {
-
-    var api: ApiServiceInterface { get }
-
-    func setup()
-}
-
-extension ServiceBuilderInterface {
-
-    func setup() {
-        self.api.setup()
-    }
-}
-

Now for each target (environment) I implement the ServiceBuilderInterface in an actual ServiceBuilder.swift file, so I can setup my services just as I need them.

final class ServiceBuilder: ServiceBuilderInterface {
-
-    lazy var api: ApiServiceInterface = {
-        // this can be the url of the development server
-        MyApiService(baseUrl: "https://jsonplaceholder.typicode.com")
-    }()
-}
-

I usually have a base service-interactor class that will receive all the services during the initialization process. So I can swap out anything without a hassle.

class ServiceInteractor {
-
-    let services: ServiceBuilderInterface
-
-    init(services: ServiceBuilderInterface = App.shared.services) {
-        self.services = services
-    }
-}
-

DI is great, but I don’t like to repeat myself too much, that’s why I’m providing a default value for this property, which is located in my only singleton class called App. I know, singletons are evil, but I already have an anti-pattern here so it really doesn’t matter if I introduce one more, right? #bastard #singleton 🤔

final class App {
-
-    let services = ServiceBuilder()
-
-    // MARK: - singleton
-
-    static let shared = App()
-
-    private init() {
-        // do nothing...
-    }
-
-    // MARK: - api
-
-    func setup() {
-        self.services.setup()
-    }
-}
-

This setup is extremely useful if it comes to testing. You can simply mock out all the services if you want to test an interactor. It’s also nice and clean, because you can reach your methods in the interactors like this: self.services.api.todos()

You can apply the same pattern for your modules, I mean you can have for example a ModuleBuilder that implements a ModuleBuilderInterface and all the routers can have them through DI, so you don’t have to initialize everything from scratch all the tim using the build function of the module. 😉

Still I want to clarify one more thing…

Object, model, entity, what the…?

A little bit about naming conventions (I also use these as suffixes all the time):

  • Object
  • Entity
  • Model

In my dictionary an Object is always a DTO, it only lives in the service layer. It’s a freakin dumb one, without any more purpose than providing a nice Swiftish API. This means you don’t have to deal with JSON objects or anything crazy like that, but you can work directly with these objects, which is usually a nice to have feature.

An Entity is related to a VIPER module. Its purpose is to act as a communication object that can be passed around between the view, interactor, presenter, router or as a parameter to another module. It can encapsulate the local stuff that’s required for the module. This means if something changes in the service layer (a DTO maybe) your module will be able to work, you only have to align your interactor. 😬

Still, sometimes I’m completely skipping entities, but I know I shouldn’t. :(

A Model refers to a view-model, which is part of my component based UI building approach on top of the UICollectionView class. You should check out the links if you want to learn more about it, the syntax is very similar to SwiftUI, but it’s obviously not as high-level. In summary a model always has the data that’s required to render a view, nothing more and nothing less.

I hope this little article will help you to structure your apps better. VIPER can be quite problematic sometimes, because of the way you have to architect the apps. Using these kind of services is a nice approach to separate all the different API connections, sensors, and many more, and finally please remember:

Not everything is a VIPER module.

You can download the source files for this article using The.Swift.Dev tutorials repository on GitHub. Thanks for reading, if you haven’t done it yet please subscribe to my newsletter below, or send me ideas, feedbacks through Twitter. 👏

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

How to build SwiftUI apps using VIPER?

-
-

In this tutorial I'll show you how to combine SwiftUI with the VIPER architecture in a real world iOS application example.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

How to write services for VIPER?

-
-

Not everything is a VIPER module. In this article I'll show you how do I separate the service layer from the modules, using Swift.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Mastering the VIPER architecture

-
-

Learn how to master the VIPER architectural design pattern, with some protocol oriented programming techniques using Swift.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

The ultimate VIPER architecture tutorial

-
-

Learn how to write scalable iOS code using the VIPER architecture with some MVVM and MVC tricks and coordinators in mind.

- -
- VIPER -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/how-to-write-swift-scripts-using-the-new-command-api-in-vapor-4/index.html b/docs/how-to-write-swift-scripts-using-the-new-command-api-in-vapor-4/index.html deleted file mode 100644 index d5c6b17..0000000 --- a/docs/how-to-write-swift-scripts-using-the-new-command-api-in-vapor-4/index.html +++ /dev/null @@ -1,487 +0,0 @@ - - - - - - - - - - - - How to write Swift scripts using the new Command API in Vapor 4? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

How to write Swift scripts using the new Command API in Vapor 4?

-
-

Shell scripts are essentials on the server side. Learn how to build Swift scripts for your backend apps using property wrappers.

-
- -

Swift Argument Parser vs Vapor Commands

Apple open-sourced a new library that can help you a lot if you want to build scripts that written in Swift. The Swift Argument Parser was previously part of the Swift Package Manager tools, but now it is even powerful & has it’s own life (I mean repository). 😉

On the other hand Vapor already had a somewhat similar approach to build scripts, but in Vapor 4 the Command API is better than ever. Property Wrappers (available from Swift 5.1) are used in both cases to handle arguments, flags & options. Personally I like this approach a lot.

Let me show you a simple hello command:

// using Argument Parser
-import ArgumentParser
-
-struct HelloCommand: ParsableCommand {
-    @Argument(help: "The name to say hello")
-    var name: String
-
-    func run() throws {
-        print("Hello \(self.name)!")
-    }
-}
-HelloCommand.main()
-// usage: swift run myProject world
-

Now I’ll show you how to implement a similar command using Vapor:

// using Vapor
-import Vapor
-
-final class HelloCommand: Command {
-    
-    let help = "This command will say hello to a given name."
-
-    struct Signature: CommandSignature {
-        @Argument(name: "name", help: "The name to say hello")
-        var name: String
-    }
-
-    func run(using context: CommandContext, signature: Signature) throws {
-        print("Hello \(signature.name)!")
-    }
-}
-
-public func configure(_ app: Application) throws {
-    app.commands.use(HelloCommand(), as: "hello")
-}
-// usage: swift run myProject hello world
-

As you can see they almost look like the same.

If you love scripting, you should definitely check swift-sh and Brisk

The Swift Argument Parser library is a lightweight solution if you are only looking for a simple Swift script. A good example is a tool that manipulates files on the system or something similar. It’s just one little dependency, but it removes so much boilerplate from your scripts. It allows you to focus on the script itself, instead of parsing the command line inputs. You can find more detailed examples and a detailed documentation inside the GitHub repository. 🙏

Vapor’s Command API is useful if you want to perform more complicated tasks with your scripts. Anything that’s part of your Vapor application can be triggered from a command, so you can easily create a backend tool that reads (or writes) records from the database using Fluent 4. This is the main advantage of using a Vapor command, instead a standalone Swift script.

Arguments, options, flags

Let’s extend the hello command with a new option and a flag. The main difference between an option and a flag is that an option has an associated value, but a flag is just something that you give to the command or not. Both options and flags start with a single - or a double dash --, usually the single dashed version uses a short name for the same thing. 🤓

Arguments are user provided values read in order (e.g. ./hello joe bob john).

Now that you know the basic definitions, here is the example:

final class HelloCommand: Command {
-        
-    struct Signature: CommandSignature {
-
-        @Argument(name: "name", help: "The name to say hello")
-        var name: String
-
-        @Option(name: "greeting", short: "g", help: "Greeting used")
-        var greeting: String?
-
-        @Flag(name: "capitalize", short: "c", help: "Capitalizes the name")
-        var capitalize: Bool
-    }
-
-    let help = "This command will say hello to a given name."
-
-    func run(using context: CommandContext, signature: Signature) throws {
-        let greeting = signature.greeting ?? "Hello"
-        var name = signature.name
-        if signature.capitalize {
-            name = name.capitalized
-        }
-        print("\(greeting) \(name)!")
-    }
-}
-

Arguments are required by default, options and flags are optionals. You can have a custom name (short and long) for everything, plus you can customize the help message for every component.

swift run Run hello john
-# Hello john!
-
-swift run Run hello john --greeting Hi
-# Hi john!
-
-swift run Run hello john --greeting Hi --capitalized
-# Hi John!
-
-swift run Run hello john -g Szia -c
-# Szia John!
-

You can call the command using multiple styles. Feel free to pick a preferred version. ⭐️

Subcommands

When command-line programs grow larger, it can be useful to divide them into a group of smaller programs, providing an interface through subcommands. Utilities such as git and the Swift package manager are able to provide varied interfaces for each of their sub-functions by implementing subcommands such as git branch or swift package init.

Vapor can handle command groups in a really cool way. I’ll add an extra static property to name our commands, since I don’t like to repeat myself or bloat the code with unnecessary strings:

final class HelloCommand: Command {
-    
-    static var name = "hello"
-        
-    //...
-}
-
-struct WelcomeCommandGroup: CommandGroup {
-    
-    static var name = "welcome"
-
-    let help: String
-    let commands: [String: AnyCommand]
-    
-    var defaultCommand: AnyCommand? {
-        self.commands[HelloCommand.name]
-    }
-
-    init() {
-        self.help = "SEO command group help"
-
-        self.commands = [
-            HelloCommand.name: HelloCommand(),
-        ]
-    }
-}
-
-public func configure(_ app: Application) throws {
-
-    app.commands.use(WelcomeCommandGroup(), as: WelcomeCommandGroup.name)
-}
-

That’s it, we just moved our hello command under the welcome namespace.

swift run Run welcome hello john --greeting "Hi" --capitalize
-

If you read the Swift Argument Parser docs, you can achieve the exact same behavior through a custom CommandConfiguration. Personally, I prefer Vapor’s approach here… 🤷‍♂️

Waiting for async tasks

Vapor builds on top of SwiftNIO including EventLoops, Futures & Promises. Most of the API is asynchronous, but in the CLI world you have to wait for the async operations to finish.

final class TodoCommand: Command {
-    
-    static let name = "todo"
-
-    struct Signature: CommandSignature { }
-        
-    let help = "This command will create a dummy Todo item"
-
-    func run(using context: CommandContext, signature: Signature) throws {
-        let app = context.application
-        app.logger.notice("Creating todos...")
-        
-        let todo = Todo(title: "Wait for async tasks...")
-        try todo.create(on: app.db).wait()
-        
-        app.logger.notice("Todo is ready.")
-    }
-}
-

There is a throwing wait() method that you can utilize to “stay in the loop” until everything is done. You can also get a pointer for the application object by using the current context. The app has the database connection, so you can tell Fluent to create a new model. Also you can use the built-in logger to print info to the console while the user waits. ⏳

Using ConsoleKit without Vapor

Let’s talk about overheads. Vapor comes with this neat commands API, but also bundles lots of other core things. What if I just want the goodies for my Swift scripts? No problem. You can use the underlying ConsoleKit by adding it as a dependency.

// swift-tools-version:5.2
-import PackageDescription
-
-let package = Package(
-    name: "myProject",
-    platforms: [
-       .macOS(.v10_15)
-    ],
-    dependencies: [
-        .package(url: "https://github.com/vapor/console-kit", from: "4.1.0"),
-    ],
-    targets: [
-        .target(name: "myProject", dependencies: [
-            .product(name: "ConsoleKit", package: "console-kit"),
-        ])
-    ]
-)
-

You still have to do some additional work in your main.swift file, but nothing serious:

import ConsoleKit
-import Foundation
-
-let console: Console = Terminal()
-var input = CommandInput(arguments: CommandLine.arguments)
-var context = CommandContext(console: console, input: input)
-
-var commands = Commands(enableAutocomplete: true)
-commands.use(HelloCommand(), as: HelloCommand.name, isDefault: false)
-
-do {
-    let group = commands.group(help: "Using ConsoleKit without Vapor.")
-    try console.run(group, input: input)
-}
-catch {
-    console.error("\(error)")
-    exit(1)
-}
-

This way you can get rid of most of the network related core packages (that are included by default if you use Vapor). This approach only fetches swift-log as a third party dependency. 😍

Summary

ConsoleKit in Vapor is a great way to write CLI tools and small scripts. The new Swift Argument Parser is a more lightweight solution for the same problem. If your plan is to maintain databases through scripts or you perform lots of networking or asynchronous operations it might be better to go with Vapor, since you can always grow by importing a new component from the ecosystem.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/hummingbird-routing-and-requests/index.html b/docs/hummingbird-routing-and-requests/index.html deleted file mode 100644 index 1ea8496..0000000 --- a/docs/hummingbird-routing-and-requests/index.html +++ /dev/null @@ -1,517 +0,0 @@ - - - - - - - - - - - - Hummingbird routing and requests - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

Hummingbird routing and requests

-
-

Beginner's guide to learn all about routing and request handling using the Hummingbird server-side Swift framework.

-
- -

Routing on the server side means the server is going to send a response based on the URL path that the client called when firing up the HTTP request. Of course the server can check additional parameters and headers to build the final response, but when we talk about routing in general, we usually refer to the path components. Hummingbird uses a trie-based router, which is a fast and efficient way of looking up routes. It’s quite simple to respond to HTTP request using the built-in router, you can simply add your basic route handlers like this:

/// on path X, when method is Y, call the handler... 
-router.on("foo", method: .HEAD) { _ -> HTTPResponseStatus in .ok }
-router.on("foo", method: .GET) { _ -> HTTPResponseStatus in .ok }
-router.on("foo", method: .POST) { _ -> HTTPResponseStatus in .ok }
-router.on("foo", method: .PUT) { _ -> HTTPResponseStatus in .ok }
-router.on("foo", method: .PATCH) { _ -> HTTPResponseStatus in .ok }
-router.on("foo", method: .DELETE) { _ -> HTTPResponseStatus in .ok }
-
-/// short version (for some HTTP methods)
-router.head("foo") { _ -> HTTPResponseStatus in .ok }
-router.get("foo") { _ -> HTTPResponseStatus in .ok }
-router.put("foo") { _ -> HTTPResponseStatus in .ok }
-router.post("foo") { _ -> HTTPResponseStatus in .ok }
-router.patch("foo") { _ -> HTTPResponseStatus in .ok }
-router.delete("foo") { _ -> HTTPResponseStatus in .ok }
-

In Hummingbird it is also possible to register use a function instead of a block. Handler functions can be async and throwing too, so you can mark the blocks with these keywords or use asynchronous Swift functions when registering route handlers. If you don’t provide the first parameter, the path as a string, the route handler is going to be attached to the base group. 👍

You can also prefix a path component with a colon, this will turn that component into a dynamic route parameter. The parameter is going to be named after the path component, by simply dropping the colon prefix. You can access parameters inside your route handler through the req.parameters property. It is also possible to register multiple components using a / character.

public extension HBApplication {
-    
-    func configure() throws {
-
-        router.get { _ async throws in "Hello, world!" }
-
-        router.get("hello/:name") { req throws in
-            guard let name = req.parameters.get("name") else {
-                throw HBHTTPError(
-                    .badRequest,
-                    message: "Invalid name parameter."
-                )
-            }
-            return "Hello, \(name)!"
-        }
-
-        let group = router.group("todos")
-        group.get(use: list)
-        group.post(use: create)
-        
-        let idGroup = group.group(":todoId")
-        idGroup.head(use: check)
-        idGroup.get(use: fetch)
-        idGroup.put(use: update)
-        idGroup.patch(use: patch)
-        idGroup.delete(use: delete)
-
-        /// short version
-        router.group("todos")
-            .get(use: list)
-            .post(use: create)
-            .group(":todoId")
-                .head(use: check)
-                .get(use: fetch)
-                .put(use: update)
-                .patch(use: patch)
-                .delete(use: delete)
-
-    }
-
-    func list(_ req: HBRequest) async throws -> HTTPResponseStatus { .ok }
-    func check(_ req: HBRequest) async throws -> HTTPResponseStatus { .ok }
-    func fetch(_ req: HBRequest) async throws -> HTTPResponseStatus { .ok }
-    func create(_ req: HBRequest) async throws -> HTTPResponseStatus { .ok }
-    func update(_ req: HBRequest) async throws -> HTTPResponseStatus { .ok }
-    func patch(_ req: HBRequest) async throws -> HTTPResponseStatus { .ok }
-    func delete(_ req: HBRequest) async throws -> HTTPResponseStatus { .ok }
-}
-

It is possible to use a wildcard character (*) when detecting path components and the recursive version (**) to catch everything. Also you can use the ${name} syntax to catch a named request parameter even with a prefix or suffix, but you can’t insert this in the middle of a path component. (e.g. “prefix-${name}.jpg” won’t work, but “${name}.jpg” is just fine) 💡

import Hummingbird
-import HummingbirdFoundation
-
-extension HBApplication {
-
-    func configure(_ args: AppArguments) throws {
-
-        router.get("foo-${name}", use: catchPrefix)
-        router.get("${name}.jpg", use: catchSuffix)
-        
-        router.get("*", use: catchOne)
-        router.get("*/*", use: catchTwo)
-
-        router.get("**", use: catchAll)
-        
-    }
-    
-    // http://localhost:8080/bar
-    func catchOne(_ req: HBRequest) async throws -> String {
-        "one"
-    }
-
-    // http://localhost:8080/bar/baz/
-    func catchTwo(_ req: HBRequest) async throws -> String {
-        "two"
-    }
-    
-    // http://localhost:8080/bar/baz/foo/bar/baz
-    func catchAll(_ req: HBRequest) async throws -> String {
-        "all: " + req.parameters.getCatchAll().joined(separator: ", ")
-    }
-    
-    // http://localhost:8080/foo-bar
-    func catchPrefix(_ req: HBRequest) async throws -> String {
-        "prefix: " + (req.parameters.get("name") ?? "n/a")
-    }
-    
-    // http://localhost:8080/bar.jpg
-    func catchSuffix(_ req: HBRequest) async throws -> String {
-        "suffix: " + (req.parameters.get("name") ?? "n/a")
-    }
-}
-

It is also possible to edit the auto-generated response if you specify the .editResponse option.

router.get("foo", options: .editResponse) { req -> String in
-    req.response.status = .ok
-    req.response.headers.replaceOrAdd(
-        name: "Content-Type", 
-        value: "application/json"
-    )
-    return #"{"foo": "bar"}"#
-}
-

Hummingbird support for body streaming is amazing, you can stream a HTTP request body by using the .streamBody option. The body stream has a sequence property, which you can use to iterate through the incoming ByteBuffer chunks when handling the request. 🔄

func configure() throws { 
-    router.post("foo", options: .streamBody) { req async throws -> String in
-        guard
-            let rawLength = req.headers["Content-Length"].first,
-            let length = Int(rawLength),
-            let stream = req.body.stream
-        else {
-            throw HBHTTPError(
-                .badRequest,
-                message: "Missing or invalid body stream."
-            )
-        }
-        var count: Int = 0
-        for try await chunk in stream.sequence {
-            count += chunk.readableBytes
-        }
-        return String("\(length) / \(count)")
-    }
-}
-
-// main.swift
-let app = HBApplication(
-    configuration: .init(
-        address: .hostname(hostname, port: port),
-        serverName: "Hummingbird",
-        maxUploadSize: 1 * 1024 * 1024 * 1024 // 1GB
-    )
-)
-

As you can see you can easily access all the incoming headers via the req.headers container, you should note that this method will return header values in a case-insensitive way. If you want to stream larger files, you also have to set a custom maxUploadSize using the configuration object when initializing the HBApplication instance.

curl -X POST http://localhost:8080/foo \
-    -H "Content-Length: 3" \
-    --data-raw 'foo'
-
-curl -X POST http://localhost:8080/foo \
-    -H "content-Length: 5242880" \
-    -T ~/test
-

You can try out streaming with a simple cURL script, feel free to experiment with these.

Another thing I’d like to show you is how to access query parameters and other properties using the request object. Here is an all-in-one example, which you can use as a cheatsheet… 😉

// curl -X GET http://localhost:8080/bar?q=foo&key=42
-router.get("bar") { req async throws -> String in
-            
-    struct Foo: Codable {
-        var a: String
-    }
-
-    print(req.method)
-    print(req.headers)
-    print(req.headers["accept"])
-    print(req.uri.queryParameters.get("q") ?? "n/a")
-    print(req.uri.queryParameters.get("key", as: Int.self) ?? 0)
-
-    if let buffer = req.body.buffer {
-        let foo = try? JSONDecoder().decode(Foo.self, from: buffer)
-        print(foo ?? "n/a")
-    }
-    return "Hello, world!"
-}
-

Anyway, there is one additional super cool feature in Hummingbird that I’d like to show you. It is possible to define a route handler, this way you can encapsulate everything into a single object. There is an async version of the route handler protocol, if you don’t need async, you can simply drop the keyword both from the protocol name & the method. I love this approach a lot. 😍

struct MyRouteHandler: HBAsyncRouteHandler {
-
-    struct Input: Decodable {
-        let foo: String
-    }
-
-    struct Output: HBResponseEncodable {
-        let id: String
-        let foo: String
-    }
-    
-    let input: Input
-
-    init(from request: HBRequest) throws {
-        self.input = try request.decode(as: Input.self)
-    }
-
-    func handle(request: HBRequest) async throws -> Output {
-        .init(
-            id: "id-1",
-            foo: input.foo
-        )
-    }
-}
-

The request.decode method uses the built-in decoder, which you have to explicitly set for the application, since we’re going to communicate using JSON data, we can use the JSON encoder / decoder from Foundation to automatically transform the data.

In order to make use of the custom route handler, you can simply register the object type.

import Hummingbird
-import HummingbirdFoundation
-
-public extension HBApplication {
-
-    func configure() throws {
-        
-        encoder = JSONEncoder()
-        decoder = JSONDecoder()
-                
-        //    curl -i -X POST http://localhost:8080/foo \
-        //        -H "Content-Type: application/json" \
-        //        -H "Accept: application/json" \
-        //        --data-raw '{"foo": "bar"}'
-        router.post("foo", use: MyRouteHandler.self)
-    }
-}
-

You can read more about how the encoding and decoding works in Hummingbird, but maybe that topic deserves its own blog post. If you have questions or suggestions, feel free to contact me. 🙈

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - -
-
- -
- - - -
- - - - diff --git a/docs/icons/apple-touch-icon-114x114.png b/docs/icons/apple-touch-icon-114x114.png deleted file mode 100644 index c434c63..0000000 Binary files a/docs/icons/apple-touch-icon-114x114.png and /dev/null differ diff --git a/docs/icons/apple-touch-icon-120x120.png b/docs/icons/apple-touch-icon-120x120.png deleted file mode 100644 index b3b3eee..0000000 Binary files a/docs/icons/apple-touch-icon-120x120.png and /dev/null differ diff --git a/docs/icons/apple-touch-icon-144x144.png b/docs/icons/apple-touch-icon-144x144.png deleted file mode 100644 index 7e75dec..0000000 Binary files a/docs/icons/apple-touch-icon-144x144.png and /dev/null differ diff --git a/docs/icons/apple-touch-icon-152x152.png b/docs/icons/apple-touch-icon-152x152.png deleted file mode 100644 index ec27b88..0000000 Binary files a/docs/icons/apple-touch-icon-152x152.png and /dev/null differ diff --git a/docs/icons/apple-touch-icon-180x180.png b/docs/icons/apple-touch-icon-180x180.png deleted file mode 100644 index 01b413d..0000000 Binary files a/docs/icons/apple-touch-icon-180x180.png and /dev/null differ diff --git a/docs/icons/apple-touch-icon-57x57.png b/docs/icons/apple-touch-icon-57x57.png deleted file mode 100644 index fb6c05a..0000000 Binary files a/docs/icons/apple-touch-icon-57x57.png and /dev/null differ diff --git a/docs/icons/apple-touch-icon-72x72.png b/docs/icons/apple-touch-icon-72x72.png deleted file mode 100644 index 379463f..0000000 Binary files a/docs/icons/apple-touch-icon-72x72.png and /dev/null differ diff --git a/docs/icons/apple-touch-icon-76x76.png b/docs/icons/apple-touch-icon-76x76.png deleted file mode 100644 index 25bd6f8..0000000 Binary files a/docs/icons/apple-touch-icon-76x76.png and /dev/null differ diff --git a/docs/icons/apple-touch-icon.png b/docs/icons/apple-touch-icon.png deleted file mode 100644 index f26f8c5..0000000 Binary files a/docs/icons/apple-touch-icon.png and /dev/null differ diff --git a/docs/icons/favicon.ico b/docs/icons/favicon.ico deleted file mode 100644 index ad0cc26..0000000 Binary files a/docs/icons/favicon.ico and /dev/null differ diff --git a/docs/icons/favicon.png b/docs/icons/favicon.png deleted file mode 100644 index f26f8c5..0000000 Binary files a/docs/icons/favicon.png and /dev/null differ diff --git a/docs/icons/icon-128.png b/docs/icons/icon-128.png deleted file mode 100644 index 857c660..0000000 Binary files a/docs/icons/icon-128.png and /dev/null differ diff --git a/docs/icons/icon-320.png b/docs/icons/icon-320.png deleted file mode 100644 index 41d257f..0000000 Binary files a/docs/icons/icon-320.png and /dev/null differ diff --git a/docs/icons/icon-512.png b/docs/icons/icon-512.png deleted file mode 100644 index a91f8fd..0000000 Binary files a/docs/icons/icon-512.png and /dev/null differ diff --git a/docs/icons/icon.png b/docs/icons/icon.png deleted file mode 100755 index cc4ed2a..0000000 Binary files a/docs/icons/icon.png and /dev/null differ diff --git a/docs/icons/icon.svg b/docs/icons/icon.svg deleted file mode 100644 index 48ad2bb..0000000 --- a/docs/icons/icon.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - Group@2x - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/docs/images/Swift.png b/docs/images/Swift.png deleted file mode 100644 index a5fcf08..0000000 Binary files a/docs/images/Swift.png and /dev/null differ diff --git a/docs/images/defaults/default.png b/docs/images/defaults/default.png deleted file mode 100644 index a4dcec0..0000000 Binary files a/docs/images/defaults/default.png and /dev/null differ diff --git a/docs/images/defaults/placeholder.png b/docs/images/defaults/placeholder.png deleted file mode 100644 index e7da516..0000000 Binary files a/docs/images/defaults/placeholder.png and /dev/null differ diff --git a/docs/images/logos/logo-3000.png b/docs/images/logos/logo-3000.png deleted file mode 100644 index c41bb41..0000000 Binary files a/docs/images/logos/logo-3000.png and /dev/null differ diff --git a/docs/images/logos/logo-large.png b/docs/images/logos/logo-large.png deleted file mode 100644 index 8bf9e3e..0000000 Binary files a/docs/images/logos/logo-large.png and /dev/null differ diff --git a/docs/images/logos/logo.png b/docs/images/logos/logo.png deleted file mode 100644 index d7b5aa6..0000000 Binary files a/docs/images/logos/logo.png and /dev/null differ diff --git a/docs/images/profiles/tiborbodecs.jpg b/docs/images/profiles/tiborbodecs.jpg deleted file mode 100644 index b012e36..0000000 Binary files a/docs/images/profiles/tiborbodecs.jpg and /dev/null differ diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index 5f1efac..0000000 --- a/docs/index.html +++ /dev/null @@ -1,421 +0,0 @@ - - - - - - - - - - - - Articles about application development - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -

The.Swift.Dev.

Articles about application development using the Swift programming language.

- - -
- -

Latest posts

-
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Hummingbird routing and requests

-
-

Beginner's guide to learn all about routing and request handling using the Hummingbird server-side Swift framework.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to server-side Swift using the Hummingbird framework

-
-

Learn about Swift on the server by creating a simple application using the brand new HTTP server library called: Hummingbird.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Running and testing async Vapor commands

-
-

In this article I'll show you how to build asynchronous Vapor commands and how to test them using ConsoleKit.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Running tasks in parallel

-
-

Learn how to run tasks in parallel using the old-school tools and frameworks plus the new structured concurrency API in Swift.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

The abstract Vapor service factory design pattern

-
-

In this tutorial I'm going to show you how you can create an abstract driver-based component for the Vapor framework.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

SwiftNIO tutorial - The echo server

-
-

This is a beginner's guide to learn the basics of the SwiftNIO network app framework by building a basic TCP echo server.

- -
- Server -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Easy multipart file upload for Swift

-
-

Let me show you how to create HTTP requests using multipart (form data) body without a third party library. Simple solution.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Utilizing Makefiles for Swift projects

-
-

In this tutorial I'll show you how to use Makefiles for server-side Swift projects to help running utility tasks in a more simple way.

- -
- Tooling -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Lenses and prisms in Swift

-
-

Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Introduction to SPM artifact bundles

-
-

In this tutorial I'm going to show you how to use the new binary target related artifact bundle using the Swift package manager.

- - -
-
- -
-
- - - -
- - - - diff --git a/docs/introduction-to-asyncawait-in-swift/index.html b/docs/introduction-to-asyncawait-in-swift/index.html deleted file mode 100644 index 9c1265d..0000000 --- a/docs/introduction-to-asyncawait-in-swift/index.html +++ /dev/null @@ -1,540 +0,0 @@ - - - - - - - - - - - - Introduction to async/await in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

Introduction to async/await in Swift

-
-

Beginners guide to the new async/await API's in Swift 5.5. Interacting with sync code, structured concurrency, async let.

-
- -

The main project

Swift 5.5 contains a lot of new features, most of them is all about “a better concurrency model” for the language. The very first step into this new asynchronous world is a proper async/await system.

Of course you can still use regular completion blocks or the Dispatch framework to write async code, but seems like the future of Swift involves a native approach to handle concurrent tasks even better. There is combine as well, but that’s only available for Apple platforms, so yeah… 🥲

Let me show you how to convert your old callback & result type based Swift code into a shiny new async/await supported API. First we are going to create our experimental async SPM project.

// swift-tools-version:5.4
-import PackageDescription
-
-let package = Package(
-    name: "AsyncSwift",
-    products: [
-        .executable(name: "AsyncSwift", targets: ["AsyncSwift"])
-    ],
-    dependencies: [
-        // none for now..
-    ],
-    targets: [
-        .executableTarget(name: "AsyncSwift",
-                          swiftSettings: [
-                            .unsafeFlags([
-                                "-parse-as-library",
-                                "-Xfrontend", "-disable-availability-checking",
-                                "-Xfrontend", "-enable-experimental-concurrency",
-                            ])
-                          ]
-        ),
-        .testTarget(name: "AsyncSwiftTests", dependencies: ["AsyncSwift"]),
-    ]
-)
-

You might have noticed that we’re using the latest swift-tools-version:5.4 and we added a few unsafe flags for this project. This is because we’re going to use the new @main attribute inside the executable package target, and the concurrency API requires the experimental flag to be present.

Now we should create a main entry point inside our main.swift file. Since we’re using the @main attribute it is possible to create a new struct with a static main method that can be automatically launched when you build & run your project using Xcode or the command line. 🚀

@main
-struct MyProgram {
-
-    static func main() {
-        print("Hello, world!")
-    }
-}
-

Now that we have a clean main entry point, we should add some standard URLSession related functionality that we are going to replace with new async/await calls as we refactor the code.

We’re going call our usual sample todo service and validate our HTTP response. To get more specific details of a possible error, we can use a simple HTTP.Error object, and of course because the dataTask API returns immediately we have to use the dispatchMain() call to wait for the asynchronous HTTP call. Finally we simply switch the result type and exit if needed. ⏳

import Foundation
-
-enum HTTP {
-    enum Error: LocalizedError {
-        case invalidResponse
-        case badStatusCode
-        case missingData
-    }
-}
-
-struct Todo: Codable {
-    let id: Int
-    let title: String
-    let completed: Bool
-    let userId: Int
-}
-
-func getTodos(completion: @escaping (Result<[Todo], Error>) -> Void) {
-    let req = URLRequest(url: URL(string: "https://jsonplaceholder.typicode.com/todos")!)
-    let task = URLSession.shared.dataTask(with: req) { data, response, error in
-        guard error == nil else  {
-            return completion(.failure(error!))
-        }
-        guard let response = response as? HTTPURLResponse else {
-            return completion(.failure(HTTP.Error.invalidResponse))
-        }
-        guard 200...299 ~= response.statusCode else {
-            return completion(.failure(HTTP.Error.badStatusCode))
-        }
-        guard let data = data else {
-            return completion(.failure(HTTP.Error.missingData))
-        }
-        do {
-            let decoder = JSONDecoder()
-            let todos = try decoder.decode([Todo].self, from: data)
-            return completion(.success(todos))
-        }
-        catch {
-            return completion(.failure(error))
-        }
-    }
-    task.resume()
-}
-
-@main
-struct MyProgram {
-
-    static func main() {
-        getTodos { result in
-            switch result {
-            case .success(let todos):
-                print(todos.count)
-                exit(EXIT_SUCCESS)
-            case .failure(let error):
-                fatalError(error.localizedDescription)
-            }
-            
-        }
-        dispatchMain()
-    }
-}
-

If you remember I already showed you the Combine version of this URLSession data task call a while back, but as I mentioned this Combine is not only available for iOS, macOS, tvOS and watchOS.

Async/await and unsafe continuation

So how do we convert our existing code into an async variant? Well, the good news is that there is a method called withUnsafeContinuation that you can use to wrap existing completion block based calls to produce async versions of your functions. The quick and dirty solution is this:

import Foundation
-
-// ... 
-
-func getTodos() async -> Result<[Todo], Error> {
-    await withUnsafeContinuation { c in
-        getTodos { result in
-            c.resume(returning: result)
-        }
-    }
-}
-
-@main
-struct MyProgram {
-
-    static func main() async {
-        let result = await getTodos()
-        switch result {
-        case .success(let todos):
-            print(todos.count)
-            exit(EXIT_SUCCESS)
-        case .failure(let error):
-            fatalError(error.localizedDescription)
-        }
-    }
-}
-

The continuations proposal was born to provide us the necessary API to interact with synchronous code. The withUnsafeContinuation function gives us a block that we can use to resume with the generic async return type, this way it is ridiculously easy to rapidly write an async version of an existing the callback based function. As always, the Swift developer team did a great job here. 👍

One thing you might have noticed, that instead of calling the dispatchMain() function we’ve changed the main function into an async function. Well, the thing is that you can’t simply call an async function inside a non-async (synchronous) method. ⚠️

Interacting with sync code

In order to call an async method inside a sync method, you have to use the new Task.detached function and you still have to wait for the async functions to complete using the dispatch APIs.

import Foundation
-
-// ...
-
-@main
-struct MyProgram {
-
-    static func main() {
-        Task.detached {
-            let result = await getTodos()
-            switch result {
-            case .success(let todos):
-                print(todos.count)
-                exit(EXIT_SUCCESS)
-            case .failure(let error):
-                fatalError(error.localizedDescription)
-            }
-        }
-        dispatchMain()
-    }
-}
-

Of course you can call any sync and async method inside an async function, so there are no restrictions there. Let me show you one more example, this time we’re going to use the Grand Central Dispatch framework, return a few numbers and add them asynchronously.

Serial vs concurrent execution

Imagine a common use-case where you’d like to combine (pun intended) the output of some long running async operations. In our example we’re going to calculate some numbers asynchronously and we’d like to sum the results afterwards. Let’s examine the following code…

import Foundation
-
-func calculateFirstNumber() async -> Int {
-    print("First number is now being calculated...")
-    return await withUnsafeContinuation { c in
-        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
-            print("First number is now ready.")
-            c.resume(returning: 42)
-        }
-    }
-}
-
-func calculateSecondNumber() async -> Int {
-    print("Second number is now being calculated...")
-    return await withUnsafeContinuation { c in
-        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
-            print("Second number is now ready.")
-            c.resume(returning: 6)
-        }
-    }
-}
-
-func calculateThirdNumber() async -> Int {
-    print("Third number is now being calculated...")
-    return await withUnsafeContinuation { c in
-        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
-            print("Third number is now ready.")
-            c.resume(returning: 69)
-        }
-    }
-}
-
-@main
-struct MyProgram {
-
-    static func main() async {
-        let x = await calculateFirstNumber()
-        let y = await calculateSecondNumber()
-        let z = await calculateThirdNumber()
-        print(x + y + z)
-    
-}
-
-/*
-First number is now being calculated...
-First number is now ready.
-Second number is now being calculated...
-Second number is now ready.
-Third number is now being calculated...
-Third number is now ready.
-117
-Program ended with exit code: 0
-*/
-

As you can see these functions are asynchronous, but they are still executed one after another. It really doesn’t matter if you change the main queue into a different concurrent queue, the async task itself is not going to fire until you call it with await. The execution order is always serial. 🤔

Spawn tasks using async let

It is possible to change this behavior by using the brand new async let syntax. If we move the await keyword just a bit down the line we can fire the async tasks right away via the async let expressions. This new feature is part of the structured concurrency proposal.

// ...
-
-@main
-struct MyProgram {
-
-    static func main() async {
-        async let x = calculateFirstNumber()
-        async let y = calculateSecondNumber()
-        async let z = calculateThirdNumber()
-
-        let res = await x + y + z
-        print(res)
-    }
-}
-/*
-First number is now being calculated...
-Second number is now being calculated...
-Third number is now being calculated...
-Second number is now ready.
-First number is now ready.
-Third number is now ready.
-117
-Program ended with exit code: 0
-*/
-

Now the execution order is concurrent, the underlying calculation still happens in a serial way on the main queue, but you’ve got the idea what I’m trying to show you here, right? 😅

Anyway, simply adding the async/await feature into a programming language won’t solve the more complex issues that we have to deal with. Fortunately Swift will have great support to async task management and concurrent code execution. I can’t wait to write more about these new features. See you next time, there is a lot to cover, I hope you’ll find my async Swift tutorials useful. 👋

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/introduction-to-spm-artifact-bundles/index.html b/docs/introduction-to-spm-artifact-bundles/index.html deleted file mode 100644 index 895beb3..0000000 --- a/docs/introduction-to-spm-artifact-bundles/index.html +++ /dev/null @@ -1,418 +0,0 @@ - - - - - - - - - - - - Introduction to SPM artifact bundles - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 4 min read - -
-

Introduction to SPM artifact bundles

-
-

In this tutorial I'm going to show you how to use the new binary target related artifact bundle using the Swift package manager.

-
- -

Binary targets and modern Swift packages

Swift 5.6 introduced quite a lot of new features for the Swift Package Manager infrastructure. We were already able to define binary targets, and use xcframeworks as binary target dependencies for our apps. They work great if you are targeting Apple platforms, but unfortunately the xcframework format is not compatible with Linux distributions, not to mention the Windows operating system.

This is where artifact bundles can help. If you are developing apps for multiple platforms you can now create an artifact bundle, place all the compatible variants into this new structure and SPM can choose the right one based on your architecture. 💪

Before we actually dive in to our main topic, I’m going to quickly show you how to create an xcframework and ship it as a binary target via SPM.

XCFrameworks and SPM

Before the introduction of the new format we had to mess around with FAT binaries to support multiple platforms. I have a deep dive article about frameworks and tools that you can use to construct a FAT binary, but I no longer recommend it since XCFrameworks are here to stay. 🔨

In order to build an XCFramework, you have to use Xcode and a process is very simple. You just have to select the Framework type under the iOS tab when you create a new project. Feel free to name it, add your Swift source code and that’s it.

You can build this project using the command line for multiple platforms via the following script.

# build for iOS devices
-xcodebuild archive \
-  -scheme MySDK \
-  -sdk iphoneos \
-  -archivePath "build/ios_devices.xcarchive" \
-  BUILD_LIBRARY_FOR_DISTRIBUTION=YES \
-  SKIP_INSTALL=NO
-  
-# build for iOS simulators
-xcodebuild archive \
-  -scheme MySDK \
-  -sdk iphonesimulator \
-  -archivePath "build/ios_simulators.xcarchive" \
-  BUILD_LIBRARY_FOR_DISTRIBUTION=YES \
-  SKIP_INSTALL=NO
-
-# build for macOS devices
-xcodebuild archive \
-  -sdk macosx MACOSX_DEPLOYMENT_TARGET=11.0 \
-  -arch x86_64 -arch arm64 \
-  BUILD_LIBRARY_FOR_DISTRIBUTION=YES \
-  -scheme "MySDK" \
-  -archivePath "build/macos_devices.xcarchive" SKIP_INSTALL=NO
-
-# combine the slices and create the xcframework file
-xcodebuild -create-xcframework \
-  -framework build/ios_devices.xcarchive/Products/Library/Frameworks/MySDK.framework \
-  -framework build/ios_simulators.xcarchive/Products/Library/Frameworks/MySDK.framework \
-  -framework build/macos_devices.xcarchive/Products/Library/Frameworks/MySDK.framework \
-  -output MySDK.xcframework
-

You can even build versions for Catalyst and other operating systems, if you do a little search you can easily figure out the required parameters and configuration. Long story short, it’s very easy to create an xcframework output including all kind of platform slices for specific devices. 😊

Now if you want to use this XCFramework, you can simply drag and drop it to your Xcode project and it should work without further issues (if it contains the necessary slices). Alternatively you can use Swift package manager and create a binary target an hook up your external framework bundle via SPM. This is how a very simple configuration file looks like.

// swift-tools-version: 5.5
-import PackageDescription
-
-let package = Package(
-    name: "MySDK",
-    products: [
-        .library(name: "MySDK", targets: ["MySDK"]),
-    ],
-    dependencies: [
-        
-    ],
-    targets: [
-        .binaryTarget(name: "MySDK", path: "./MySDK.xcframework")
-    ]
-)
-

In your project you can use the library product as a standard dependency, and the underlying binary target will take care of importing the necessary header files and linking the actual library. The only problem with this approach is that it is macOS (or to be even more precise Apple OS only).

Say hello to artifact bundles for Swift PM

All right, so XCFrameworks can’t be used under Linux, but people like to write command line scripts in Swift and use them for server side projects. In some cases those scripts (or plugins), would like to call external scripts that are not installed on the system by default. This is where artifact bundles can help, because it makes possible to ship multiple versions of the same executable binary file. 🤔

Artifact bundles are not a replacement for xcframeworks, but more like an addition, or improvement as the proposal title indicates this, for the Swift package manager plugin architecture. They allow us to ship precompiled binary files for multiple platforms, this way plugin authors don’t have to compile those tools from source and the plugin execution time can be heavily reduced.

There is a great blog post about wrapping the SwiftLint executable in an artifact bundle, so I don’t really want to get into the details this time, because it’s pretty straightforward. The proposal itself helps a lot to understand the basic setup, also the older binary dependencies proposal contains some related info nice job Swift team. 👍

I’d like to give an honorable mention to Karim Alweheshy, who is actively working with the new Swift package manager plugin infrastructure, he has an amazing repository on GitHub that demos artifact bundles so please take a look if you have time. 🙏

Anyway, I’m going to show you how to wrap an executable into an artifact bundle. Currently there’s no way to wrap libraries into artifact bundles, that’s going to be added later on.

# create a simple hello world executable project
-mkdir MyApp
-cd $_
-swift package init --type=executable
-
-# build the project using release config
-swift build -c release
-
-# copy the binary
-cp $(swift build --show-bin-path -c release)/MyApp ./myapp
-
-
-# init a new example project
-mkdir MyPluginExample
-cd $_
-swift package init 
-
-mkdir myapp.artifactbundle
-cd $_
-mkdir myapp-1.0.0-macos
-cd $_
-mkdir bin
-

Now the file structure is ready, we should create a new info.json file under the artifactbundle directory with the following contents. This will describe your bundle with the available binary variants, you can take a look at the proposals for the available triplets versions.

{
-    "schemaVersion": "1.0",
-    "artifacts": {
-        "myapp": {
-            "version": "1.0.0",
-            "type": "executable",
-            "variants": [
-                {
-                    "path": "myapp-1.0.0-macos/bin/myapp",
-                    "supportedTriples": ["x86_64-apple-macosx", "arm64-apple-macosx"]
-                }
-            ]
-        }
-    }
-}
-

Copy the myapp binary under the myapp-1.0.0-macos/bin/myapp location, and finally we’re going to make a very simple command plugin to take advangate of this newly added tool.

import PackagePlugin
-import Foundation
-
-@main
-struct MyDistCommandPlugin: CommandPlugin {
-    
-    func performCommand(context: PluginContext, arguments: [String]) throws {
-        let myAppTool = try context.tool(named: "myapp")
-        let myAppToolURL = URL(fileURLWithPath: myAppTool.path.string)
-
-        let process = try Process.run(myAppToolURL, arguments: [])
-        process.waitUntilExit()
-    }
-}
-

Be careful with the paths and file names, I used lowercase letters for everything in this example, I recommend to follow this pattern when you create your artifact bundle binaries.

swift package plugin --list
-# ‘hello’ (plugin ‘HelloCommand’ in package ‘MyPluginExample’)
-swift package hello
-# Hello, world!
-

That’s it, now we’ve got a working artifact bundle with a custom made executable available for macOS. We can use this artifact bundle as a dependency for a plugin and run the tool by using the plugin APIs. I’d really love to be able to cross compile Swift libraries and executable files later on, this could make the development / deployment workflow a bit more easy. Anyway, artifact bundles are a nice little addition, I really like the way you can ship binaries for multiple platforms and I hope that we’re going to be able to share libraries as well in a similar fashion. 😊

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginner's guide to Swift package manager command plugins

-
-

Learn how to create command plugins for the Swift Package Manager to execute custom actions using SPM and other tools.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

How to create a Swift package collection?

-
-

In this tutorial I'm going to show you how to create your own package collection from your favorite Swift libraries.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Introduction to SPM artifact bundles

-
-

In this tutorial I'm going to show you how to use the new binary target related artifact bundle using the Swift package manager.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Swift Package Manager tutorial

-
-

Learn how to use the Swift Package Manager to handle external dependencies, create your library or app on macOS and Linux.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/ios-auto-layout-tutorial-programmatically/index.html b/docs/ios-auto-layout-tutorial-programmatically/index.html deleted file mode 100644 index 78c2058..0000000 --- a/docs/ios-auto-layout-tutorial-programmatically/index.html +++ /dev/null @@ -1,598 +0,0 @@ - - - - - - - - - - - - iOS Auto Layout tutorial programmatically - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 6 min read - -
-

iOS Auto Layout tutorial programmatically

-
-

In this great iOS Auto Layout tutorial I'll teach you how to support rotation, use constraints, work with layers, animate corner radius.

-
- -

Rotation support

If your application is going to support multiple device orientations, you should implement the following methods inside your view controller.

class ViewController: UIViewController {
-
-    override var shouldAutorotate: Bool {
-        return false
-    }
-
-    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
-        return .portrait
-    }
-
-    override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
-        return .portrait
-    }
-}
-

Obviously you can change the return values to support not just portrait, but landscape mode as well. This is quite easy, however if your controller is embedded inside a navigation or a tab bar controller the rotation stops working. In this case, you have to subclass the UINavigationController, and you have to return the correct values from the top view controller.

class NavigationController: UINavigationController {
-
-    override var shouldAutorotate: Bool {
-        if let shouldRotate = topViewController?.shouldAutorotate {
-            return shouldRotate
-        }
-        return super.shouldAutorotate
-    }
-
-    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
-        if let orientation = topViewController?.supportedInterfaceOrientations {
-            return orientation
-        }
-        return super.supportedInterfaceOrientations
-    }
-
-    override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
-        if let orientation = topViewController?.preferredInterfaceOrientationForPresentation {
-            return orientation
-        }
-        return super.preferredInterfaceOrientationForPresentation
-    }
-}
-

The same logic applies if you have a UITabBarController, but instead of the top view controller, you have to use the selectedIndex, and return the properties based on the selected view controller.

class TabBarController: UITabBarController {
-
-    override var shouldAutorotate: Bool {
-        if let viewController = viewControllers?[selectedIndex] {
-            return viewController.shouldAutorotate
-        }
-        return super.shouldAutorotate
-    }
-
-    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
-        if let viewController = viewControllers?[selectedIndex] {
-            return viewController.supportedInterfaceOrientations
-        }
-        return super.supportedInterfaceOrientations
-    }
-
-    override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
-        if  let viewController = viewControllers?[selectedIndex] {
-            return viewController.preferredInterfaceOrientationForPresentation
-        }
-        return super.preferredInterfaceOrientationForPresentation
-    }
-}
-

This way your embedded controller can control the supported orientations. Oh, by the way you can use this method to change the status bar style.

Constraints

In order to understand constraints and the current state of the Auto Layout engine, we should go back to in time and start the story from the beginning.

Springs and struts

Remember the first iPhone? One screen to rule them all! 320x480, no constraints, no adaptivity, just frames and bounds. Positioning views on a fixed size canvas is absolutely a no-brainer, here is an example.

class ViewController: UIViewController {
-
-    weak var square: UIView!
-
-    var squareFrame: CGRect {
-        let midX = view.bounds.midX
-        let midY = view.bounds.midY
-        let size: CGFloat = 64
-        return CGRect(
-            x: midX-size/2, 
-            y: midY-size/2, 
-            width: size, 
-            height: size
-        )
-    }
-
-    override func loadView() {
-        super.loadView()
-
-        let square = UIView()
-        view.addSubview(square)
-        square = square
-    }
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        square.backgroundColor = .yellow
-    }
-
-    override func viewDidLayoutSubviews() {
-        super.viewDidLayoutSubviews()
-
-        square.frame = squareFrame
-    }
-}
-

With the viewDidLayoutSubviews method it’s super convenient to support rotation, I just have to re-calculate the frame of the view every time if the bounding rectangle changes. You might think hey, this is easy, but what happens if you have to support lots of device sizes?

Do the math!

For one single object it’s so easy to make the calculations, but usually you have more than one view on screen. Those views can have relations to each other, and a simple math trick can lead you to a complete chaos of frame calculations, do you even like mathematics? There must be a better way!

Auto Layout

With iOS6 Apple brought us the holy grail of layout technologies. It was the perfect successor of the previous system. Everyone adopted it fast, that’s why Apple engineers completely removed frame based layout APIs in the next release… #justkidding

Apart from the joke, it was the beginning of a new era, more and more devices were born, and with Auto Layout constraints it was super easy to maintain views. Now we should refactor the previous example with layout constraints.

class ViewController: UIViewController {
-
-    weak var square: UIView!
-
-    override func loadView() {
-        super.loadView()
-
-        let square = UIView()
-        view.addSubview(square)
-        square.translatesAutoresizingMaskIntoConstraints = false
-        view.addConstraints([
-            NSLayoutConstraint(
-                item: square, 
-                attribute: .width, 
-                relatedBy: .equal, 
-                toItem: nil, 
-                attribute: .width, 
-                multiplier: 1.0, 
-                constant: 64
-            ),
-            NSLayoutConstraint(
-                item: square, 
-                attribute: .height, 
-                relatedBy: .equal, 
-                toItem: nil, 
-                attribute: .height, 
-                multiplier: 1.0, 
-                constant: 64
-            ),
-            NSLayoutConstraint(
-                item: square,
-                 attribute: .centerX, 
-                 relatedBy: .equal, 
-                 toItem: view, 
-                 attribute: .centerX, 
-                 multiplier: 1.0, 
-                 constant: 0
-            ),
-            NSLayoutConstraint(
-                item: square, 
-                attribute: .centerY, 
-                relatedBy: .equal, 
-                toItem: view, 
-                attribute: .centerY,
-                multiplier: 1.0, 
-                constant: 0
-            ),
-        ])
-        self.square = square
-    }
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        square.backgroundColor = .yellow
-    }
-}
-

As you can see we don’t need to manually calculate the frame of the view, however creating constraints programmatically is not so convenient. That’s why Apple made the constraint Visual Format Language.

VFL = WTF?

Actually this VFL is so bad that I don’t even want to demo it, but anyway…

class ViewController: UIViewController {
-
-    weak var square: UIView!
-
-    override func loadView() {
-        super.loadView()
-
-        let square = UIView()
-        view.addSubview(square)
-        square.translatesAutoresizingMaskIntoConstraints = false
-
-        let views: [String:Any] = [
-            "view": view, 
-            "subview": square
-        ]
-        let vertical = NSLayoutConstraint.constraints(
-            withVisualFormat: "V:[view]-(<=1)-[subview(==64)]", 
-            options: .alignAllCenterX, 
-            metrics: nil, 
-            views: views
-        )
-
-        let horizontal = NSLayoutConstraint.constraints(
-            withVisualFormat: "H:[view]-(<=1)-[subview(==64)]",
-            options: .alignAllCenterY, 
-            metrics: nil, 
-            views: views
-        )
-        view.addConstraints(vertical)
-        view.addConstraints(horizontal)
-        self.square = square
-    }
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        square.backgroundColor = .yellow
-    }
-}
-

God forbid the engineer who invented this black magic. 😅

So as you can see we definitely have a problem with constraints. Creating all your constraints sucks, at least it’s going to cost many many lines of code. Of course you can use the magical interface builder, but where’s the fun if you just drag lines?

Creating constraints programmatically is no better than calculating frames, it will lead you to the same level of complexity or even worse, this is why so many 3rd party frameworks came alive and eventually Apple issued the problem as well.

I have an amazing article about mastering Auto Layout anchors, I highly recommend reading it if you want to get familiar with anchors. 📖

Anchors

Anchors were born because Auto Layout had some construction flaws.

The NSLayoutAnchor class is a factory class for creating NSLayoutConstraint objects using a fluent API. Use these constraints to programmatically define your layout using Auto Layout.

class ViewController: UIViewController {
-
-    weak var square: UIView!
-
-    override func loadView() {
-        super.loadView()
-
-        let square = UIView()
-        view.addSubview(square)
-        square.translatesAutoresizingMaskIntoConstraints = false
-        NSLayoutConstraint.activate([
-            square.widthAnchor.constraint(equalToConstant: 64),
-            square.heightAnchor.constraint(equalToConstant: 64),
-            square.centerXAnchor.constraint(equalTo: view.centerXAnchor),
-            square.centerYAnchor.constraint(equalTo: view.centerYAnchor),
-        ])
-        self.square = square
-    }
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        square.backgroundColor = .yellow
-    }
-}
-

See, totally rocks! Anchors are the best way of using for Auto Layout constraints.

Adaptive layout

If you look at the current state of built-in apps provided by Apple, you can see that only some of them are responsive / adaptive. In general, apps that using collection views are more easy to adapt for bigger screens, or different device orientations.

Always use collection views, except if it’s just one view on the center of the screen, you should build up your user interfaces using collection views. It will give you reusability, lower memory overhead, scrolling and many more benefits. You don’t even have to calculate the stupid index paths if you are using my CollectionView micro framework.

Auto Layout with layers

Auto Layout is great, but sometimes you have to work with layers directly. Now in this situation, you still have to do some calculations. You can easily override the bounds property and update frames in the didSet block if you are dealing with a view subclass.

override var bounds: CGRect {
-    didSet {
-        gradientLayer.frame = bounds
-    }
-}
-

Another option is to override the viewDidLayoutSubviews method inside the view controller, and set the frame of the layer based on the new bounds.

override func viewDidLayoutSubviews() {
-    super.viewDidLayoutSubviews()
-
-    gradientView.gradientLayer.frame = gradientView.bounds
-}
-

You can also use plain old Key-Value Observing to observe an objet’s bounds property and update the frame of the layer according to that.

// somewhere in the init method
-addObserver(
-    self, 
-    forKeyPath: "bounds", 
-    options: .new, 
-    context: nil
-)
-
-override func observeValue(
-    forKeyPath keyPath: String?, 
-    of object: Any?, 
-    change: [NSKeyValueChangeKey : Any]?, 
-    context: UnsafeMutableRawPointer?
-) {
-    guard keyPath == "bounds" else {
-        return super.observeValue(
-            forKeyPath: keyPath, 
-            of: object, 
-            change: change, 
-            context: context
-        )
-    }
-    gradientLayer.frame = bounds
-}
-
-deinit {
-    removeObserver(self, forKeyPath: "bounds")
-}
-

Animating corner radius

First of all if you want to animate a view while using constraint based layouts, you have to do something like this.

widthConstraint.constant = 64
-UIView.animate(withDuration: 0.5, animations: {
-    view.layoutIfNeeded()
-}, completion: nil)
-

Now if you want to animate the corner radius of a view, you can always use the traditional way, and set the cornerRadius property of the layer on a bounds change.

But, we’ve got this fancy new UIViewPropertyAnimator API since iOS 10.

self.imageView.layer.cornerRadius = 16
-UIViewPropertyAnimator(duration: 2.5, curve: .easeInOut) {
-    self.imageView.layer.cornerRadius = 32
-}.startAnimation()
-

It’s pretty simple, you can even apply a cornerMask to round just some of the corners. The layer based layout examples are inside the provided source code for the article alongside with a complete sample for each Auto Layout technique. You can download or clone it from the The.Swift.Dev tutorials repository.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/ios-custom-transition-tutorial-in-swift/index.html b/docs/ios-custom-transition-tutorial-in-swift/index.html deleted file mode 100644 index 8ba8bc5..0000000 --- a/docs/ios-custom-transition-tutorial-in-swift/index.html +++ /dev/null @@ -1,545 +0,0 @@ - - - - - - - - - - - - iOS custom transition tutorial in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

iOS custom transition tutorial in Swift

-
-

In this tutorial, you'll learn how to replace the push, pop and modal animations with custom transitions & percent driven interactions.

-
- -

UIKit custom transition API - a theoretical lesson

There are many classes and delegates involved during the process of making a custom transition, let’s walk through these items real quick, and do some coding afterwards.

UIViewControllerTransitioningDelegate

Every view controller can have a transition delegate, in that delegate implementation you can provide the custom animation and interaction controllers. Those objects will be responsible for the actual animation process, and this delegate is the place where you can “inject your code” to the UIKit framework. 💉

UINavigationControllerDelegate

The navigation controller delegate also has two methods that are responsible for custom push and pop animations. It’s almost the same as the transitioning delegate for the view controllers, but you’ll see this in action later on. 💥

UINavigationController.Operation

The navigation controller operation is just an enum which contains the “direction” of the navigation animation. Usually push or pop.

Presenting and dismissing something modally is not exactly the same thing as pushing & popping view controllers inside a navigation stack. More on this later.

UIViewControllerAnimatedTransitioning

These objects are returned by the transition delegate, so basically this is the place where you implement the fancy custom view animations. 😉

UIViewControllerContextTransitioning

This context encapsulates all the info about the transitioning, you can get the participating views, controllers and many more from this object. The transitioning context is available for you to use it during the animation.

UIPercentDrivenInteractiveTransition

An object that drives an interactive animation between one view controller and another.

In a nutshell, this is the thing that gives you the magical ability to swipe a navigation controller interactively back (and forth if you changed your mind) with your fingers from the edge of the screen. 📱

Custom transition animations programmatically

Let’s do some real coding! I’ll show you how to make a basic fade animation between view controllers inside a navigation stack. First we’ll start with the push animation.

open class FadePushAnimator: NSObject, UIViewControllerAnimatedTransitioning {
-
-    open func transitionDuration(
-        using transitionContext: UIViewControllerContextTransitioning?
-    ) -> TimeInterval {
-        0.5
-    }
-
-    open override func animateTransition(
-        using transitionContext: UIViewControllerContextTransitioning
-    ) {
-        guard
-            let toViewController = transitionContext.viewController(forKey: .to)
-        else {
-            return
-        }
-        transitionContext.containerView.addSubview(toViewController.view)
-        toViewController.view.alpha = 0
-
-        let duration = self.transitionDuration(using: transitionContext)
-        UIView.animate(withDuration: duration, animations: {
-            toViewController.view.alpha = 1
-        }, completion: { _ in
-            transitionContext.completeTransition(
-                !transitionContext.transitionWasCancelled
-            )
-        })
-    }
-}
-

As you can see creating a custom transition animation is really simple. You just have to implement two delegate methods. One of them will return the duration of the animation, and the other will contain the actual transition.

The transition context provides a custom containterView object that you can use in the animation, also you can grab the participating views and controllers from this object as I mentioned it before. Now let’s reverse this animation. 👈

open class FadePopAnimator: CustomAnimator {
-
-    open func transitionDuration(
-        using transitionContext: UIViewControllerContextTransitioning?
-    ) -> TimeInterval {
-        0.5
-    }
-
-    open override func animateTransition(
-        using transitionContext: UIViewControllerContextTransitioning
-    ) {
-        guard
-            let fromViewController = transitionContext.viewController(forKey: .from),
-            let toViewController = transitionContext.viewController(forKey: .to)
-        else {
-            return
-        }
-
-        transitionContext.containerView.insertSubview(toViewController.view, belowSubview: fromViewController.view)
-
-        let duration = self.transitionDuration(using: transitionContext)
-        UIView.animate(withDuration: duration, animations: {
-            fromViewController.view.alpha = 0
-        }, completion: { _ in
-            transitionContext.completeTransition(
-                !transitionContext.transitionWasCancelled
-            )
-        })
-    }
-}
-

Finally you just have to implement the navigation controller’s delegate method in order to replace the built-in UIKit system animations. 🛠

extension MainViewController: UINavigationControllerDelegate {
-
-    func navigationController(
-        _ navigationController: UINavigationController,
-        animationControllerFor operation: UINavigationController.Operation,
-        from fromVC: UIViewController,
-        to toVC: UIViewController
-    ) -> UIViewControllerAnimatedTransitioning? {
-        switch operation {
-        case .push:
-            return FadePushAnimator()
-        case .pop:
-            return FadePopAnimator()
-        default:
-            return nil
-        }
-    }
-}
-

Note that you don’t have to make two separate classes (pop & push), you can also pass the operation and implement the animations in a single animated transitioning class.

Percent driven interactive transitions
So, now you know how to implement a custom transition, but it’s time to make it interactive! The process is pretty simple, you’ll only need a gesture recognizer and a proper delegate method to make things work. ⌨️

class DetailViewController: UIViewController {
-
-    var interactionController: UIPercentDrivenInteractiveTransition?
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        self.view.backgroundColor = .lightGray
-
-        let edge = UIScreenEdgePanGestureRecognizer(
-            target: self,
-            action: #selector(self.handleEdgePan(_:))
-        )
-        edge.edges = .left
-        self.view.addGestureRecognizer(edge)
-    }
-
-    override func viewDidAppear(_ animated: Bool) {
-        super.viewDidAppear(animated)
-
-        self.navigationController?.delegate = self
-    }
-
-    @objc func handleEdgePan(_ gesture: UIScreenEdgePanGestureRecognizer) {
-        let translate = gesture.translation(in: gesture.view)
-        let percent = translate.x / gesture.view!.bounds.size.width
-
-        switch gesture.state {
-        case .began:
-            self.interactionController = UIPercentDrivenInteractiveTransition()
-            self.navigationController?.popViewController(animated: true)
-        case .changed:
-            self.interactionController?.update(percent)
-        case .ended:
-            let velocity = gesture.velocity(in: gesture.view)
-
-            if percent > 0.5 || velocity.x > 0 {
-                self.interactionController?.finish()
-            }
-            else {
-                self.interactionController?.cancel()
-            }
-            self.interactionController = nil
-        default:
-            break
-        }
-    }
-}
-
-extension DetailViewController: UINavigationControllerDelegate {
-
-    /* ... */
-
-    func navigationController(
-        _ navigationController: UINavigationController,
-        interactionControllerFor animationController: UIViewControllerAnimatedTransitioning
-    ) -> UIViewControllerInteractiveTransitioning? {
-        interactionController
-    }
-}
-

Inside the controller that will be popped you can take ownership of the navigation controller’s delegate and implement the interactive transition controller using a left screen edge pan gesture recognizer. This whole code usually goes into a new subclass of UIPercentDrivenInteractiveTransition but for the sake of simplicity this time we’ll skip that, and go with this really easy solution. In the final example code you’ll find the “subclassed version” of the interactive transition. 😅

Ok, let’s cover one more thing real quick: customizing modal presentation animations for view controllers. There is a minor difference between customizing the navigation stack animations and modal presentation styles. If you want to customize a view controller transition you’d usually do something like this. 👍

class DetailViewController: UIViewController {
-
-     /* ... */
-
-    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
-        super.prepare(for: segue, sender: sender)
-
-        guard let controller = segue.destination as? ModalViewController else {
-            return
-        }
-
-        controller.transitioningDelegate = self
-        controller.modalPresentationStyle = .custom
-        controller.modalPresentationCapturesStatusBarAppearance = true
-    }
-}
-Here comes the transitioning delegate, using the same objects that we already have.
-
-extension DetailViewController: UIViewControllerTransitioningDelegate {
-
-    func animationController(forPresented presented: UIViewController,
-                             presenting: UIViewController,
-                             source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
-        return FadePushAnimator()
-    }
-
-    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
-        return FadePopAnimator()
-    }
-}
-

If you run the code and present the modal view controller, that’ll work just fine. The problem occurs when you try to dismiss the presented view controller. The whole app will turn to a black screen of death (BSOD). 🖥

(pop != dismiss) && (push != present)

You have to modify the pop animation in order to support modal dismissal animations. In short: the problem is with placing views and memory management.

open class FadePopAnimator: NSObject, UIViewControllerAnimatedTransitioning {
-
-    public enum TransitionType {
-        case navigation
-        case modal
-    }
-
-    let type: TransitionType
-    let duration: TimeInterval
-
-    public init(type: TransitionType, duration: TimeInterval = 0.25) {
-        self.type = type
-        self.duration = duration
-
-        super.init()
-    }
-
-    open func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
-        return self.duration
-    }
-
-    open override func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
-        guard
-            let fromViewController = transitionContext.viewController(forKey: .from)
-        else {
-            return
-        }
-
-        if self.type == .navigation, let toViewController = transitionContext.viewController(forKey: .to) {
-            transitionContext.containerView.insertSubview(toViewController.view, belowSubview: fromViewController.view)
-        }
-
-        let duration = self.transitionDuration(using: transitionContext)
-        UIView.animate(withDuration: duration, animations: {
-            fromViewController.view.alpha = 0
-        }, completion: { _ in
-            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
-        })
-    }
-}
-

The most simple solution is to introduce a new property so you can make a decision to pop or dismiss the view controller based on that flag. Now you can safely use the same animators for modally presented view controllers as well. 😬

The sample code is inside The.Swift.Dev. tutorials repository, you’ll find examples for replacing the default push & pop navigation animations with custom ones.

Note that the navigation bar will always use a fade animation, unfortunately that can not be customized. Also I’ve made a custom modal presentation, and everything is using the interactive transitions too. Obviously there is a lot more, but below are some links that you can follow if you hit an obstacle during your journey.

Also if you don’t want to manually implement custom animation effects you can use Hero the elegant transition library.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/iterator-design-pattern-in-swift/index.html b/docs/iterator-design-pattern-in-swift/index.html deleted file mode 100644 index 301c3a3..0000000 --- a/docs/iterator-design-pattern-in-swift/index.html +++ /dev/null @@ -1,497 +0,0 @@ - - - - - - - - - - - - Iterator design pattern in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 4 min read - -
-

Iterator design pattern in Swift

-
-

Learn the iterator design pattern by using some custom sequences, conforming to the IteratorProtocol from the Swift standard library.

-
- -

This time I’m going to focus on the iterator design pattern. The pattern is heavily used in the Swift standard library, there are protocols that will give you support if you need to create an iterator, but honestly: I’ve never implemented this pattern directly. 😅

The truth is that probably in 99% of the use cases you’ll never have to deal with this pattern, because there is amazing support for iterators built-in directly into Swift. Always use sequences, arrays, dictionaries instead of directly implementing this pattern, but it’s good to know how things are working under the hood, isn’t it? 🙃

What is the iterator design pattern?

As the name suggests, the pattern enables you to iterate over a collection of elements. Here is the definition from the gang of four book:

Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

Long story short the iterator gives you an interface that will enable you to iterate over collections regardless of how they are implemented in the background. Here is a quick example of the theory above using a string iterator.

import Foundation
-
-protocol StringIterator {
-    func next() -> String?
-}
-
-class ArrayStringIterator: StringIterator {
-
-    private let values: [String]
-    private var index: Int?
-
-    init(_ values: [String]) {
-        self.values = values
-    }
-
-    private func nextIndex(for index: Int?) -> Int? {
-        if let index = index, index < self.values.count - 1 {
-            return index + 1
-        }
-        if index == nil, !self.values.isEmpty {
-            return 0
-        }
-        return nil
-    }
-
-    func next() -> String? {
-        if let index = self.nextIndex(for: self.index) {
-            self.index = index
-            return self.values[index]
-        }
-        return nil
-    }
-}
-
-
-protocol Iterable {
-    func makeIterator() -> StringIterator
-}
-
-class DataArray: Iterable {
-
-    private var dataSource: [String]
-
-    init() {
-        self.dataSource = ["🐶", "🐔", "🐵", "🦁", "🐯", "🐭", "🐱", "🐮", "🐷"]
-    }
-
-    func makeIterator() -> StringIterator {
-        return ArrayStringIterator(self.dataSource)
-    }
-}
-
-let data = DataArray()
-let iterator = data.makeIterator()
-
-while let next = iterator.next() {
-    print(next)
-}
-

As you can see there are two main protocols and a really simple implementation for both of them. Our DataArray class now acts like a real array, the underlying elements can be iterated through using a loop. Let’s ditch the theory and re-implement the example from above by using real Swift standard library components. 😉

Custom sequences in Swift

Swift has a built-in sequence protocol to help you creating iterators. Implementing your own sequence in Swift is all about hiding your underlying data structure by creating a custom iterator object. You just have to store the current index and return your next element according to that each time the next function gets called. 😛

import Foundation
-
-struct Emojis: Sequence {
-    let animals: [String]
-
-    func makeIterator() -> EmojiIterator {
-        return EmojiIterator(self.animals)
-    }
-}
-
-struct EmojiIterator: IteratorProtocol {
-
-    private let values: [String]
-    private var index: Int?
-
-    init(_ values: [String]) {
-        self.values = values
-    }
-
-    private func nextIndex(for index: Int?) -> Int? {
-        if let index = index, index < self.values.count - 1 {
-            return index + 1
-        }
-        if index == nil, !self.values.isEmpty {
-            return 0
-        }
-        return nil
-    }
-
-    mutating func next() -> String? {
-        if let index = self.nextIndex(for: self.index) {
-            self.index = index
-            return self.values[index]
-        }
-        return nil
-    }
-}
-
-let emojis = Emojis(animals: ["🐶", "🐔", "🐵", "🦁", "🐯", "🐭", "🐱", "🐮", "🐷"])
-for emoji in emojis {
-    print(emoji)
-}
-

So the Sequence protocol is a generic counterpart of our custom iterable protocol used in the first example. The IteratorProtocol is somewhat like the string iterator protocol used before, but more Swift-ish and of course more generic.

So, this is great. Finally you know how to create a custom sequence. Which is good if you’d like to hide your data structure and provide a generic iterable interface. Imagine what would happen if you were about to start using a dictionary instead of an array for storing named emojis without an iterator that wraps them. 🤔

Now the thing is that there is one more super useful thing in the Swift standard library that I’d like to talk about. That’s right, one abstraction level up and here we are:

Custom collections in Swift

Collections are one step beyond sequences. Elements inside of them can be accessed via subscript they also define both a startIndex and an endIndex, plus individual elements of a collection can be accessed multiple times. Sounds good? 👍

Sometimes it can be useful to create a custom collection type. For example if you’d like to eliminate optional values. Imagine a categorized favorite mechanism, for every category you’d have an array of favorites, so you’d have to deal with empty and non-existing cases. With a custom collection you could hide that extra code inside your custom data structure and provide a clean interface for the rest of your app. 😍

class Favorites {
-
-    typealias FavoriteType = [String: [String]]
-
-    private(set) var list: FavoriteType
-
-    public static let shared = Favorites()
-
-    private init() {
-        self.list = FavoriteType()
-    }
-}
-
-
-extension Favorites: Collection {
-
-    typealias Index = FavoriteType.Index
-    typealias Element = FavoriteType.Element
-
-    var startIndex: Index {
-        return self.list.startIndex
-    }
-    var endIndex: Index {
-        return self.list.endIndex
-    }
-
-    subscript(index: Index) -> Iterator.Element {
-        return self.list[index]
-    }
-
-    func index(after i: Index) -> Index {
-        return self.list.index(after: i)
-    }
-}
-
-extension Favorites {
-
-    subscript(index: String) -> [String] {
-        return self.list[index] ?? []
-    }
-
-    func add(_ value: String, category: String) {
-        if var values = self.list[category] {
-            guard !values.contains(value) else {
-                return
-            }
-            values.append(value)
-            self.list[category] = values
-        }
-        else {
-            self.list[category] = [value]
-        }
-    }
-
-    func remove(_ value: String, category: String) {
-        guard var values = self.list[category] else {
-            return
-        }
-        values = values.filter { $0 == value }
-
-        if values.isEmpty {
-            self.list.removeValue(forKey: category)
-        }
-        else {
-            self.list[category] = values
-        }
-    }
-}
-
-Favorites.shared.add("apple", category: "fruits")
-Favorites.shared.add("pear", category: "fruits")
-Favorites.shared.add("apple", category: "fruits")
-
-Favorites.shared["fruits"]
-
-Favorites.shared.remove("apple", category: "fruits")
-Favorites.shared.remove("pear", category: "fruits")
-Favorites.shared.list
-

I know, this is a really dumb example, but it demonstrates why collections are more advanced compared to pure sequences. Also in the links below there are great demos of well written collections. Feel free to learn more about these super protocols and custom data types hidden (not so deep) inside the Swift standard library. 🤐

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Event-driven generic hooks for Swift

-
-

In this article I am going to show you how to implement a basic event processing system for your modular Swift application.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Iterator design pattern in Swift

-
-

Learn the iterator design pattern by using some custom sequences, conforming to the IteratorProtocol from the Swift standard library.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Lazy initialization in Swift

-
-

Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Lenses and prisms in Swift

-
-

Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/lazy-initialization-in-swift/index.html b/docs/lazy-initialization-in-swift/index.html deleted file mode 100644 index d5739cb..0000000 --- a/docs/lazy-initialization-in-swift/index.html +++ /dev/null @@ -1,435 +0,0 @@ - - - - - - - - - - - - Lazy initialization in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 4 min read - -
-

Lazy initialization in Swift

-
-

Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.

-
- -

According to Wikipedia:

In computer programming, lazy initialization is the tactic of delaying the creation of an object, the calculation of a value, or some other expensive process until the first time it is needed.

That little quote pretty much sums up everything, however because we’re working with the Swift programming language, we have a thing called optionals. If you don’t know what are those, please read the linked articles first, and come back afterwards. 🤐

The ultimate guide of being lazy

When a property is only needed at some point in time, you can prefix it with the lazy keyword so it’ll be “excluded” from the initialization process and it’s default value will be assigned on-demand. This can be useful for types that are expensive to create, or needs more time to be created. Here is a quick tale of a lazy princess. 👸💤

class SleepingBeauty {
-
-    init() {
-        print("zzz...sleeping...")
-        sleep(2)
-        print("sleeping beauty is ready!")
-    }
-}
-
-class Castle {
-
-    var princess = SleepingBeauty()
-
-    init() {
-        print("castle is ready!")
-    }
-}
-
-print("a new castle...")
-let castle = Castle()
-

The output of this code snippet is something like below, but as you can see the princess is sleeping for a very long time, she is also “blocking” the castle. 🏰

a new castle...
-zzz...sleeping...
-sleeping beauty is ready!
-castle is ready!
-

Now, we can speed things up by adding the lazy keword, so our hero will have time to slay the dragon and our princess can sleep in her bed until she’s needed… 🐉 🗡 🤴

class SleepingBeauty {
-
-    init() {
-        print("zzz...sleeping...")
-        sleep(2)
-        print("sleeping beauty is ready!")
-    }
-}
-
-class Castle {
-
-    lazy var princess = SleepingBeauty()
-
-    init() {
-        print("castle is ready!")
-    }
-}
-
-print("a new castle...")
-let castle = Castle()
-castle.princess
-

Much better! Now the castle is instantly ready for the battle, so the prince can wake up his loved one and… they lived happily ever after. End of story. 👸 ❤️ 🤴

a new castle...
-castle is ready!
-zzz...sleeping...
-sleeping beauty is ready!
-

I hope you enjoyed the fairy tale, but let’s do some real coding! 🤓

Avoiding optionals with lazyness

As you’ve seen in the previous example lazy properties can be used to improve the performance of your Swift code. Also you can eliminate optionals in your objects. This can be useful if you’re dealing with UIView derived classes. For example if you need a UILabel for your view hierarchy you usually have to declare that property as optional or as an implicitly unwrapped optional stored property. Let’s remake this example by using lazy & eliminating the need of the evil optional requirement. 😈

class ViewController: UIViewController {
-
-    lazy var label: UILabel = UILabel(frame: .zero)
-
-    override func loadView() {
-        super.loadView()
-
-        self.view.addSubview(self.label)
-    }
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        self.label.textColor = .black
-        self.label.font = UIFont.systemFont(ofSize: 16, weight: .bold)
-    }
-}
-

It isn’t so bad, however I still prefer to declare my views as implicitly unwrapped optionals. Maybe I’ll change my mind later on, but old habits die hard… 💀

Using a lazy closure

You can use a lazy closure to wrap some of your code inside it. The main advantage of being lazy - over stored properties - is that your block will be executed ONLY if a read operation happens on that variable. You can also populate the value of a lazy property with a regular stored proeprty. Let’s see this in practice.

class ViewController: UIViewController {
-
-    lazy var label: UILabel = {
-        let label = UILabel(frame: .zero)
-        label.translatesAutoresizingMaskIntoConstraints = false
-        label.textColor = .black
-        label.font = UIFont.systemFont(ofSize: 16, weight: .bold)
-        return label
-    }()
-}
-

This one is a nice practice if you’d like to declutter your init method. You can put all the object customization logic inside a closure. The closure executes itself on read (self-executing closure), so when you call self.label your block will be executed and voilá: your view will be ready to use.

You can’t use self in stored properties, but you are allowed to do so with lazy blocks. Be careful: you should always use [unowned self], if you don’t want to create reference cycles and memory leaks. ♻️

Lazy initialization using factories

I already have a couple of articles about factories in Swift, so now i just want to show you how to use a factory method & a static factory combined with a lazy property.

Factory method

If you don’t like self-executing closures, you can move out your code into a factory method and use that one with your lazy variable. It’s simple like this:

class ViewController: UIViewController {
-
-    lazy var label: UILabel = self.createCustomLabel()
-
-    private func createCustomLabel() -> UILabel {
-        print("called")
-        let label = UILabel(frame: .zero)
-        label.translatesAutoresizingMaskIntoConstraints = false
-        label.textColor = .black
-        label.font = UIFont.systemFont(ofSize: 16, weight: .bold)
-        return label
-    }
-}
-

Now the factory method works like a private initializer for your lazy property. Let’s bring this one step further, so we can improve reusability a little bit…

Static factory

Outsourcing your lazy initializer code into a static factory can be a good practice if you’d like to reuse that code in multiple parts of your application. For example this is a good fit for initializing custom views. Also creating a custom view is not really a view controller task, so the responsibilities in this example are more separated.

class ViewController: UIViewController {
-
-    lazy var label: UILabel = UILabel.createCustomLabel()
-}
-
-extension UILabel {
-
-    static func createCustomLabel() -> UILabel {
-        let label = UILabel(frame: .zero)
-        label.translatesAutoresizingMaskIntoConstraints = false
-        label.textColor = .black
-        label.font = UIFont.systemFont(ofSize: 16, weight: .bold)
-        return label
-    }
-}
-

As a gratis you can enjoy the advantages of static factory properties / methods, like caching or returning specific subtypes. Pretty neat! 👍

Conclusion

Lazy variables are a really convenient way to optimize your code, however they can only used on structs and classes. You can’t use them as computed properties, this means they won’t return the closure block every time you are trying to access them.

Another important thing is that lazy properties are NOT thread safe, so you have to be careful with them. Plus you don’t always want to eliminate implicitly unwrapped optional values, sometimes it’s just way better to simply crash! 🐛

Don’t be lazy!

…but feel free to use lazy properties whenever you can! 😉

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Event-driven generic hooks for Swift

-
-

In this article I am going to show you how to implement a basic event processing system for your modular Swift application.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Iterator design pattern in Swift

-
-

Learn the iterator design pattern by using some custom sequences, conforming to the IteratorProtocol from the Swift standard library.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Lazy initialization in Swift

-
-

Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Lenses and prisms in Swift

-
-

Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/lenses-and-prisms-in-swift/index.html b/docs/lenses-and-prisms-in-swift/index.html deleted file mode 100644 index cf3b6c1..0000000 --- a/docs/lenses-and-prisms-in-swift/index.html +++ /dev/null @@ -1,530 +0,0 @@ - - - - - - - - - - - - Lenses and prisms in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

Lenses and prisms in Swift

-
-

Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.

-
- -

Understanding optics

Optics is a pattern borrowed from Haskell, that enables you to zoom down into objects. In other words, you can set or get a property of an object in a functional way. By functional I mean you can set a property without causing mutation, so instead of altering the original object, a new one will be created with the updated property. Trust me it’s not that complicated as it might sounds. 😅

We’re going to need just a bit of Swift code to understand everything.

struct Address {
-    let street: String
-    let city: String
-}
-
-struct Company {
-    let name: String
-    let address: Address
-}
-
-struct Person {
-    let name: String
-    let company: Company
-}
-

As you can see it is possible to build up a hierarchy using these structs. A person can have a company and the company has an address, for example:

let oneInfiniteLoop = Address(street: "One Infinite Loop", city: "Cupertino")
-let appleInc = Company(name: "Apple Inc.", address: oneInfiniteLoop)
-let steveJobs = Person(name: "Steve Jobs", company: appleInc)
-

Now let’s imagine that the street name of the address changes, how do we alter this one field and propagate the property change for the entire structure? 🤔

struct Address {
-    var street: String
-    let city: String
-}
-
-struct Company {
-    let name: String
-    var address: Address
-}
-
-struct Person {
-    let name: String
-    var company: Company
-}
-
-var oneInfiniteLoop = Address(street: "One Infinite Loop", city: "Cupertino")
-var appleInc = Company(name: "Apple Inc.", address: oneInfiniteLoop)
-var steveJobs = Person(name: "Steve Jobs", company: appleInc)
-
-oneInfiniteLoop.street = "Apple Park Way"
-appleInc.address = oneInfiniteLoop
-steveJobs.company = appleInc
-
-print(steveJobs) // address is updated
-

In order to update the street property we had to do quite a lot of work, first we had to change some of the properties to variables, and we also had to manually update all the references, since structs are not reference types, but value types, hence copies are being used all around.

This looks really bad, we’ve also caused quite a lot of mutation and now others can also change these variable properties, which we don’t necessary want. Is there a better way? Well…

let newSteveJobs = Person(
-    name: steveJobs.name,
-    company: Company(
-        name: appleInc.name,
-        address: Address(
-            street: "Apple Park Way",
-            city: oneInfiniteLoop.city
-        )
-    )
-)
-

Ok, this is ridiculous, can we actually do something better? 🙄

Lenses

We can use a lens to zoom on a property and use that lens to construct complex types. A lens is a value representing maps between a complex type and one of its property.

Let’s keep it simple and define a Lens struct that can transform a whole object to a partial value using a getter, and set the partial value on the entire object using a setter, then return a new “whole object”. This is how the lens definition looks like in Swift.

struct Lens<Whole, Part> {
-    let get: (Whole) -> Part
-    let set: (Part, Whole) -> Whole
-}
-

Now we can create a lens that zooms on the street property of an address and construct a new address using an existing one.

let oneInfiniteLoop = Address(street: "One Infinite Loop", city: "Cupertino")
-let appleInc = Company(name: "Apple Inc.", address: oneInfiniteLoop)
-let steveJobs = Person(name: "Steve Jobs", company: appleInc)
-
-let addressStreetLens = Lens<Address, String>(get: { $0.street },
-                                              set: { Address(street: $0, city: $1.city) })
-
-
-let newSteveJobs = Person(name: steveJobs.name,
-                          company: Company(name: appleInc.name,
-                                           address: addressStreetLens.set("Apple Park Way", oneInfiniteLoop)))
-

Let’s try to build lenses for the other properties as well.

let oneInfiniteLoop = Address(street: "One Infinite Loop", city: "Cupertino")
-let appleInc = Company(name: "Apple Inc.", address: oneInfiniteLoop)
-let steveJobs = Person(name: "Steve Jobs", company: appleInc)
-
-let addressStreetLens = Lens<Address, String>(get: { $0.street },
-                                              set: { Address(street: $0, city: $1.city) })
-
-let companyAddressLens = Lens<Company, Address>(get: { $0.address },
-                                                set: { Company(name: $1.name, address: $0) })
-
-let personCompanyLens = Lens<Person, Company>(get: { $0.company },
-                                              set: { Person(name: $1.name, company: $0) })
-
-let newAddress = addressStreetLens.set("Apple Park Way", oneInfiniteLoop)
-let newCompany = companyAddressLens.set(newAddress, appleInc)
-let newPerson = personCompanyLens.set(newCompany, steveJobs)
-
-print(newPerson)
-

This might looks a bit strange at first sight, but we’re just scratching the surface here. It is possible to compose lenses and create a transition from an object to another property inside the hierarchy.

struct Lens<Whole, Part> {
-    let get: (Whole) -> Part
-    let set: (Part, Whole) -> Whole
-}
-
-extension Lens {
-    func transition<NewPart>(_ to: Lens<Part, NewPart>) -> Lens<Whole, NewPart> {
-        .init(get: { to.get(get($0)) },
-              set: { set(to.set($0, get($1)), $1) })
-    }
-
-}
-
-// ...
-
-let personStreetLens = personCompanyLens.transition(companyAddressLens)
-                                        .transition(addressStreetLens)
-
-
-let newPerson = personStreetLens.set("Apple Park Way", steveJobs)
-
-print(newPerson)
-

So in our case we can come up with a transition method and create a lens between the person and the street property, this will allow us to directly modify the street using this newly created lens.

Oh, by the way, we can also extend the original structs to provide these lenses by default. 👍

extension Address {
-    struct Lenses {
-        static var street: Lens<Address, String> {
-            .init(get: { $0.street },
-                  set: { Address(street: $0, city: $1.city) })
-        }
-    }
-}
-
-extension Company {
-
-    struct Lenses {
-        static var address: Lens<Company, Address> {
-            .init(get: { $0.address },
-                  set: { Company(name: $1.name, address: $0) })
-        }
-    }
-}
-
-extension Person {
-
-    struct Lenses {
-        static var company: Lens<Person, Company> {
-            .init(get: { $0.company },
-                  set: { Person(name: $1.name, company: $0) })
-        }
-        
-        static var companyAddressStreet: Lens<Person, String> {
-            Person.Lenses.company
-                .transition(Company.Lenses.address)
-                .transition(Address.Lenses.street)
-        }
-    }
-
-}
-
-let oneInfiniteLoop = Address(street: "One Infinite Loop", city: "Cupertino")
-let appleInc = Company(name: "Apple Inc.", address: oneInfiniteLoop)
-let steveJobs = Person(name: "Steve Jobs", company: appleInc)
-
-let newPerson = Person.Lenses.companyAddressStreet.set("Apple Park Way", steveJobs)
-
-print(newPerson)
-

On the call site we were able to use one single line to update the street property of an immutable structure, of course we’re creating a new copy of the entire object, but that’s good since we wanted to avoid mutations. Of course we have to create quite a lot of lenses to make this magic happen under the hood, but sometimes it is worth the effort. ☺️

Prisms

Now that we know how to set properties of a struct hierarchy using a lens, let me show you one more data type that we can use to alter enum values. Prisms are just like lenses, but they work with sum types. Long story short, enums are sum types, structs are product types, and the main difference is how many unique values can you represent with them.

// 512 possible values (= 2 * 256)
-struct ProductExample {
-    let a: Bool // 2 possible values
-    let b: Int8 // 256 possible values
-}
-
-
-// 258 possible values (= 2 + 256)
-enum SumExample {
-    case a(Bool) // 2 possible values
-    case b(Int8) // 256 possible values
-}
-

Another difference is that a prism getter can return a nil value and the setter can “fail”, this means if it is not possible to set the value of the property it’ll return the original data value instead.

struct Prism<Whole, Part> {
-    let tryGet: (Whole) -> Part?
-    let inject: (Part) -> Whole
-}
-

This is how we can implement a prism, we call the getter tryGet, since it returns an optional value, the setter is called inject because we try to inject a new partial value and return the whole if possible. Let me show you an example so it’ll make more sense.

enum State {
-    case loading
-    case ready(String)
-}
-
-extension State {
-
-    enum Prisms {
-        static var loading: Prism<State, Void> {
-            .init(tryGet: {
-                guard case .loading = $0 else {
-                    return nil
-                }
-                return ()
-            },
-            inject: { .loading })
-        }
-        
-        static var ready: Prism<State, String> {
-            .init(tryGet: {
-                guard case let .ready(message) = $0 else {
-                    return nil
-                }
-                return message
-            },
-            inject: { .ready($0) })
-        }
-    }
-}
-

we’ve created a simple State enum, plus we’ve extended it and added a new Prism namespace as an enum with two static properties. ExactlyOne static prism for every case that we have in the original State enum. We can use these prisms to check if a given state has the right value or construct a new state using the inject method.

// create enums cases the regular way
-let loadingState = State.loading
-let readyState = State.ready("I'm ready.")
-
-// this creates a new loading state using the prism
-let newLoadingState = State.Prisms.loading.inject(())
-// this creates a new ready state with a given value
-let newReadyState = State.Prisms.ready.inject("Hurray!")
-
-
-// trying to access the ready message through the prism
-let nilMessage = State.Prisms.ready.tryGet(loadingState)
-print(nilMessage)
-
-// returns the message if the state has a ready value
-let message = State.Prisms.ready.tryGet(readyState)
-print(message)
-

The syntax seems like a bit strange at the first sight, but trust me Prisms can be very useful. You can also apply transformations on prisms, but that’s a more advanced topic for another day.

Anyway, this time I’d like to stop here, since optics are quite a huge topic and I simply can’t cover everything in one article. Hopefully this little article will help you to understand lenses and prisms just a bit better using the Swift programming language. 🙂

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Event-driven generic hooks for Swift

-
-

In this article I am going to show you how to implement a basic event processing system for your modular Swift application.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Iterator design pattern in Swift

-
-

Learn the iterator design pattern by using some custom sequences, conforming to the IteratorProtocol from the Swift standard library.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Lazy initialization in Swift

-
-

Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Lenses and prisms in Swift

-
-

Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/logging-for-beginners-in-swift/index.html b/docs/logging-for-beginners-in-swift/index.html deleted file mode 100644 index d4125fb..0000000 --- a/docs/logging-for-beginners-in-swift/index.html +++ /dev/null @@ -1,394 +0,0 @@ - - - - - - - - - - - - Logging for beginners in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 6 min read - -
-

Logging for beginners in Swift

-
-

Learn how to print variables to the debug console using different functions such as print, dump, NSLog and the unified os.log API.

-
- -

Basic output in Swift using print

The very first method I’d like to show you is the print function. It can write the textual representation of the given items to the standard output. In other words we can simply say that it can print text to the screen. Most of the hello word programs utilize this method to display the famous “Hello world!” message. In Swift, print is quite a powerful method, since you can pass around multiple items for printing out plus you can specify a separator string and a terminator parameter. 🤔

print("Hello World!")
-// output: Hello World!\n
-

The snippet above will display the Hello World! text followed by a newline character (\n), this is because the default terminator is always a newline. You can override this behavior by providing your own terminator string.

print("Hello World!", terminator: "")
-// output: Hello World!
-

If you run this example using Xcode you should see that the “Program ended with exit code: 0” text will appear in a newline in the first case, but in the second scenario it’ll be printed out right after the “Hello World!” sentence. If you run the program using a Terminal application, a % character be present instead of the new line in the second case. 💡

What about printing out multiple variables? It is possible to give multiple items to the print function, they can be literally anything, print can handle strings, integers and all kinds of other variables. Print under the hood will convert the variable into a proper string representation, so you don’t have to mess around with type casting all the time, but simply print out anything.

print(1, 2, 3, 4, 5)
-// output: 1 2 3 4 5\n
-
-print(1, "two", 3.14, true)
-// output: 1 two 3.14 true\n
-

You can also customize the separator character through an argument. So if you need a coma character (followed by a space) in between the elements, you can write something like this:

print("a", "b", "c", separator: ", ")
-// output: a, b, c\n
-

Well, in my previous article you have seen how to construct various strings using literals and interpolation, you can use all those variables to print out stuff to the console.

print("""
-            __
-           / _)
-    .-^^^-/ /
- __/       /
-<__.|_|-|_|
-""")
-

For example, here’s a cute multi-line ascii art dinosaur. 🦕

Debugging and print

Sometimes it would be cool to know just a little bit of extra info about the printed variable, this is when debugPrint can help you. The main difference between print and debugPrint is that while print simply converts everything to string, debug print will give you a brief debug info about the given items. The debugPrint method will print out numbers just like print does, it’ll add double quotes around strings, and it’ll print some extra info about most of the other “complex” types.

print(1) // 1
-debugPrint(1) // 1
-
-print("foo") // foo
-debugPrint("foo") // "foo"
-
-print(1...5) // 1...5
-debugPrint(1...5) // ClosedRange(1...5)
-

Honestly I’ve almost never used this method, and I always preferred print if I had to print out something to the console, but it’s always good to know that there is such an option available built-in to the standard library, however there is a method that can give you way more info… 🧐

Debugging using dump

The dump method can print out the given object’s content using its mirror to the standard output. Long story short, this function will show you a more detailed view about the property. For scalar values the dump method will produce almost the same output as debug-print, except the dump line always starts with a dash character, but for more complex types it’ll output the underlying structure of the object. Don’t worry, you don’t need to understand the output of this method, just remember that it can show you helpful info during debugging. 🐞

dump(1)
-dump(3.14)
-dump("foo")
-dump(1...5)
-/*
- - 1
- - 3.14
- - "foo"
- ▿ ClosedRange(1...5)
-   - lowerBound: 1
-   - upperBound: 5
- */
-

The ClosedRange struct is a built-in type with a lowerBound and an upperBound property. While the print function only returned the defined range (1…5), the debugPrint method also revealed the type of the object, dump takes this one step further by showing us the exact lower and upper bound properties of the value. This can be extremely helpful when you have a complex type with lots of underlying properties that you want to quickly inspect for some reason. 🔍

By the way, debugging is the act of finding (and resolving) bugs. Bugs are problems in your program code that prevent normal operation. Developers can use debugger tools to run and inspect code step by step, line by line or per instruction, but most of them are simply putting print statements into the code to see the current state or result of a given function. 🤷‍♂️

Dump has a few more function arguments that you can configure:

dump("test", name: "my-variable", indent: 4, maxDepth: 5, maxItems: 5)
-// output:     - my-variable: "test"
-

You can give a name to each dumped variable, add some extra indentation before the dash character, specify the maximum depth for descendents and the maximum number of elements for which to write the full contents. Feel free to play with these parameters for a while. 😉

As you can see dump is quite a powerful method, but still there are other functions for logging purposes, let me show you one that is coming from the Objective-C times.

NSLog - the legacy logger function

If you have ever worked with Objective-C you should be familiar with the NS prefixes. The NSLog function can log an error message to the Apple System Log facility console. It’s not part of the Swift standard library, but you have to import the Foundation framework in order to use NSLog.

import Foundation
-
-NSLog("I'm a dinosaur.")
-
-// output: [date][time][program-name][process-id][thread-id][message]
-

You should know that NSLog will print the current date & time first, then it’ll display the name of the running program with the process and thread identifiers and only then it’ll print your message.

Just to be clear, NSLog is coming from the Objective-C era, it is not a recommended logging solution anymore. It is also very slow and that can cause some issues if you need precisely timed outputs. That’s why I do NOT recommend using NSLog at all, but you also have to know that until a few years ago there was no better built-in alternative for it, I’m not judging, just saying… 😅

Unified Logging and Activity Tracing

If you want to send log messages on an Apple device to the unified logging system, you can use the OSLog framework. This new tool was introduced at WWDC 2016 and recently got some nice API refinements & updates. You should definitely check the OSLog and Unified Logging recommended by Apple article if you want to learn more about this topic it’s a great write up.

My only concern about this logging API is that it is not that universal. It works great on Apple platforms, but since Swift is an universal language if you want to add Linux or even Windows support, this solution won’t work for you…

SwiftLog - A Logging API package for Swift

This open source package can be easily integrated into your Swift projects via the Swift Package Manager. You just have to set it up as a dependency in the Package.swift manifest file or you can hook it using Xcode under the File > Swift Packages menu as an SPM dependency.

// swift-tools-version:5.3
-import PackageDescription
-
-let package = Package(
-    name: "myProject",
-    dependencies: [
-        .package(url: "https://github.com/apple/swift-log.git", from: "1.4.0"),
-    ],
-    targets: [
-        .target(name: "myProject", dependencies: [
-            .product(name: "Logging", package: "swift-log")
-        ])
-    ]
-)
-

The usage is really straightforward. First you have to import the Logging framework, then you create a logger and you use that logger instance to print out various log messages.

import Logging
-
-let logger = Logger(label: "app-identifier")
-
-logger.info("Hello World!")
-

The following log levels are supported:

  • trace
  • debug
  • info
  • notice
  • warning
  • error
  • critical

You can also attach additional logging metadata to the logger, you should check the readme for more info about this option. SwiftLog is used in many real-world projects, such as Vapor 4 (a server side Swift framework), this also means that it works great on Linux operating systems. 🐧

Conclusion

If it comes to logging, there are several good options to choose from. It only depends on your needs which one is the best, but in general we can say that it is time to leave behind NSLog, and time to use the new OSLog framework. If you are using Swift on non-Apple platform you should consider using the SwiftLog library, which is also provided by Apple.

Alternatively if you are just scratching the surface and you don’t need that many options or log levels you can simply stick with print and dump statements. It’s perfectly fine to debug using these simple techniques in the beginning. Mastering something takes time and debuggers can be quite frightening at first sight. Use print as much as you need, but always try to improve your tools & knowledge over time, I hope this article gives you a better view of the available logging tools. 🤓

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/mastering-ios-auto-layout-anchors-programmatically-from-swift/index.html b/docs/mastering-ios-auto-layout-anchors-programmatically-from-swift/index.html deleted file mode 100644 index 4157887..0000000 --- a/docs/mastering-ios-auto-layout-anchors-programmatically-from-swift/index.html +++ /dev/null @@ -1,531 +0,0 @@ - - - - - - - - - - - - Mastering iOS auto layout anchors programmatically from Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

Mastering iOS auto layout anchors programmatically from Swift

-
-

Looking for best practices of using layout anchors? Let's learn how to use the iOS autolayout system in the proper way using Swift.

-
- -

Creating views and constraints programmatically

First of all I’d like to recap the UIViewController life cycle methods, you are might familiar with some of them. They are being called in the following order:

  • loadView
  • viewDidLoad
  • viewWillAppear
  • viewWillLayoutSubviews
  • viewDidLayoutSubviews
  • viewDidAppear

In the pre-auto layout era, you had to do your layout calculations inside the viewDidLayoutSubviews method, but since this is a pro auto layout tutorial we are only going to focus on the loadView & viewDidLoad methods. 🤓

These are the basic rules of creating view hierarchies using auto layout:

  • Never calculate frames manually by yourself!
  • Initialize your views with .zero rect frame
  • Set translatesAutoresizingMaskIntoConstraints to false
  • Add your view to the view hierarchy using addSubview
  • Create and activate your layout constraints NSLayoutConstraint.activate
  • Use loadView instead of viewDidLoad for creating views with constraints
  • Take care of memory management by using weak properties
  • Set every other property like background color, etc. in viewDidLoad

Enough theory, here is a short example:

class ViewController: UIViewController {
-
-    weak var testView: UIView!
-
-    override func loadView() {
-        super.loadView()
-
-        let testView = UIView(frame: .zero)
-        testView.translatesAutoresizingMaskIntoConstraints = false
-        self.view.addSubview(testView)
-        NSLayoutConstraint.activate([
-            testView.widthAnchor.constraint(equalToConstant: 64),
-            testView.widthAnchor.constraint(equalTo: testView.heightAnchor),
-            testView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
-            testView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
-        ])
-        self.testView = testView
-    }
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        self.testView.backgroundColor = .red
-    }
-}
-

Pretty simple, huh? Just a few lines of code and you have a fixed size center aligned view with a dedicated class property reference. If you create the exact same through interface builder, the system will “make” you the loadView method for free, but you’ll have to setup an IBOutlet reference to the view.

The eternal dilemma: code vs Interface Builder.

It really doesn’t matters, feel free to chose your path. Sometimes I love playing around with IB, but in most of the cases I prefer the programmatic way of doing things. 😛

Common UIKit auto layout constraint use cases

So I promised that I’ll show you how to make constraints programmatically, right? Let’s do that now. First of all, I use nothing but layout anchors. You could waste your time with the visual format language, but that’s definitely a dead end. So mark my words: use only anchors or stack views, but nothing else! 😇

Here are the most common patterns that I use to create nice layouts. 😉

Set fixed with or height

First one is the most simple one: set a view’s height or a width to a fixed point.

testView.widthAnchor.constraint(equalToConstant: 320),
-testView.heightAnchor.constraint(equalToConstant: 240),
-

Set aspect ratio

Settings a view’s aspect ratio is just constrainting the width to the height or vica versa, you can simply define the rate by the multiplier.

testView.widthAnchor.constraint(equalToConstant: 64),
-testView.widthAnchor.constraint(equalTo: testView.heightAnchor, multiplier: 16/9),
-

Center horizontally & vertically

Centering views inside another one is a trivial task, there are specific anchors for that.

testView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
-testView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
-

Stretch or fill inside view with padding

The only tricky part here is that trailing and bottom constraints behave a little bit different, than top & leading if it comes to the constants. Usually you have to work with negative values, but after a few tries you’ll understand the logic here. 😅

testView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 32),
-testView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 32),
-testView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -32),
-testView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -32),
-

Proportional width or height

If you don’t want to work with constant values, you can use the multiplier.

testView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 1/3),
-testView.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 2/3),
-

Using safe area layout guides

With the latest iPhone you’ll need some guides in order to keep you safe from the notch. This is the reason why views have the safeAreaLayoutGuide property. You can get all the usual anchors after calling out to the safe area guide. 💪

testView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor),
-testView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor),
-testView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor),
-testView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor),
-

Animating layout constraints

Animation with constraints is easy, you shouldn’t believe what others might say. I made some rules and an example that’ll help you understanding the basic principles of animating constant values of a constraint, plus toggling various constraints. 👍

Rules:

  • Use standard UIView animation with layoutIfNeeded
  • Always deactivate constraints first
  • Hold to your deactivated constraints strongly
  • Have fun! 😛

Constraint animation example:

class ViewController: UIViewController {
-
-    weak var testView: UIView!
-    weak var topConstraint: NSLayoutConstraint!
-    var bottomConstraint: NSLayoutConstraint!
-    var heightConstraint: NSLayoutConstraint!
-
-    override func loadView() {
-        super.loadView()
-
-        let testView = UIView(frame: .zero)
-        testView.translatesAutoresizingMaskIntoConstraints = false
-        self.view.addSubview(testView)
-
-        let topConstraint = testView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor)
-        let bottomConstraint = testView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor)
-
-        NSLayoutConstraint.activate([
-            topConstraint,
-            testView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor),
-            testView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor),
-            bottomConstraint,
-        ])
-
-        let heightConstraint = testView.heightAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.heightAnchor, multiplier: 0.5)
-
-        self.testView = testView
-        self.topConstraint = topConstraint
-        self.bottomConstraint = bottomConstraint
-        self.heightConstraint = heightConstraint
-    }
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        self.testView.backgroundColor = .red
-
-        let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapped))
-        self.view.addGestureRecognizer(tap)
-    }
-
-    @objc func tapped() {
-        if self.topConstraint.constant != 0 {
-            self.topConstraint.constant = 0
-        }
-        else {
-            self.topConstraint.constant = 64
-        }
-
-        if self.bottomConstraint.isActive {
-            NSLayoutConstraint.deactivate([self.bottomConstraint])
-            NSLayoutConstraint.activate([self.heightConstraint])
-
-        }
-        else {
-            NSLayoutConstraint.deactivate([self.heightConstraint])
-            NSLayoutConstraint.activate([self.bottomConstraint])
-        }
-
-        UIView.animate(withDuration: 0.25) {
-            self.view.layoutIfNeeded()
-        }
-    }
-}
-

It’s not that bad, next: adaptivity and supporting multiple device screen sizes. 🤔

How to create adaptive layouts for iOS?
Even Apple is struggling with adaptive layouts in the built-in iOS applications. If you look at apps that are made with collection views - like photos - layouts are pretty okay on every device. However there are a few other ones, that - in my opinion - are horrible experiences on a bigger screen. 🤐

Rotation support

Your first step to adaptive layout is supporting multiple device orientations. You can check my previous article about iOS auto layout there are lots of great stuff inside that article about rotation support, working with layers inside auto layout land, etc. 🌈

Trait collections

Second step is to adapt trait collections. UITraitCollection is there for you to group all the environmental specific traits such as size classes, display scale, user interface idiom and many more. Most of the times you will have to check the vertical & horizontal size classes. There is a reference of device size classes and all the possible variations made by Apple, see the external sources section below. 😉

This little Swift code example below is demonstrating how to check size classes for setting different layouts for compact and regular screens.

class ViewController: UIViewController {
-
-    weak var testView: UIView!
-
-    var regularConstraints: [NSLayoutConstraint] = []
-    var compactConstraints: [NSLayoutConstraint] = []
-
-    override func loadView() {
-        super.loadView()
-
-        let testView = UIView(frame: .zero)
-        testView.translatesAutoresizingMaskIntoConstraints = false
-        self.view.addSubview(testView)
-
-        self.regularConstraints = [
-            testView.widthAnchor.constraint(equalToConstant: 64),
-            testView.widthAnchor.constraint(equalTo: testView.heightAnchor),
-            testView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
-            testView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
-        ]
-
-        self.compactConstraints = [
-            testView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor),
-            testView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor),
-            testView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor),
-            testView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor),
-        ]
-
-        self.activateCurrentConstraints()
-
-        self.testView = testView
-    }
-
-    private func activateCurrentConstraints() {
-        NSLayoutConstraint.deactivate(self.compactConstraints + self.regularConstraints)
-
-        if self.traitCollection.verticalSizeClass == .regular {
-            NSLayoutConstraint.activate(self.regularConstraints)
-        }
-        else {
-            NSLayoutConstraint.activate(self.compactConstraints)
-        }
-    }
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        self.testView.backgroundColor = .red
-    }
-
-    // MARK: - rotation support
-
-    override var shouldAutorotate: Bool {
-        return true
-    }
-
-    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
-        return .allButUpsideDown
-    }
-
-    override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
-        return .portrait
-    }
-
-    // MARK: - trait collections
-
-    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
-        super.traitCollectionDidChange(previousTraitCollection)
-
-        self.activateCurrentConstraints()
-    }
-}
-

Device detection

You can also check the user interface idiom through the UIDevice class (aka. is this freakin’ device an iPhone or an iPad?) to set for example font sizes based on it. 📱

UIDevice.current.userInterfaceIdiom == .pad
-

Screen size

Another option to figure out your environment is checking the size of the screen. You can check the native pixel count or a relative size based in points.

//iPhone X
-UIScreen.main.nativeBounds   // 1125x2436
-UIScreen.main.bounds         // 375x812
-

Usually I’m trying to keep myself to these rules. I don’t really remember a scenario where I needed more than all the things I’ve listed above, but if you have a specific case or questions, please don’t hesitate to contact me. 😉

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/mastering-the-viper-architecture/index.html b/docs/mastering-the-viper-architecture/index.html deleted file mode 100644 index 16a6bad..0000000 --- a/docs/mastering-the-viper-architecture/index.html +++ /dev/null @@ -1,392 +0,0 @@ - - - - - - - - - - - - Mastering the VIPER architecture - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 4 min read - -
-

Mastering the VIPER architecture

-
-

Learn how to master the VIPER architectural design pattern, with some protocol oriented programming techniques using Swift.

-
- -

After writing my best practices article about VIPER, I’ve made a few changes to the codebase. I was playing with these ideas in my mind already, but never had enough time to implement them properly. Let’s me show you the changes…

VIPER protocols

My generic issue was that I wanted to have a common interface for every single module component. That’s why I created simple protocols for the following:

  • View
  • Interactor
  • Presenter
  • Entity
  • Router
  • Module

This way for example my router objects are implementing the Router protocol, so if I make an extension on it, every single one will have that particular functionality. It’s a pretty small, but very pleasant addition that makes my modules way more powerful than they were before. Honestly speaking I should have had this from the very beginning, but anyway from now on it’s gona be like this. 😬

This move implied to organize my VIPER protocols into a custom framework, so I made one, with these components. You can find it on GitHub, it’s a really basic one, feel free to use it, you just have to import VIPER in your project.

Module protocols

Since I was using VIPER it had this great urge to implement a custom module for presenting system default alert messages on iOS. You know UIAlertController is the one I’m talking about. Actually Robi (my true metal friend) suggested a surprisingly nice general solution for the problem. His idea sounded like this:

Why don’t we create a protocol for the router, so we could implement this on every other router, also we could simply call show(alert:) on them?

I loved this approach, so we’ve built it. Turned out, it’s freakin awesome. So we introduced a new protocol for the module router, implemented a default protocol extension and voilà routers are now capable of presenting error messages.

Note that you can use the same pattern for lots of other (similar) things as well. The basic implementation looks like this one below, I hope you get the idea. 💡

import VIPER
-
-class AlertModule: Module {
-    /* ... */
-}
-
-protocol AlertModuleRouter: class {
-
-    func show(alert: AlertEntity)
-}
-
-extension AlertModuleRouter where Self: Router {
-
-    func show(alert: AlertEntity) {
-        /* ... */
-    }
-}
-
-// MARK: - other module
-
-protocol MyModuleRouter: Router, AlertModuleRouter {
-
-    // show(alert:) is now available from this router too
-}
-

Of course this technique can work for other VIPER components as well, it’s quite easy to implment and the protocol oriented approach gives us a huge win. 🏆

Presenter to presenter interactions

I also changed my mind about the place of the delegate implementations participating in the module communication flow. In my last article I told you that I’m storing the delegate on the router, but later on I realized that delegation is mostly related to business logic, so I simply moved them to the presenter layer. Sorry about this. 🤷‍♂️

import VIPER
-
-protocol AModulePresenterDelegate {
-    func didDoSomething()
-}
-
-class AModule: Module {
-
-    func build(with delegate: AModulePresenterDelegate? = nil) -> UIViewController {
-        // insert classic viper stuff here
-
-        presenter.delegate = delegate
-
-        /* ... */
-
-        return view
-    }
-}
-
-class AModulePresenter: Presenter {
-
-    func someAction() {
-        self.delegate?.didDoSomething()
-        self.router?.dismiss()
-    }
-}
-
-// MARK: - other module
-
-class BModulePresenter: Presenter, AModulePresenterDelegate {
-
-    func didDoSomething() {
-        print("Hello from module A!")
-    }
-}
-

This way you can skip the entire router layer, plus all the business related logic will be implemented in the presenter layer, which should be the only way to go. 🤪

Entities are here to stay

Apart from the service layer sometimes it’s quite useful to have an entity wrapper with some additional metadata for the model objects. That’s why I also made an Entity protocol, and started to use it in my modules. For example a web view module that can open a link can have a WebViewEntity with a title and a content URL property. 😅

import VIPER
-
-struct AlertEntity: Entity {
-    let title: String
-    let message: String
-}
-

The sample alert module from above can use an AlertEntity with some properties that can define the title, message or the buttons. This way you don’t really have to think about where to put those objects, because those are the real VIPER entities.

IO protocols

This is a WIP (work-in-progress) idea that I’d like to try out, but the basic concept is somewhat like that I want to separate input and output protocols for VIPER module layers. Also this IO differentiation can be reflected on the service layers too (maybe the whole object “mess” from the service layer is going to be used as IO entities in the future), by mess I mean that there can be way too many objects in the Service/Objects directory, so this means that those could be also grouped by modules (aka. entities).

Anyway, I’m thinking of something like RequestEntity, ResponseEntity for service communication, and for the VIPER layer communication I could imagine two separate protocols, e.g. PresenterInput, PresenterOutput. We’ll see, but at first sight, it’s seems like quite an over-engineered thing (hahaha, says the VIPER advocate 😂).

VIPER vs [put your architecture name here]

No! Please don’t think that x is better than y. Architectures and design patterns are simple tools that can be utilized to make your life more easy. If you don’t like x, you should try y, but you should not blame x, just because that’s your personal opinion.

My current favorite architecture is VIPER, so what? Maybe in a year or two I’ll go crazy in love with reactive programming. Does it really matters? I don’t think so. I’ve learned and tried so many things during the past, that I can’t even remember. 🧠

I’m also constantly trying to figure out new things, as you can see this whole series of articles about VIPER is the result of my learning progress & experiences. If you really want to master something, you should practice, research and try a lot, and most importantly be proud of your successes and stay humble at the same time. 🙏

That’s it about the VIPER architecture for a while. I hope you enjoyed reading the whole series. If you have any questions, feel free to ask me through Twitter. 💭

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

How to build SwiftUI apps using VIPER?

-
-

In this tutorial I'll show you how to combine SwiftUI with the VIPER architecture in a real world iOS application example.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

How to write services for VIPER?

-
-

Not everything is a VIPER module. In this article I'll show you how do I separate the service layer from the modules, using Swift.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Mastering the VIPER architecture

-
-

Learn how to master the VIPER architectural design pattern, with some protocol oriented programming techniques using Swift.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

The ultimate VIPER architecture tutorial

-
-

Learn how to write scalable iOS code using the VIPER architecture with some MVVM and MVC tricks and coordinators in mind.

- -
- VIPER -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/memory-layout-in-swift/index.html b/docs/memory-layout-in-swift/index.html deleted file mode 100644 index 827324b..0000000 --- a/docs/memory-layout-in-swift/index.html +++ /dev/null @@ -1,408 +0,0 @@ - - - - - - - - - - - - Memory layout in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 8 min read - -
-

Memory layout in Swift

-
-

Start learning about how Swift manages, stores and references various data types and objects using a memory safe approach.

-
- -

Memory layout of value types in Swift

Memory is just a bunch of 1s and 0s, simply called bits (binary digits). If we group the flow of bits into groups of 8, we can call this new unit byte (eight bit is a byte, e.g. binary 10010110 is hex 96). We can also visualize these bytes in a hexadecimal form (e.g. 96 A6 6D 74 B2 4C 4A 15 etc). Now if we put these hexa representations into groups of 8, we’ll get a new unit called word.

This 64bit memory (a word represents 64bit) layout is the basic foundation of our modern x64 CPU architecture. Each word is associated with a virtual memory address which is also represented by a (usually 64bit) hexadecimal number. Before the x86-64 era the x32 ABI used 32bit long addresses, with a maximum memory limitation of 4GiB. Fortunately we use x64 nowadays. 💪

So how do we store our data types in this virtual memory address space? Well, long story short, we allocate just the right amount of space for each data type and write the hex representation of our values into the memory. It’s magic, provided by the operating system and it just works.

We could also start talking about memory segmentation, paging, and other low level stuff, but honestly speaking I really don’t know how those things work just yet. As I’m digging deeper and deeper into low level stuff like this I’m learning a lot about how computers work under the hood.

One important thing is that I already know and I want to share with you. It is all about memory access on various architectures. For example if a CPU’s bus width is 32bit that means the CPU can only read 32bit words from the memory under 1 read cycle. Now if we simply write every object to the memory without proper data separation that can cause some trouble.

┌──────────────────────────┬──────┬───────────────────────────┐
-│           ...            │  4b  │            ...            │
-├──────────────────────────┴───┬──┴───────────────────────────┤
-│            32 bytes          │            32 bytes          │
-└──────────────────────────────┴──────────────────────────────┘
-

As you can see if our memory data is misaligned, the first 32bit read cycle can only read the very first part of our 4bit data object. It’ll take 2 read cycles to get back our data from the given memory space. This is very inefficient and also dangerous, that’s why most of the systems won’t allow you unaligned access and the program will simply crash. So how does our memory layout looks like in Swift? Let’s take a quick look at our data types using the built-in MemoryLayout enum type.

print(MemoryLayout<Bool>.size)      // 1
-print(MemoryLayout<Bool>.stride)    // 1
-print(MemoryLayout<Bool>.alignment) // 1
-
-
-print(MemoryLayout<Int>.size)       // 8
-print(MemoryLayout<Int>.stride)     // 8
-print(MemoryLayout<Int>.alignment)  // 8
-

As you can see Swift stores a Bool value using 1 byte and (on 64bit systems) Int will be stored using 8 bytes. So, what the heck is the difference between size, stride and alignment?

The alignment will tell you how much memory is needed (multiple of the alignment value) to save things perfectly aligned on a memory buffer. Size is the number of bytes required to actually store that type. Stride will tell you about the distance between two elements on the buffer. Don’t worry if you don’t understand a word about these informal definitions, it’ll all make sense just in a moment.

struct Example {
-    let foo: Int  // 8
-    let bar: Bool // 1
-}
-
-print(MemoryLayout<Example>.size)      // 9
-print(MemoryLayout<Example>.stride)    // 16
-print(MemoryLayout<Example>.alignment) // 8
-

When constructing new data types, a struct in our case (classes work different), we can calculate the memory layout properties, based on the memory layout attributes of the participating variables.

┌─────────────────────────────────────┬─────────────────────────────────────┐
-│         16 bytes stride (8x2)       │         16 bytes stride (8x2)       │
-├──────────────────┬──────┬───────────┼──────────────────┬──────┬───────────┤
-│       8 bytes    │  1b  │  7 bytes  │      8 bytes     │  1b  │  7 bytes  │
-├──────────────────┴──────┼───────────┼──────────────────┴──────┼───────────┤
-│   9 bytes size (8+1)    │  padding  │   9 bytes size (8+1)    │  padding  │
-└─────────────────────────┴───────────┴─────────────────────────┴───────────┘
-

In Swift, simple types have the same alignment value size as their size. If you store standard Swift data types on a contiguous memory buffer there’s no padding needed, so every stride will be equal with the alignment for those types.

When working with compound types, such as the Example struct is, the memory alignment value for that type will be selected using the maximum value (8) of the properties alignments. Size will be the sum of the properties (8 + 1) and stride can be calculated by rounding up the size to the next the next multiple of the alignment. Is this true in every case? Well, not exactly…

struct Example {
-    let bar: Bool // 1
-    let foo: Int  // 8
-}
-
-print(MemoryLayout<Example>.size)      // 16
-print(MemoryLayout<Example>.stride)    // 16
-print(MemoryLayout<Example>.alignment) // 8
-

What the heck happened here? Why did the size increase? Size is tricky, because if the padding comes in between the stored variables, then it’ll increase the overall size of our type. You can’t start with 1 byte then put 8 more bytes next to it, because you’d misalign the integer type, so you need 1 byte, then 7 bytes of padding and finally the 8 bypes to store the integer value.

┌─────────────────────────────────────┬─────────────────────────────────────┐
-│        16 bytes stride (8x2)        │        16 bytes stride (8x2)        │
-├──────────────────┬───────────┬──────┼──────────────────┬───────────┬──────┤
-│     8 bytes      │  7 bytes  │  1b  │     8 bytes      │  7 bytes  │  1b  │
-└──────────────────┼───────────┼──────┴──────────────────┼───────────┼──────┘
-                   │  padding  │                         │  padding  │       
-┌──────────────────┴───────────┴──────┬──────────────────┴───────────┴──────┐
-│       16 bytes size (1+7+8)         │       16 bytes size (1+7+8)         │
-└─────────────────────────────────────┴─────────────────────────────────────┘
-

This is the main reason why the second example struct has a slightly increased size value. Feel free to create other types and practice by drawing the memory layout for them, you can always check if you were correct or not by printing the memory layout at runtime using Swift. 💡

This whole problem is real nicely explained on the [swift unboxed] blog. I would also like to recommend this article by Steven Curtis and there is one more great post about Unsafe Swift: A road to memory. These writings helped me a lot to understand memory layout in Swift. 🙏

Reference types and memory layout in Swift

I mentioned earlier that classes behave quite different that’s because they are reference types. Let me change the Example type to a class and see what happens with the memory layout.

class Example {
-    let bar: Bool = true // 1
-    let foo: Int = 0 // 8
-}
-
-print(MemoryLayout<Example>.size)      // 8
-print(MemoryLayout<Example>.stride)    // 8
-print(MemoryLayout<Example>.alignment) // 8
-

What, why? We were talking about memory reserved in the stack, until now. The stack memory is reserved for static memory allocation and there’s an other thing called heap for dynamic memory allocation. We could simply say, that value types (struct, Int, Bool, Float, etc.) live in the stack and reference types (classes) are allocated in the heap, which is not 100% true. Swift is smart enough to perform additional memory optimizations, but for the sake of “simplicity” let’s just stop here.

You might ask the question: why is there a stack and a heap? The answer is that they are quite different. The stack can be faster, because memory allocation happens using push / pop operations, but you can only add or remove items to / from it. The stack size is also limited, have you ever seen a stack overflow error? The heap allows random memory allocations and you have to make sure that you also deallocate what you’ve reserved. The other downside is that the allocation process has some overhead, but there is no size limitation, except the physical amount of RAM. The stack and the heap is quite different, but they are both extremely useful memory storage. 👍

Back to the topic, how did we get 8 for every value (size, stride, alignment) here? We can calculate the real size (in bytes) of an object on the heap by using the class_getInstanceSize method. A class always has a 16 bytes of metadata (just print the size of an empty class using the get instance size method) plus the calculated size for the instance variables.

class Empty {}
-print(class_getInstanceSize(Empty.self)) // 16
-
-class Example {
-    let bar: Bool = true // 1 + 7 padding
-    let foo: Int = 0     // 8
-}
-print(class_getInstanceSize(Example.self)) // 32 (16 + 16)
-

The memory layout of a class is always 8 byte, but the actual size that it’ll take from the heap depends on the instance variable types. The other 16 byte comes from the “is a” pointer and the reference count. If you know about the Objective-C runtime a bit then this can sound familiar, but if not, then don’t worry too much about ISA pointers for now. We’ll talk about them next time. 😅

Swift uses Automatic Reference Counting (ARC) to track and manage your app’s memory usage. In most of the cases you don’t have to worry about manual memory management, thanks to ARC. You just have to make sure that you don’t create strong reference cycles between class instances. Fortunately those cases can be resolved easily with weak or unowned references. 🔄

class Author {
-    let name: String
-
-    /// weak reference is required to break the cycle.
-    weak var post: Post?
-
-    init(name: String) { self.name = name }
-    deinit { print("Author deinit") }
-}
-
-class Post {
-    let title: String
-    
-    /// this can be a strong reference
-    var author: Author?
-
-    init(title: String) { self.title = title }
-    deinit { print("Post deinit") }
-}
-
-
-var author: Author? = Author(name: "John Doe")
-var post: Post? = Post(title: "Lorem ipsum dolor sit amet")
-
-post?.author = author
-author?.post = post
-
-post = nil
-author = nil
-
-/// Post deinit
-/// Author deinit
-

As you can see in the example above if we don’t use a weak reference then objects will reference each other strongly, this creates a reference cycle and they won’t be deallocated (deinit won’t be called at all) even if you set individual pointers to nil. This is a very basic example, but the real question is when do I have to use weak, unowned or strong? 🤔

I don’t like to say “it depends”, so instead, I’d like to point you into the right direction. If you take a closer look at the official documentation about Closures, you’ll see what captures values:

  • Global functions are closures that have a name and don’t capture any values.
  • Nested functions are closures that have a name and can capture values from their enclosing function.
  • Closure expressions are unnamed closures written in a lightweight syntax that can capture values from their surrounding context.

As you can see global (static functions) don’t increment reference counters. Nested functions on the other hand will capture values, same thing applies to closure expressions and unnamed closures, but it’s a bit more complicated. I’d like to recommend the following two articles to understand more about closures and capturing values:

Long story short, retain cycles suck, but in most of the cases you can avoid them just by using just the right keyword. Under the hood, ARC does a great job, except a few edge cases when you have to break the cycle. Swift is a memory-safe programming language by design. The language ensures that every object will be initialized before you could use them, and objects living in the memory that aren’t referenced anymore will be deallocated automatically. Array indices are also checked for out-of-bounds errors. This gives us an extra layer of safety, except if you write unsafe Swift code… 🤓

Anyway, in a nutshell, this is how the memory layout looks like in the Swift programming language.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/modules-and-hooks-in-swift/index.html b/docs/modules-and-hooks-in-swift/index.html deleted file mode 100644 index 4329009..0000000 --- a/docs/modules-and-hooks-in-swift/index.html +++ /dev/null @@ -1,426 +0,0 @@ - - - - - - - - - - - - Modules and hooks in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 4 min read - -
-

Modules and hooks in Swift

-
-

Learn how to extend your application with new functionalities using a loosely coupled modular plugin system written in Swift.

-
- -

How do modules (plugins) work?

Wouldn’t be cool if you could create objects that could work together without knowing about each other? Imagine that you are building a dynamic form. Based on some internal conditions, the fields are going to be composed using the data coming from the enabled modules.

For example you have module A, B, C, where A is providing you Field 1, 2, 3, the B module is taking care of Field 4, 5 and C is the provider of Field 6. Now if you turn off B, you should only be able to see field 1, 2, 3 and 6. If everything is turned on you should see all the fields from 1 to 6.

We can apply this exact same pattern to many things. Just think about one of the biggest plugin ecosystem. Wordpress is using hooks to extend the core functionalities through them. It’s all based on the concept I just mentioned above. This is part of the event-driven architecture design pattern. Now the question is how do we implement something similar using Swift? 🤔

A hook system implementation

First we start with a protocol with a point of invocation. This method will be called by the module manager to invoke the proper hook function by name. We’re going to pass around a dictionary of parameters, so our hooks can have arguments. We’re using the Any type here as a value, so you can send anything as a parameter under a given key.

protocol Module {
-    func invoke(name: String, params: [String: Any]) -> Any?
-}
-
-extension Module {
-    func invoke(name: String, params: [String: Any]) -> Any? { nil }
-}
-

Now let’s implement our modules using a simplified version based on the form example. 🤓

class A: Module {
-
-    func invoke(name: String, params: [String: Any]) -> Any? {
-        switch name {
-        case "example_form":
-            return self.exampleFormHook()
-        default:
-            return nil
-        }
-    }
-
-    private func exampleFormHook() -> [String] {
-        ["Field 1", "Field 2", "Field 3"]
-    }
-}
-
-class B: Module {
-    func invoke(name: String, params: [String: Any]) -> Any? {
-        switch name {
-        case "example_form":
-            return self.exampleFormHook()
-        default:
-            return nil
-        }
-    }
-
-    private func exampleFormHook() -> [String] {
-        ["Field 4", "Field 5"]
-    }
-}
-
-class C: Module {
-    func invoke(name: String, params: [String: Any]) -> Any? {
-        switch name {
-        case "example_form":
-            return self.exampleFormHook()
-        default:
-            return nil
-        }
-    }
-
-    private func exampleFormHook() -> [String] {
-        ["Field 6"]
-    }
-}
-

Next we need a module manager that can be initialized with an array of modules. This manager will be responsible for calling the right invocation method on every single module and it’ll handle the returned response in a type-safe manner. We’re going to implement two invoke method versions right away. One for merging the result and the other to return the first result of a hook.

You can try to implement a version that can merge Bool values using the && operator

Here is our module manager implementation with the two generic methods:

struct ModuleManager {
-
-    let  modules: [Module]
-    
-    func invokeAllHooks<T>(_ name: String, type: T.Type, params: [String: Any] = [:]) -> [T] {
-        let result = self.modules.map { module in
-            module.invoke(name: name, params: params)
-        }
-        return result.compactMap { $0 as? [T] }.flatMap { $0 }
-    }
-
-    func invokeHook<T>(_ name: String, type: T.Type, params: [String: Any] = [:]) -> T? {
-        for module in self.modules {
-            let result = module.invoke(name: name, params: params)
-            if result != nil {
-                return result as? T
-            }
-        }
-        return nil
-    }
-}
-

You can use the the invokeAllHooks method to merge together an array of a generic type. This is the one that we can use to gather all he form fields using the underlying hook methods.

let manager1 = ModuleManager(modules: [A(), B(), C()])
-let form1 = manager1.invokeAllHooks("example_form", type: String.self)
-print(form1) // 1, 2, 3, 4, 5, 6
-
-let manager2 = ModuleManager(modules: [A(), C()])
-let form2 = manager2.invokeAllHooks("example_form", type: String.self)
-print(form2) // 1, 2, 3, 6
-

Using the invokeHook method you can achieve a similar behavior like the chain of responsibility design pattern. The responder chain works very similar similar, Apple uses responders on almost every platform to handle UI events. Let me show you how it works by updating module B. 🐝

class B: Module {
-    func invoke(name: String, params: [String: Any]) -> Any? {
-        switch name {
-        case "example_form":
-            return self.exampleFormHook()
-        case "example_responder":
-            return self.exampleResponderHook()
-        default:
-            return nil
-        }
-    }
-
-    private func exampleFormHook() -> [String] {
-        ["Field 4", "Field 5"]
-    }
-    
-    private func exampleResponderHook() -> String {
-        "Hello, this is module B."
-    }
-}
-

If we trigger the new example_responder hook with the invokeHook method on both managers we’ll see that the outcome is quite different.

if let value = manager1.invokeHook("example_responder", type: String.self) {
-    print(value) // Hello, this is module B.
-}
-
-if let value = manager2.invokeHook("example_responder", type: String.self) {
-    print(value) // this won't be called at all...
-}
-

In the first case, since we have an implementation in one of our modules for this hook, the return value will be present, so we can print it. In the second case there is no module to handle the event, so the block inside the condition won’t be executed. Told ya’, it’s like a responder chain. 😜

Conclusion

Using modules or plugins is a powerful approach to decouple some parts of the code. I really love hook functions since they can provide extension points for almost anything in the application.

Mix this with a dynamic module loader and you have a fully-extensible next-gen backend solution on top of Vapor. You can have a compiled core system independently from the modules and later on you can upgrade just some parts of the entire stuff without touching the others. Whops… I just made that happen and I think (just like Swift) it totally rulez. 🤘🏻

I’m working hard both on my upcoming Practical server side Swift book and the open-source blog engine that’s powering this site for quite a while now. I used this modular architecture a lot during the creation of my engine. Can’t wait to release everything and show it to you. 😉

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Event-driven generic hooks for Swift

-
-

In this article I am going to show you how to implement a basic event processing system for your modular Swift application.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Iterator design pattern in Swift

-
-

Learn the iterator design pattern by using some custom sequences, conforming to the IteratorProtocol from the Swift standard library.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Lazy initialization in Swift

-
-

Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Lenses and prisms in Swift

-
-

Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/networking-examples-for-appleos/index.html b/docs/networking-examples-for-appleos/index.html deleted file mode 100644 index 2977aaf..0000000 --- a/docs/networking-examples-for-appleos/index.html +++ /dev/null @@ -1,355 +0,0 @@ - - - - - - - - - - - - Networking examples for appleOS - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 3 min read - -
-

Networking examples for appleOS

-
-

Learn how to use Bonjour, with UDP/TCP sockets, streams and how to communicate through CoreBluetooth or the watch APIs.

-
- -

This article was originally written back in the end of 2015. The source code is deprecated and not compatible with current Swift versions, so I removed it.

If you want to learn how to make a network connection between your devices using Bonjour discovery service you are on the right place. In this post I am going to show you the basics, so for example you will be able to make a remote controller from your watch or iOS device in order to send data directly to any tvOS or macOS machines.

Multi-platform development

If you want to create an app that supports multiple platforms, you might want to target macOS, iOS, watchOS, tvOS and soon Linux as well. The code snippet below is going to help you detecting the current platform that you are working with.

#if os(iOS)
-    let platform = "iOS"
-#elseif os(macOS)
-    let platform = "macOS"
-#elseif os(watchOS)
-    let platform = "watchOS"
-#elseif os(tvOS)
-    let platform = "tvOS"
-#elseif os(Linux)
-    let platform = "linux"
-#else
-    let platform = "unknown"
-#endif
-
-print(platform)
-

Network connection 101

Bonjour discovery service

Bonjour, also known as zero-configuration networking, enables automatic discovery of devices and services on a local network using industry standard IP protocols.

So basically with Bonjour you can find network devices on your local network. This comes very handy if you are trying to figure out the list of devices that are connected to your LAN. Using NetService class will help you to detect all the devices with the available services that they support. The whole Bonjour API is relatively small and well-written. From the aspect of server side you just have to create the NetService object, and listen to the incoming connections through the NetServiceDelegate.

You have to be on the same WiFi network with all devices / simulators.

TCP connection

TCP provides reliable, ordered, and error-checked delivery of a stream of octets (bytes) between applications running on hosts communicating by an IP network.

With the help of TCP you can build up a reliable network connection. There is a Stream class in Foundation to help you sending data back and forth between devices. If you have a working connection form NetServiceDelegate, just listen to the stream events to handle incoming data through the StreamDelegate. I don’t want to go into the details, just download the example code and check it out for yourself.

UDP connection

With UDP, computer applications can send messages, in this case referred to as datagrams, to other hosts on an Internet Protocol (IP) network.

If you look at the article about UDP you’ll clearly see that the main difference from TCP is that this protocol will not guarantee you safety of the data delivery. Data may arrives unordered or duplicated, it’s your task to handle these scenarios, but the UDP is fast. If you want to build a file transfer app you should definitely go with TCP, but for example controlling a real-time action game UDP is just as good enough.

CocoaAsyncSocket

This library is the absolute winner for me and probably it is the best option for everyone who wants to set up a network connection really quickly, because it requires way less code than implementing delegates. Of course you’ll still need the Bonjour layer above the whole thing, but that’s just fine to deal with.

If you are using CocoaAsyncSocket you will see that the API is straightforward, only after 5 minutes I could relatively easily figure it out what’s going on and I was able to send messages through the network. It supports all the Apple platforms, you can seamlessly integrate it using Carthage or CocoaPods.

CoreBluetooth APIs

I was not really familiar with the CoreBluetooth framework API’s, that’s the reason why I basically just followed and ported this tutsplus.com code example to Swift 4. Honestly I felt that the API is somehow over-complicated with all those messy delegate functions. If I have to chose between CoreBluetooth or CocoaAsyncSocket, I’d go with the last one. So yes, obviously I am not a Bluetooth expert, but this little project was a good first impression about how things are working inside the CB framework.

WatchConnectivity framework

If you want to communicate between iOS and watchOS you’ll probably use the WatchConnectivity framework, especially the WKSession class. It’s really not so complicated, with just a few lines of code you can send messages form the watch to the iPhone. You can read this tutorial, but if you download my final sources for this article, you’ll find almost the same thing inside the package.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

ClockKit complications cheatsheet

-
-

ClockKit families and templates, there are so many of them. It's a little bit time consuming if you are looking for the right one.

- -
- watchOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

How to build macOS apps using only the Swift Package Manager?

-
-

In this article we're going to create a macOS application without ever touching an Xcode project file, but only working with SPM.

- -
- macOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

How to launch a macOS app at login?

-
-

In this tutorial I'll show you how to launch a completely sandboxed macOS application on system startup written in Swift.

- -
- Tooling - macOS -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/page/1/index.html b/docs/page/1/index.html deleted file mode 100644 index 6bd317e..0000000 --- a/docs/page/1/index.html +++ /dev/null @@ -1,683 +0,0 @@ - - - - - - - - - - - - Posts - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -

Posts

Browse all posts.

- -
- -
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Hummingbird routing and requests

-
-

Beginner's guide to learn all about routing and request handling using the Hummingbird server-side Swift framework.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to server-side Swift using the Hummingbird framework

-
-

Learn about Swift on the server by creating a simple application using the brand new HTTP server library called: Hummingbird.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Running and testing async Vapor commands

-
-

In this article I'll show you how to build asynchronous Vapor commands and how to test them using ConsoleKit.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Running tasks in parallel

-
-

Learn how to run tasks in parallel using the old-school tools and frameworks plus the new structured concurrency API in Swift.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

The abstract Vapor service factory design pattern

-
-

In this tutorial I'm going to show you how you can create an abstract driver-based component for the Vapor framework.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

SwiftNIO tutorial - The echo server

-
-

This is a beginner's guide to learn the basics of the SwiftNIO network app framework by building a basic TCP echo server.

- -
- Server -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Easy multipart file upload for Swift

-
-

Let me show you how to create HTTP requests using multipart (form data) body without a third party library. Simple solution.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Utilizing Makefiles for Swift projects

-
-

In this tutorial I'll show you how to use Makefiles for server-side Swift projects to help running utility tasks in a more simple way.

- -
- Tooling -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Lenses and prisms in Swift

-
-

Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Introduction to SPM artifact bundles

-
-

In this tutorial I'm going to show you how to use the new binary target related artifact bundle using the Swift package manager.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginner's guide to Swift package manager command plugins

-
-

Learn how to create command plugins for the Swift Package Manager to execute custom actions using SPM and other tools.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Swift visitor design pattern

-
-

The visitor design pattern in Swift allows us to add new features to an existing group of objects without altering the original code.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

Working with diffable data sources and table views using UIKit

-
-

In this tutorial we're going to build a screen to allow single and multiple selections using diffable data source and a table view.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Beginner's guide to Swift arrays

-
-

Learn how to manipulate arrays in Swift like a pro. This tutorial covers lots of useful array related methods, tips and tricks.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

The repository pattern for Vapor 4

-
-

In this article I'm going to talk about the repository design pattern and give you a few Fluent ORM tips for your Vapor 4 app.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

How to use a Swift library in C

-
-

In this tutorial, we're going to build a C app by importing a Swift library and talk a bit about the Swift / C Interoperability in general.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

UIKit - loadView vs viewDidLoad

-
-

When to use these methods? Common questions and answers about the iOS view hierarchy including memory management.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 12 min read -
- -

Progressive Web Apps on iOS

-
-

This is a beginner's guide about creating PWAs for iOS including custom icons, splash screens, safe area and dark mode support.

- -
- iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

How to create a Swift package collection?

-
-

In this tutorial I'm going to show you how to create your own package collection from your favorite Swift libraries.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

How to write HTML in Swift?

-
-

This tutorial is all about rendering HTML docs using a brand new DSL library called SwiftHtml and the Vapor web framework.

- -
- Server - Vapor -
-
-
- - - -
- -
- - - -
- - - - diff --git a/docs/page/2/index.html b/docs/page/2/index.html deleted file mode 100644 index b121117..0000000 --- a/docs/page/2/index.html +++ /dev/null @@ -1,687 +0,0 @@ - - - - - - - - - - - - Posts - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -

Posts

Browse all posts.

- -
- -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

The future of server side Swift

-
-

What's going to happen with Swift on the Server in 2022? Distributed actors, Vapor 5, some predictions and wishes.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Building a global storage for Vapor

-
-

This tutorial is about a shared global storage that you can implement using a common design pattern in Vapor 4.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Building tree data structures in Swift

-
-

This tutorial is about showing the pros and cons of various Swift tree data structures using structs, enums and classes.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Practical guide to binary operations using the UInt8 type in Swift

-
-

Introduction to the basics of signed number representation and some practical binary operation examples in Swift using UInt8.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

How to build better command line apps and tools using Swift?

-
-

These tips will help you to create amazing CLI tools, utility apps, server side projects or terminal scripts using the Swift language.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Swift structured concurrency tutorial

-
-

Learn how to work with the Task object to perform asynchronous operations in a safe way using the new concurrency APIs in Swift.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Swift actors tutorial - a beginner's guide to thread safe concurrency

-
-

Learn how to use the brand new actor model to protect your application from unwanted data-races and memory issues.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

Beginner's guide to the async/await concurrency API in Vapor & Fluent

-
-

Learn how to convert your existing EventLoopFuture based Vapor server app using the new async/await Swift feature.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Introduction to async/await in Swift

-
-

Beginners guide to the new async/await API's in Swift 5.5. Interacting with sync code, structured concurrency, async let.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

Dynamic libraries and code replacements in Swift

-
-

How to load a dynamic library and use native method swizzling in Swift? This article is all about the magic behind SwiftUI previews.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

Declarative unit tests for Vapor

-
-

Learn how to test your server side Swift backend app in a declarative style using a lightweight library called Spec.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

How to design type safe RESTful APIs using Swift & Vapor?

-
-

Learn to make proper data transfer objects for CRUD operations and integrate them both into the client and server side API layer.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

Unsafe memory pointers in Swift

-
-

Learn how to use raw pointer references, interact with unsafe pointers and manually manage memory addresses in Swift.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

Memory layout in Swift

-
-

Start learning about how Swift manages, stores and references various data types and objects using a memory safe approach.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

How to use C libraries in Swift?

-
-

Learn how to use system libraries and call C code from Swift. Interoperability between the Swift language and C for beginners.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

Building static and dynamic Swift libraries using the Swift compiler

-
-

This tutorial is all about emitting various Swift binaries without the Swift package manager, but only using the Swift compiler.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

The Swift compiler for beginners

-
-

Learn how to build executable files using the swiftc command, meet the build pipeline, compilers and linkers under the hood.

- -
- Swift - Tooling -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Custom working directory in Xcode

-
-

Learn how to set a custom working directory in Xcode to solve one of the most common beginner issue when using Vapor.

- -
- Tooling - Xcode -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

File upload API server in Vapor 4

-
-

Learn how to build a very simple file upload API server using Vapor 4 and URLSession upload task on the client side.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

File upload using Vapor 4

-
-

Learn how to implement a basic HTML file upload form using the Leaf template engine and Vapor, all written in Swift of course.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Event-driven generic hooks for Swift

-
-

In this article I am going to show you how to implement a basic event processing system for your modular Swift application.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Getting started with SwiftIO

-
-

SwiftIO is an electronic circuit board that runs Swift on the bare metal. It can control sensors, displays, lights, motors and more.

- -
- Swift -
-
-
- - - -
- -
- - - -
- - - - diff --git a/docs/page/3/index.html b/docs/page/3/index.html deleted file mode 100644 index 9826f02..0000000 --- a/docs/page/3/index.html +++ /dev/null @@ -1,691 +0,0 @@ - - - - - - - - - - - - Posts - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -

Posts

Browse all posts.

- -
- -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

How to build macOS apps using only the Swift Package Manager?

-
-

In this article we're going to create a macOS application without ever touching an Xcode project file, but only working with SPM.

- -
- macOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Logging for beginners in Swift

-
-

Learn how to print variables to the debug console using different functions such as print, dump, NSLog and the unified os.log API.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

How to define strings, use escaping sequences and interpolations?

-
-

As a beginner it can be hard to understand String interpolation and escaping sequences, in this tutorial I'll teach you the basics.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

Swift on the Server in 2020

-
-

Why choose Swift as a backend language in 2020? What are the available frameworks to build your server? Let me guide you.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

How to store keys in env files?

-
-

In this tutorial I'll show you how to save and load secret keys as base64 encoded strings using dotenv files in Vapor 4.

- -
- Server - Tooling - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Table joins in Fluent 4

-
-

In this quick tutorial I'm going to show you how to join and query database models using the Fluent ORM framework in Vapor 4.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 13 min read -
- -

Websockets for beginners using Vapor 4 and Vanilla JavaScript

-
-

Learn how to create a websocket server using Swift & Vapor. Multiplayer game development using JavaScript in the browser.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Building and loading dynamic libraries at runtime in Swift

-
-

Learn how to create a plugin system using dynamic libraries and the power of Swift, aka. modular frameworks on the server-side.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

What's new in Swift 5.3?

-
-

Swift 5.3 is going to be an exciting new release. This post is a showcase of the latest Swift programming language features.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 11 min read -
- -

Sign in with Apple using Vapor 4

-
-

A complete tutorial for beginners about how to implement the Sign in with Apple authentication service for your website.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

The Swift package manifest file

-
-

This article is a complete Swift Package Manager cheatsheet for the package manifest file, using the latest Swift 5.2 tools version.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

Server side Swift projects inside Docker using Vapor 4

-
-

Learn how to setup Vapor 4 projects inside a Docker container. Are you completely new to Docker? This article is just for you.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Modules and hooks in Swift

-
-

Learn how to extend your application with new functionalities using a loosely coupled modular plugin system written in Swift.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 16 min read -
- -

All about authentication in Vapor 4

-
-

Learn how to implement a user login mechanism with various auth methods using sessions, JWTs, written in Swift only.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

The anatomy of Vapor commands

-
-

Learn how to build and run your existing Vapor apps using various command line arguments, flags and environments.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

How to use middlewares in Vapor 4?

-
-

Learn how to create middlewares for a Vapor based server side Swift application to handle common routing functionalities.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

How to write Swift scripts using the new Command API in Vapor 4?

-
-

Shell scripts are essentials on the server side. Learn how to build Swift scripts for your backend apps using property wrappers.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 15 min read -
- -

Get started with the Fluent ORM framework in Vapor 4

-
-

Learn how to use the Fluent ORM framework. Migrations, schemas, relations powered by PostgreSQL, written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

How to set up pgSQL for Fluent 4?

-
-

This is a tutorial for beginners about using PostgreSQL. I'll show you how to automatically backup and restore the database.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

How to create your first website using Vapor 4 and Leaf?

-
-

Let's build a web page in Swift. Learn how to use the brand new template engine of the most popular server side Swift framework.

- -
-
-
-
- - -
- - Tibor Bödecs - -
-
- - · 12 min read -
- -

How to download files with URLSession using Combine Publishers and Subscribers?

-
-

Learn how to load a remote image into an UIImageView asynchronously using URLSessionDownloadTask and the Combine framework in Swift.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 18 min read -
- -

Beginner's guide to Server side Swift using Vapor 4

-
-

Learn how to build and host your very first backend application using Vapor 4 and the brief history of server side Swift.

- -
- Server - Vapor -
-
-
- - - -
- -
- - - -
- - - - diff --git a/docs/page/4/index.html b/docs/page/4/index.html deleted file mode 100644 index 01d2446..0000000 --- a/docs/page/4/index.html +++ /dev/null @@ -1,683 +0,0 @@ - - - - - - - - - - - - Posts - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -

Posts

Browse all posts.

- -
- -
-
- - -
- - Tibor Bödecs - -
-
- - · 13 min read -
- -

What are the best practices to learn iOS / Swift in 2020?

-
-

Are you learning iOS development? Looking for Swift best practices? This is the right place to start your journey as a mobile application developer.

- -
- iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

How to write services for VIPER?

-
-

Not everything is a VIPER module. In this article I'll show you how do I separate the service layer from the modules, using Swift.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

How to build SwiftUI apps using VIPER?

-
-

In this tutorial I'll show you how to combine SwiftUI with the VIPER architecture in a real world iOS application example.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Picking and playing videos in Swift

-
-

Learn how to record or select a video file using a video picker controller and the AVPlayer class, written entirely in Swift 5.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

What's new in Vapor 4?

-
-

Vapor is the most popular server side Swift web application framework. This time we'll cover what's new in Vapor 4.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Swift init patterns

-
-

The ultimate guide how to init your Swift data types, with the help of designated, convenience, failable intitializers and more.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

URLSession and the Combine framework

-
-

Learn how to make HTTP requests and parse the response using the brand new Combine framework with foundation networking.

- -
- iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

Promises in Swift for beginners

-
-

Everything you ever wanted to know about futures and promises. The beginner's guide about asynchronous programming in Swift.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Uniquely identifying views

-
-

Learn how to use string based UIView identifiers instead of tags. If you are tired of tagging views, check out these alternative solutions.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Mastering the VIPER architecture

-
-

Learn how to master the VIPER architectural design pattern, with some protocol oriented programming techniques using Swift.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

VIPER best practices for iOS developers

-
-

In this tutorial I'm going to show you a complete guide about how to build a VIPER based iOS application, written entirely in Swift.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Styling by subclassing

-
-

Learn how to design and build reusable user interface elements by using custom view subclasses from the UIKit framework in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Picking images with UIImagePickerController in Swift 5

-
-

Learn how to get an image from the photo library or directly from the camera by using the UIImagePickerController class in Swift 5.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

How to use the result type to handle errors in Swift 5?

-
-

From this tutorial you can learn how to utilize the do-try-catch syntax with the brand new result type to handle errors in Swift.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Swift Package Manager and the Swift toolchain

-
-

Learn everything about the SPM architecture. I'll also teach you how to integrate your binary executable into the Swift toolchain.

- -
-
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Lazy initialization in Swift

-
-

Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift object pool design pattern

-
-

In this quick tutorial I'll explain & show you how to implement the object pool design pattern using the Swift programming language.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

UITableView tutorial in Swift

-
-

This guide is made for beginners to learn the foundations of the UITableView class programmatically with auto layout in Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Swift 5 and ABI stability

-
-

Apple's Swift 5 language version will be a huge milestone for the developer community, let's see what are the possible benefits of it.

- -
- Swift - Tooling -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - - -
- -
- - - -
- - - - diff --git a/docs/page/5/index.html b/docs/page/5/index.html deleted file mode 100644 index 9fa156b..0000000 --- a/docs/page/5/index.html +++ /dev/null @@ -1,682 +0,0 @@ - - - - - - - - - - - - Posts - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -

Posts

Browse all posts.

- -
- -
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift facade design pattern

-
-

The facade design pattern is a simplified interface over a complex subsystem. Let me show you a real quick example using Swift.

- -
-
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Iterator design pattern in Swift

-
-

Learn the iterator design pattern by using some custom sequences, conforming to the IteratorProtocol from the Swift standard library.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Generating random numbers in Swift

-
-

Learn everything what you'll ever need to generate random values in Swift using the latest methods and covering some old techniques.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Swift adapter design pattern

-
-

Turn an incompatible object into a target interface or class by using a real world example and the adapter design pattern in Swift.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Swift dependency injection design pattern

-
-

Want to learn the Dependency Injection pattern using Swift? This tutorial will show you how to write loosely coupled code using DI.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 11 min read -
- -

Ultimate Grand Central Dispatch tutorial in Swift

-
-

Learn the principles of multi-threading with the GCD framework in Swift. Queues, tasks, groups everything you'll ever need I promise.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift delegate design pattern

-
-

The delegate design pattern is a relatively easy way to communicate between two objects through a common interface, protocol in Swift.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

UICollectionView data source and delegates programmatically

-
-

In this quick UIKit tutorial I'll show you how to create a simple UICollectionView without Interface Builder, but only using Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Mastering iOS auto layout anchors programmatically from Swift

-
-

Looking for best practices of using layout anchors? Let's learn how to use the iOS autolayout system in the proper way using Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift command design pattern

-
-

This time I'm going to show you a behavioral pattern. Here is a little example of the command design patten written in Swift.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift prototype design pattern

-
-

The prototype design pattern is used to create clones of a base object, so let's see some practical examples written in Swift.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Comparing factory design patterns

-
-

Learn what's the difference between static factory, simple factory, factory method and abstract factory using the Swift language.

- -
-
-
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift abstract factory design pattern

-
-

Let's combine factory method with simple factory voilá: here is the abstract factory design pattern written in Swift language!

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift factory method design pattern

-
-

The factory method design pattern is a dedicated non-static method for hiding the creation logic of an object. Let's make it in Swift!

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift simple factory design pattern

-
-

This time let's talk about the simple factory design pattern to encapsulate object creation in a really simple way using Swift.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Swift static factory design pattern

-
-

In this article I'll teach you about the static factory design pattern and show some use cases using the Swift programming language.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Swift builder design pattern

-
-

Learn how to implement the builder pattern in Swift to hide the complexity of creating objects with lots of individual properties.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Swift singleton design pattern

-
-

Singleton is the most criticized design pattern of all time. Learn the proper way of using Swift singleton classes inside iOS projects.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

How to use iCloud drive documents?

-
-

Learn how to sync files and data through a shared iCloud drive folder using the latest version of Swift programming language.

- -
- iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

UIColor best practices in Swift

-
-

Learn what are color models, how to convert hex values to UIColor and back, generate random colors, where to find beautiful palettes.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

iOS custom transition tutorial in Swift

-
-

In this tutorial, you'll learn how to replace the push, pop and modal animations with custom transitions & percent driven interactions.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 12 min read -
- -

Ultimate UICollectionView guide with iOS examples written in Swift

-
-

Learn how to use UICollectionView, with highly reusable UIKit components and some MVVM pattern without the going nuts with index path calculations.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

The ultimate VIPER architecture tutorial

-
-

Learn how to write scalable iOS code using the VIPER architecture with some MVVM and MVC tricks and coordinators in mind.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Networking examples for appleOS

-
-

Learn how to use Bonjour, with UDP/TCP sockets, streams and how to communicate through CoreBluetooth or the watch APIs.

- - -
-
- - - -
- -
- - - -
- - - - diff --git a/docs/page/6/index.html b/docs/page/6/index.html deleted file mode 100644 index 00eab70..0000000 --- a/docs/page/6/index.html +++ /dev/null @@ -1,602 +0,0 @@ - - - - - - - - - - - - Posts - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -

Posts

Browse all posts.

- -
- -
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

How to parse JSON in Swift using Codable protocol?

-
-

In this Swift tutorial, I'd like to give you an example about getting and parsing JSON data using URLSession and Codable protocol.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 11 min read -
- -

Deep dive into Swift frameworks

-
-

Learn everything about Swift modules, libraries, packages, closed source frameworks, command line tools and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

UICollectionView cells with circular images plus rotation support

-
-

Learn how to make rounded corners for UIImageView items wrapped inside collection view cells, with rotation support.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Self sizing cells with rotation support

-
-

How to make self sizing cells in Swift both for table & collection views supporting orientation changes and dynamic font types.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

How to call C code from Swift

-
-

Interacting with C libraries from the Swift language is really amazing, from this post can learn the most of C interoperability.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Swift Package Manager tutorial

-
-

Learn how to use the Swift Package Manager to handle external dependencies, create your library or app on macOS and Linux.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

iOS Auto Layout tutorial programmatically

-
-

In this great iOS Auto Layout tutorial I'll teach you how to support rotation, use constraints, work with layers, animate corner radius.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

How to launch a macOS app at login?

-
-

In this tutorial I'll show you how to launch a completely sandboxed macOS application on system startup written in Swift.

- -
- Tooling - macOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

How to make a Swift framework?

-
-

Creating a Swift framework shouldn't be hard. This tutorial will help you making a universal framework for complex projects.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift enum all values

-
-

In this quick tutorial I'll show you how to get all the possible values for a Swift enum type with a generic solution written in Swift.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 13 min read -
- -

Asynchronous validation for Vapor

-
-

Learn how to validate input data using an async technique. Unified request validation API for your server side Swift app.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Everything about public and private Swift attributes

-
-

Have you ever heard about Swift language attributes? In this article I'm trying to gather all the @ annotations and their meanings.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

How to create reusable views for modern collection views?

-
-

A quick intro to modern collection views using compositional layout, diffable data source and reusable view components.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 12 min read -
- -

Result builders in Swift

-
-

If you want to make a result builder in Swift, this article will help you to deal with the most common cases when creating a DSL.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

The ultimate Combine framework tutorial in Swift

-
-

Get started with the brand new declarative Combine framework in practice using Swift. I'll teach you all the goodies from zero to hero.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Top 20 iOS libraries written in Swift

-
-

I gathered the best open source Swift frameworks on github that will help you to speed up mobile application development in 2019.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

UIKit init patterns

-
-

Learn about the initialization process of the two well known classes in UIKit. Say hello to UIViewcontroller, and UIView init patterns.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Awesome native Xcode extensions

-
-

This is the biggest and the best collection of the currently available natively created source editor extensions for Xcode.

- -
- Xcode -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Conventions for Xcode

-
-

Learn how to organize your codebase. If you are struggling with Xcode project structure, files, naming conventions, read this.

- -
- Tooling - Xcode -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

ClockKit complications cheatsheet

-
-

ClockKit families and templates, there are so many of them. It's a little bit time consuming if you are looking for the right one.

- -
- watchOS -
-
-
- - - -
- -
- - - -
- - - - diff --git a/docs/picking-and-playing-videos-in-swift/index.html b/docs/picking-and-playing-videos-in-swift/index.html deleted file mode 100644 index 3cdba50..0000000 --- a/docs/picking-and-playing-videos-in-swift/index.html +++ /dev/null @@ -1,578 +0,0 @@ - - - - - - - - - - - - Picking and playing videos in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 4 min read - -
-

Picking and playing videos in Swift

-
-

Learn how to record or select a video file using a video picker controller and the AVPlayer class, written entirely in Swift 5.

-
- -

Let’s pick some videos!

If you remember my previous tutorial about image picking in iOS, then you know that I already made quite a reusable picker class built on top of UIKit. If you don’t know how the UIImagePickerController class works, please read that tutorial first because it gives you a great overview about the basics.

First of all you’ll need to add some keys into your Info.plist file, because you’d like to access some personal data. You know: privacy is very important. 🤫

<key>NSCameraUsageDescription</key>
-<string>This app wants to take pictures & videos.</string>
-<key>NSPhotoLibraryUsageDescription</key>
-<string>This app wants to use your picture & video library.</string>
-<key>NSMicrophoneUsageDescription</key>
-<string>This app wants to record sound.</string>
-<key>NSPhotoLibraryAddUsageDescription</key>
-<string>This app wants to save pictures & videos to your library.</string>
-

Since we’re not going to capture silent videos we also have to add the Privacy - Microphone Usage Description field. Ready, set, action! 🎬

I’m not going to lie to you, but I was a little bit lazy this time, so our VideoPicker class will be 90% the same as our ImagePicker class was. You can make an abstract class, whatever, I’ll show you the final code, then we can talk about the differences. 😅

import UIKit
-
-public protocol VideoPickerDelegate: class {
-    func didSelect(url: URL?)
-}
-
-open class VideoPicker: NSObject {
-
-    private let pickerController: UIImagePickerController
-    private weak var presentationController: UIViewController?
-    private weak var delegate: VideoPickerDelegate?
-
-    public init(presentationController: UIViewController, delegate: VideoPickerDelegate) {
-        self.pickerController = UIImagePickerController()
-
-        super.init()
-
-        self.presentationController = presentationController
-        self.delegate = delegate
-
-        self.pickerController.delegate = self
-        self.pickerController.allowsEditing = true
-        self.pickerController.mediaTypes = ["public.movie"]
-        self.pickerController.videoQuality = .typeHigh
-    }
-
-    private func action(for type: UIImagePickerController.SourceType, title: String) -> UIAlertAction? {
-        guard UIImagePickerController.isSourceTypeAvailable(type) else {
-            return nil
-        }
-
-        return UIAlertAction(title: title, style: .default) { [unowned self] _ in
-            self.pickerController.sourceType = type
-            self.presentationController?.present(self.pickerController, animated: true)
-        }
-    }
-
-    public func present(from sourceView: UIView) {
-
-        let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
-
-        if let action = self.action(for: .camera, title: "Take video") {
-            alertController.addAction(action)
-        }
-        if let action = self.action(for: .savedPhotosAlbum, title: "Camera roll") {
-            alertController.addAction(action)
-        }
-        if let action = self.action(for: .photoLibrary, title: "Video library") {
-            alertController.addAction(action)
-        }
-
-        alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
-
-        if UIDevice.current.userInterfaceIdiom == .pad {
-            alertController.popoverPresentationController?.sourceView = sourceView
-            alertController.popoverPresentationController?.sourceRect = sourceView.bounds
-            alertController.popoverPresentationController?.permittedArrowDirections = [.down, .up]
-        }
-
-        self.presentationController?.present(alertController, animated: true)
-    }
-
-    private func pickerController(_ controller: UIImagePickerController, didSelect url: URL?) {
-        controller.dismiss(animated: true, completion: nil)
-
-        self.delegate?.didSelect(url: url)
-    }
-}
-
-extension VideoPicker: UIImagePickerControllerDelegate {
-
-    public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
-        self.pickerController(picker, didSelect: nil)
-    }
-
-    public func imagePickerController(_ picker: UIImagePickerController,
-                                      didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
-
-        guard let url = info[.mediaURL] as? URL else {
-            return self.pickerController(picker, didSelect: nil)
-        }
-
-//        //uncomment this if you want to save the video file to the media library
-//        if UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(url.path) {
-//            UISaveVideoAtPathToSavedPhotosAlbum(url.path, self, nil, nil)
-//        }
-        self.pickerController(picker, didSelect: url)
-    }
-}
-
-extension VideoPicker: UINavigationControllerDelegate {
-
-}
-

There are just a few small that changes. The first one is the mediaTypes property, you can use the “public.movie” value this time. Also you should set the videoQuality property on the pickerController, because 4k is always better than 320. 🤪

The delegate is the last thing that changed a little bit. After the picker finish the job you can get the .mediaURL property, which is a URL to get your media file (a.k.a. the captured / selected video file). If a new file was recorded you can also save it to the media library, that’s just two lines of extra code.

Congrats, play-back time! 📹

Playing video files using AVPlayer & UIView

Isn’t it great when a webpage has some nicely themed video in the background of the header? Well, you can have the exact same thing in iOS by using AVFoundation, UIKit and some low-level layer magic. Don’t worry it’s not that difficult. 😬

You can use a regular UIView subclass, then replace its default layer with an AVPlayerLayer. This will allow you to play videos directly in the view. Also an AVPlayer is just a simple controller object that can manage the playback and timing of a media file.

The hardest part was checking the status changes of the media file. For example when I first tried to record a new video the payback of the player view constantly stopped after a second. I had to search for answers, because I’m not an AVFoundation expert at all, but it turned out that you should watch for the rate property, because the system is trying to buffer the video and that can cause some problems.

Anyway I was able to put together a fairly nice VideoView with some nice additional features like constantly looping the video or choosing between the fill / fit aspect content modes. I’m not telling you that this is a 100% bulletproof solution, but it’s a good starting point, plus it’s more than enough in some cases. 👻

import UIKit
-import AVFoundation
-
-open class VideoView: UIView {
-
-    public enum Repeat {
-        case once
-        case loop
-    }
-
-    override open class var layerClass: AnyClass {
-        return AVPlayerLayer.self
-    }
-
-    private var playerLayer: AVPlayerLayer {
-        return self.layer as! AVPlayerLayer
-    }
-
-    public var player: AVPlayer? {
-        get {
-            self.playerLayer.player
-        }
-        set {
-            self.playerLayer.player = newValue
-        }
-    }
-
-
-    open override var contentMode: UIView.ContentMode {
-        didSet {
-            switch self.contentMode {
-            case .scaleAspectFit:
-                self.playerLayer.videoGravity = .resizeAspect
-            case .scaleAspectFill:
-                self.playerLayer.videoGravity = .resizeAspectFill
-            default:
-                self.playerLayer.videoGravity = .resize
-            }
-        }
-    }
-
-    public var `repeat`: Repeat = .once
-
-    public var url: URL? {
-        didSet {
-            guard let url = self.url else {
-                self.teardown()
-                return
-            }
-            self.setup(url: url)
-        }
-    }
-
-    @available(*, unavailable)
-    override init(frame: CGRect) {
-        super.init(frame: frame)
-
-        self.initialize()
-    }
-
-    @available(*, unavailable)
-    public required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-
-        self.initialize()
-    }
-
-    public init() {
-        super.init(frame: .zero)
-
-        self.translatesAutoresizingMaskIntoConstraints = false
-
-        self.initialize()
-    }
-
-    open func initialize() {
-
-    }
-
-    deinit {
-        self.teardown()
-    }
-
-
-    private func setup(url: URL) {
-
-        self.player = AVPlayer(playerItem: AVPlayerItem(url: url))
-
-        self.player?.currentItem?.addObserver(self,
-                                              forKeyPath: "status",
-                                              options: [.old, .new],
-                                              context: nil)
-
-        self.player?.addObserver(self, forKeyPath: "rate", options: [.old, .new], context: nil)
-
-
-        NotificationCenter.default.addObserver(self,
-                                               selector: #selector(self.itemDidPlayToEndTime(_:)),
-                                               name: .AVPlayerItemDidPlayToEndTime,
-                                               object: self.player?.currentItem)
-
-        NotificationCenter.default.addObserver(self,
-                                               selector: #selector(self.itemFailedToPlayToEndTime(_:)),
-                                               name: .AVPlayerItemFailedToPlayToEndTime,
-                                               object: self.player?.currentItem)
-    }
-
-    private func teardown() {
-        self.player?.pause()
-
-        self.player?.currentItem?.removeObserver(self, forKeyPath: "status")
-
-        self.player?.removeObserver(self, forKeyPath: "rate")
-
-        NotificationCenter.default.removeObserver(self,
-                                                  name: .AVPlayerItemDidPlayToEndTime,
-                                                  object: self.player?.currentItem)
-
-        NotificationCenter.default.removeObserver(self,
-                                                  name: .AVPlayerItemFailedToPlayToEndTime,
-                                                  object: self.player?.currentItem)
-
-        self.player = nil
-    }
-
-
-
-    @objc func itemDidPlayToEndTime(_ notification: NSNotification) {
-        guard self.repeat == .loop else {
-            return
-        }
-        self.player?.seek(to: .zero)
-        self.player?.play()
-    }
-
-    @objc func itemFailedToPlayToEndTime(_ notification: NSNotification) {
-        self.teardown()
-    }
-
-
-    open override func observeValue(forKeyPath keyPath: String?,
-                                          of object: Any?,
-                                          change: [NSKeyValueChangeKey : Any]?,
-                                          context: UnsafeMutableRawPointer?) {
-        if keyPath == "status", let status = self.player?.currentItem?.status, status == .failed {
-            self.teardown()
-        }
-
-        if
-            keyPath == "rate",
-            let player = self.player,
-            player.rate == 0,
-            let item = player.currentItem,
-            !item.isPlaybackBufferEmpty,
-            CMTimeGetSeconds(item.duration) != CMTimeGetSeconds(player.currentTime())
-        {
-            self.player?.play()
-        }
-    }
-}
-

I made a sample project for you and honestly my view controller is simple as f.ck. It demonstrates both the image picking and the video capturing capabilities. Feel free to download it from The.Swift.Dev tutorials repository, it’s called Pickers.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/picking-images-with-uiimagepickercontroller-in-swift-5/index.html b/docs/picking-images-with-uiimagepickercontroller-in-swift-5/index.html deleted file mode 100644 index 0b9b010..0000000 --- a/docs/picking-images-with-uiimagepickercontroller-in-swift-5/index.html +++ /dev/null @@ -1,444 +0,0 @@ - - - - - - - - - - - - Picking images with UIImagePickerController in Swift 5 - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

Picking images with UIImagePickerController in Swift 5

-
-

Learn how to get an image from the photo library or directly from the camera by using the UIImagePickerController class in Swift 5.

-
- -

Are you looking for a video picker as well? 🍿 Check out my another post about picking & playing video files in iOS.

A reusable image picker class for iOS

So in this tutorial we’re going to create a reusable class built on top of UIKit in order to make image selection more pleasant for your apps, everything written in Swift 5.

This article was inspired by my previous attempt to solve the image picking issue in a protocol oriented way, but that article is nowadays a little bit obsolated, plus I wouldn’t use that technique anymore.

People always learn from the past, so instead of using a protocol oriented approach, this time I’ll simply go with an ImagePicker class. No singletons, no extra library, just a small helper class that can be instantiated in the appropriate place, to do it’s job. 🌄

I’m only going to focus on picking edited images, if you’d like to use live photos or movies, you can always customize the ImagePicker class, or create an abstract one and implement subclasses for each media type. I’d do so too. 😅

So let’s dive in, here is my basic implementation, but you can download a complete example project with video picking as well from The.Swift.Dev. tutorials repository on GitHub.

Privacy first!

Nowadays privacy matters a lot, so you have to add two important keys to your applications Info.plist file, otherwise you’ll end up with a horrible crash! ⚠️

Since you’d like to get some private data, you have to provide an explanation message for the user (and for Apple) why the app is requesting camera & photo library access. The NSCameraUsageDescription is for camera and NSPhotoLibraryUsageDescription key is for photo library access. Both values should be a straightforward string that’ll explain the user why you need his/her nude pictures. Privacy is important! 🔒

<key>NSCameraUsageDescription</key>
-<string>This app wants to take pictures.</string>
-<key>NSPhotoLibraryUsageDescription</key>
-<string>This app wants to use your photos.</string>
-

Obviously if you’d like to use photos directly taken from the camera, but you don’t want to access the photo library, you just have to add the proper key. That’s it now we’re ready to do some actual coding. ⌨️

The anatomy of UIImagePickerController

The anatomy of a UIPickerController is quite simple. Basically it’s a regular view controller, you just have to set a few extra properties to make it work.

let pickerController = UIImagePickerController()
-pickerController.delegate = self
-pickerController.allowsEditing = true
-pickerController.mediaTypes = ["public.image", "public.movie"]
-pickerController.sourceType = .camera
-

Allows editing is a flag that indicates if the resizing & cropping interface should be presented after selecting & taking a picture, if true you should use the .editedImage instead of the .originalImage key - inside the picker delegate - to get the proper image from the image info dictionary.

There are basically two kinds of media types available: images and movies. You can get the available media type strings for each source type by calling a class method on the picker:

UIImagePickerController.availableMediaTypes(
-    for: .camera
-)
-

There are 3 available source types: .camera, which is the camera, and there are two other options to get pictures from the photo library. The .photoLibrary enum case will give you full access, but you can limit the selection scope only for the camera roll if you choose .savedPhotosAlbum.

The delegate should implement both the UIImagePickerControllerDelegate and the UINavigationControllerDelegate protocols, however usually my navigation controller delegate is just an empty implementation. If you need extra navigation related logic, you might need to create a few methods there as well.

Awww, let’s just put everything together…

import UIKit
-
-public protocol ImagePickerDelegate: class {
-    func didSelect(image: UIImage?)
-}
-
-open class ImagePicker: NSObject {
-
-    private let pickerController: UIImagePickerController
-    private weak var presentationController: UIViewController?
-    private weak var delegate: ImagePickerDelegate?
-
-    public init(presentationController: UIViewController, delegate: ImagePickerDelegate) {
-        self.pickerController = UIImagePickerController()
-
-        super.init()
-
-        self.presentationController = presentationController
-        self.delegate = delegate
-
-        self.pickerController.delegate = self
-        self.pickerController.allowsEditing = true
-        self.pickerController.mediaTypes = ["public.image"]
-    }
-
-    private func action(for type: UIImagePickerController.SourceType, title: String) -> UIAlertAction? {
-        guard UIImagePickerController.isSourceTypeAvailable(type) else {
-            return nil
-        }
-
-        return UIAlertAction(title: title, style: .default) { [unowned self] _ in
-            self.pickerController.sourceType = type
-            self.presentationController?.present(self.pickerController, animated: true)
-        }
-    }
-
-    public func present(from sourceView: UIView) {
-
-        let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
-
-        if let action = self.action(for: .camera, title: "Take photo") {
-            alertController.addAction(action)
-        }
-        if let action = self.action(for: .savedPhotosAlbum, title: "Camera roll") {
-            alertController.addAction(action)
-        }
-        if let action = self.action(for: .photoLibrary, title: "Photo library") {
-            alertController.addAction(action)
-        }
-
-        alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
-
-        if UIDevice.current.userInterfaceIdiom == .pad {
-            alertController.popoverPresentationController?.sourceView = sourceView
-            alertController.popoverPresentationController?.sourceRect = sourceView.bounds
-            alertController.popoverPresentationController?.permittedArrowDirections = [.down, .up]
-        }
-
-        self.presentationController?.present(alertController, animated: true)
-    }
-
-    private func pickerController(_ controller: UIImagePickerController, didSelect image: UIImage?) {
-        controller.dismiss(animated: true, completion: nil)
-
-        self.delegate?.didSelect(image: image)
-    }
-}
-
-extension ImagePicker: UIImagePickerControllerDelegate {
-
-    public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
-        self.pickerController(picker, didSelect: nil)
-    }
-
-    public func imagePickerController(_ picker: UIImagePickerController,
-                                      didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
-        guard let image = info[.editedImage] as? UIImage else {
-            return self.pickerController(picker, didSelect: nil)
-        }
-        self.pickerController(picker, didSelect: image)
-    }
-}
-
-extension ImagePicker: UINavigationControllerDelegate {
-
-}
-

If you don’t need to select from a source type, things are pretty straightforward, you can simply present your picker view controller, handle everything in the delegate and you are done. However, if you need to choose from an input source, that involves a little bit more logic, especially on iPads. 📱

I’m using a UIAlertController in order to compose a source type selection dialog. I’m trying to add 3 actions (based on the picking source type), but only if the source type is available on that given device (e.g. .camera is not available in the simulator). You can check availability through: UIImagePickerController.isSourceTypeAvailable(type).

Alert controllers needs a few extra things on iPads, that’s why I’m setting up the popoverPresentationController properties in the present method. It’s usually enough to set the sourceView and the sourceRect properties, but you can also customize arrow directions. ⬅️➡️⬆️⬇️

It’s always your task to check if the device is an iPad & set the proper source view & rect if it’s needed, otherwise your app will crash on iPads. Another thing is that you have to dismiss the UIPickerViewController after the picker did it’s job! ⚠️

Time to say cheese! 🧀

How to use the image picker class?

Well, now you are ready to take some pictures. I’ve made a simple view controller to show you a real quick example. You only need a UIImageView and a UIButton.

Now this is the code for the sample view controller. Nothing magical, I just pass the controller as a presentationController for the ImagePicker so it’ll be able to present the UIImagePickerController on top of that. I separated the delegate from the presentation controller, because sometimes it comes handy. 🤷‍♂️

class ViewController: UIViewController {
-
-    @IBOutlet var imageView: UIImageView!
-
-    var imagePicker: ImagePicker!
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        self.imagePicker = ImagePicker(presentationController: self, delegate: self)
-    }
-
-    @IBAction func showImagePicker(_ sender: UIButton) {
-        self.imagePicker.present(from: sender)
-    }
-}
-
-extension ViewController: ImagePickerDelegate {
-
-    func didSelect(image: UIImage?) {
-        self.imageView.image = image
-    }
-}
-

The ImagePickerDelegate delegate in this case is the most simple one I can imagine. It just gives the picked image so you’re ready to use it. However in some cases you might need a few additional info from the image picker.

If you want to take this approach one step further, you can create an abstract class or a protocol that defines the basic functionality and based on that you can implement various media picker controllers to fit your needs.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/practical-guide-to-binary-operations-using-the-uint8-type-in-swift/index.html b/docs/practical-guide-to-binary-operations-using-the-uint8-type-in-swift/index.html deleted file mode 100644 index 2caba9a..0000000 --- a/docs/practical-guide-to-binary-operations-using-the-uint8-type-in-swift/index.html +++ /dev/null @@ -1,470 +0,0 @@ - - - - - - - - - - - - Practical guide to binary operations using the UInt8 type in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 9 min read - -
-

Practical guide to binary operations using the UInt8 type in Swift

-
-

Introduction to the basics of signed number representation and some practical binary operation examples in Swift using UInt8.

-
- -

Integer types in Swift

The Swift programming language has a bunch of different integer types. The Swift integer APIs were cleaned up by an old proposal named Protocol-oriented Integers, which resulted in a more generic way of expressing these kind of data types.

Numeric data types in Swift are type safe by default, this makes a bit harder to perform operation using different integer (or floating point) types. Integers are divided into two main groups: signed and unsigned integers. In addition each members of these groups can be categorized by bit sizes. There are 8, 16, 32 & 64 bit long signed & unsigned integers plus generic integers. 🤔

Generic integers:

  • Int (32 or 64 bit)
  • UInt (32 or 64 bit)

Signed integers:

Unsigned integers:

You should know that the Int and UInt type size may vary on different platforms (32 vs 64 bits), but in order to be consistent, Apple recommends to always prefer the generic Int type over all the other variants. The Swift language always identifies all the integers using the Int type by default, so if you keep using this type you’ll be able to perform integer operations without type conversions, your code will be easier to read and it’s going to be easier to move between platforms too. 💪

Most of the time you shouldn’t care about the length of the integer types, we can say that the generic Int and UInt types are quite often the best choices when you write Swift code. Except in those cases when your goal is to write extremely memory efficient or low level code…

Representing numbers as integers

Now that we know what kind of integers are available in Swift, it’s time to talk a bit about what kind of numbers can we represent using these data types.

/// generic integers
-print(Int.min)      //  -9223372036854775808
-print(Int.max)      //   9223372036854775807
-print(UInt.min)     //                     0
-print(UInt.max)     //  18446744073709551615
-
-/// unsigned integers
-print(UInt8.min)    //                     0
-print(UInt8.max)    //                   255
-print(UInt16.min)   //                     0
-print(UInt16.max)   //                 65535
-print(UInt32.min)   //                     0
-print(UInt32.max)   //            4294967295
-print(UInt64.min)   //                     0
-print(UInt64.max)   //  18446744073709551615
-
-/// signed integers
-print(Int8.min)     //                  -128
-print(Int8.max)     //                   127
-print(Int16.min)    //                -32768
-print(Int16.max)    //                 32767
-print(Int32.min)    //           -2147483648
-print(Int32.max)    //            2147483647
-print(Int64.min)    //  -9223372036854775808
-print(Int64.max)    //   9223372036854775807
-

So there is a minimum and maximum value for each integer type that we can store in a given variable. For example, we can’t store the value 69420 inside a UInt8 type, because there are simply not enough bits to represent this huge number. 🤓

Let’s examine our 8 bit long unsigned integer type. 8 bit means that we have literally 8 places to store boolean values (ones and zeros) using the binary number representation. 0101 0110 in binary is 86 using the “regular” decimal number format. This binary number is a base-2 numerical system (a positional notation) with a radix of 2. The number 86 can be interpreted as:

0*28+1*27+0*26+1*25+0*24 + 1*23+1*22+0*21+0*20
-0*128+1*64+0*32+1*16 + 0*8+1*4+1*2+0*1
-64+16+4+2
-86
-

We can convert back and forth between decimal and binary numbers, it’s not that hard at all, but let’s come back to this topic later on. In Swift we can check if a type is a signed type and we can also get the length of the integer type through the bitWidth property.

print(Int.isSigned)     // true
-print(UInt.isSigned)    // false
-print(Int.bitWidth)     // 64
-print(UInt8.bitWidth)   // 8
-

Based on this logic, now it’s quite straightforward that an 8 bit long unsigned type can only store 255 as the maximum value (1111 1111), since that’s 128+64+32+16+8+4+2+1.

What about signed types? Well, the trick is that 1 bit from the 8 is reserved for the positive / negative symbol. Usually the first bit represents the sign and the remaining 7 bits can store the actual numeric values. For example the Int8 type can store numbers from -128 til 127, since the maximum positive value is represented as 0111 1111, 64+32+16+8+4+2+1, where the leading zero indicates that we’re talking about a positive number and the remaining 7 bits are all ones.

So how the hack can we represent -128? Isn’t -127 (1111 1111) the minimum negative value? 😅

Nope, that’s not how negative binary numbers work. In order to understand negative integer representation using binary numbers, first we have to introduce a new term called two’s complement, which is a simple method of signed number representation.

Basic signed number maths

It is relatively easy to add two binary numbers, you just add the bits in order with a carry, just like you’d do addition using decimal numbers. Subtraction on the other hand is a bit harder, but fortunately it can be replaced with an addition operation if we store negative numbers in a special way and this is where two’s complement comes in.

Let’s imagine that we’d like to add two numbers:

  • 0010 1010 (+42)
  • 0100 0101 +(+69)
  • 0110 1111 =(+111)

Now let’s add a positive and a negative number stored using two’s complement, first we need to express -6 using a signed 8 bit binary number format:

  • 0000 0110 (+6)
  • 1111 1001 (one’s complement = inverted bits)
  • 1111 1010 (two’s complement = add +1 (0000 0001) to one’s complement)

Now we can simply perform an addition operation on the positive and negative numbers.

  • 0010 1010 (+42)
  • 1111 1010 +(-6)
  • (1) 0010 0100 =(+36)

So, you might think, what’s the deal with the extra 1 in the beginning of the 8 bit result? Well, that’s called a carry bit, and in our case it won’t affect our final result, since we’ve performed a subtraction instead of an addition. As you can see the remaining 8 bit represents the positive number 36 and 42-6 is exactly 36, we can simply ignore the extra flag for now. 😅

Binary operators in Swift

Enough from the theory, let’s dive in with some real world examples using the UInt8 type. First of all, we should talk about bitwise operators in Swift. In my previous article we’ve talked about Bool operators (AND, OR, NOT) and the Boolean algebra, now we can say that those functions operate using a single bit. This time we’re going to see how bitwise operators can perform various transformations using multiple bits. In our sample cases it’s always going to be 8 bit. 🤓

Bitwise NOT operator

This operator (~) inverts all bits in a number. We can use it to create one’s complement values.

// one's complement
-let x: UInt8 = 0b00000110    // 6 using binary format
-let res = ~x                 // bitwise NOT
-print(res)                   // 249, but why?
-print(String(res, radix: 2)) // 1111 1001
-

Well, the problem is that we’ll keep seeing decimal numbers all the time when using int types in Swift. We can print out the correct 1111 1001 result, using a String value with the base of 2, but for some reason the inverted number represents 249 according to our debug console. 🙃

This is because the meaning of the UInt8 type has no understanding about the sign bit, and the 8th bit is always refers to the 28 value. Still, in some cases e.g. when you do low level programming, such as building a NES emulator written in Swift, this is the right data type to choose.

The Data type from the Foundation framework is considered to be a collection of UInt8 numbers. Actually you’ll find quite a lot of use-cases for the UInt8 type if you take a deeper look at the existing frameworks & libraries. Cryptography, data transfers, etc.

Anyway, you can make an extension to easily print out the binary representation for any unsigned 8 bit number with leading zeros if needed. 0️⃣0️⃣0️⃣0️⃣ 0️⃣1️⃣1️⃣0️⃣

/// UInt8+Binary.swift
-import Foundation
-
-fileprivate extension String {
-    
-    func leftPad(with character: Character, length: UInt) -> String {
-        let maxLength = Int(length) - count
-        guard maxLength > 0 else {
-            return self
-        }
-        return String(repeating: String(character), count: maxLength) + self
-    }
-}
-
-extension UInt8 {
-    var bin: String {
-        String(self, radix: 2).leftPad(with: "0", length: 8)
-    }
-}
-
-let x: UInt8 = 0b00000110   // 6 using binary format
-print(String(x, radix: 2))  // 110
-print(x.bin)                // 00000110
-print((~x).bin)             // 11111001 - one's complement
-let res = (~x) + 1          // 11111010 - two's complement
-print(res.bin)
-

We still have to provide our custom logic if we want to express signed numbers using UInt8, but that’s only going to happen after we know more about the other bitwise operators.

Bitwise AND, OR, XOR operators

These operators works just like you’d expect it from the truth tables. The AND operator returns a one if both the bits were true, the OR operator returns a 1 if either of the bits were true and the XOR operator only returns a true value if only one of the bits were true.

  • AND & - 1 if both bits were 1
  • OR | - 1 if either of the bits were 1
  • XOR ^ - 1 if only one of the bits were 1

Let me show you a quick example for each operator in Swift.

let x: UInt8 = 42   // 00101010
-let y: UInt8 = 28   // 00011100
-// AND
-print((x & y).bin)  // 00001000
-// OR
-print((x | y).bin)  // 00111110
-// XOR
-print((x ^ y).bin)  // 00110110
-

Mathematically speaking, there is not much reason to perform these operations, it won’t give you a sum of the numbers or other basic calculation results, but they have a different purpose.

You can use the bitwise AND operator to extract bits from a given number. For example if you want to store 8 (or less) individual true or false values using a single UInt8 type you can use a bitmask to extract & set given parts of the number. 😷

var statusFlags: UInt8 = 0b00000100
-
-// check if the 3rd flag is one (value equals to 4)
-print(statusFlags & 0b00000100 == 4)   // true
-
-// check if the 5th flag is one (value equals to 16)
-print(statusFlags & 0b00010000 == 16)  // false
-
-// set the 5th flag to 1
-statusFlags = statusFlags & 0b11101111 | 16
-print(statusFlags.bin)  // 00010100
-
-// set the 3rd flag to zero
-statusFlags = statusFlags & 0b11111011 | 0
-print(statusFlags.bin) // 00000100
-
-// set the 5th flag back to zero
-statusFlags = statusFlags & 0b11101111 | 0
-print(statusFlags.bin) // 00000000
-
-// set the 3rd flag back to one
-statusFlags = statusFlags & 0b11101011 | 4
-print(statusFlags.bin) // 00000100
-

This is nice, especially if you don’t want to mess around with 8 different Bool variables, but one there is one thing that is very inconvenient about this solution. We always have to use the right power of two, of course we could use pow, but there is a more elegant solution for this issue.

Bitwise left & right shift operators

By using a bitwise shift operation you can move a bit in a given number to left or right. Left shift is essentially a multiplication operation and right shift is identical with a division by a factor of two.

“Shifting an integer’s bits to the left by one position doubles its value, whereas shifting it to the right by one position halves its value.” - swift.org

It’s quite simple, but let me show you a few practical examples so you’ll understand it in a bit. 😅

let meaningOfLife: UInt8 = 42
-
-
-// left shift 1 bit (42 * 2)
-print(meaningOfLife << 1) // 84
-
-// left shift 2 bits (42 * 2 * 2)
-print(meaningOfLife << 2) // 168
-
-// left shift 3 bits (42 * 2 * 2 * 2)
-print(meaningOfLife << 3) // 80, it's an overflow !!!
-
-
-// right shift 1 bit (42 / 2)
-print(meaningOfLife >> 1) // 21
-
-// right shift 2 bits (42 / 2 / 2)
-print(meaningOfLife >> 2) // 10
-
-// right shift 3 bits (42 / 2 / 2 / 2)
-print(meaningOfLife >> 3) // 5
-
-// right shift 4 bits (42 / 2 / 2 / 2 / 2)
-print(meaningOfLife >> 4) // 2
-
-// right shift 5 bits (42 / 2 / 2 / 2 / 2 / 2)
-print(meaningOfLife >> 5) // 1
-
-// right shift 6 bits (42 / 2 / 2 / 2 / 2 / 2 / 2)
-print(meaningOfLife >> 6) // 0
-
-// right shift 7 bits (42 / 2 / 2 / 2 / 2 / 2 / 2 / 2)
-print(meaningOfLife >> 7) // 0
-

As you can see we have to be careful with left shift operations, since the result can overflow the 8 bit range. If this happens, the extra bit will just go away and the remaining bits are going to be used as a final result. Right shifting is always going to end up as a zero value. ⚠️

Now back to our status flag example, we can use bit shifts, to make it more simple.

var statusFlags: UInt8 = 0b00000100
-
-// check if the 3rd flag is one
-print(statusFlags & 1 << 2 == 1 << 2)
-
-// set the 3rd flag to zero
-statusFlags = statusFlags & ~(1 << 2) | 0
-print(statusFlags.bin)
-
-// set back the 3rd flag to one
-statusFlags = statusFlags & ~(1 << 2) | 1 << 2
-print(statusFlags.bin)
-

As you can see we’ve used quite a lot of bitwise operations here. For the first check we use left shift to create our mask, bitwise and to extract the value using the mask and finally left shift again to compare it with the underlying value. Inside the second set operation we use left shift to create a mask then we use the not operator to invert the bits, since we’re going to set the value using a bitwise or function. I suppose you can figure out the last line based on this info, but if not just practice these operators, they are very nice to use once you know all the little the details. ☺️

I think I’m going to cut it here, and I’ll make just another post about overflows, carry bits and various transformations, maybe we’ll involve hex numbers as well, anyway don’t want to promise anything specific. Bitwise operations are usueful and fun, just practice & don’t be afraid of a bit of math. 👾

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/practical-server-side-swift/index.html b/docs/practical-server-side-swift/index.html deleted file mode 100644 index 6141d61..0000000 --- a/docs/practical-server-side-swift/index.html +++ /dev/null @@ -1,157 +0,0 @@ - - - - - - - - - - - - Practical Server Side Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -

Practical Server Side Swift


-

Practical
Server Side Swift

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that’ll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

Download sample

Available on Gumroad

- -
- - - -
- - - - diff --git a/docs/progressive-web-apps-on-ios/index.html b/docs/progressive-web-apps-on-ios/index.html deleted file mode 100644 index 8a09625..0000000 --- a/docs/progressive-web-apps-on-ios/index.html +++ /dev/null @@ -1,857 +0,0 @@ - - - - - - - - - - - - Progressive Web Apps on iOS - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 12 min read - -
-

Progressive Web Apps on iOS

-
-

This is a beginner's guide about creating PWAs for iOS including custom icons, splash screens, safe area and dark mode support.

-
- -

How to make a PWA for iOS?

A progressive web application is just a special kind of website, that can look and behave like a native iOS app. In order to build a PWA, first we’re going to create a regular website using SwiftHtml. We can start with a regular executable Swift package with the following dependencies.

// swift-tools-version:5.5
-import PackageDescription
-
-let package = Package(
-    name: "Example",
-    platforms: [
-        .macOS(.v12)
-    ],
-    dependencies: [
-        .package(url: "https://github.com/binarybirds/swift-html", from: "1.2.0"),
-        .package(url: "https://github.com/vapor/vapor", from: "4.54.0"),
-    ],
-    targets: [
-        .executableTarget(name: "Example", dependencies: [
-            .product(name: "SwiftHtml", package: "swift-html"),
-            .product(name: "Vapor", package: "vapor"),
-        ]),
-        .testTarget(name: "ExampleTests", dependencies: ["Example"]),
-    ]
-)
-

As you can see we’re going to use the vapor Vapor library to serve our HTML site. If you don’t know much about Vapor let’s just say that it is a web application framework, which can be used to build server side Swift applications, it’s a pretty amazing tool I have a beginner’s guide post about it.

Of course we’re going to need some components for rendering views using SwiftHtml, you can use the source snippets from my previous article, but here it is again how the SwiftHtml-based template engine should look like. You should read my other article if you want to know more about it. 🤓

import Vapor
-import SwiftSgml
-
-public protocol TemplateRepresentable {
-    
-    @TagBuilder
-    func render(_ req: Request) -> Tag
-}
-
-public struct TemplateRenderer {
-    
-    var req: Request
-    
-    init(_ req: Request) {
-        self.req = req
-    }
-
-    public func renderHtml(_ template: TemplateRepresentable, minify: Bool = false, indent: Int = 4) -> Response {
-        let doc = Document(.html) { template.render(req) }
-        let body = DocumentRenderer(minify: minify, indent: indent).render(doc)
-        return Response(status: .ok, headers: ["content-type": "text/html"], body: .init(string: body))
-    }
-}
-
-public extension Request {
-    var templates: TemplateRenderer { .init(self) }
-}
-

We’re also going to need an index template for our main HTML document. Since we’re using a Swift DSL to write HTML code we don’t have to worry too much about mistyping a tag, the compiler will protect us and helps to maintain a completely valid HTML structure.

import Vapor
-import SwiftHtml
-
-
-struct IndexContext {
-    let title: String
-    let message: String
-}
-
-struct IndexTemplate: TemplateRepresentable {
-    
-    let context: IndexContext
-    
-    init(_ context: IndexContext) {
-        self.context = context
-    }
-    
-    func render(_ req: Request) -> Tag {
-        Html {
-            Head {
-                Title(context.title)
-                
-                Meta().charset("utf-8")
-                Meta().name(.viewport).content("width=device-width, initial-scale=1")
-            }
-            Body {
-                Main {
-                    Div {
-                        H1(context.title)
-                        P(context.message)
-                    }
-                }
-            }
-        }
-    }
-}
-

Finally we can simply render the bootstrap our Vapor server instance, register our route handler and render the index template inside the main entry point of our Swift package by using the previously defined template helper methods on the Request object.

import Vapor
-import SwiftHtml
-
-var env = try Environment.detect()
-try LoggingSystem.bootstrap(from: &env)
-let app = Application(env)
-defer { app.shutdown() }
-
-app.get { req -> Response in
-    let template = IndexTemplate(.init(title: "Hello, World!",
-                                    message: "This page was generated by the SwiftHtml library."))
-    
-    return req.templates.renderHtml(template)
-}
-
-try app.run()
-

It is just that easy to setup and bootstrap a fully working web server that is capable of rendering a HTML document using the power of Swift and the Vapor framework. If you run the app you should be able to see a working website by visiting the http://localhost:8080/ address.

Turning a website into a real iOS PWA

Now if we want to transform our website into a standalone PWA, we have to provide a link a special web app manifest file inside the head section of the index template.

Meta tags vs manifest.json

Seems like Apple follows kind of a strange route if it comes to PWA support. They have quite a history of “thinking outside of the box”, this mindset applies to progressive web apps on iOS, since they don’t tend to follow the standards at all. For Android devices you could create a manifest.json file with some predefined keys and you’d be just fine with your PWA. On the other hand Apple nowadays prefers various HTML meta tags instead of the web manifest format.

Personally I don’t like this approach, because your HTML code will be bloated with all the PWA related stuff (as you’ll see this is going to happen if it comes to launch screen images) and I believe it’s better to separate these kind of things, but hey it’s Apple, they can’t be wrong, right? 😅

Anyways, let me show you how to support various PWA features on iOS.

Enabling standalone app mode

The very first few keys that we’d like to add to the index template has the apple-mobile-web-app-capable name and you should use the “yes” string as content. This will indicate that the app should run in full-screen mode, otherwise it’s going to be displayed using Safari just like a regular site.

struct IndexTemplate: TemplateRepresentable {
-    
-    let context: IndexContext
-    
-    init(_ context: IndexContext) {
-        self.context = context
-    }
-    
-    func render(_ req: Request) -> Tag {
-        Html {
-            Head {
-                Title(context.title)
-                
-                Meta().charset("utf-8")
-                Meta().name(.viewport).content("width=device-width, initial-scale=1")
-
-                Meta()
-                    .name(.appleMobileWebAppCapable)
-                    .content("yes")
-            }
-            Body {
-                Main {
-                    Div {
-                        H1(context.title)
-                        P(context.message)
-                    }
-                }
-            }
-        }
-    }
-}
-

We should change the hostname of the server and listen on the 0.0.0.0 address, this way if your phone is on the same local WiFi network you should be able to reach your web server directly.

import Vapor
-import SwiftHtml
-
-var env = try Environment.detect()
-try LoggingSystem.bootstrap(from: &env)
-let app = Application(env)
-defer { app.shutdown() }
-
-app.http.server.configuration.hostname = "0.0.0.0"
-if let hostname = Environment.get("SERVER_HOSTNAME") {
-    app.http.server.configuration.hostname = hostname
-}
-
-app.get { req -> Response in
-    let template = IndexTemplate(.init(title: "Hello, World!",
-                                    message: "This page was generated by the SwiftHtml library."))
-    
-    return req.templates.renderHtml(template)
-}
-
-try app.run()
-

You can find out your local IP address by typing the following command into the Terminal app.

# using ifconfig & grep
-ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1'
-# using ifconfig & sed
-ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'
-

Just use that IP address and go to the http://[ip-address]:8080/ website using your iOS device, then you should be able to add your website to your home screen as a bookmark. Just tap the Share icon using Safari and select the Add to Home Screen menu item from the list. On the new screen tap the Add button on the top right corner, this will create a new icon on your home screen as a bookmark to your page. Optionally, you can provide a custom name for the bookmark. ☺️

Since we’ve added the meta tag, if you touch the newly created icon it should open the webpage as a standalone app (without using the browser). Of course the app is still just a website rendered using a web view. The status bar won’t match the white background and it has no custom icon or splash screen yet, but we’re going to fix those issues right now. 📱

Custom name and icon

To provide a custom name we just have to add a new meta tag, fortunately the SwiftHtml library has predefined enums for all the Apple related meta names, so you don’t have to type that much. The icon situation is a bit more difficult, since we have to add a bunch of size variants.

struct IndexTemplate: TemplateRepresentable {
-    
-    let context: IndexContext
-    
-    init(_ context: IndexContext) {
-        self.context = context
-    }
-    
-    func render(_ req: Request) -> Tag {
-        Html {
-            Head {
-                Title(context.title)
-                
-                Meta().charset("utf-8")
-                Meta().name(.viewport).content("width=device-width, initial-scale=1")
-                
-                Meta()
-                    .name(.appleMobileWebAppCapable)
-                    .content("yes")
-                
-                Meta()
-                    .name(.appleMobileWebAppTitle)
-                    .content("Hello PWA")
-                
-                Link(rel: .appleTouchIcon)
-                    .href("/img/apple/icons/192.png")
-
-                for size in [57, 72, 76, 114, 120, 144, 152, 180] {
-                    Link(rel: .appleTouchIcon)
-                        .sizes("\(size)x\(size)")
-                        .href("/img/apple/icons/\(size).png")
-                }
-            }
-            Body {
-                Main {
-                    Div {
-                        H1(context.title)
-                        P(context.message)
-                    }
-                }
-            }
-        }
-    }
-}
-

As you can see icons are referenced by using the Link tag, using the Apple touch icon rel attribute. The default icon without the sizes attribute can be a 192x192 pixel image, plus I’m providing some smaller sizes by using a for loop here. We also need to serve these icon files by using Vapor, that’s why we’re going to alter the configuration file and enable the FileFiddleware.

import Vapor
-import SwiftHtml
-
-var env = try Environment.detect()
-try LoggingSystem.bootstrap(from: &env)
-let app = Application(env)
-defer { app.shutdown() }
-
-app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
-
-app.http.server.configuration.hostname = "0.0.0.0"
-if let hostname = Environment.get("SERVER_HOSTNAME") {
-    app.http.server.configuration.hostname = hostname
-}
-
-app.get { req -> Response in
-    let template = IndexTemplate(.init(title: "Hello, World!",
-                                    message: "This page was generated by the SwiftHtml library."))
-    
-    return req.templates.renderHtml(template)
-}
-
-try app.run()
-

By adding the FileMiddleware to the app with the public directory path configuration your server app is able to serve static files from the Public directory. Feel free to create it and place the app icons under the Public/img/apple/icons folder. If you are running the server from the command line you’ll be fine, but if you are using Xcode you have to specify a custom working directory for Vapor, this will allow the system to look up the public files from the right place.

Your custom icons won’t show up if you are using a self-signed certificate.

Build and run the server and try to bookmark your page again using your phone. When you see the add bookmark page you should be able to validate that the app now uses the predefined Hello PWA name and the image preview should show the custom icon file instead of a screenshot of the page.

Proper status bar color for iOS PWAs

Long story short, there is a great article on CSS-Tricks about the most recent version of Safari and how it handles various theme colors on different platforms. It’s a great article, you should definitely read it, but in most of the cases you won’t need this much info, but you simply want to support light and dark mode for your progressive web app. That’s what I’m going to show you here.

For light mode we’re going to use a white background color and for dark mode we use black. We’re also going to link a new style.css file so we can change the background of the site and the font color according to the current color scheme. First, the new meta tags to support theme colors both for light and dark mode.

struct IndexTemplate: TemplateRepresentable {
-    
-    let context: IndexContext
-    
-    init(_ context: IndexContext) {
-        self.context = context
-    }
-    
-    func render(_ req: Request) -> Tag {
-        Html {
-            Head {
-                Title(context.title)
-                
-                Meta().charset("utf-8")
-                Meta().name(.viewport).content("width=device-width, initial-scale=1")
-                
-                Meta()
-                    .name(.appleMobileWebAppCapable)
-                    .content("yes")
-                Meta()
-                    .name(.appleMobileWebAppTitle)
-                    .content("Hello PWA")
-                
-                Meta()
-                    .name(.colorScheme)
-                    .content("light dark")
-                Meta()
-                    .name(.themeColor)
-                    .content("#fff")
-                    .media(.prefersColorScheme(.light))
-                Meta()
-                    .name(.themeColor)
-                    .content("#000")
-                    .media(.prefersColorScheme(.dark))
-                
-                Link(rel: .stylesheet)
-                    .href("/css/style.css")
-                
-                Link(rel: .appleTouchIcon)
-                    .href("/img/apple/icons/192.png")
-                for size in [57, 72, 76, 114, 120, 144, 152, 180] {
-                    Link(rel: .appleTouchIcon)
-                        .sizes("\(size)x\(size)")
-                        .href("/img/apple/icons/\(size).png")
-                }
-            }
-            Body {
-                Main {
-                    Div {
-                        H1(context.title)
-                        P(context.message)
-                    }
-                }
-            }
-        }
-    }
-}
-

Inside the style CSS file we can use a media query to detect the preferred color scheme, just like we did it for the .themeColor meta tag using SwiftHtml.

body {
-    background: #fff;
-    color: #000;
-}
-@media (prefers-color-scheme: dark) {
-    body {
-        background: #000;
-        color: #fff;
-    }
-}
-

That’s it, now the status bar should use the same color as your main background. Try to switch between dark and light mode and make sure everything works, there is a cool PWA demo project here with different colors for each mode if you want to double check the code. ✅

Splash screen support

Hint: it’s ridiculous. Splash screens on iOS are problematic. Even native apps tend to cache the wrong splash screen or won’t render PNG files properly, now if it comes to PWAs this isn’t necessary better. I was able to provide splash screen images for my app, but it took me quite a while and switching between dark and light mode is totally broken (as far as I know it). 😅

In order to cover every single device screen size, you have to add lots of linked splash images to your markup. It’s so ugly I even had to create a bunch of extension methods to my index template.

extension IndexTemplate {
-    
-    @TagBuilder
-    func splashTags() -> [Tag] {
-        splash(320, 568, 2, .landscape)
-        splash(320, 568, 2, .portrait)
-        splash(414, 896, 3, .landscape)
-        splash(414, 896, 2, .landscape)
-        splash(375, 812, 3, .portrait)
-        splash(414, 896, 2, .portrait)
-        splash(375, 812, 3, .landscape)
-        splash(414, 736, 3, .portrait)
-        splash(414, 736, 3, .landscape)
-        splash(375, 667, 2, .landscape)
-        splash(375, 667, 2, .portrait)
-        splash(1024, 1366, 2, .landscape)
-        splash(1024, 1366, 2, .portrait)
-        splash(834, 1194, 2, .landscape)
-        splash(834, 1194, 2, .portrait)
-        splash(834, 1112, 2, .landscape)
-        splash(414, 896, 3, .portrait)
-        splash(834, 1112, 2, .portrait)
-        splash(768, 1024, 2, .portrait)
-        splash(768, 1024, 2, .landscape)
-    }
-    
-    @TagBuilder
-    func splash(_ width: Int,
-                _ height: Int,
-                _ ratio: Int,
-                _ orientation: MediaQuery.Orientation) -> Tag {
-        splashTag(.light, width, height, ratio, orientation)
-        splashTag(.dark, width, height, ratio, orientation)
-    }
-        
-    func splashTag(_ mode: MediaQuery.ColorScheme,
-                   _ width: Int,
-                   _ height: Int,
-                   _ ratio: Int,
-                   _ orientation: MediaQuery.Orientation) -> Tag {
-        Link(rel: .appleTouchStartupImage)
-            .media([
-                .prefersColorScheme(mode),
-                .deviceWidth(px: width),
-                .deviceHeight(px: height),
-                .webkitDevicePixelRatio(ratio),
-                .orientation(orientation),
-            ])
-            .href("/img/apple/splash/\(calc(width, height, ratio, orientation))\(mode == .light ? "" : "_dark").png")
-    }
-    
-    func calc(_ width: Int,
-              _ height: Int,
-              _ ratio: Int,
-              _ orientation: MediaQuery.Orientation) -> String {
-        let w = String(width * ratio)
-        let h = String(height * ratio)
-        switch orientation {
-        case .portrait:
-            return w + "x" + h
-        case .landscape:
-            return h + "x" + w
-        }
-    }
-}
-

Now I can simply add the splashTags() call into the head section, but I’m not sure if the result is something I can totally agree with. Here, take a look at the end of this tutorial about splash screens, the code required to support iOS splash screens is very long and I haven’t even told you about the 40 different image files that you’ll need. People are literally using PWA asset generators to reduce the time needed to generate these kind of pictures, because it’s quite out of control. 💩

Safe area & the notch

A special topic I’d like to talk about is the safe area support and the notch. I can highly recommend to read this article on CSS-Tricks about The Notch and CSS first, but the main trick is that we can use four environmental variables in CSS to set proper margin and padding values.

First we have to change the viewport meta tag and extend our page beyond the safe area. This can be done by using the viewport-fit cover value. Inside the body of the template we’re going to add a header and a footer section, those areas will have custom background colors and fill the screen.

struct IndexTemplate: TemplateRepresentable {
-    
-    let context: IndexContext
-    
-    init(_ context: IndexContext) {
-        self.context = context
-    }
-    
-    func render(_ req: Request) -> Tag {
-        Html {
-            Head {
-                Title(context.title)
-                
-                Meta()
-                    .charset("utf-8")
-                Meta()
-                    .name(.viewport)
-                    .content("width=device-width, initial-scale=1, viewport-fit=cover")
-                    //.content("width=device-width, initial-scale=1, viewport-fit=cover, maximum-scale=1, user-scalable=no")
-                
-                Meta()
-                    .name(.appleMobileWebAppCapable)
-                    .content("yes")
-                Meta()
-                    .name(.appleMobileWebAppTitle)
-                    .content("Hello PWA")
-                
-                Meta()
-                    .name(.colorScheme)
-                    .content("light dark")
-                Meta()
-                    .name(.themeColor)
-                    .content("#fff")
-                    .media(.prefersColorScheme(.light))
-                Meta()
-                    .name(.themeColor)
-                    .content("#000")
-                    .media(.prefersColorScheme(.dark))
-                
-                Link(rel: .stylesheet)
-                    .href("/css/style.css")
-                
-                Link(rel: .appleTouchIcon)
-                    .href("/img/apple/icons/192.png")
-                for size in [57, 72, 76, 114, 120, 144, 152, 180] {
-                    Link(rel: .appleTouchIcon)
-                        .sizes("\(size)x\(size)")
-                        .href("/img/apple/icons/\(size).png")
-                }
-                
-                splashTags()
-            }
-            Body {
-                Header {
-                    Div {
-                        P("Header area")
-                    }
-                    .class("safe-area")
-                }
-                
-                Main {
-                    Div {
-                        Div {
-                            H1(context.title)
-                            for _ in 0...42 {
-                                P(context.message)
-                            }
-                            A("Refresh page")
-                                .href("/")
-                        }
-                        .class("wrapper")
-                    }
-                    .class("safe-area")
-                }
-
-                Footer {
-                    Div {
-                        P("Footer area")
-                    }
-                    .class("safe-area")
-                }
-            }
-        }
-    }
-}
-

Except the background color we don’t want other content to flow outside the safe area, so we can define a new CSS class and place some margins on it based on the environment. Also we can safely use the calc CSS function if we want to add some extra value to the environment.

* {
-    margin: 0;
-    padding: 0;
-}
-body {
-    background: #fff;
-    color: #000;
-}
-header, footer {
-    padding: 1rem;
-}
-header {
-    background: #eee;
-}
-footer {
-    background: #eee;
-    padding-bottom: calc(1rem + env(safe-area-inset-bottom));
-}
-.safe-area {
-    margin: 0 env(safe-area-inset-right) 0 env(safe-area-inset-left);
-}
-.wrapper {
-    padding: 1rem;
-}
-@media (prefers-color-scheme: dark) {
-    body {
-        background: #000;
-        color: #fff;
-    }
-    header {
-        background: #222;
-    }
-    footer {
-        background: #222;
-    }
-}
-

It looks nice, but what if we’d like to use custom styles for the PWA version only?

Detecting standalone mode

If you want to use the display mode media query in your CSS file we have to add a manifest file to our PWA. Yep, that’s right, I’ve mentioned before that Apple prefers to use meta tags and links, but if you want to use a CSS media query to check if the app runs in a standalone mode you’ll have to create a web manifest.json file with the following contents.

{
-  "display": "standalone"
-}
-

Next you have to provide a link to the manifest file inside the template file.

struct IndexTemplate: TemplateRepresentable {
-    
-    // ...
-    
-    func render(_ req: Request) -> Tag {
-        Html {
-            Head {
-                // ...
-                
-                splashTags()
-                
-                Link(rel: .manifest)
-                    .href("/manifest.json")
-            }
-            Body {
-                // ...
-            }
-        }
-    }
-}
-

In the CSS file now you can use the display-mode selector to check if the app is running in a standalone mode, you can even combine these selectors and detect standalone mode and dark mode using a single query. Media queries are pretty useful. 😍

/* ... */
-
-@media (display-mode: standalone) {
-    header, footer {
-        background: #fff;
-    }
-    header {
-        position: sticky;
-        top: 0;
-        border-bottom: 1px solid #eee;
-    }
-}
-@media (display-mode: standalone) and (prefers-color-scheme: dark) {
-    header, footer {
-        background: #000;
-    }
-    header {
-        border-bottom: 1px solid #333;
-    }
-}
-

You can turn the header into a sticky section by using the position: sticky attribute. I usually prefer to follow the iOS style when the website is presented to the end-user as a standalone app and I keep the original theme colors for the web only.

Don’t forget to rebuild the backend server, before you test your app. Since we’ve made some meta changes you might have to delete the PWA bookmark and install it again to make things work. ⚠️

As you can see building good-looking progressive web apps for iOS is quite tricky, especially if it comes to the metadata madness that Apple created. Anyway, I hope this tutorial will help you to build better PWAs for the iOS platform. This is just the tip of the iceberg, we haven’t talked about JavaScript at all, but maybe I’ll come back with that topic in a new tutorial later on.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

How to use iCloud drive documents?

-
-

Learn how to sync files and data through a shared iCloud drive folder using the latest version of Swift programming language.

- -
- iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

iOS Auto Layout tutorial programmatically

-
-

In this great iOS Auto Layout tutorial I'll teach you how to support rotation, use constraints, work with layers, animate corner radius.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

iOS custom transition tutorial in Swift

-
-

In this tutorial, you'll learn how to replace the push, pop and modal animations with custom transitions & percent driven interactions.

- -
- UIKit - iOS -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/promises-in-swift-for-beginners/index.html b/docs/promises-in-swift-for-beginners/index.html deleted file mode 100644 index 552e556..0000000 --- a/docs/promises-in-swift-for-beginners/index.html +++ /dev/null @@ -1,508 +0,0 @@ - - - - - - - - - - - - Promises in Swift for beginners - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 7 min read - -
-

Promises in Swift for beginners

-
-

Everything you ever wanted to know about futures and promises. The beginner's guide about asynchronous programming in Swift.

-
- -

Sync vs async execution

Writing asynchronous code is one of the hardest part of building an app.

What exactly is the difference between a synchronous and an asynchronous execution? Well, I already explained this in my Dispatch framework tutorial, but here is a quick recap. A synchronous function usually blocks the current thread and returns some value later on. An asynchronous function will instantly return and passes the result value into a completion handler. You can use the GCD framework to perform tasks sync on async on a given queue. Let me show you a quick example:

func aBlockingFunction() -> String {
-    sleep(.random(in: 1...3))
-    return "Hello world!"
-}
-
-func syncMethod() -> String {
-    return aBlockingFunction()
-}
-
-func asyncMethod(completion block: @escaping ((String) -> Void)) {
-    DispatchQueue.global(qos: .background).async {
-        block(aBlockingFunction())
-    }
-}
-
-print(syncMethod())
-print("sync method returned")
-asyncMethod { value in
-    print(value)
-}
-print("async method returned")
-
-// "Hello world!"
-// "sync method returned"
-// "async method returned"
-// "Hello world!"
-

As you can see the async method runs entirely on a background queue, the function won’t block the current thread. This is why the async method can return instantly, so you’ll always see the return output before the last hello output. The async method’s completion block is stored for later execution, that’s the reason why is it possible to call-back and return the string value way after the original function have returned.

What happens if you don’t use a different queue? The completion block will be executed on the current queue, so your function will block it. It’s going to be somewhat async-like, but in reality you’re just moving the return value into a completion block.

func syncMethod() -> String {
-    return "Hello world!"
-}
-
-func fakeAsyncMethod(completion block: ((String) -> Void)) {
-    block("Hello world!")
-}
-
-print(syncMethod())
-print("sync method returned")
-fakeAsyncMethod { value in
-    print(value)
-}
-print("fake async method returned")
-

I don’t really want to focus on completion blocks in this article, that could be a standalone post, but if you are still having trouble with the concurrency model or you don’t understand how tasks and threading works, you should read do a little research first.

Callback hell and the pyramid of doom

What’s the problem with async code? Or what’s the result of writing asynchronous code? The short answer is that you have to use completion blocks (callbacks) in order to handle future results.

The long answer is that managing callbacks sucks. You have to be careful, because in a block you can easily create a retain-cycle, so you have to pass around your variables as weak or unowned references. Also if you have to use multiple async methods, that’ll be a pain in the donkey. Sample time! 🐴

struct Todo: Codable {
-    let id: Int
-    let title: String
-    let completed: Bool
-}
-
-let url = URL(string: "https://jsonplaceholder.typicode.com/todos")!
-
-URLSession.shared.dataTask(with: url) { data, response, error in
-    if let error = error {
-        fatalError("Network error: " + error.localizedDescription)
-    }
-    guard let response = response as? HTTPURLResponse else {
-        fatalError("Not a HTTP response")
-    }
-    guard response.statusCode <= 200, response.statusCode > 300 else {
-        fatalError("Invalid HTTP status code")
-    }
-    guard let data = data else {
-        fatalError("No HTTP data")
-    }
-
-    do {
-        let todos = try JSONDecoder().decode([Todo].self, from: data)
-        print(todos)
-    }
-    catch {
-        fatalError("JSON decoder error: " + error.localizedDescription)
-    }
-}.resume()
-

The snippet above is a simple async HTTP data request. As you can see there are lots of optional values involved, plus you have to do some JSON decoding if you want to use your own types. This is just one request, but what if you’d need to get some detailed info from the first element? Let’s write a helper! #no 🤫

func request(_ url: URL, completion: @escaping ((Data) -> Void)) {
-    URLSession.shared.dataTask(with: url) { data, response, error in
-        if let error = error {
-            fatalError("Network error: " + error.localizedDescription)
-        }
-        guard let response = response as? HTTPURLResponse else {
-            fatalError("Not a HTTP response")
-        }
-        guard response.statusCode <= 200, response.statusCode > 300 else {
-            fatalError("Invalid HTTP status code")
-        }
-        guard let data = data else {
-            fatalError("No HTTP data")
-        }
-        completion(data)
-    }.resume()
-}
-
-let url = URL(string: "https://jsonplaceholder.typicode.com/todos")!
-request(url) { data in
-    do {
-        let todos = try JSONDecoder().decode([Todo].self, from: data)
-        guard let first = todos.first else {
-            return
-        }
-        let url = URL(string: "https://jsonplaceholder.typicode.com/todos/\(first.id)")!
-        request(url) { data in
-            do {
-                let todo = try JSONDecoder().decode(Todo.self, from: data)
-                print(todo)
-            }
-            catch {
-                fatalError("JSON decoder error: " + error.localizedDescription)
-            }
-        }
-    }
-    catch {
-        fatalError("JSON decoder error: " + error.localizedDescription)
-    }
-}
-

See? My problem is that we’re slowly moving down the rabbit hole. Now what if we have a 3rd request? Hell no! You have to nest everything one level deeper again, plus you have to pass around the necessary variables eg. a weak or unowned view controller reference because at some point in time you have to update the entire UI based on the outcome. There must be a better way to fix this. 🤔

Results vs futures vs promises?

The result type was introduced in Swift 5 and it’s extremely good for eliminating the optional factor from the equation. This means you don’t have to deal with an optional data, and an optional error type, but your result is either one of them.

Futures are basically representing a value in the future. The underlying value can be for example a result and it should have one of the following states:

  • pending - no value yet, waiting for it…
  • fulfilled - success, now the result has a value
  • rejected - failed with an error

By definition a futures shouldn’t be writeable by the end-user. This means that developers should not be able to create, fulfill or reject one. But if that’s the case and we follow the rules, how do we make futures?

We promise them. You have to create a promise, which is basically a wrapper around a future that can be written (fulfilled, rejected) or transformed as you want. You don’t write futures, you make promises. However some frameworks allows you to get back the future value of a promise, but you shouldn’t be able to write that future at all.

Enough theory, are you ready to fall in love with promises? ❤️

Promises 101 - a beginner’s guide

Let’s refactor the previous example by using my promise framework!

extension URLSession {
-
-    enum HTTPError: LocalizedError {
-        case invalidResponse
-        case invalidStatusCode
-        case noData
-    }
-
-    func dataTask(url: URL) -> Promise<Data> {
-        return Promise<Data> { [unowned self] fulfill, reject in
-            self.dataTask(with: url) { data, response, error in
-                if let error = error {
-                    reject(error)
-                    return
-                }
-                guard let response = response as? HTTPURLResponse else {
-                    reject(HTTPError.invalidResponse)
-                    return
-                }
-                guard response.statusCode <= 200, response.statusCode > 300 else {
-                    reject(HTTPError.invalidStatusCode)
-                    return
-                }
-                guard let data = data else {
-                    reject(HTTPError.noData)
-                    return
-                }
-                fulfill(data)
-            }.resume()
-        }
-    }
-}
-
-enum TodoError: LocalizedError {
-    case missing
-}
-
-let url = URL(string: "https://jsonplaceholder.typicode.com/todos")!
-URLSession.shared.dataTask(url: url)
-.thenMap { data in
-    return try JSONDecoder().decode([Todo].self, from: data)
-}
-.thenMap { todos -> Todo in
-    guard let first = todos.first else {
-        throw TodoError.missing
-    }
-    return first
-}
-.then { first in
-    let url = URL(string: "https://jsonplaceholder.typicode.com/todos/\(first.id)")!
-    return URLSession.shared.dataTask(url: url)
-}
-.thenMap { data in
-    try JSONDecoder().decode(Todo.self, from: data)
-}
-.onSuccess { todo in
-    print(todo)
-}
-.onFailure(queue: .main) { error in
-    print(error.localizedDescription)
-}
-

What just happened here? Well, I made sort of a promisified version of the data task method implemented on the URLSession object as an extension. Of course you can return the HTTP result or just the status code plus the data if you need further info from the network layer. You can use a new response data model or even a tuple. 🤷‍♂️

Anyway, the more interesting part is the bottom half of the source. As you can see I’m calling the brand new dataTask method which returns a Promise object. As I mentioned this before a promise can be transformed. Or should I say: chained?

Chaining promises is the biggest advantage over callbacks. The source code is not looking like a pyramid anymore with crazy indentations and do-try-catch blocks, but more like a chain of actions. In every single step you can transform your previous result value into something else. If you are familiar with some functional paradigms, it’s going to be really easy to understand the following:

  • thenMap is a simple map on a Promise
  • then is basically flatMap on a Promise
  • onSuccess only gets called if everything was fine in the chain
  • onFailure only gets called if some error happened in the chain
  • always runs always regardless of the outcome

If you want to get the main queue, you can simply pass it through a queue parameter, like I did it with the onFailure method, but it works for every single element in the chain. These functions above are just the tip of the iceberg. You can also tap into a chain, validate the result, put a timeout on it or recover from a failed promise.

There is also a Promises namespace for other useful methods, like zip, which is capable of zipping together 2, 3 or 4 different kind of promises. Just like the Promises.all method the zip function waits until every promise is being completed, then it gives you the result of all the promises in a single block.

//executing same promises from the same kind, eg. [Promise<Data>]
-Promises.all(promises)
-.thenMap { arrayOfResults in
-    // e.g. [Data]
-}
-//zipping together different kind of promises, eg. Proimse<[Todos]>, Promise<Todo>;
-Promises.zip(promise1, promise2)
-.thenMap { result1, result2 in
-    //e.g [Todos], Todo
-}
-

It’s also worth to mention that there is a first, delay, timeout, race, wait and a retry method under the Promises namespace. Feel free to play around with these as well, sometimes they’re extremly useful and powerful too. 💪

There are only two problems with promises

The first issue is cancellation. You can’t simply cancel a running promise. It’s doable, but it requires some advanced or some say “hacky” techniques.

The second one is async / await. If you want to know more about it, you should read the concurrency manifesto by Chis Lattner, but since this is a beginner’s guide, let’s just say that these two keywords can add some syntactic sugar to your code. You won’t need the extra (then, thenMap, onSuccess, onFailure) lines anymore, this way you can focus on your code. I really hope that we’ll get something like this in Swift 6, so I can throw away my Promise library for good. Oh, by the way, libraries…

Promise libraries worth to check

My promise implementation is far from perfect, but it’s a quite simple one (~450 lines of code) and it serves me really well. This blog post by khanlou helped me a lot to understand promises better, you should read it too! 👍

There are lots of promise libraries on github, but if I had to choose from them (instead my own implementation), I’d definitely go with one of the following ones:

  • PromiseKit - The most popular one
  • Promises by Google - feature rich, quite popular as well
  • Promise by Khanlou - small, but based on on the JavaScript Promises/A+ spec
  • SwiftNIO - not an actual promise library, but it has a beautifully written event loop based promise implementation under the hood

Pro tip: don’t try to make your own Promise framework, because multi-threading is extremely hard, and you don’t want to mess around with threads and locks.

Promises are really addictive. Once you start using them, you can’t simply go back and write async code with callbacks anymore. Make a promise today! 😅

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/result-builders-in-swift/index.html b/docs/result-builders-in-swift/index.html deleted file mode 100644 index 7448279..0000000 --- a/docs/result-builders-in-swift/index.html +++ /dev/null @@ -1,733 +0,0 @@ - - - - - - - - - - - - Result builders in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 12 min read - -
-

Result builders in Swift

-
-

If you want to make a result builder in Swift, this article will help you to deal with the most common cases when creating a DSL.

-
- -

Swift result builder basics

The result builder proposal (originally it was called function builders) was implemented in Swift 5.4. This feature allows us to build up a result value using a sequence of components. At first sight, you might think, hey this looks like an array with a series of elements, except the coma in between the items, but nope, this is completely different. But why is it good for us?

Result builder can be used to create entirely new Domain-Specific Languages (DSLs) inside Swift. Creating a DSL has many advantages, since DSLs are usually tied to a specific problem, the syntax that you use to describe the language is very lightweight, yet powerful and capable. Since Swift DSLs are type safe, it is much safer to use one instead of manually concatenate objects. Swift DSLs also allows us to use basic control flows inside these embedded micro-languages. 🤔

Let me give you an example: you can write HTML in Swift, you can simply write out all the tags and glue a bunch of String values together, but that wouldn’t be so safe, right?

func buildWebpage(title: String, body: String) -> String {
-    """
-    <html>
-        <head>
-            <title>\(title)</title>
-        </head>
-        <body>
-            <h1>\(title)</h1>
-            <h1>\(body)</h1>
-        </body>
-    </html>
-    """
-}
-
-let html = buildWebpage(title: "Lorem ipsum", body: "dolor sit amet")
-print(html)
-

We can all agree that this is ugly and the compiler won’t help you to detect the semantic issues at all. Now if we replace the following code with a DSL, we will greatly benefit of the Swift compiler features. Swift will give us type safety, so our code will be less error prone. A DSL can have many constraints and restrictions that’ll help others to write better code. In our case the list of tags is going to be a predefined set of values, so you won’t be able to provide a wrong tag or miss the closing tag, in other words your DSL is going to be syntactically valid. Of course you still can have logical mistakes, but that’s always the case, no matter what tool you choose. 🧠

import SwiftHtml
-
-func buildWebpage(title: String, body: String) -> String {
-    let doc = Document(.unspecified) {
-        Html {
-            Head {
-                Title(title)
-            }
-            Body {
-                H1(title)
-                P(body)
-            }
-        }
-    }
-    return DocumentRenderer().render(doc)
-}
-

As you can see the snippet above looks way more Swifty and we were also able to remove the duplicate HTML closing tags from the code. We don’t have to write the < and > characters at all and the compiler can type check everything for us, so type-o accidents can’t happen. ✅

Before you think that result builders are just syntactic sugar over underlying data types, I have to assure you that they are far more complex than this. It is an extremely advanced and powerful feature that you should definitely know about.

You can create all kinds of result builders, for example I’m using them to build validators, user interface elements and layout constraints. Of course SGML (HTML, XML) and CSS is also a great use-case, but the list is endless. Let me show you how to build a simple result builder.

Building a HTML tree structure

I’m going to show you how I created my SwiftHtml HTML DSL library, because it was a fun project to work with and I’ve learned a lot about it, it’s also going to replace the Leaf/Tau template in my future projects. The main idea behind SwiftHtml was that I wanted to follow the HTML specifications as closely as possible. So I’ve created a Node structure to represent a node inside the document tree.

public struct Node {
-
-    public enum `Type` {
-        case standard     // <name>contents</name>
-        case comment      // <!-- contents -->
-        case empty        // <name>
-        case group        // *group*<p>Lorem ipsum</p>*group*
-    }
-
-    public let type: `Type`
-    public let name: String?
-    public let contents: String?
-
-    public init(type: `Type` = .standard,
-                name: String? = nil,
-                contents: String? = nil) {
-        self.type = type
-        self.name = name
-        self.contents = contents
-    }
-}
-

A node has four variants defined by the Type. A standard node will render as a standard HTML tag using the name and the contents. A comment will only use the contents and empty tag won’t have a closing tag and use the name property as a tag name. Finally the group node will be used to group together multiple nodes, it won’t render anything, it’s just a grouping element for other tags.

The trick in my solution is that these Node objects only contain the visual representation of a tag, but I’ve decided to separate the hierarchical relationship from this level. That’s why I actually introduced a Tag class that can have multiple children. In my previous article I showed multiple ways to build a tree structure using Swift, I’ve experimented with all the possible solutions and my final choice was to use reference types instead of value types. Don’t hate me. 😅

open class Tag {
-
-    public var node: Node
-    public var children: [Tag]
-
-    public init(_ node: Node, children: [Tag] = []) {
-        self.node = node
-        self.children = children
-    }
-
-}
-

Now this is how a Tag object looks like, it’s pretty simple. It has an underlying node and a bunch of children. It is possible to extend this tag and provide functionalities for all the HTML tags, such as the capability of adding common attributes and I’m also able to create subclasses for the tags.

public final class Html: Tag {
-
-    public init(_ children: [Tag]) {
-        super.init(.init(type: .standard, name: "html", contents: nil), children: children)
-    }
-}
-
-public final class Head: Tag {
-
-    public init(_ children: [Tag]) {
-        super.init(.init(type: .standard, name: "head", contents: nil), children: children)
-    }
-}
-
-public final class Title: Tag {
-
-    public init(_ contents: String) {
-        super.init(.init(type: .standard, name: "title", contents: contents))
-    }
-}
-
-public final class Body: Tag {
-
-    public init(_ children: [Tag]) {
-        super.init(.init(type: .standard, name: "body", contents: nil), children: children)
-    }
-}
-
-public final class H1: Tag {
-
-    public init(_ contents: String) {
-        super.init(.init(type: .standard, name: "h1", contents: contents))
-    }
-}
-
-public final class P: Tag {
-
-    public init(_ contents: String) {
-        super.init(.init(type: .standard, name: "p", contents: contents))
-    }
-}
-

All right, now we are able to initialize our Tag tree, but I warn you, it’s going to look very awkward.

func buildWebpage(title: String, body: String) -> Html {
-    Html([
-        Head([
-            Title(title),
-        ]),
-        Body([
-            H1(title),
-            P(body),
-        ]),
-    ])
-}
-

It is still not possible to render the tree and the syntax is not so eye-catchy. It’s time to make things better and we should definitely introduce some result builders for good.

The anatomy of Swift result builders
Now that we have our data structure prepared, we should focus on the DSL itself. Before we dive in, I highly recommend to carefully read the official proposal and watch this WWDC video about result builders, since both resources are amazing. 🤓

Building an array of elements

The main thing that I don’t like about our previous buildWebpage function is that I have to constantly write brackets and comas, in order to build our structure. This can be easily eliminated by introducing a new result builder for the Tag objects. We just have to mark an enum with the @resultBuilder attribute and provide a static buildBlock method with the given type.

@resultBuilder
-public enum TagBuilder {
-    public static func buildBlock(_ components: Tag...) -> [Tag] {
-        components
-    }
-}
-

This will allow us to use a list of components inside of our DSL building blocks, but before we could use it we also have to change our specific HTML tag init methods to take advantage of this newly created result builder. Just use a closure with the return type that we want to use and mark the entire function argument with the @TagBuilder keyword.

public final class Html: Tag {
-    public init(@TagBuilder _ builder: () -> [Tag]) {
-        super.init(.init(type: .standard, name: "html", contents: nil), children: builder())
-    }
-}
-
-public final class Head: Tag {
-    public init(@TagBuilder _ builder: () -> [Tag]) {
-        super.init(.init(type: .standard, name: "head", contents: nil), children: builder())
-    }
-}
-
-public final class Body: Tag {
-    public init(@TagBuilder _ builder: () -> [Tag]) {
-        super.init(.init(type: .standard, name: "body", contents: nil), children: builder())
-    }
-}
-

Now we can refactor the build webpage method since it can now use the underlying result builder to construct the building blocks based on the components. If you take a look at the introduction section inside the proposal you’ll get a better idea about what happens under the hood.

func buildWebpage(title: String, body: String) -> Html {
-    Html {
-        Head {
-            Title(title)
-        }
-        Body {
-            H1(title)
-            P(body)
-        }
-    }
-}
-
-let html = buildWebpage(title: "title", body: "body")
-

Anyway, it’s quite magical how we can transform our complex array based code into something clean and nice by taking advantage of the Swift compiler. I love this approach, but there is more.

Optionals and further build blocks

If you want to provide if support inside your DSL you have to implement some additional methods inside your result builder object. Try this code, but it won’t compile:

func buildWebpage(title: String, body: String) -> Html {
-    Html {
-        Head {
-            Title(title)
-        }
-        Body {
-            if title == "magic" {
-                H1(title)
-                P(body)
-            }
-        }
-    }
-}
-

The build an optional result with an if statement we have to think about what happens here. If the title is magic we would like to return an array of Tags, otherwise nil. So this could be expressed as a [Tag]? type but we always want to have a bunch of [Tag] elements, now this is easy.

@resultBuilder
-public enum TagBuilder {
-
-    public static func buildBlock(_ components: Tag...) -> [Tag] {
-        components
-    }
-
-    public static func buildOptional(_ component: [Tag]?) -> [Tag] {
-        component ?? []
-    }
-}
-

But wait, why is it not working? Well, since we return an array of tags, but the outer Body element was expecting Tag elements one after another, so a [Tag] array won’t fit our needs there. What can we do about this? Well, we can introduce a new buildBlock method that can transform our [Tag]... values into a plain Tag array. Let me show you real this quick.

@resultBuilder
-public enum TagBuilder {
-
-    public static func buildBlock(_ components: Tag...) -> [Tag] {
-        components
-    }
-    
-    public static func buildBlock(_ components: [Tag]...) -> [Tag] {
-        components.flatMap { $0 }
-    }
-
-    public static func buildOptional(_ component: [Tag]?) -> [Tag] {
-        component ?? []
-    }
-}
-
-func buildWebpage(title: String, body: String) -> Html {
-    Html {
-        Head {
-            Title(title)
-        }
-        Body { // expects Tag... 
-            // but the first build block transforms it to [Tag]
-
-            // returns [Tag], but we'd need Tag...
-            if title == "magic" { 
-                H1("Hello")
-                P("World")
-            } 
-
-            // this could also returns [Tag]
-            // if title = "other" {
-            //    H1("Other")
-            //    P("World")  
-            //} 
-
-            // both if block returns [Tag], that's [Tag]... here
-
-            // ...the new build block transforms [Tag]... into [Tag], 
-            // which is just fine for the body init method
-    }
-}
-

I hope it’s not too complicated, but it’s all about building the proper return type for the underlying method. We wanted to have just an array of tags, but with the if support we’ve ended up with a list of tag arrays, that’s why we have to transform it back to a flattened array of tags with the new build block. If you want to take a look at a more simple example, you should read this post. ☺️

If and else support and either blocks

If blocks can return optional values, now what about if-else blocks? Well, it’s quite a similar approach, we just want to return either the first or the second array of tags.

@resultBuilder
-public enum TagBuilder {
-
-    public static func buildBlock(_ components: Tag...) -> [Tag] {
-        components
-    }
-    
-    public static func buildBlock(_ components: [Tag]...) -> [Tag] {
-        components.flatMap { $0 }
-    }    
-
-    public static func buildOptional(_ component: [Tag]?) -> [Tag] {
-        component ?? []
-    }
-
-    public static func buildEither(first component: [Tag]) -> [Tag] {
-        component
-    }
-
-    public static func buildEither(second component: [Tag]) -> [Tag] {
-        component
-    }
-}
-
-func buildWebpage(title: String, body: String) -> Html {
-    Html {
-        Head {
-            Title(title)
-        }
-        Body {
-            if title == "magic" {
-                H1("Hello")
-                P("World")
-            }
-            else {
-                P(body)
-            }
-        }
-    }
-}
-
-let html = buildWebpage(title: "title", body: "body")
-

As you can see now we don’t need additional building blocks, since we’ve already covered the variadic Tag array issue with the optional support. Now it is possible to write if and else blocks inside our HTML DSL. Looks pretty nice so far, what’s next? 🧐

Enabling for loops and maps through expressions

Imagine that you have a bunch of paragraphs inside of the body that you’d like to use. Pretty easy, right? Just change the body into an array of strings and use a for loop to transform them into P tags.

func buildWebpage(title: String, paragraphs: [String]) -> Html {
-    Html {
-        Head {
-            Title(title)
-        }
-        Body {
-            H1(title)
-            for item in paragraphs {
-                P(item)
-            }
-        }
-    }
-}
-
-let html = buildWebpage(title: "title", paragraphs: ["a", "b", "c"])
-

Not so fast, what’s the actual return type here and how can we solve the problem? Of course the first impression is that we are returning a Tag, but in reality we’d like to be able to return multiple tags from a for loop, so it’s a [Tag], in the end, it’s going to be an array of Tag arrays: [[Tag]].

The buildArray method can transform these array of tag arrays into Tag arrays, that’s good enough to provide for support, but we still need one more method to be able to use it properly. We have to build an expression from a single Tag to turn it into an array of tags. 🔖

@resultBuilder
-public enum TagBuilder {
-
-    public static func buildBlock(_ components: Tag...) -> [Tag] {
-        components
-    }
-    
-    public static func buildBlock(_ components: [Tag]...) -> [Tag] {
-        components.flatMap { $0 }
-    }
-
-    public static func buildEither(first component: [Tag]) -> [Tag] {
-        component
-    }
-
-    public static func buildEither(second component: [Tag]) -> [Tag] {
-        component
-    }
-
-    public static func buildOptional(_ component: [Tag]?) -> [Tag] {
-        component ?? []
-    }
-
-    public static func buildExpression(_ expression: Tag) -> [Tag] {
-        [expression]
-    }
-
-    public static func buildArray(_ components: [[Tag]]) -> [Tag] {
-        components.flatMap { $0 }
-    }
-}
-

This way our for loop will work. The build expression method is very powerful, it enables us to provide various input types and turn them into the data type that we actually need. I’m going to show you one more build expression example in this case to support the map function on an array of elements. This is the final result builder:

@resultBuilder
-public enum TagBuilder {
-
-    public static func buildBlock(_ components: Tag...) -> [Tag] {
-        components
-    }
-    
-    public static func buildBlock(_ components: [Tag]...) -> [Tag] {
-        components.flatMap { $0 }
-    }
-
-
-    public static func buildEither(first component: [Tag]) -> [Tag] {
-        component
-    }
-
-    public static func buildEither(second component: [Tag]) -> [Tag] {
-        component
-    }
-
-    public static func buildOptional(_ component: [Tag]?) -> [Tag] {
-        component ?? []
-    }
-
-    public static func buildExpression(_ expression: Tag) -> [Tag] {
-        [expression]
-    }
-
-    public static func buildExpression(_ expression: [Tag]) -> [Tag] {
-        expression
-    }
-
-    public static func buildArray(_ components: [[Tag]]) -> [Tag] {
-        components.flatMap { $0 }
-    }
-}
-

Now we can use maps instead of for loops if we prefer functional methods. 😍

func buildWebpage(title: String, paragraphs: [String]) -> Html {
-    Html {
-        Head {
-            Title(title)
-        }
-        Body {
-            H1(title)
-            paragraphs.map { P($0) }
-        }
-    }
-}
-
-let html = buildWebpage(title: "title", paragraphs: ["a", "b", "c"])
-

That’s how I was able to create a DSL for my Tag hierarchy. Please note that I might had some things wrong, this was the very first DSL that I’ve made, but so far so good, it serves all my needs.

A simple HTML renderer

Before we close this article I’d like to show you how I created my HTML document renderer.

struct Renderer {
-
-    func render(tag: Tag, level: Int = 0) -> String {
-        let indent = 4
-        let spaces = String(repeating: " ", count: level * indent)
-        switch tag.node.type {
-        case .standard:
-            return spaces + open(tag) + (tag.node.contents ?? "") + renderChildren(tag, level: level, spaces: spaces) + close(tag)
-        case .comment:
-            return spaces + "<!--" + (tag.node.contents ?? "") + "-->"
-        case .empty:
-            return spaces + open(tag)
-        case .group:
-            return spaces + (tag.node.contents ?? "") + renderChildren(tag, level: level, spaces: spaces)
-        }
-    }
-
-    private func renderChildren(_ tag: Tag, level: Int, spaces: String) -> String {
-        var children = tag.children.map { render(tag: $0, level: level + 1) }.joined(separator: "\n")
-        if !children.isEmpty {
-            children = "\n" + children + "\n" + spaces
-        }
-        return children
-    }
-    
-    private func open(_ tag: Tag) -> String {
-        return "<" + tag.node.name! + ">"
-    }
-    
-    private func close(_ tag: Tag) -> String {
-        "</" + tag.node.name! + ">"
-    }
-}
-

As you can see it’s a pretty simple, yet complex struct. The open and close methods are straightforward, the interesting part happens in the render methods. The very first render function can render a tag using the node type. We just switch the type and return the HTML value according to it. if the node is a standard or a group type we also render the children using the same method.

Of course the final implementation is a bit more complex, it involves HTML attributes, it supports minification and custom indentation level, but for educational purposes this lightweight version is more than enough. Here’s the final code snippet to render a HTML structure:

func buildWebpage(title: String, paragraphs: [String]) -> Html {
-    Html {
-        Head {
-            Title(title)
-        }
-        Body {
-            H1(title)
-            paragraphs.map { P($0) }
-        }
-    }
-}
-
-let html = buildWebpage(title: "title", paragraphs: ["a", "b", "c"])
-let output = Renderer().render(tag: html)
-print(output)
-

If we compare this to our very first string based solution we can say that the difference is huge. Honestly speaking I was afraid of result builders for a very long time, I thought it’s just unnecessary complexity and we don’t really need them, but hey things change, and I’ve also changed my mind about this feature. Now I can’t live without result builders and I love the code that I’m able to write by using them. I really hope that this article helped you to understand them a bit better. 🙏

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/rss.xml b/docs/rss.xml deleted file mode 100644 index c671ec5..0000000 --- a/docs/rss.xml +++ /dev/null @@ -1,994 +0,0 @@ - - - The.Swift.Dev. - Articles about application development using the Swift programming language. - https://theswiftdev.com - - Tue, 20 May 2025 16:00:03 +0200 - Fri, 16 May 2025 09:46:15 +0200 - 250 - - - - https://theswiftdev.com/10-little-uikit-tips-you-should-know/ - <![CDATA[ 10 little UIKit tips you should know ]]> - - https://theswiftdev.com/10-little-uikit-tips-you-should-know/ - Thu, 03 Feb 2022 16:20:00 +0100 - - - https://theswiftdev.com/10-short-advices-that-will-make-you-a-better-vapor-developer-right-away/ - <![CDATA[ 10 short advices that will make you a better Vapor developer right away ]]> - - https://theswiftdev.com/10-short-advices-that-will-make-you-a-better-vapor-developer-right-away/ - Wed, 15 Jul 2020 16:20:00 +0200 - - - https://theswiftdev.com/a-generic-crud-solution-for-vapor-4/ - <![CDATA[ A generic CRUD solution for Vapor 4 ]]> - - https://theswiftdev.com/a-generic-crud-solution-for-vapor-4/ - Wed, 01 Apr 2020 16:20:00 +0200 - - - https://theswiftdev.com/a-simple-http2-server-using-vapor-4/ - <![CDATA[ A simple HTTP/2 server using Vapor 4 ]]> - - https://theswiftdev.com/a-simple-http2-server-using-vapor-4/ - Tue, 08 Oct 2019 16:20:00 +0200 - - - https://theswiftdev.com/ajax-calls-using-vapor-4/ - <![CDATA[ AJAX calls using Vapor 4 ]]> - - https://theswiftdev.com/ajax-calls-using-vapor-4/ - Fri, 18 Dec 2020 16:20:00 +0100 - - - https://theswiftdev.com/all-about-authentication-in-vapor-4/ - <![CDATA[ All about authentication in Vapor 4 ]]> - - https://theswiftdev.com/all-about-authentication-in-vapor-4/ - Tue, 07 Apr 2020 16:20:00 +0200 - - - https://theswiftdev.com/all-about-the-bool-type-in-swift/ - <![CDATA[ All about the Bool type in Swift ]]> - - https://theswiftdev.com/all-about-the-bool-type-in-swift/ - Fri, 10 Sep 2021 16:20:00 +0200 - - - https://theswiftdev.com/all-about-the-swift-package-manager-and-the-swift-toolchain/ - <![CDATA[ All about the Swift Package Manager and the Swift toolchain ]]> - - https://theswiftdev.com/all-about-the-swift-package-manager-and-the-swift-toolchain/ - Mon, 14 Jan 2019 16:20:00 +0100 - - - https://theswiftdev.com/async-http-api-clients-in-swift/ - <![CDATA[ Async HTTP API clients in Swift ]]> - - https://theswiftdev.com/async-http-api-clients-in-swift/ - Fri, 18 Mar 2022 16:20:00 +0100 - - - https://theswiftdev.com/asynchronous-validation-for-vapor/ - <![CDATA[ Asynchronous validation for Vapor ]]> - - https://theswiftdev.com/asynchronous-validation-for-vapor/ - Tue, 10 Oct 2017 16:20:00 +0200 - - - https://theswiftdev.com/awesome-native-xcode-extensions/ - <![CDATA[ Awesome native Xcode extensions ]]> - - https://theswiftdev.com/awesome-native-xcode-extensions/ - Wed, 04 Oct 2017 16:20:00 +0200 - - - https://theswiftdev.com/beginners-guide-to-functional-swift/ - <![CDATA[ Beginners guide to functional Swift ]]> - - https://theswiftdev.com/beginners-guide-to-functional-swift/ - Tue, 05 Feb 2019 16:20:00 +0100 - - - https://theswiftdev.com/beginners-guide-to-modern-generic-programming-in-swift/ - <![CDATA[ Beginner's guide to modern generic programming in Swift ]]> - - https://theswiftdev.com/beginners-guide-to-modern-generic-programming-in-swift/ - Tue, 28 Jun 2022 16:20:00 +0200 - - - https://theswiftdev.com/beginners-guide-to-server-side-swift-using-the-hummingbird-framework/ - <![CDATA[ Beginner's guide to server-side Swift using the Hummingbird framework ]]> - - https://theswiftdev.com/beginners-guide-to-server-side-swift-using-the-hummingbird-framework/ - Wed, 08 Mar 2023 16:20:00 +0100 - - - https://theswiftdev.com/beginners-guide-to-server-side-swift-using-vapor-4/ - <![CDATA[ Beginner's guide to Server side Swift using Vapor 4 ]]> - - https://theswiftdev.com/beginners-guide-to-server-side-swift-using-vapor-4/ - Mon, 13 Jan 2020 16:20:00 +0100 - - - https://theswiftdev.com/beginners-guide-to-swift-arrays/ - <![CDATA[ Beginner's guide to Swift arrays ]]> - - https://theswiftdev.com/beginners-guide-to-swift-arrays/ - Thu, 10 Mar 2022 16:20:00 +0100 - - - https://theswiftdev.com/beginners-guide-to-swift-package-manager-command-plugins/ - <![CDATA[ Beginner's guide to Swift package manager command plugins ]]> - - https://theswiftdev.com/beginners-guide-to-swift-package-manager-command-plugins/ - Mon, 16 May 2022 16:20:00 +0200 - - - https://theswiftdev.com/beginners-guide-to-the-asyncawait-concurrency-api-in-vapor-fluent/ - <![CDATA[ Beginner's guide to the async/await concurrency API in Vapor & Fluent ]]> - - https://theswiftdev.com/beginners-guide-to-the-asyncawait-concurrency-api-in-vapor-fluent/ - Thu, 03 Jun 2021 16:20:00 +0200 - - - https://theswiftdev.com/building-a-global-storage-for-vapor/ - <![CDATA[ Building a global storage for Vapor ]]> - - https://theswiftdev.com/building-a-global-storage-for-vapor/ - Thu, 16 Dec 2021 16:20:00 +0100 - - - https://theswiftdev.com/building-and-loading-dynamic-libraries-at-runtime-in-swift/ - <![CDATA[ Building and loading dynamic libraries at runtime in Swift ]]> - - https://theswiftdev.com/building-and-loading-dynamic-libraries-at-runtime-in-swift/ - Wed, 20 May 2020 16:20:00 +0200 - - - https://theswiftdev.com/building-input-forms-for-ios-apps/ - <![CDATA[ Building input forms for iOS apps ]]> - - https://theswiftdev.com/building-input-forms-for-ios-apps/ - Thu, 23 May 2019 16:20:00 +0200 - - - https://theswiftdev.com/building-static-and-dynamic-swift-libraries-using-the-swift-compiler/ - <![CDATA[ Building static and dynamic Swift libraries using the Swift compiler ]]> - - https://theswiftdev.com/building-static-and-dynamic-swift-libraries-using-the-swift-compiler/ - Tue, 16 Feb 2021 16:20:00 +0100 - - - https://theswiftdev.com/building-tree-data-structures-in-swift/ - <![CDATA[ Building tree data structures in Swift ]]> - - https://theswiftdev.com/building-tree-data-structures-in-swift/ - Tue, 23 Nov 2021 16:20:00 +0100 - - - https://theswiftdev.com/clockkit-complications-cheat-sheet/ - <![CDATA[ ClockKit complications cheatsheet ]]> - - https://theswiftdev.com/clockkit-complications-cheat-sheet/ - Thu, 28 Apr 2016 16:20:00 +0200 - - - https://theswiftdev.com/comparing-factory-design-patterns/ - <![CDATA[ Comparing factory design patterns ]]> - - https://theswiftdev.com/comparing-factory-design-patterns/ - Tue, 05 Jun 2018 16:20:00 +0200 - - - https://theswiftdev.com/conventions-for-xcode/ - <![CDATA[ Conventions for Xcode ]]> - - https://theswiftdev.com/conventions-for-xcode/ - Wed, 06 Jul 2016 16:20:00 +0200 - - - https://theswiftdev.com/custom-uiview-subclass-from-a-xib-file/ - <![CDATA[ Custom UIView subclass from a xib file ]]> - - https://theswiftdev.com/custom-uiview-subclass-from-a-xib-file/ - Tue, 16 Oct 2018 16:20:00 +0200 - - - https://theswiftdev.com/custom-views-input-forms-and-mistakes/ - <![CDATA[ Custom views, input forms and mistakes ]]> - - https://theswiftdev.com/custom-views-input-forms-and-mistakes/ - Mon, 21 Oct 2019 16:20:00 +0200 - - - https://theswiftdev.com/custom-working-directory-in-xcode/ - <![CDATA[ Custom working directory in Xcode ]]> - - https://theswiftdev.com/custom-working-directory-in-xcode/ - Thu, 07 Jan 2021 16:20:00 +0100 - - - https://theswiftdev.com/declarative-unit-tests-for-vapor/ - <![CDATA[ Declarative unit tests for Vapor ]]> - - https://theswiftdev.com/declarative-unit-tests-for-vapor/ - Tue, 04 May 2021 16:20:00 +0200 - - - https://theswiftdev.com/deep-dive-into-swift-frameworks/ - <![CDATA[ Deep dive into Swift frameworks ]]> - - https://theswiftdev.com/deep-dive-into-swift-frameworks/ - Thu, 25 Jan 2018 16:20:00 +0100 - - - https://theswiftdev.com/dynamic-libraries-and-code-replacements-in-swift/ - <![CDATA[ Dynamic libraries and code replacements in Swift ]]> - - https://theswiftdev.com/dynamic-libraries-and-code-replacements-in-swift/ - Thu, 20 May 2021 16:20:00 +0200 - - - https://theswiftdev.com/easy-multipart-file-upload-for-swift/ - <![CDATA[ Easy multipart file upload for Swift ]]> - - https://theswiftdev.com/easy-multipart-file-upload-for-swift/ - Tue, 17 Jan 2023 16:20:00 +0100 - - - https://theswiftdev.com/encoding-and-decoding-data-using-the-hummingbird-framework/ - <![CDATA[ Encoding and decoding data using the Hummingbird framework ]]> - - https://theswiftdev.com/encoding-and-decoding-data-using-the-hummingbird-framework/ - Wed, 22 Mar 2023 16:20:00 +0100 - - - https://theswiftdev.com/event-driven-generic-hooks-for-swift/ - <![CDATA[ Event-driven generic hooks for Swift ]]> - - https://theswiftdev.com/event-driven-generic-hooks-for-swift/ - Fri, 27 Nov 2020 16:20:00 +0100 - - - https://theswiftdev.com/everything-about-public-and-private-swift-attributes/ - <![CDATA[ Everything about public and private Swift attributes ]]> - - https://theswiftdev.com/everything-about-public-and-private-swift-attributes/ - Tue, 10 Oct 2017 16:20:00 +0200 - - - https://theswiftdev.com/file-upload-api-server-in-vapor-4/ - <![CDATA[ File upload API server in Vapor 4 ]]> - - https://theswiftdev.com/file-upload-api-server-in-vapor-4/ - Wed, 30 Dec 2020 16:20:00 +0100 - - - https://theswiftdev.com/file-upload-using-vapor-4/ - <![CDATA[ File upload using Vapor 4 ]]> - - https://theswiftdev.com/file-upload-using-vapor-4/ - Thu, 10 Dec 2020 16:20:00 +0100 - - - https://theswiftdev.com/generating-random-numbers-in-swift/ - <![CDATA[ Generating random numbers in Swift ]]> - - https://theswiftdev.com/generating-random-numbers-in-swift/ - Tue, 07 Aug 2018 16:20:00 +0200 - - - https://theswiftdev.com/get-started-with-the-fluent-orm-framework-in-vapor-4/ - <![CDATA[ Get started with the Fluent ORM framework in Vapor 4 ]]> - - https://theswiftdev.com/get-started-with-the-fluent-orm-framework-in-vapor-4/ - Thu, 27 Feb 2020 16:20:00 +0100 - - - https://theswiftdev.com/getting-started-with-swiftio/ - <![CDATA[ Getting started with SwiftIO ]]> - - https://theswiftdev.com/getting-started-with-swiftio/ - Thu, 12 Nov 2020 16:20:00 +0100 - - - https://theswiftdev.com/how-to-build-better-command-line-apps-and-tools-using-swift/ - <![CDATA[ How to build better command line apps and tools using Swift? ]]> - - https://theswiftdev.com/how-to-build-better-command-line-apps-and-tools-using-swift/ - Thu, 05 Aug 2021 16:20:00 +0200 - - - https://theswiftdev.com/how-to-build-macos-apps-using-only-the-swift-package-manager/ - <![CDATA[ How to build macOS apps using only the Swift Package Manager? ]]> - - https://theswiftdev.com/how-to-build-macos-apps-using-only-the-swift-package-manager/ - Mon, 26 Oct 2020 16:20:00 +0100 - - - https://theswiftdev.com/how-to-build-swiftui-apps-using-viper/ - <![CDATA[ How to build SwiftUI apps using VIPER? ]]> - - https://theswiftdev.com/how-to-build-swiftui-apps-using-viper/ - Wed, 18 Sep 2019 16:20:00 +0200 - - - https://theswiftdev.com/how-to-call-c-code-from-swift/ - <![CDATA[ How to call C code from Swift ]]> - - https://theswiftdev.com/how-to-call-c-code-from-swift/ - Mon, 15 Jan 2018 16:20:00 +0100 - - - https://theswiftdev.com/how-to-create-a-swift-package-collection/ - <![CDATA[ How to create a Swift package collection? ]]> - - https://theswiftdev.com/how-to-create-a-swift-package-collection/ - Thu, 20 Jan 2022 16:20:00 +0100 - - - https://theswiftdev.com/how-to-create-reusable-views-for-modern-collection-views/ - <![CDATA[ How to create reusable views for modern collection views? ]]> - - https://theswiftdev.com/how-to-create-reusable-views-for-modern-collection-views/ - Tue, 10 Oct 2017 16:20:00 +0200 - - - https://theswiftdev.com/how-to-create-your-first-website-using-vapor-4-and-leaf/ - <![CDATA[ How to create your first website using Vapor 4 and Leaf? ]]> - - https://theswiftdev.com/how-to-create-your-first-website-using-vapor-4-and-leaf/ - Thu, 13 Feb 2020 16:20:00 +0100 - - - https://theswiftdev.com/how-to-define-strings-use-escaping-sequences-and-interpolations/ - <![CDATA[ How to define strings, use escaping sequences and interpolations? ]]> - - https://theswiftdev.com/how-to-define-strings-use-escaping-sequences-and-interpolations/ - Wed, 16 Sep 2020 16:20:00 +0200 - - - https://theswiftdev.com/how-to-design-type-safe-restful-apis-using-swift-and-vapor/ - <![CDATA[ How to design type safe RESTful APIs using Swift & Vapor? ]]> - - https://theswiftdev.com/how-to-design-type-safe-restful-apis-using-swift-and-vapor/ - Thu, 29 Apr 2021 16:20:00 +0200 - - - https://theswiftdev.com/how-to-download-files-with-urlsession-using-combine-publishers-and-subscribers/ - <![CDATA[ How to download files with URLSession using Combine Publishers and Subscribers? ]]> - - https://theswiftdev.com/how-to-download-files-with-urlsession-using-combine-publishers-and-subscribers/ - Tue, 28 Jan 2020 16:20:00 +0100 - - - https://theswiftdev.com/how-to-launch-a-macos-app-at-login/ - <![CDATA[ How to launch a macOS app at login? ]]> - - https://theswiftdev.com/how-to-launch-a-macos-app-at-login/ - Fri, 27 Oct 2017 16:20:00 +0200 - - - https://theswiftdev.com/how-to-make-a-swift-framework/ - <![CDATA[ How to make a Swift framework? ]]> - - https://theswiftdev.com/how-to-make-a-swift-framework/ - Mon, 23 Oct 2017 16:20:00 +0200 - - - https://theswiftdev.com/how-to-parse-json-in-swift-using-codable-protocol/ - <![CDATA[ How to parse JSON in Swift using Codable protocol? ]]> - - https://theswiftdev.com/how-to-parse-json-in-swift-using-codable-protocol/ - Mon, 29 Jan 2018 16:20:00 +0100 - - - https://theswiftdev.com/how-to-set-up-pgsql-for-fluent-4/ - <![CDATA[ How to set up pgSQL for Fluent 4? ]]> - - https://theswiftdev.com/how-to-set-up-pgsql-for-fluent-4/ - Tue, 25 Feb 2020 16:20:00 +0100 - - - https://theswiftdev.com/how-to-store-keys-in-env-files/ - <![CDATA[ How to store keys in env files? ]]> - - https://theswiftdev.com/how-to-store-keys-in-env-files/ - Tue, 30 Jun 2020 16:20:00 +0200 - - - https://theswiftdev.com/how-to-use-a-swift-library-in-c/ - <![CDATA[ How to use a Swift library in C ]]> - - https://theswiftdev.com/how-to-use-a-swift-library-in-c/ - Wed, 23 Feb 2022 16:20:00 +0100 - - - https://theswiftdev.com/how-to-use-c-libraries-in-swift/ - <![CDATA[ How to use C libraries in Swift? ]]> - - https://theswiftdev.com/how-to-use-c-libraries-in-swift/ - Fri, 05 Mar 2021 16:20:00 +0100 - - - https://theswiftdev.com/how-to-use-icloud-drive-documents/ - <![CDATA[ How to use iCloud drive documents? ]]> - - https://theswiftdev.com/how-to-use-icloud-drive-documents/ - Thu, 17 May 2018 16:20:00 +0200 - - - https://theswiftdev.com/how-to-use-middlewares-in-vapor-4/ - <![CDATA[ How to use middlewares in Vapor 4? ]]> - - https://theswiftdev.com/how-to-use-middlewares-in-vapor-4/ - Tue, 17 Mar 2020 16:20:00 +0100 - - - https://theswiftdev.com/how-to-use-the-result-type-to-handle-errors-in-swift/ - <![CDATA[ How to use the result type to handle errors in Swift 5? ]]> - - https://theswiftdev.com/how-to-use-the-result-type-to-handle-errors-in-swift/ - Mon, 28 Jan 2019 16:20:00 +0100 - - - https://theswiftdev.com/how-to-write-html-in-swift/ - <![CDATA[ How to write HTML in Swift? ]]> - - https://theswiftdev.com/how-to-write-html-in-swift/ - Wed, 12 Jan 2022 16:20:00 +0100 - - - https://theswiftdev.com/how-to-write-services-for-viper/ - <![CDATA[ How to write services for VIPER? ]]> - - https://theswiftdev.com/how-to-write-services-for-viper/ - Wed, 25 Sep 2019 16:20:00 +0200 - - - https://theswiftdev.com/how-to-write-swift-scripts-using-the-new-command-api-in-vapor-4/ - <![CDATA[ How to write Swift scripts using the new Command API in Vapor 4? ]]> - - https://theswiftdev.com/how-to-write-swift-scripts-using-the-new-command-api-in-vapor-4/ - Tue, 03 Mar 2020 16:20:00 +0100 - - - https://theswiftdev.com/hummingbird-routing-and-requests/ - <![CDATA[ Hummingbird routing and requests ]]> - - https://theswiftdev.com/hummingbird-routing-and-requests/ - Fri, 17 Mar 2023 16:20:00 +0100 - - - https://theswiftdev.com/introduction-to-asyncawait-in-swift/ - <![CDATA[ Introduction to async/await in Swift ]]> - - https://theswiftdev.com/introduction-to-asyncawait-in-swift/ - Tue, 25 May 2021 16:20:00 +0200 - - - https://theswiftdev.com/introduction-to-spm-artifact-bundles/ - <![CDATA[ Introduction to SPM artifact bundles ]]> - - https://theswiftdev.com/introduction-to-spm-artifact-bundles/ - Tue, 24 May 2022 16:20:00 +0200 - - - https://theswiftdev.com/ios-auto-layout-tutorial-programmatically/ - <![CDATA[ iOS Auto Layout tutorial programmatically ]]> - - https://theswiftdev.com/ios-auto-layout-tutorial-programmatically/ - Tue, 31 Oct 2017 16:20:00 +0100 - - - https://theswiftdev.com/ios-custom-transition-tutorial-in-swift/ - <![CDATA[ iOS custom transition tutorial in Swift ]]> - - https://theswiftdev.com/ios-custom-transition-tutorial-in-swift/ - Thu, 26 Apr 2018 16:20:00 +0200 - - - https://theswiftdev.com/iterator-design-pattern-in-swift/ - <![CDATA[ Iterator design pattern in Swift ]]> - - https://theswiftdev.com/iterator-design-pattern-in-swift/ - Sun, 19 Aug 2018 16:20:00 +0200 - - - https://theswiftdev.com/lazy-initialization-in-swift/ - <![CDATA[ Lazy initialization in Swift ]]> - - https://theswiftdev.com/lazy-initialization-in-swift/ - Mon, 17 Dec 2018 16:20:00 +0100 - - - https://theswiftdev.com/lenses-and-prisms-in-swift/ - <![CDATA[ Lenses and prisms in Swift ]]> - - https://theswiftdev.com/lenses-and-prisms-in-swift/ - Fri, 12 Aug 2022 16:20:00 +0200 - - - https://theswiftdev.com/logging-for-beginners-in-swift/ - <![CDATA[ Logging for beginners in Swift ]]> - - https://theswiftdev.com/logging-for-beginners-in-swift/ - Wed, 30 Sep 2020 16:20:00 +0200 - - - https://theswiftdev.com/mastering-ios-auto-layout-anchors-programmatically-from-swift/ - <![CDATA[ Mastering iOS auto layout anchors programmatically from Swift ]]> - - https://theswiftdev.com/mastering-ios-auto-layout-anchors-programmatically-from-swift/ - Thu, 14 Jun 2018 16:20:00 +0200 - - - https://theswiftdev.com/mastering-the-viper-architecture/ - <![CDATA[ Mastering the VIPER architecture ]]> - - https://theswiftdev.com/mastering-the-viper-architecture/ - Tue, 19 Mar 2019 16:20:00 +0100 - - - https://theswiftdev.com/memory-layout-in-swift/ - <![CDATA[ Memory layout in Swift ]]> - - https://theswiftdev.com/memory-layout-in-swift/ - Mon, 08 Mar 2021 16:20:00 +0100 - - - https://theswiftdev.com/modules-and-hooks-in-swift/ - <![CDATA[ Modules and hooks in Swift ]]> - - https://theswiftdev.com/modules-and-hooks-in-swift/ - Thu, 16 Apr 2020 16:20:00 +0200 - - - https://theswiftdev.com/networking-examples-for-appleos/ - <![CDATA[ Networking examples for appleOS ]]> - - https://theswiftdev.com/networking-examples-for-appleos/ - Tue, 27 Feb 2018 16:20:00 +0100 - - - https://theswiftdev.com/picking-and-playing-videos-in-swift/ - <![CDATA[ Picking and playing videos in Swift ]]> - - https://theswiftdev.com/picking-and-playing-videos-in-swift/ - Wed, 28 Aug 2019 16:20:00 +0200 - - - https://theswiftdev.com/picking-images-with-uiimagepickercontroller-in-swift-5/ - <![CDATA[ Picking images with UIImagePickerController in Swift 5 ]]> - - https://theswiftdev.com/picking-images-with-uiimagepickercontroller-in-swift-5/ - Wed, 30 Jan 2019 16:20:00 +0100 - - - https://theswiftdev.com/practical-guide-to-binary-operations-using-the-uint8-type-in-swift/ - <![CDATA[ Practical guide to binary operations using the UInt8 type in Swift ]]> - - https://theswiftdev.com/practical-guide-to-binary-operations-using-the-uint8-type-in-swift/ - Thu, 16 Sep 2021 16:20:00 +0200 - - - https://theswiftdev.com/progressive-web-apps-on-ios/ - <![CDATA[ Progressive Web Apps on iOS ]]> - - https://theswiftdev.com/progressive-web-apps-on-ios/ - Thu, 27 Jan 2022 16:20:00 +0100 - - - https://theswiftdev.com/promises-in-swift-for-beginners/ - <![CDATA[ Promises in Swift for beginners ]]> - - https://theswiftdev.com/promises-in-swift-for-beginners/ - Tue, 28 May 2019 16:20:00 +0200 - - - https://theswiftdev.com/result-builders-in-swift/ - <![CDATA[ Result builders in Swift ]]> - - https://theswiftdev.com/result-builders-in-swift/ - Tue, 10 Oct 2017 16:20:00 +0200 - - - https://theswiftdev.com/running-and-testing-async-vapor-commands/ - <![CDATA[ Running and testing async Vapor commands ]]> - - https://theswiftdev.com/running-and-testing-async-vapor-commands/ - Thu, 23 Feb 2023 16:20:00 +0100 - - - https://theswiftdev.com/running-tasks-in-parallel/ - <![CDATA[ Running tasks in parallel ]]> - - https://theswiftdev.com/running-tasks-in-parallel/ - Thu, 09 Feb 2023 16:20:00 +0100 - - - https://theswiftdev.com/self-sizing-cells-with-rotation-support/ - <![CDATA[ Self sizing cells with rotation support ]]> - - https://theswiftdev.com/self-sizing-cells-with-rotation-support/ - Tue, 23 Jan 2018 16:20:00 +0100 - - - https://theswiftdev.com/server-side-swift-projects-inside-docker-using-vapor-4/ - <![CDATA[ Server side Swift projects inside Docker using Vapor 4 ]]> - - https://theswiftdev.com/server-side-swift-projects-inside-docker-using-vapor-4/ - Sun, 19 Apr 2020 16:20:00 +0200 - - - https://theswiftdev.com/sign-in-with-apple-using-vapor-4/ - <![CDATA[ Sign in with Apple using Vapor 4 ]]> - - https://theswiftdev.com/sign-in-with-apple-using-vapor-4/ - Thu, 30 Apr 2020 16:20:00 +0200 - - - https://theswiftdev.com/styling-by-subclassing/ - <![CDATA[ Styling by subclassing ]]> - - https://theswiftdev.com/styling-by-subclassing/ - Tue, 19 Feb 2019 16:20:00 +0100 - - - https://theswiftdev.com/swift-5-and-abi-stability/ - <![CDATA[ Swift 5 and ABI stability ]]> - - https://theswiftdev.com/swift-5-and-abi-stability/ - Fri, 16 Nov 2018 16:20:00 +0100 - - - https://theswiftdev.com/swift-abstract-factory-design-pattern/ - <![CDATA[ Swift abstract factory design pattern ]]> - - https://theswiftdev.com/swift-abstract-factory-design-pattern/ - Sun, 03 Jun 2018 16:20:00 +0200 - - - https://theswiftdev.com/swift-actors-tutorial-a-beginners-guide-to-thread-safe-concurrency/ - <![CDATA[ Swift actors tutorial - a beginner's guide to thread safe concurrency ]]> - - https://theswiftdev.com/swift-actors-tutorial-a-beginners-guide-to-thread-safe-concurrency/ - Thu, 17 Jun 2021 16:20:00 +0200 - - - https://theswiftdev.com/swift-adapter-design-pattern/ - <![CDATA[ Swift adapter design pattern ]]> - - https://theswiftdev.com/swift-adapter-design-pattern/ - Sun, 29 Jul 2018 16:20:00 +0200 - - - https://theswiftdev.com/swift-builder-design-pattern/ - <![CDATA[ Swift builder design pattern ]]> - - https://theswiftdev.com/swift-builder-design-pattern/ - Thu, 24 May 2018 16:20:00 +0200 - - - https://theswiftdev.com/swift-command-design-pattern/ - <![CDATA[ Swift command design pattern ]]> - - https://theswiftdev.com/swift-command-design-pattern/ - Wed, 13 Jun 2018 16:20:00 +0200 - - - https://theswiftdev.com/swift-delegate-design-pattern/ - <![CDATA[ Swift delegate design pattern ]]> - - https://theswiftdev.com/swift-delegate-design-pattern/ - Wed, 27 Jun 2018 16:20:00 +0200 - - - https://theswiftdev.com/swift-dependency-injection-design-pattern/ - <![CDATA[ Swift dependency injection design pattern ]]> - - https://theswiftdev.com/swift-dependency-injection-design-pattern/ - Tue, 17 Jul 2018 16:20:00 +0200 - - - https://theswiftdev.com/swift-enum-all-values/ - <![CDATA[ Swift enum all values ]]> - - https://theswiftdev.com/swift-enum-all-values/ - Wed, 11 Oct 2017 16:20:00 +0200 - - - https://theswiftdev.com/swift-facade-design-pattern/ - <![CDATA[ Swift facade design pattern ]]> - - https://theswiftdev.com/swift-facade-design-pattern/ - Tue, 25 Sep 2018 16:20:00 +0200 - - - https://theswiftdev.com/swift-factory-method-design-pattern/ - <![CDATA[ Swift factory method design pattern ]]> - - https://theswiftdev.com/swift-factory-method-design-pattern/ - Thu, 31 May 2018 16:20:00 +0200 - - - https://theswiftdev.com/swift-init-patterns/ - <![CDATA[ Swift init patterns ]]> - - https://theswiftdev.com/swift-init-patterns/ - Sun, 25 Aug 2019 16:20:00 +0200 - - - https://theswiftdev.com/swift-object-pool-design-pattern/ - <![CDATA[ Swift object pool design pattern ]]> - - https://theswiftdev.com/swift-object-pool-design-pattern/ - Sun, 09 Dec 2018 16:20:00 +0100 - - - https://theswiftdev.com/swift-on-the-server-in-2020/ - <![CDATA[ Swift on the Server in 2020 ]]> - - https://theswiftdev.com/swift-on-the-server-in-2020/ - Fri, 28 Aug 2020 16:20:00 +0200 - - - https://theswiftdev.com/swift-package-manager-tutorial/ - <![CDATA[ Swift Package Manager tutorial ]]> - - https://theswiftdev.com/swift-package-manager-tutorial/ - Sun, 19 Nov 2017 16:20:00 +0100 - - - https://theswiftdev.com/swift-prototype-design-pattern/ - <![CDATA[ Swift prototype design pattern ]]> - - https://theswiftdev.com/swift-prototype-design-pattern/ - Fri, 08 Jun 2018 16:20:00 +0200 - - - https://theswiftdev.com/swift-simple-factory-design-pattern/ - <![CDATA[ Swift simple factory design pattern ]]> - - https://theswiftdev.com/swift-simple-factory-design-pattern/ - Tue, 29 May 2018 16:20:00 +0200 - - - https://theswiftdev.com/swift-singleton-design-pattern/ - <![CDATA[ Swift singleton design pattern ]]> - - https://theswiftdev.com/swift-singleton-design-pattern/ - Tue, 22 May 2018 16:20:00 +0200 - - - https://theswiftdev.com/swift-static-factory-design-pattern/ - <![CDATA[ Swift static factory design pattern ]]> - - https://theswiftdev.com/swift-static-factory-design-pattern/ - Mon, 28 May 2018 16:20:00 +0200 - - - https://theswiftdev.com/swift-structured-concurrency-tutorial/ - <![CDATA[ Swift structured concurrency tutorial ]]> - - https://theswiftdev.com/swift-structured-concurrency-tutorial/ - Wed, 30 Jun 2021 16:20:00 +0200 - - - https://theswiftdev.com/swift-visitor-design-pattern/ - <![CDATA[ Swift visitor design pattern ]]> - - https://theswiftdev.com/swift-visitor-design-pattern/ - Wed, 06 Apr 2022 16:20:00 +0200 - - - https://theswiftdev.com/swiftnio-tutorial-the-echo-server/ - <![CDATA[ SwiftNIO tutorial - The echo server ]]> - - https://theswiftdev.com/swiftnio-tutorial-the-echo-server/ - Thu, 26 Jan 2023 16:20:00 +0100 - - - https://theswiftdev.com/table-joins-in-fluent-4/ - <![CDATA[ Table joins in Fluent 4 ]]> - - https://theswiftdev.com/table-joins-in-fluent-4/ - Thu, 11 Jun 2020 16:20:00 +0200 - - - https://theswiftdev.com/the-abstract-vapor-service-factory-design-pattern/ - <![CDATA[ The abstract Vapor service factory design pattern ]]> - - https://theswiftdev.com/the-abstract-vapor-service-factory-design-pattern/ - Wed, 01 Feb 2023 16:20:00 +0100 - - - https://theswiftdev.com/the-anatomy-of-vapor-commands/ - <![CDATA[ The anatomy of Vapor commands ]]> - - https://theswiftdev.com/the-anatomy-of-vapor-commands/ - Wed, 25 Mar 2020 16:20:00 +0100 - - - https://theswiftdev.com/the-future-of-server-side-swift/ - <![CDATA[ The future of server side Swift ]]> - - https://theswiftdev.com/the-future-of-server-side-swift/ - Wed, 05 Jan 2022 16:20:00 +0100 - - - https://theswiftdev.com/the-repository-pattern-for-vapor-4/ - <![CDATA[ The repository pattern for Vapor 4 ]]> - - https://theswiftdev.com/the-repository-pattern-for-vapor-4/ - Thu, 03 Mar 2022 16:20:00 +0100 - - - https://theswiftdev.com/the-swift-compiler-for-beginners/ - <![CDATA[ The Swift compiler for beginners ]]> - - https://theswiftdev.com/the-swift-compiler-for-beginners/ - Wed, 10 Feb 2021 16:20:00 +0100 - - - https://theswiftdev.com/the-swift-package-manifest-file/ - <![CDATA[ The Swift package manifest file ]]> - - https://theswiftdev.com/the-swift-package-manifest-file/ - Fri, 24 Apr 2020 16:20:00 +0200 - - - https://theswiftdev.com/the-ultimate-combine-framework-tutorial-in-swift/ - <![CDATA[ The ultimate Combine framework tutorial in Swift ]]> - - https://theswiftdev.com/the-ultimate-combine-framework-tutorial-in-swift/ - Tue, 10 Oct 2017 16:20:00 +0200 - - - https://theswiftdev.com/the-ultimate-viper-architecture-tutorial/ - <![CDATA[ The ultimate VIPER architecture tutorial ]]> - - https://theswiftdev.com/the-ultimate-viper-architecture-tutorial/ - Mon, 12 Mar 2018 16:20:00 +0100 - - - https://theswiftdev.com/top-20-ios-libraries-of-2019/ - <![CDATA[ Top 20 iOS libraries written in Swift ]]> - - https://theswiftdev.com/top-20-ios-libraries-of-2019/ - Tue, 10 Oct 2017 16:20:00 +0200 - - - https://theswiftdev.com/uicollectionview-cells-with-circular-images-plus-rotation-support/ - <![CDATA[ UICollectionView cells with circular images plus rotation support ]]> - - https://theswiftdev.com/uicollectionview-cells-with-circular-images-plus-rotation-support/ - Wed, 24 Jan 2018 16:20:00 +0100 - - - https://theswiftdev.com/uicollectionview-data-source-and-delegates-programmatically/ - <![CDATA[ UICollectionView data source and delegates programmatically ]]> - - https://theswiftdev.com/uicollectionview-data-source-and-delegates-programmatically/ - Tue, 26 Jun 2018 16:20:00 +0200 - - - https://theswiftdev.com/uicolor-best-practices-in-swift/ - <![CDATA[ UIColor best practices in Swift ]]> - - https://theswiftdev.com/uicolor-best-practices-in-swift/ - Thu, 03 May 2018 16:20:00 +0200 - - - https://theswiftdev.com/uikit-init-patterns/ - <![CDATA[ UIKit init patterns ]]> - - https://theswiftdev.com/uikit-init-patterns/ - Tue, 10 Oct 2017 16:20:00 +0200 - - - https://theswiftdev.com/uikit-loadview-vs-viewdidload/ - <![CDATA[ UIKit - loadView vs viewDidLoad ]]> - - https://theswiftdev.com/uikit-loadview-vs-viewdidload/ - Wed, 09 Feb 2022 16:20:00 +0100 - - - https://theswiftdev.com/uitableview-tutorial-in-swift/ - <![CDATA[ UITableView tutorial in Swift ]]> - - https://theswiftdev.com/uitableview-tutorial-in-swift/ - Sat, 01 Dec 2018 16:20:00 +0100 - - - https://theswiftdev.com/ultimate-grand-central-dispatch-tutorial-in-swift/ - <![CDATA[ Ultimate Grand Central Dispatch tutorial in Swift ]]> - - https://theswiftdev.com/ultimate-grand-central-dispatch-tutorial-in-swift/ - Tue, 10 Jul 2018 16:20:00 +0200 - - - https://theswiftdev.com/ultimate-uicollectionview-guide-with-ios-examples-written-in-swift/ - <![CDATA[ Ultimate UICollectionView guide with iOS examples written in Swift ]]> - - https://theswiftdev.com/ultimate-uicollectionview-guide-with-ios-examples-written-in-swift/ - Tue, 17 Apr 2018 16:20:00 +0200 - - - https://theswiftdev.com/uniquely-identifying-views/ - <![CDATA[ Uniquely identifying views ]]> - - https://theswiftdev.com/uniquely-identifying-views/ - Tue, 02 Apr 2019 16:20:00 +0200 - - - https://theswiftdev.com/unsafe-memory-pointers-in-swift/ - <![CDATA[ Unsafe memory pointers in Swift ]]> - - https://theswiftdev.com/unsafe-memory-pointers-in-swift/ - Thu, 18 Mar 2021 16:20:00 +0100 - - - https://theswiftdev.com/urlsession-and-the-combine-framework/ - <![CDATA[ URLSession and the Combine framework ]]> - - https://theswiftdev.com/urlsession-and-the-combine-framework/ - Thu, 15 Aug 2019 16:20:00 +0200 - - - https://theswiftdev.com/utilizing-makefiles-for-swift-projects/ - <![CDATA[ Utilizing Makefiles for Swift projects ]]> - - https://theswiftdev.com/utilizing-makefiles-for-swift-projects/ - Tue, 10 Jan 2023 16:20:00 +0100 - - - https://theswiftdev.com/viper-best-practices-for-ios-developers/ - <![CDATA[ VIPER best practices for iOS developers ]]> - - https://theswiftdev.com/viper-best-practices-for-ios-developers/ - Mon, 11 Mar 2019 16:20:00 +0100 - - - https://theswiftdev.com/websockets-for-beginners-using-vapor-4-and-vanilla-javascript/ - <![CDATA[ Websockets for beginners using Vapor 4 and Vanilla JavaScript ]]> - - https://theswiftdev.com/websockets-for-beginners-using-vapor-4-and-vanilla-javascript/ - Thu, 28 May 2020 16:20:00 +0200 - - - https://theswiftdev.com/what-are-the-best-practices-to-learn-ios-swift-in-2020/ - <![CDATA[ What are the best practices to learn iOS / Swift in 2020? ]]> - - https://theswiftdev.com/what-are-the-best-practices-to-learn-ios-swift-in-2020/ - Mon, 06 Jan 2020 16:20:00 +0100 - - - https://theswiftdev.com/whats-new-in-swift-5-3/ - <![CDATA[ What's new in Swift 5.3? ]]> - - https://theswiftdev.com/whats-new-in-swift-5-3/ - Thu, 14 May 2020 16:20:00 +0200 - - - https://theswiftdev.com/whats-new-in-vapor-4/ - <![CDATA[ What's new in Vapor 4? ]]> - - https://theswiftdev.com/whats-new-in-vapor-4/ - Mon, 26 Aug 2019 16:20:00 +0200 - - - https://theswiftdev.com/working-with-diffable-data-sources-and-table-views-using-uikit/ - <![CDATA[ Working with diffable data sources and table views using UIKit ]]> - - https://theswiftdev.com/working-with-diffable-data-sources-and-table-views-using-uikit/ - Thu, 24 Mar 2022 16:20:00 +0100 - - - - diff --git a/docs/running-and-testing-async-vapor-commands/index.html b/docs/running-and-testing-async-vapor-commands/index.html deleted file mode 100644 index 30ee94f..0000000 --- a/docs/running-and-testing-async-vapor-commands/index.html +++ /dev/null @@ -1,449 +0,0 @@ - - - - - - - - - - - - Running and testing async Vapor commands - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 2 min read - -
-

Running and testing async Vapor commands

-
-

In this article I'll show you how to build asynchronous Vapor commands and how to test them using ConsoleKit.

-
- -

How to run async commands in Vapor?

The async / await feature is relatively new in Swift and some framework authors haven’t converted everything to take advantage of these new keywords. Currently, this is the situation with the Command API in Vapor 4. You can already define async commands, but there’s no way to register them using the Vapor framework. Fortunately, there is a relatively straightforward workaround that you can use if you want to execute commands using an asynchronous context. 🔀

First we’re going to define a helper protocol and create an asyncRun function. We are going to extend the original Command protocol and provide a default implementation for the run method.

import Vapor
-
-public protocol AsyncCommand: Command {
-    
-    func asyncRun(
-        using context: CommandContext,
-        signature: Signature
-    ) async throws
-}
-
-public extension AsyncCommand {
-
-    func run(
-        using context: CommandContext,
-        signature: Signature
-    ) throws {
-        let promise = context
-            .application
-            .eventLoopGroup
-            .next()
-            .makePromise(of: Void.self)
-        
-        promise.completeWithTask {
-            try await asyncRun(
-                using: context,
-                signature: signature
-            )
-        }
-        try promise.futureResult.wait()
-    }
-}
-

This way you should be able to create a new async command and you should implement the asyncRun method if you want to call some asynchronous Swift code.

import Vapor
-
-final class MyAsyncCommand: AsyncCommand {
-    
-    static let name = "async"
-    
-    let help = "This command run asynchronously."
-
-    struct Signature: CommandSignature {}
-
-    func asyncRun(
-        using context: CommandContext,
-        signature: Signature
-    ) async throws {
-        context.console.info("This is async.")
-    }
-}
-

It is possible to register the command using the configure method, you can try this out by running the swift run Run async snippet if you are using the standard Vapor template. 💧

import Vapor
-
-public func configure(
-    _ app: Application
-) throws {
-
-    app.commands.use(
-        MyAsyncCommand(),
-        as: MyAsyncCommand.name
-    )
-
-    try routes(app)
-}
-

As you can see it’s a pretty neat trick, it’s also mentioned on GitHub, but hopefully we don’t need this workaround for too long and proper async command support will arrive in Vapor 4.x.

Unit testing Vapor commands

This topic has literally zero documentation, so I thought it would be nice to tell you a bit about how to unit test scripts created via ConsoleKit. First of all we need a TestConsole that we can use to collect the output of our commands. This is a shameless ripoff from ConsoleKit. 😅

import Vapor
-
-final class TestConsole: Console {
-
-    var testInputQueue: [String]
-    var testOutputQueue: [String]
-    var userInfo: [AnyHashable : Any]
-
-    init() {
-        self.testInputQueue = []
-        self.testOutputQueue = []
-        self.userInfo = [:]
-    }
-
-    func input(isSecure: Bool) -> String {
-        testInputQueue.popLast() ?? ""
-    }
-
-    func output(_ text: ConsoleText, newLine: Bool) {
-        let line = text.description + (newLine ? "\n" : "")
-        testOutputQueue.insert(line, at: 0)
-    }
-
-    func report(error: String, newLine: Bool) {
-        //
-    }
-
-    func clear(_ type: ConsoleClear) {
-        //
-    }
-
-    var size: (width: Int, height: Int) {
-        (0, 0)
-    }
-}
-

Now inside the test suite, you should create a new application instance using the test environment and configure it for testing purposes. Then you should initiate the command that you’d like to test and run it using the test console. You just have to create a new context and a proper input with the necessary arguments and the console.run function will take care of everything else.

@testable import App
-import XCTVapor
-
-final class AppTests: XCTestCase {
-    
-    func testCommand() throws {
-        let app = Application(.testing)
-        defer { app.shutdown() }
-        try configure(app)
-        
-        let command = MyAsyncCommand()
-        let arguments = ["async"]
-        
-        let console = TestConsole()
-        let input = CommandInput(arguments: arguments)
-        var context = CommandContext(
-            console: console,
-            input: input
-        )
-        context.application = app
-        
-        try console.run(command, with: context)
-
-        let output = console
-            .testOutputQueue
-            .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
-        
-        let expectation = [
-            "This is async."
-        ]
-        XCTAssertEqual(output, expectation)
-    }
-}
-

The nice thing about this solution is that the ConsoleKit framework will automatically parse the arguments, options and the flags. You can provide these as standalone array elements using the input arguments array (e.g. ["arg1", "--option1", "value1", "--flag1"]).

It is possible to test command groups, you just have to add the specific command name as the first argument that you’d like to run from the group and you can simply check the output through the test console if you are looking for the actual command results. 💪

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/running-tasks-in-parallel/index.html b/docs/running-tasks-in-parallel/index.html deleted file mode 100644 index 796e3d6..0000000 --- a/docs/running-tasks-in-parallel/index.html +++ /dev/null @@ -1,403 +0,0 @@ - - - - - - - - - - - - Running tasks in parallel - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 3 min read - -
-

Running tasks in parallel

-
-

Learn how to run tasks in parallel using the old-school tools and frameworks plus the new structured concurrency API in Swift.

-
- -

Being able to run tasks in parallel is nice, it can speed up things for sure when you can utilize multiple CPU cores, but how can we actually implement these kind of operations in Swift? 🤔

There are multiple ways of running parallel operations, I had a longer article about the Grand Central Dispatch (GCD) framework, there I explained the differences between parallelism and concurrency. I also demonstrated how to set up serial and concurrent dispatch queues, but this time I’d like to focus a bit more on tasks, workers and jobs.

Imagine that you have a picture which is 50000 pixel wide and 20000 pixel long, that’s exactly one billion pixels. How would you alter the color of each pixel? Well, we could do this by iterating through each pixel and let one core do the job, or we could run tasks in parallel.

The Dispatch framework offers multiple ways to solve this issue. The first solution is to use the concurrentPerform function and specify some number of workers. For the sake of simplicity, I’m going to add up the numbers from zero to 1 billion using 8 workers. 💪

import Dispatch
-
-let workers: Int = 8
-let numbers: [Int] = Array(repeating: 1, count: 1_000_000_000)
-
-var sum = 0
-DispatchQueue.concurrentPerform(iterations: workers) { index in
-    let start = index * numbers.count / workers
-    let end = (index + 1) * numbers.count / workers
-    print("Worker #\(index), items: \(numbers[start..<end].count)")
-
-    sum += numbers[start..<end].reduce(0, +)
-}
-
-print("Sum: \(sum)")
-

Cool, but still each worker has to work on quite a lot of numbers, maybe we shouldn’t start all the workers at once, but use a pool and run only a subset of them at a time. This is quite an easy task with operation queues, let me show you a basic example. 😎

import Foundation
-
-let workers: Int = 8
-let numbers: [Int] = Array(repeating: 1, count: 1_000_000_000)
-
-let operationQueue = OperationQueue()
-operationQueue.maxConcurrentOperationCount = 4
-
-var sum = 0
-for index in 0..<workers {
-    let operation = BlockOperation {
-        let start = index * numbers.count / workers
-        let end = (index + 1) * numbers.count / workers
-        print("Worker #\(index), items: \(numbers[start..<end].count)")
-        
-        sum += numbers[start..<end].reduce(0, +)
-    }
-    operationQueue.addOperation(operation)
-}
-
-operationQueue.waitUntilAllOperationsAreFinished()
-
-print("Sum: \(sum)")
-

Both of the examples are above are more ore less good to go (if we look through at possible data race & synchronization), but they depend on additional frameworks. In other words they are non-native Swift solutions. What if we could do something better using structured concurrency?

let workers: Int = 8
-let numbers: [Int] = Array(repeating: 1, count: 1_000_000_000)
-
-let sum = await withTaskGroup(of: Int.self) { group in
-    for i in 0..<workers {
-        group.addTask {
-            let start = i * numbers.count / workers
-            let end = (i + 1) * numbers.count / workers
-            return numbers[start..<end].reduce(0, +)
-        }
-    }
-
-    var summary = 0
-    for await result in group {
-        summary += result
-    }
-    return summary
-}
-
-print("Sum: \(sum)")
-

By using task groups you can easily setup the workers and run them in parallel by adding a task to the group. Then you can wait for the partial sum results to arrive and sum everything up using a thread-safe solution. This approach is great, but is it possible to limit the maximum number of concurrent operations, just like we did with operation queues? 🤷‍♂️

func parallelTasks<T>(
-    iterations: Int,
-    concurrency: Int,
-    block: @escaping ((Int) async throws -> T)
-) async throws -> [T] {
-    try await withThrowingTaskGroup(of: T.self) { group in
-        var result: [T] = []
-
-        for i in 0..<iterations {
-            if i >= concurrency {
-                if let res = try await group.next() {
-                    result.append(res)
-                }
-            }
-            group.addTask {
-                try await block(i)
-            }
-        }
-
-        for try await res in group {
-            result.append(res)
-        }
-        return result
-    }
-}
-
-
-let workers: Int = 8
-let numbers: [Int] = Array(repeating: 1, count: 1_000_000_000)
-
-let res = try await parallelTasks(
-    iterations: workers,
-    concurrency: 4
-) { i in
-    print(i)
-    let start = i * numbers.count / workers
-    let end = (i + 1) * numbers.count / workers
-    return numbers[start..<end].reduce(0, +)
-}
-
-print("Sum: \(res.reduce(0, +))")
-

It is possible, I made a little helper function similar to the concurrentPerform method, this way you can execute a number of tasks and limit the level of concurrency. The main idea is to run a number of iterations and when the index reaches the maximum number of concurrent items you wait until a work item finishes and then you add a new task to the group. Before you finish the task you also have to await all the remaining results and append those results to the grouped result array. 😊

That’s it for now, I hope this little article will help you to manage concurrent operations a bit better.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - -
-
- -
- - - -
- - - - diff --git a/docs/self-sizing-cells-with-rotation-support/index.html b/docs/self-sizing-cells-with-rotation-support/index.html deleted file mode 100644 index 9cca9da..0000000 --- a/docs/self-sizing-cells-with-rotation-support/index.html +++ /dev/null @@ -1,517 +0,0 @@ - - - - - - - - - - - - Self sizing cells with rotation support - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 4 min read - -
-

Self sizing cells with rotation support

-
-

How to make self sizing cells in Swift both for table & collection views supporting orientation changes and dynamic font types.

-
- -

UITableView

So let’s start with a standard single-view template for iOS. Name the project, and go straight to the Main.storyboard file. Select your view controller, delete it and create a new UITableViewController scene.

Main storyboard

Set the table view controller scene as initial view controller and create a TableViewController.swift file with the corresponding class.

import UIKit
-
-class TableViewController: UITableViewController {
-
-    var dataSource: [String] = [
-        "Donec id elit non mi porta gravida at eget metus.",
-        "Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.",
-        "Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Vestibulum id ligula porta felis euismod semper. Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam quis risus eget urna mollis ornare vel eu leo.",
-        "Maecenas faucibus mollis interdum.",
-        "Donec ullamcorper nulla non metus auctor fringilla. Aenean lacinia bibendum nulla sed consectetur. Cras mattis consectetur purus sit amet fermentum.",
-        "Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Maecenas faucibus mollis interdum.",
-    ]
-}
-
-extension TableViewController {
-
-    override func tableView(
-        _ tableView: UITableView, 
-        numberOfRowsInSection section: Int
-    ) -> Int {
-        return dataSource.count
-    }
-
-    override func tableView(
-        _ tableView: UITableView, 
-        cellForRowAt indexPath: IndexPath
-    ) -> UITableViewCell {
-        let cell = tableView.dequeueReusableCell(
-            withIdentifier: "Cell", 
-            for: indexPath
-        ) as! TableViewCell
-
-        cell.dynamicLabel?.text = dataSource[indexPath.row]
-        cell.dynamicLabel.font  = UIFont.preferredFont(forTextStyle: .body)
-
-        return cell
-    }
-}
-

The setup is really self-descriptive. You’ve got a string array as data source, and the required implementation of the UITableViewDataSource protocol.

The only thing that is missing is the TableViewCell class.

class TableViewCell: UITableViewCell {
-
-    @IBOutlet weak var dynamicLabel: UILabel!
-}
-

First, create the class itself, then with interface builder select the table view controller scene and drag a label to the prototype cell. Set the class of the prototype cell to TableViewCell. The reusable identifier can be simply "Cell". Connect the dynamicLabel outlet to the view. Give the label top, bottom, leading, trailing constraints to the superview with the default value of 8. Select the label, set the font to body style and the lines property to zero. That’s how simple it is. 😂

Table view cell

Now you are almost ready. You just need to set the estimated row height on the table view. Inside the TableViewController class change the viewDidLoad method like this:

override func viewDidLoad() {
-    super.viewDidLoad()
-
-    tableView.estimatedRowHeight = 44
-    tableView.rowHeight = UITableView.automaticDimension
-}
-

The estimatedRowHeight property will tell the system that the table view should try to figure out the height of each cell dynamically. You should also change the rowHeight property to automatic dimension, if you don’t do then the system will use a static cell height - that one from interface builder that you can set on the cell. Now build & run. You have a wonderful table view with self sizing cells. You can even rotate your device, it’s going to work in both orientations.

One more thing

If you change the text size under the iOS accessibility settings, the table view will reflect the changes, so it’ll adapt the layout to the new value. The font size of the table view is going to change according to the slider value. You might want to subscribe to the UIContentSizeCategory.didChangeNotification in order to detect size changes and reload the UI. This feature is called dynamic type.

NotificationCenter.default.addObserver(
-    self.tableView, 
-    selector: #selector(UITableView.reloadData), 
-    name: UIContentSizeCategory.didChangeNotification, 
-    object: nil
-)
-

UICollectionView

So we’ve finished the easy part. Now let’s try to achieve the same functionality with a collection view. UICollectionView is a generic class, that is designed to create custom layouts, because of this generic behavior you will not be able to create self sizing cells from interface builder. You have to do it from code.

Before we start, we can still play with IB a little bit. Create a new collection view controller scene, and drag a push segue from the previous table view cell to this new controller. Finally embed the whole thing in a navigation controller.

Collection view

The cell is going to be the exact same as we used for the table view, but it’s a subclass of UICollectionViewCell, and we are going to construct the layout directly from code.

class CollectionViewCell: UICollectionViewCell {
-
-    weak var dynamicLabel: UILabel!
-
-    required init?(coder aDecoder: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
-
-    override init(frame: CGRect) {
-        super.init(frame: frame)
-
-        translatesAutoresizingMaskIntoConstraints = false
-
-        let label = UILabel(frame: bounds)
-        label.translatesAutoresizingMaskIntoConstraints = false
-        label.font = UIFont.preferredFont(forTextStyle: .body)
-        label.backgroundColor = UIColor.darkGray
-        label.numberOfLines = 0
-        label.preferredMaxLayoutWidth = frame.size.width
-
-        self.contentView.addSubview(label)
-        self.dynamicLabel = label
-
-        NSLayoutConstraint.activate([
-            contentView.topAnchor.constraint(
-                equalTo: dynamicLabel.topAnchor
-            ),
-            contentView.bottomAnchor.constraint(
-                equalTo: dynamicLabel.bottomAnchor
-            ),
-            contentView.leadingAnchor.constraint(
-                equalTo: dynamicLabel.leadingAnchor
-            ),
-            contentView.trailingAnchor.constraint(
-                equalTo: dynamicLabel.trailingAnchor
-            ),
-        ])
-    }
-
-    override func prepareForReuse() {
-        super.prepareForReuse()
-
-        dynamicLabel.font = UIFont.preferredFont(forTextStyle: .body)
-    }
-
-    func setPreferred(width: CGFloat) {
-        dynamicLabel.preferredMaxLayoutWidth = width
-    }
-}
-

We have a subclass for our cell, now let’s create the view controller class. Inside the viewDidLoad method you have to set the estimatedItemSize property on the collection view. There if you give wrong size, the auto-rotation won’t work as expected.

override func viewDidLoad() {
-    super.viewDidLoad()
-
-    navigationItem.rightBarButtonItem = UIBarButtonItem(
-        barButtonSystemItem: .refresh, 
-        target: self,
-        action: #selector(toggleColumns)
-    )
-
-    collectionView?.register(
-        CollectionViewCell.self, 
-        forCellWithReuseIdentifier: "Cell"
-    )
-
-    if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
-        flowLayout.itemSize = CGSize(width: 64, height: 64)
-        flowLayout.minimumInteritemSpacing = 10
-        flowLayout.minimumLineSpacing = 20
-        flowLayout.sectionInset = UIEdgeInsets(
-            top: 10, 
-            left: 10, 
-            bottom: 10, 
-            right: 10
-        )
-        flowLayout.estimatedItemSize = CGSize(
-            width: preferredWith(forSize: view.bounds.size), 
-            height: 64
-        )
-    }
-
-    collectionView?.reloadData()
-
-    NotificationCenter.default.addObserver(
-        collectionView!, 
-        selector: #selector(UICollectionView.reloadData), 
-        name: UIContentSizeCategory.didChangeNotification, 
-        object: nil
-    )
-}
-

Inside the rotation methods, you have to invalidate the collection view layout, and recalculate the visible cell sizes when the transition happens.

override func traitCollectionDidChange(
-    _ previousTraitCollection: UITraitCollection?
-) {
-    super.traitCollectionDidChange(previousTraitCollection)
-
-    guard
-        let previousTraitCollection = previousTraitCollection,
-        traitCollection.verticalSizeClass != previousTraitCollection.verticalSizeClass ||
-        traitCollection.horizontalSizeClass != previousTraitCollection.horizontalSizeClass
-    else {
-        return
-    }
-
-    collectionView?.collectionViewLayout.invalidateLayout()
-    collectionView?.reloadData()
-}
-
-override func viewWillTransition(
-    to size: CGSize, 
-    with coordinator: UIViewControllerTransitionCoordinator
-) {
-    super.viewWillTransition(to: size, with: coordinator)
-
-    collectionView?.collectionViewLayout.invalidateLayout()
-    estimateVisibleCellSizes(to: size)
-
-    coordinator.animate(alongsideTransition: { context in
-
-    }, completion: { context in
-        collectionView?.collectionViewLayout.invalidateLayout()
-    })
-}
-

There are two helper methods to calculate the preferred width for the estimated item size and to recalculate the visible cell sizes.

func preferredWith(forSize size: CGSize) -> CGFloat {
-    var columnFactor: CGFloat = 1.0
-    if twoColumns {
-        columnFactor = 2.0
-    }
-    return (size.width - 30) / columnFactor
-}
-
-func estimateVisibleCellSizes(to size: CGSize) {
-    guard let collectionView else {
-        return
-    }
-
-    if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
-        flowLayout.estimatedItemSize = CGSize(
-            width: preferredWith(forSize: size), 
-            height: 64
-        )
-    }
-
-    collectionView.visibleCells.forEach { cell in
-        if let cell = cell as? CollectionViewCell {
-            cell.setPreferred(width: preferredWith(forSize: size))
-        }
-    }
-}
-

You can even have multiple columns if you do the appropriate calculations.

There is only one thing that I could not solve, but that’s just a log message. If you rotate back the device some of the cells are not going to be visible and the layout engine will complain about that those cells can not be snapshotted.

Snapshotting a view that has not been rendered results in an empty snapshot. Ensure your view has been rendered at least once before snapshotting or snapshot after screen updates.

If you can make this message disappear somehow OS_ACTIVITY_MODE=disable, please don’t hesitate to submit a pull request for the tutorials repository on GitHub. 😉

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/server-side-swift-projects-inside-docker-using-vapor-4/index.html b/docs/server-side-swift-projects-inside-docker-using-vapor-4/index.html deleted file mode 100644 index 3b94aed..0000000 --- a/docs/server-side-swift-projects-inside-docker-using-vapor-4/index.html +++ /dev/null @@ -1,470 +0,0 @@ - - - - - - - - - - - - Server side Swift projects inside Docker using Vapor 4 - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 8 min read - -
-

Server side Swift projects inside Docker using Vapor 4

-
-

Learn how to setup Vapor 4 projects inside a Docker container. Are you completely new to Docker? This article is just for you.

-
- -

What the heck is Docker?

Operating-system-level virtualization is called containerization technology. It’s more lightweight than virtual machines, since all the containers are run by a single operating system kernel.

Docker used to run software packages in these self-contained isolated environments. These containers bundle their own tools, libraries and configuration files. They can communicate with each other through well-defined channels. Containers are being made from images that specify their precise contents. You can find plenty of Docker images on DockerHub.

Docker is extremely useful if you don’t want to spend hours to setup & configure your work environment. It helps the software deployment process, so patches, hotfixes and new code releases can be delivered more frequently. In other words it’s a DevOps tool.

Guess what: you can use Swift right ahead through a single Docker container, you don’t even need to install anything else on your computer, but Docker. 🐳

Docker architecture in a nutshell

There is a nice get to know post about the Docker ecosystem, but if you want to get a detailed overview you should read the Docker glossary. In this tutorial I’m going to focus on images and containers. Maybe a little bit on the hub, engine & machines. 😅

Docker engine

Lightweight and powerful open source containerization technology combined with a work flow for building and containerizing your applications.

Docker image

Docker images are the basis (templates) of containers.

Docker container

A container is a runtime instance of a docker image.

Docker machine

A tool that lets you install Docker Engine on virtual hosts, and manage the hosts with docker-machine commands.

Docker hub

A centralized resource for working with Docker and its components.

So just a little clarification: Docker images can be created through Dockerfiles, these are the templates for running containers. Imagine them like “pre-built install disks” for your container environments. If we approach this from an object-oriented programming perspective, then an image is a class definition and the container is the instance created from it. 💾

How to run Swift in a Docker container?

Let me show you how to run Swift under Linux inside a Docker container. First of all, install Docker (fastest way is brew install docker), start the app itself (give it some permissions), and pull the official Swift Docker image from the cloud by using the docker pull swift command. 😎

You can also use the official Vapor Docker images for server side Swift development.

Packaging Swift code into an image

The first thing I’d like to teach you is how to create a custom Docker image & pack all your Swift source code into it. Just create a new Swift project swift package init --type=executable inside a folder and also make a new Dockerfile:

FROM swift
-WORKDIR /app
-COPY . ./
-CMD swift package clean
-CMD swift run
-

The FROM directive tells Docker to set our base image, which will be the previously pulled official Swift Docker image with some minor changes. Let’s make those changes right ahead! We’re going to add a new WORKDIR that’s called /app, and from now on we’ll literally work inside that. The COPY command will copy our local files to the remote (working) directory, CMD will run the given command if you don’t specify an external command e.g. run shell. 🐚

Please note that we could use the ADD instruction instead of COPY or the RUN instuction instead of CMD, but there are slight differneces (see the links).

Now build, tag & finally run the image. 🔨

# build the image
-docker build -t my-swift-image .
-
-# run the container based on the image and remove it after exit
-docker run --rm my-swift-image
-

Congratulations, you just made your first Docker image, used your first Docker container with Swift, but wait… is it necessary to re-build every time a code change happens? 🤔

Editing Swift code inside a Docker container on-the-fly

The first option is that you execute a bash docker run -it my-swift-image bash and log in to your container so you’ll be able to edit Swift source files inside of it & build the whole package by using swift build or you can run swift test if you’d just like to test your app under Linux.

This method is a little bit inconvenient, because all the Swift files are copied during the image build process so if you would like to pull out changes from the container you have to manually copy everything, also you can’t use your favorite editor inside a terminal window. 🤐

Second option is to run the original Swift image, instead of our custom one and attach a local directory to it. Imagine that the sources are under the current directory, so you can use:

docker run --rm -v $(pwd):/app -it swift
-

This command will start a new container with the local folder mapped to the remote app directory. Now you can use Xcode or anything else to make modifications, and run your Swift package, by entering swift run to the command line. Pretty simple. 🏃

How to run a Vapor 4 project using Docker?

You can run a server side Swift application through Docker. If reate a new Vapor 4 project using the toolbox (vapor new myProject), the generated project will also include both a Dockerfile and a docker-compose.yml file, those are pretty good starting points, let’s take a look at them.

# Build image
-FROM vapor/swift:5.2 as build
-WORKDIR /build
-COPY ./Package.* ./
-RUN swift package resolve
-COPY . .
-RUN swift build --enable-test-discovery -c release -Xswiftc -g
-
-# Run image
-FROM vapor/ubuntu:18.04
-WORKDIR /run
-COPY --from=build /build/.build/release /run
-COPY --from=build /usr/lib/swift/ /usr/lib/swift/
-COPY --from=build /build/Public /run/Public
-ENTRYPOINT ["./Run"]
-CMD ["serve", "--env", "production", "--hostname", "0.0.0.0"]
-

The Dockerfile separates the build and run process into two distinct images, which totally makes sense since the final product is a binary executable file (with additional resources), so you won’t need the Swift compiler at all in the run image, this makes it extremely lightweight. 🐋

docker build -t vapor-image .
-
-# simply run the container instance & bind the port
-docker run --name vapor-server -p 8080:8080 vapor-image
-
-# run the instance, bind the port, see logs remove after exit (CTRL+C)
-docker run --rm -p 8080:8080 -it vapor-image
-

Building and running the image is pretty straightforward, we use the -p parameter to map the port inside the container to our local port. This will allow the Docker container to “listen on the given port” and if you visit the http://localhost:8080 you should see the proper response generated by the server. Vapor is running inside a container and it works like magic! ⭐️

Using Fluent in a separate Docker container

The docker-compose command can be used to start multiple docker containers at once. You can have separate containers for every single service, like your Swift application, or the database that you are going to use. You can deploy & start all of your microservices with just one command. 🤓

As I mentioned before, the starter template comes with a compose file somewhat like this:

version: '3.7'
-
-volumes:
-  db_data:
-
-x-shared_environment: &shared_environment
-  LOG_LEVEL: ${LOG_LEVEL:-debug}
-  DATABASE_HOST: db
-  DATABASE_NAME: vapor_database
-  DATABASE_USERNAME: vapor_username
-  DATABASE_PASSWORD: vapor_password
-
-services:
-  app:
-    image: dockerproject:latest
-    build:
-      context: .
-    environment:
-      <<: *shared_environment
-    depends_on:
-      - db
-    ports:
-      - '8080:80'
-    command: ["serve", "--env", "production", "--hostname", "0.0.0.0", "--port", "80"]
-  migrate:
-    image: dockerproject:latest
-    build:
-      context: .
-    environment:
-      <<: *shared_environment
-    depends_on:
-      - db
-    command: ["migrate", "--yes"]
-    deploy:
-      replicas: 0
-  revert:
-    image: dockerproject:latest
-    build:
-      context: .
-    environment:
-      <<: *shared_environment
-    depends_on:
-      - db
-    command: ["migrate", "--revert", "--yes"]
-    deploy:
-      replicas: 0
-  db:
-    image: postgres:12.1-alpine
-    volumes:
-      - db_data:/var/lib/postgresql/data/pgdata
-    environment:
-      PGDATA: /var/lib/postgresql/data/pgdata
-      POSTGRES_USER: vapor_username
-      POSTGRES_PASSWORD: vapor_password
-      POSTGRES_DB: vapor_database
-    ports:
-      - '5432:5432'
-

The main thing to remember here is that you should NEVER run docker-compose up, because it’ll run every single container defined in the compose file including the app, db, migrations and revert. You don’t really want that, instead you can use individual components by providing the identifier after the up argument. Again, here are your options:

# Build images:
-docker-compose build
-
-# Run app
-docker-compose up app
-# Run database
-docker-compose up db
-# Run migrations:
-docker-compose up migrate
-
-# Stop all:
-docker-compose down
-# Stop & wipe database
-docker-compose down -v
-

You should always start with the database container, since the server requires a working database instance. Despite fact that the docker-compose command can manage dependencies, still you won’t be able to automate the startup process completely, because the PostgreSQL database service needs just a little extra time to boot up. In a production environment you could solve this issue by using health checks. Honestly I’ve never tried this, feel free to tell me your story. 😜

Anyway, as you can see the docker-compose.yaml file contains all the necessary configuration. Under each key there is a specific Vapor command that Docker will execute during the container initialization process. You can also see that there is a shared environment section for all the apps where you can change the configuration or introduce a new environmental variable according to your needs. Environment variables will be passed to the images (you can reach out to other containers by using the service names) and the API service will be exposed on port 8080. You can even add your own custom command by following the exact same pattern. 🌍

Ready? Just fire up a terminal window and enter docker-compose up db to start the PostgreSQL database container. Now you can run both the migration and the app container at once by executing the docker-compose up migrate app command in a new terminal tab or window.

If you visit http://localhost:8080 after everything is up and running you’ll see that the server is listening on the given port and it is communicating with the database server inside another container. You can also “get into the containers” - if you want to run a special script - by executing docker exec -it bash. This is pretty cool, isn’t it? 🐳 +🐘 +💧 = ❤️

Docker cheatsheet for beginners

If you want to learn Docker commands, but you don’t know where to start here is a nice list of cli commands that I use to manage containers, images and many more using Docker from terminal. Don’t worry you don’t have to remember any of these commands, you can simply bookmark this page and everything will be just a click away. Enjoy! 😉

Docker machine commands

  • Create new: docker-machine create MACHINE
  • List all: docker-machine ls
  • Show env: docker-machine env default
  • Use: eval "$(docker-machine env default)"
  • Unset: docker-machine env -u
  • Unset: eval $(docker-machine env -u)

Docker image commands

  • Download: docker pull IMAGE[:TAG]
  • Build from local Dockerfile: docker build -t TAG .
  • Build with user and tag: docker build -t USER/IMAGE:TAG .
  • List: docker image ls or docker images
  • List all: docker image ls -a or docker images -a
  • Remove (image or tag): docker image rm IMAGE or docker rmi IMAGE
  • Remove all dangling (nameless): docker image prune
  • Remove all unused: docker image prune -a
  • Remove all: docker rmi $(docker images -aq)
  • Tag: docker tag IMAGE TAG
  • Save to file: docker save IMAGE > FILE
  • Load from file: docker load -i FILE

Docker container commands

  • Run from image: docker run IMAGE
  • Run with name: docker run --name NAME IMAGE
  • Map a port: docker run -p HOST:CONTAINER IMAGE
  • Map all ports: docker run -P IMAGE
  • Start in background: docker run -d IMAGE
  • Set hostname: docker run --hostname NAME IMAGE
  • Set domain: docker run --add-host HOSTNAME:IP IMAGE
  • Map local directory: docker run -v HOST:TARGET IMAGE
  • Change entrypoint: docker run -it --entrypoint NAME IMAGE
  • List running: docker ps or docker container ls
  • List all: docker ps -a or docker container ls -a
  • Stop: docker stop ID or docker container stop ID
  • Start: docker start ID
  • Stop all: docker stop $(docker ps -aq)
  • Kill (force stop): docker kill ID or docker container kill ID
  • Remove: docker rm ID or docker container rm ID
  • Remove running: docker rm -f ID
  • Remove all stopped: docker container prune
  • Remove all: docker rm $(docker ps -aq)
  • Rename: docker rename OLD NEW
  • Create image from container: docker commit ID
  • Show modified files: docker diff ID
  • Show mapped ports: docker port ID
  • Copy from container: docker cp ID:SOURCE TARGET
  • Copy to container: docker cp TARGET ID:SOURCE
  • Show logs: docker logs ID
  • Show processes: docker top ID
  • Start shell: docker exec -it ID bash

Other useful Docker commands

  • Log in: docker login
  • Run compose file: docker-compose
  • Get info about image: docker inspect IMAGE
  • Show stats of running containers: docker stats
  • Show version: docker version
- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/sign-in-with-apple-using-vapor-4/index.html b/docs/sign-in-with-apple-using-vapor-4/index.html deleted file mode 100644 index 819f66d..0000000 --- a/docs/sign-in-with-apple-using-vapor-4/index.html +++ /dev/null @@ -1,596 +0,0 @@ - - - - - - - - - - - - Sign in with Apple using Vapor 4 - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 11 min read - -
-

Sign in with Apple using Vapor 4

-
-

A complete tutorial for beginners about how to implement the Sign in with Apple authentication service for your website.

-
- -

Apple developer portal setup

In order to make the Sign in with Apple work for your website you’ll need a payed developer account. That’ll cost you $99 / year if you are an individual developer. You can compare various membership options or just simply enroll using this link, but you’ll need an existing Apple ID.

I assume that you made it so far and you have a working Apple developer account by now. A common misbelief about Sign in with Apple (SiwA) is that you need an existing iOS application publised to the App Store to make it work, but that’s not the case. It works without a companion app, however you’ll need an application identifier registered in the dev portal.

App identifier

Select the Identifiers menu item from the list on the left, press the plus (+) button, select the App IDs option and press the Continue button. Fill out the description field and enter a custom bunde indentifier that you’d like to use (e.g. com.mydomain.ios.app). Scroll down the Capabilities list until you find the Sign in With Apple option, mark the checkbox (the Enable as primary App ID should appear right next to an edit button) and press the Continue button on the top right corner. Register the application identifier using the top right button, after you find everything all right.

You should see the newly created AppID in the list, if not there is a search icon on the right side of the screen. Pick the AppIDs option and the application identifer item should appear. 🔍

Service identifier

Next we need a service identifier for SiwA. Press the add button again and now select the Services IDs option. Enter a description and fill out the identifier using the same reverse-domain name style. I prefer to use my domain name with a suffix, that can be something like com.example.siwa.service. Press the Continue and the Register buttons, we’re almost ready with the configuration part.

Filter the list of identifiers by Service IDs and click on the newly created one. There is a Configure button, that you should press. Now associate the Primary App ID to this service identifier by selecting the application id that we made previously from the option list. Press the plus button next to the Website URLs text and enter the given domain that you’d like to use (e.g. example.com).

You’ll also have to add at least one Return URL, which is basically a redirect URL that the service can use after an auth request. You should always use HTTPS, but apart from this constraint the redirect URL can be anything (e.g. https://example.com/siwa-redirect). #notrailingslash

You can add or remove URLs at any time using this screen, thankfully there is a remove option for every domain and redirect URL. Press Next to save the URLs and Done when you are ready with the Sign in with Apple service configuration process.

Keys

The last thing that we need to create on the dev portal is a private key for client authentication. Select the Keys menu item on the left and press the add new button. Name the key as you want, select the Sign in with Apple option from the list. In the Configure menu select the Primary App ID, it should be connected with the application identifier we made earlier. Click Save to return to the previous screen and press Continue. Review the data and finally press the Register button.

Now this is your only chance to get the registered private key, if you pressed the done button without downloading it, you will lose the key forever, you have to make a new one, but don’t worry too much if you messed it up you can click on the key, press the big red Revoke button to delete it and start the process again. This comes handy if the key gets compromised, so don’t share it with anybody else otherwise you’ll have to make a new one. 🔑

Team & JWK identifier

I almost forget that you’ll need your team identifier and the JWK identifier for the sign in process. The JWK id can be found under the previously generated key details page. If you click on the name of the key you can view the details. The Key ID is on that page alongside with the revoke button and the Sign in with Apple configuration section where you can get the team identifier too, since the service bundle identifier is prefixed with that. Alternatively you can copy the team id from the very top right corner of the dev portal, it’s right next to your name.

Implementing Sign in With Apple

Before we write a single line of Swift code let me explain a simplified version of the entire process.

The entire login flow has 3 main components:

  • Initiate a web auth request using the SiwA button (start the OAuth flow)
  • Validate the returned user identity token using Apple’s JWK service
  • Exchange the user identity token for an access token

Some of the tutorials overcomplicate this, but you’ll see how easy is to write the entire flow using Vapor 4. We don’t even need additional scripts that generate tokens we can do everything in pure Swift, which is good. Lets start a new Vapor project. You’ll need the JWT package as well.

// swift-tools-version:5.2
-import PackageDescription
-
-let package = Package(
-    name: "binarybirds",
-    platforms: [
-       .macOS(.v10_15)
-    ],
-    dependencies: [
-        .package(url: "https://github.com/vapor/vapor.git", from: "4.4.0"),
-        .package(url: "https://github.com/vapor/leaf.git", from: "4.0.0-rc"),
-        .package(url: "https://github.com/vapor/jwt.git", from: "4.0.0-rc"),
-    ],
-    targets: [
-        .target(name: "App", dependencies: [
-            .product(name: "Vapor", package: "vapor"),
-            .product(name: "Leaf", package: "leaf"),
-            .product(name: "JWT", package: "jwt"),
-        ]),
-        .target(name: "Run", dependencies: ["App"]),
-    ]
-)
-

If you don’t know how to build the project you should read my beginners guide about Vapor 4.

The Sign in with Apple button

We’re going to use the Leaf template engine to render our views, it’s pretty simple to make it work, I’ll show you the configuration file in a second. We’re going to use just one simple template this time. We can call it index.leaf and save the file into the Resources/Views directory.

<!DOCTYPE html>
-<html>
-    <head>
-        <meta charset="utf-8">
-        <meta name="viewport" content="width=device-width, initial-scale=1">
-        <style>
-            .signin-button {
-                width: 240px;
-                height: 40px;
-            }
-            .signin-button > div > div > svg {
-                width: 100%;
-                height: 100%;
-                color: red;
-            }
-            .signin-button:hover {
-                cursor: pointer;
-            }
-            .signin-button > div {
-                outline: none;
-            }
-      </style>
-    </head>
-    <body>
-        <script type="text/javascript" src="https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js"></script>
-        <div id="appleid-signin" data-color="black" data-border="false" data-type="sign in" class="signin-button"></div>
-        <script type="text/javascript">
-            AppleID.auth.init({
-                clientId : '#(clientId)',
-                scope : '#(scope)',
-                redirectURI: '#(redirectUrl)',
-                state : '#(state)',
-                usePopup : #(popup),
-            });
-        </script>
-    </body>
-</html>
-

The Sign in with Apple JS framework can be used to render the login button on the website. There is a similar thing for iOS called AuthenticationServices, but this time we’re only going to focus on the web. Unfortunately the sign in button is quite buggy so we have to add some extra CSS hack to fix the underlying issues. Come on Apple, why do we have to hack these things? 😅

Starting the AppleID auth process is really simple you just have to configure a few parameters. The client id is service the bundle identifier that we entered on the developer portal. The scope can be either name or email, but you can use both if you want. The redirect URI is the redirect URL that we registered on the dev portal, and the state should be something unique that you can use to identify the request. Apple will send this state back to you in the response.

Noone talks about the usePopup parameter, so we’ll leave it that way too… 🤔

Alternatively you can use meta tags to configure the authorization object, you can read more about this in the Configuring your webpage for Sign in with Apple documentation.

Vapor configuration

It’s time to configure our Vapor application so we can render this Leaf template file and use the signing key that we acquired from Apple using the dev portal. We are dealing with some secret info here, so you should never store it in the repository, but you can use Vapor’s environment for this purpose. I prefer to have an extension for the available environment variables.

extension Environment {
-    // service bundle identifier
-    static var siwaId = Environment.get("SIWA_ID")!
-    // registered redirect url
-    static let siwaRedirectUrl = Environment.get("SIWA_REDIRECT_URL")!
-    // team identifier
-    static var siwaTeamId = Environment.get("SIWA_TEAM_ID")!
-    // key identifier
-    static var siwaJWKId = Environment.get("SIWA_JWK_ID")!
-    // contents of the downloaded key file
-    static var siwaKey = Environment.get("SIWA_KEY")!
-}
-

In Vapor 4 you can setup a custom JWT signer that can sign the payload with the proper keys and other values based on the configuration. This JWT signer can be used to verify the token in the response. It works like magic. JWT & JWTKit is an official Vapor package, there is definitely no need to implement your own solution. In this first example we will just prepare the signer for later use and render the index page so we can initalize the OAuth request using the website.

import Vapor
-import Leaf
-import JWT
-
-extension JWKIdentifier {
-    static let apple = JWKIdentifier(string: Environment.siwaJWKId)
-}
-
-extension String {
-    var bytes: [UInt8] {
-        return .init(self.utf8)
-    }
-}
-
-public func configure(_ app: Application) throws {
-    
-    app.views.use(.leaf)
-    //app.leaf.cache.isEnabled = false
-
-    app.middleware.use(SessionsMiddleware(session: app.sessions.driver))
-
-    app.jwt.apple.applicationIdentifier = Environment.siwaId
-    
-    let signer = try JWTSigner.es256(key: .private(pem: Environment.siwaKey.bytes))
-    app.jwt.signers.use(signer, kid: .apple, isDefault: false)
-
-    app.get { req -> EventLoopFuture<View> in
-        struct ViewContext: Encodable {
-            var clientId: String
-            var scope: String = "name email"
-            var redirectUrl: String
-            var state: String
-            var popup: Bool = false
-        }
-
-        let state = [UInt8].random(count: 16).base64
-        req.session.data["state"] = state
-        let context = ViewContext(clientId: Environment.siwaId,
-                                  redirectUrl: Environment.siwaRedirectUrl,
-                                  state: state)
-        return req.view.render("index", context)
-    }
-}
-

The session middleware is used to transfer a random generated code between the index page and the redirect handler. Now if you run the app and click on the Sign in with Apple button you’ll see that the flow starts, but it’ll fail after you identified yourself. That’s ok, step one is completed. ✅

The redirect handler

Apple will try to send a POST request with an object that contains the Apple ID token to the registered redirect URI after you’ve identified yourself using their login box. We can model this response object as an AppleAuthResponse struct in the following way:

import Foundation
-
-struct AppleAuthResponse: Decodable {
-
-    enum CodingKeys: String, CodingKey {
-        case code
-        case state
-        case idToken = "id_token"
-        case user
-    }
-
-    let code: String
-    let state: String
-    let idToken: String
-    let user: String
-}
-

The authorization code is the first parameter, the state shuld be equal with your state value that you send as a parameter when you press the login button, if they don’t match don’t trust the response somebody is trying to hack you. The idToken is the Apple ID token, we have to validate that using the JWKS validation endpoint. The user string is the email address of the user.

app.post("siwa-redirect") { req in
-    let state = req.session.data["state"] ?? ""
-    let auth = try req.content.decode(AppleAuthResponse.self)
-    guard !state.isEmpty, state == auth.state else {
-        return req.eventLoop.future("Invalid state")
-    }
-
-    return req.jwt.apple.verify(auth.idToken, applicationIdentifier: Environment.siwaId)
-    .flatMap { token in
-        //...
-    }
-}
-

The code above will handle the incoming response. First it’ll try to decode the AppleAuthResponse object from the body, next it’ll call the Apple verification service using your private key and the idToken value from the response. This validation service returns an AppleIdentityToken object. That’s part of the JWTKit package. We’ve just completed Step 2. ☺️

Exchanging the access token

The AppleIdentityToken only lives for a short period of time so we have to exchange it for an access token that can be used for much longer. We have to construct a request, we are going to use the following request body to exchange tokens:

struct AppleTokenRequestBody: Encodable {
-    
-    enum CodingKeys: String, CodingKey {
-        case clientId = "client_id"
-        case clientSecret = "client_secret"
-        case code
-        case grantType = "grant_type"
-        case redirectUri = "redirect_uri"
-    }
-    
-    /// The application identifier for your app.
-    let clientId: String
-
-    /// A secret generated as a JSON Web Token that uses the secret key generated by the WWDR portal.
-    let clientSecret: String
-
-    /// The authorization code received from your application’s user agent. The code is single use only and valid for five minutes.
-    let code: String
-    
-    /// The destination URI the code was originally sent to.
-    let redirectUri: String
-    
-    /// The grant type that determines how the client interacts with the server.
-    let grantType: String = "authorization_code"
-}
-

We’ll also need to generate the client secret, based on the response we are going to make a new AppleAuthToken object for this that can be signed using the already configured JWT service.

struct AppleAuthToken: JWTPayload {
-    let iss: String
-    let iat = Int(Date().timeIntervalSince1970)
-    let exp: Int
-    let aud = "https://appleid.apple.com"
-    let sub: String
-
-    init(clientId: String, teamId: String, expirationSeconds: Int = 86400 * 180) {
-        sub = clientId
-        iss = teamId
-        exp = self.iat + expirationSeconds
-    }
-
-    func verify(using signer: JWTSigner) throws {
-        guard iss.count == 10 else {
-            throw JWTError.claimVerificationFailure(name: "iss", reason: "TeamId must be your 10-character Team ID from the developer portal")
-        }
-
-        let lifetime = exp - iat
-        guard 0...15777000 ~= lifetime else {
-            throw JWTError.claimVerificationFailure(name: "exp", reason: "Expiration must be between 0 and 15777000")
-        }
-    }
-}
-

Since we have to make a new request we can use the built-in AysncHTTPClient service. I’ve made a little extension around the HTTPClient object to simplify the request creation process.

extension HTTPClient {
-    static func appleAuthTokenRequest(_ body: AppleTokenRequestBody) throws -> HTTPClient.Request {
-        var request = try HTTPClient.Request(url: "https://appleid.apple.com/auth/token", method: .POST)
-        request.headers.add(name: "User-Agent", value: "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6'")
-        request.headers.add(name: "Accept", value: "application/json")
-        request.headers.add(name: "Content-Type", value: "application/x-www-form-urlencoded")
-        request.body = .string(try URLEncodedFormEncoder().encode(body))
-        return request
-    }
-}
-

The funny thing here is if you don’t add the User-Agent header the SiwA service will return with an error, the problem was mentioned in this article also discussed on the Apple Developer Fourms.

Anyway, let me show you the complete redirect handler. 🤓

app.post("siwa-redirect") { req -> EventLoopFuture<String> in
-    let state = req.session.data["state"] ?? ""
-    let auth = try req.content.decode(AppleAuthResponse.self)
-    guard !state.isEmpty, state == auth.state else {
-        return req.eventLoop.future("Invalid state")
-    }
-
-    return req.jwt.apple.verify(auth.idToken, applicationIdentifier: Environment.siwaId)
-    .flatMap { token -> EventLoopFuture<HTTPClient.Response> in
-        do {
-            let secret = AppleAuthToken(clientId: Environment.siwaId, teamId: Environment.siwaTeamId)
-            let secretJwtToken = try app.jwt.signers.sign(secret, kid: .apple)
-
-            let body = AppleTokenRequestBody(clientId: Environment.siwaId,
-                                             clientSecret: secretJwtToken,
-                                             code: auth.code,
-                                             redirectUri: Environment.siwaRedirectUrl)
-
-            let request = try HTTPClient.appleAuthTokenRequest(body)
-            return app.http.client.shared.execute(request: request)
-        }
-        catch {
-            return req.eventLoop.future(error: error)
-        }
-    }
-    .map { response -> String in
-        guard var body = response.body else {
-            return "n/a"
-        }
-        return body.readString(length: body.readableBytes) ?? "n/a"
-    }
-}
-

As you can see I’m just sending the exchange request and map the final response to a string. From this point it is really easy to implement a decoder, the response is something like this:

struct AppleAccessToken: Decodable {
-
-    enum CodingKeys: String, CodingKey {
-        case accessToken = "access_token"
-        case tokenType = "token_type"
-        case expiresIn = "expires_in"
-        case refreshToken = "refresh_token"
-        case idToken = "id_token"
-    }
-
-    let accessToken: String
-    let tokenType: String
-    let expiresIn: Int
-    let refreshToken: String
-    let idToken: String
-}
-

You can use this response to authenticate your users, but that’s up-to-you based on your own business logic & requirements. You can use the same authTokenRequest method to refresh the token, you just have to set the grant type to refresh_token instead of authorization_code

I know that there is still room for improvements, the code is far from perfect, but it’s a working proof of concept. The article is getting really long, so maybe this is the right time stop. 😅

If you are looking for a good place to learn more about SiwA, you should check this link.

Conclusion

You can have a working Sign in with Apple implementation within an hour if you are using Vapor 4. The hardest part here is that you have to figure out every single little detail by yourself, looking at other people’s source code. I’m trying to explain things as easy as possible but hey, I’m still putting together the pieces for myself too.

This is an extremely fun journey for me. Moving back to the server side after almost a decade of iOS development is a refreshing experience. I can only hope you’ll enjoy my upcoming book called Practical Server Side Swift, as much as I enjoy learning and writing about the Vapor. ❤️

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/sitemap.xml b/docs/sitemap.xml deleted file mode 100644 index 6879176..0000000 --- a/docs/sitemap.xml +++ /dev/null @@ -1,335 +0,0 @@ - - - - https://theswiftdev.com/ - 2025-05-16 - https://theswiftdev.com/about/ - 2025-05-16 - https://theswiftdev.com/authors/ - 2025-05-16 - https://theswiftdev.com/practical-server-side-swift/ - 2025-05-16 - https://theswiftdev.com/tags/ - 2025-05-16 - - https://theswiftdev.com/10-little-uikit-tips-you-should-know/ - 2025-05-16 - https://theswiftdev.com/10-short-advices-that-will-make-you-a-better-vapor-developer-right-away/ - 2025-05-16 - https://theswiftdev.com/a-generic-crud-solution-for-vapor-4/ - 2025-05-16 - https://theswiftdev.com/a-simple-http2-server-using-vapor-4/ - 2025-05-16 - https://theswiftdev.com/ajax-calls-using-vapor-4/ - 2025-05-16 - https://theswiftdev.com/all-about-authentication-in-vapor-4/ - 2025-05-16 - https://theswiftdev.com/all-about-the-bool-type-in-swift/ - 2025-05-16 - https://theswiftdev.com/all-about-the-swift-package-manager-and-the-swift-toolchain/ - 2025-05-16 - https://theswiftdev.com/async-http-api-clients-in-swift/ - 2025-05-16 - https://theswiftdev.com/asynchronous-validation-for-vapor/ - 2025-05-16 - https://theswiftdev.com/awesome-native-xcode-extensions/ - 2025-05-16 - https://theswiftdev.com/beginners-guide-to-functional-swift/ - 2025-05-16 - https://theswiftdev.com/beginners-guide-to-modern-generic-programming-in-swift/ - 2025-05-16 - https://theswiftdev.com/beginners-guide-to-server-side-swift-using-the-hummingbird-framework/ - 2025-05-16 - https://theswiftdev.com/beginners-guide-to-server-side-swift-using-vapor-4/ - 2025-05-16 - https://theswiftdev.com/beginners-guide-to-swift-arrays/ - 2025-05-16 - https://theswiftdev.com/beginners-guide-to-swift-package-manager-command-plugins/ - 2025-05-16 - https://theswiftdev.com/beginners-guide-to-the-asyncawait-concurrency-api-in-vapor-fluent/ - 2025-05-16 - https://theswiftdev.com/building-a-global-storage-for-vapor/ - 2025-05-16 - https://theswiftdev.com/building-and-loading-dynamic-libraries-at-runtime-in-swift/ - 2025-05-16 - https://theswiftdev.com/building-input-forms-for-ios-apps/ - 2025-05-16 - https://theswiftdev.com/building-static-and-dynamic-swift-libraries-using-the-swift-compiler/ - 2025-05-16 - https://theswiftdev.com/building-tree-data-structures-in-swift/ - 2025-05-16 - https://theswiftdev.com/clockkit-complications-cheat-sheet/ - 2025-05-16 - https://theswiftdev.com/comparing-factory-design-patterns/ - 2025-05-16 - https://theswiftdev.com/conventions-for-xcode/ - 2025-05-16 - https://theswiftdev.com/custom-uiview-subclass-from-a-xib-file/ - 2025-05-16 - https://theswiftdev.com/custom-views-input-forms-and-mistakes/ - 2025-05-16 - https://theswiftdev.com/custom-working-directory-in-xcode/ - 2025-05-16 - https://theswiftdev.com/declarative-unit-tests-for-vapor/ - 2025-05-16 - https://theswiftdev.com/deep-dive-into-swift-frameworks/ - 2025-05-16 - https://theswiftdev.com/dynamic-libraries-and-code-replacements-in-swift/ - 2025-05-16 - https://theswiftdev.com/easy-multipart-file-upload-for-swift/ - 2025-05-16 - https://theswiftdev.com/encoding-and-decoding-data-using-the-hummingbird-framework/ - 2025-05-16 - https://theswiftdev.com/event-driven-generic-hooks-for-swift/ - 2025-05-16 - https://theswiftdev.com/everything-about-public-and-private-swift-attributes/ - 2025-05-16 - https://theswiftdev.com/file-upload-api-server-in-vapor-4/ - 2025-05-16 - https://theswiftdev.com/file-upload-using-vapor-4/ - 2025-05-16 - https://theswiftdev.com/generating-random-numbers-in-swift/ - 2025-05-16 - https://theswiftdev.com/get-started-with-the-fluent-orm-framework-in-vapor-4/ - 2025-05-16 - https://theswiftdev.com/getting-started-with-swiftio/ - 2025-05-16 - https://theswiftdev.com/how-to-build-better-command-line-apps-and-tools-using-swift/ - 2025-05-16 - https://theswiftdev.com/how-to-build-macos-apps-using-only-the-swift-package-manager/ - 2025-05-16 - https://theswiftdev.com/how-to-build-swiftui-apps-using-viper/ - 2025-05-16 - https://theswiftdev.com/how-to-call-c-code-from-swift/ - 2025-05-16 - https://theswiftdev.com/how-to-create-a-swift-package-collection/ - 2025-05-16 - https://theswiftdev.com/how-to-create-reusable-views-for-modern-collection-views/ - 2025-05-16 - https://theswiftdev.com/how-to-create-your-first-website-using-vapor-4-and-leaf/ - 2025-05-16 - https://theswiftdev.com/how-to-define-strings-use-escaping-sequences-and-interpolations/ - 2025-05-16 - https://theswiftdev.com/how-to-design-type-safe-restful-apis-using-swift-and-vapor/ - 2025-05-16 - https://theswiftdev.com/how-to-download-files-with-urlsession-using-combine-publishers-and-subscribers/ - 2025-05-16 - https://theswiftdev.com/how-to-launch-a-macos-app-at-login/ - 2025-05-16 - https://theswiftdev.com/how-to-make-a-swift-framework/ - 2025-05-16 - https://theswiftdev.com/how-to-parse-json-in-swift-using-codable-protocol/ - 2025-05-16 - https://theswiftdev.com/how-to-set-up-pgsql-for-fluent-4/ - 2025-05-16 - https://theswiftdev.com/how-to-store-keys-in-env-files/ - 2025-05-16 - https://theswiftdev.com/how-to-use-a-swift-library-in-c/ - 2025-05-16 - https://theswiftdev.com/how-to-use-c-libraries-in-swift/ - 2025-05-16 - https://theswiftdev.com/how-to-use-icloud-drive-documents/ - 2025-05-16 - https://theswiftdev.com/how-to-use-middlewares-in-vapor-4/ - 2025-05-16 - https://theswiftdev.com/how-to-use-the-result-type-to-handle-errors-in-swift/ - 2025-05-16 - https://theswiftdev.com/how-to-write-html-in-swift/ - 2025-05-16 - https://theswiftdev.com/how-to-write-services-for-viper/ - 2025-05-16 - https://theswiftdev.com/how-to-write-swift-scripts-using-the-new-command-api-in-vapor-4/ - 2025-05-16 - https://theswiftdev.com/hummingbird-routing-and-requests/ - 2025-05-16 - https://theswiftdev.com/introduction-to-asyncawait-in-swift/ - 2025-05-16 - https://theswiftdev.com/introduction-to-spm-artifact-bundles/ - 2025-05-16 - https://theswiftdev.com/ios-auto-layout-tutorial-programmatically/ - 2025-05-16 - https://theswiftdev.com/ios-custom-transition-tutorial-in-swift/ - 2025-05-16 - https://theswiftdev.com/iterator-design-pattern-in-swift/ - 2025-05-16 - https://theswiftdev.com/lazy-initialization-in-swift/ - 2025-05-16 - https://theswiftdev.com/lenses-and-prisms-in-swift/ - 2025-05-16 - https://theswiftdev.com/logging-for-beginners-in-swift/ - 2025-05-16 - https://theswiftdev.com/mastering-ios-auto-layout-anchors-programmatically-from-swift/ - 2025-05-16 - https://theswiftdev.com/mastering-the-viper-architecture/ - 2025-05-16 - https://theswiftdev.com/memory-layout-in-swift/ - 2025-05-16 - https://theswiftdev.com/modules-and-hooks-in-swift/ - 2025-05-16 - https://theswiftdev.com/networking-examples-for-appleos/ - 2025-05-16 - https://theswiftdev.com/picking-and-playing-videos-in-swift/ - 2025-05-16 - https://theswiftdev.com/picking-images-with-uiimagepickercontroller-in-swift-5/ - 2025-05-16 - https://theswiftdev.com/practical-guide-to-binary-operations-using-the-uint8-type-in-swift/ - 2025-05-16 - https://theswiftdev.com/progressive-web-apps-on-ios/ - 2025-05-16 - https://theswiftdev.com/promises-in-swift-for-beginners/ - 2025-05-16 - https://theswiftdev.com/result-builders-in-swift/ - 2025-05-16 - https://theswiftdev.com/running-and-testing-async-vapor-commands/ - 2025-05-16 - https://theswiftdev.com/running-tasks-in-parallel/ - 2025-05-16 - https://theswiftdev.com/self-sizing-cells-with-rotation-support/ - 2025-05-16 - https://theswiftdev.com/server-side-swift-projects-inside-docker-using-vapor-4/ - 2025-05-16 - https://theswiftdev.com/sign-in-with-apple-using-vapor-4/ - 2025-05-16 - https://theswiftdev.com/styling-by-subclassing/ - 2025-05-16 - https://theswiftdev.com/swift-5-and-abi-stability/ - 2025-05-16 - https://theswiftdev.com/swift-abstract-factory-design-pattern/ - 2025-05-16 - https://theswiftdev.com/swift-actors-tutorial-a-beginners-guide-to-thread-safe-concurrency/ - 2025-05-16 - https://theswiftdev.com/swift-adapter-design-pattern/ - 2025-05-16 - https://theswiftdev.com/swift-builder-design-pattern/ - 2025-05-16 - https://theswiftdev.com/swift-command-design-pattern/ - 2025-05-16 - https://theswiftdev.com/swift-delegate-design-pattern/ - 2025-05-16 - https://theswiftdev.com/swift-dependency-injection-design-pattern/ - 2025-05-16 - https://theswiftdev.com/swift-enum-all-values/ - 2025-05-16 - https://theswiftdev.com/swift-facade-design-pattern/ - 2025-05-16 - https://theswiftdev.com/swift-factory-method-design-pattern/ - 2025-05-16 - https://theswiftdev.com/swift-init-patterns/ - 2025-05-16 - https://theswiftdev.com/swift-object-pool-design-pattern/ - 2025-05-16 - https://theswiftdev.com/swift-on-the-server-in-2020/ - 2025-05-16 - https://theswiftdev.com/swift-package-manager-tutorial/ - 2025-05-16 - https://theswiftdev.com/swift-prototype-design-pattern/ - 2025-05-16 - https://theswiftdev.com/swift-simple-factory-design-pattern/ - 2025-05-16 - https://theswiftdev.com/swift-singleton-design-pattern/ - 2025-05-16 - https://theswiftdev.com/swift-static-factory-design-pattern/ - 2025-05-16 - https://theswiftdev.com/swift-structured-concurrency-tutorial/ - 2025-05-16 - https://theswiftdev.com/swift-visitor-design-pattern/ - 2025-05-16 - https://theswiftdev.com/swiftnio-tutorial-the-echo-server/ - 2025-05-16 - https://theswiftdev.com/table-joins-in-fluent-4/ - 2025-05-16 - https://theswiftdev.com/the-abstract-vapor-service-factory-design-pattern/ - 2025-05-16 - https://theswiftdev.com/the-anatomy-of-vapor-commands/ - 2025-05-16 - https://theswiftdev.com/the-future-of-server-side-swift/ - 2025-05-16 - https://theswiftdev.com/the-repository-pattern-for-vapor-4/ - 2025-05-16 - https://theswiftdev.com/the-swift-compiler-for-beginners/ - 2025-05-16 - https://theswiftdev.com/the-swift-package-manifest-file/ - 2025-05-16 - https://theswiftdev.com/the-ultimate-combine-framework-tutorial-in-swift/ - 2025-05-16 - https://theswiftdev.com/the-ultimate-viper-architecture-tutorial/ - 2025-05-16 - https://theswiftdev.com/top-20-ios-libraries-of-2019/ - 2025-05-16 - https://theswiftdev.com/uicollectionview-cells-with-circular-images-plus-rotation-support/ - 2025-05-16 - https://theswiftdev.com/uicollectionview-data-source-and-delegates-programmatically/ - 2025-05-16 - https://theswiftdev.com/uicolor-best-practices-in-swift/ - 2025-05-16 - https://theswiftdev.com/uikit-init-patterns/ - 2025-05-16 - https://theswiftdev.com/uikit-loadview-vs-viewdidload/ - 2025-05-16 - https://theswiftdev.com/uitableview-tutorial-in-swift/ - 2025-05-16 - https://theswiftdev.com/ultimate-grand-central-dispatch-tutorial-in-swift/ - 2025-05-16 - https://theswiftdev.com/ultimate-uicollectionview-guide-with-ios-examples-written-in-swift/ - 2025-05-16 - https://theswiftdev.com/uniquely-identifying-views/ - 2025-05-16 - https://theswiftdev.com/unsafe-memory-pointers-in-swift/ - 2025-05-16 - https://theswiftdev.com/urlsession-and-the-combine-framework/ - 2025-05-16 - https://theswiftdev.com/utilizing-makefiles-for-swift-projects/ - 2025-05-16 - https://theswiftdev.com/viper-best-practices-for-ios-developers/ - 2025-05-16 - https://theswiftdev.com/websockets-for-beginners-using-vapor-4-and-vanilla-javascript/ - 2025-05-16 - https://theswiftdev.com/what-are-the-best-practices-to-learn-ios-swift-in-2020/ - 2025-05-16 - https://theswiftdev.com/whats-new-in-swift-5-3/ - 2025-05-16 - https://theswiftdev.com/whats-new-in-vapor-4/ - 2025-05-16 - https://theswiftdev.com/working-with-diffable-data-sources-and-table-views-using-uikit/ - 2025-05-16 - - https://theswiftdev.com/authors/tibor-bodecs/ - 2025-05-16 - - https://theswiftdev.com/tags/design-pattern/ - 2025-05-16 - https://theswiftdev.com/tags/hummingbird/ - 2025-05-16 - https://theswiftdev.com/tags/ios/ - 2025-05-16 - https://theswiftdev.com/tags/ipados/ - 2025-05-16 - https://theswiftdev.com/tags/macos/ - 2025-05-16 - https://theswiftdev.com/tags/server/ - 2025-05-16 - https://theswiftdev.com/tags/swift/ - 2025-05-16 - https://theswiftdev.com/tags/swift-package-manager/ - 2025-05-16 - https://theswiftdev.com/tags/swiftui/ - 2025-05-16 - https://theswiftdev.com/tags/tooling/ - 2025-05-16 - https://theswiftdev.com/tags/tvos/ - 2025-05-16 - https://theswiftdev.com/tags/uikit/ - 2025-05-16 - https://theswiftdev.com/tags/vapor/ - 2025-05-16 - https://theswiftdev.com/tags/viper/ - 2025-05-16 - https://theswiftdev.com/tags/visionos/ - 2025-05-16 - https://theswiftdev.com/tags/watchos/ - 2025-05-16 - https://theswiftdev.com/tags/xcode/ - 2025-05-16 - - - diff --git a/docs/styling-by-subclassing/index.html b/docs/styling-by-subclassing/index.html deleted file mode 100644 index a433681..0000000 --- a/docs/styling-by-subclassing/index.html +++ /dev/null @@ -1,426 +0,0 @@ - - - - - - - - - - - - Styling by subclassing - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

Styling by subclassing

-
-

Learn how to design and build reusable user interface elements by using custom view subclasses from the UIKit framework in Swift.

-
- -

The problem: UI, UX, design

Building user interfaces is the hardest part of the job!

In a nutshell: design is a process of figuring out the best solution that fits a specific problem. Graphic design usually means the physical drawing on a canvas or a paper. UX is literally how the user interacts with the application, in other words: the overall virtual experience of the “customer” journey. UI is the visible interface that he/she will see and interact with by touching the screen. 👆

If I have to put on the designer hat (or even the developer hat) I have to tell you that figuring out and implementing proper user interfaces is the most challenging problem in most of the cases. Frontend systems nowadays (mobile, tablet, even desktop apps) are just fancy overlays on top of some JSON data from a service / API. 🤷‍♂️

Why is it so hard? Well, I believe that if you want to be a good designer, you need a proper engineering mindset as well. You have to be capable of observing the whole system (big picture), construct consistent UI elements (that actually look the same everywhere), plan the desired experience based on the functional specification and many more. It’s also quite a basic requirement to be an artist, think outside of the box, and be able to explain (describe) your idea to others. 🤯

Now tell me whose job is the hardest in the tech industry? Yep, as a gratis everyone is a designer nowadays, also some companies don’t hire this kind of experts at all, but simply let the work done by the developers. Anyway, let’s focus on how to create nice and reusable design implementations by using subclasses in Swift. 👍

Appearance, themes and styles

Let me start with a confession: I barely use the UIAppearance API. This is a personal preference, but I like to set design properties like font, textColor, backgroundColor directly on the view instances. Although in some cases I found the appearance proxy very nice, but still a little buggy. Maybe this will change with iOS 13 and the arrival of the long awaited dark mode.

Dear Apple please make an auto switch based on day / night cycles (you know like the sunset, sunrise option in the home app). 🌙

  • Style is a collection of attributes that specify the appearance for a single view.
  • Theme is a set of similar looking view styles, applied to the whole application.

Nowadays I usually create some predefined set of styling elements, most likely fonts, colors, but sometimes icons, etc. I like to go with the following structure:

Fonts

  • title
  • heading
  • subheading
  • body
  • small

Colors

  • title
  • heading
  • background

Icons

  • back
  • share

You can have even more elements, but for the sake of simplicity let’s just implement these ones with a really simple Swift solution using nested structs:

struct App {
-
-    struct Fonts {
-        static let title = UIFont.systemFont(ofSize: 32)
-        static let heading = UIFont.systemFont(ofSize: 24)
-        static let subheading = UIFont.systemFont(ofSize: 20)
-        static let body = UIFont.systemFont(ofSize: 16)
-        static let small = UIFont.systemFont(ofSize: 14)
-    }
-
-    struct Colors {
-        static let title = UIColor.blue
-        static let heading = UIColor.black
-        static let background = UIColor.white
-    }
-
-    struct Icons {
-        static let back = UIImage(named: "BackIcon")!
-        static let share = UIImage(named: "ShareIcon")!
-    }
-
-}
-
-//usage example:
-App.Fonts.title
-App.Colors.background
-App.Icons.back
-

This way I get a pretty simple syntax, which is nice, although this won’t let me do dynamic styling, so I can not switch between light / dark theme, but I really don’t mind that, because in most of the cases it’s not a requirement. 😅

Structs vs enums:

I could use enums instead of structs with static properties, but in this case I like the simplicity of this approach. I don’t want to mess around with raw values or extensions that accepts enums. It’s just a personal preference.

What if you have to support multiple themes?

That’s not a big issue, you can define a protocol for your needs, and implement the required theme protocol as you want. The real problem is when you have to switch between your themes, because you have to refresh / reload your entire UI. ♻️

There are some best practices, for example you can use the NSNotificationCenter class in order to notify every view / controller in your app to refresh if a theme change occurs. Another solution is to simply reinitialize the whole UI of the application, so this means you basically start from scratch with a brand new rootViewController. 😱

Anyway, check the links below if you need something like this, but if you just want to support dark mode in your app, I’d suggest to wait until the first iOS 13 beta comes out. Maybe Apple will give some shiny new API to make things easy.

Custom views as style elements

I promised styling by subclassing, so let’s dive into the topic. Now that we have a good solution to define fonts, colors and other basic building blocks, it’s time to apply those styles to actual UI elements. Of course you can use the UIAppearance API, but for example you can’t simply set custom fonts through the appearance proxy. 😢

Another thing is that I love consistency in design. So if a title is a blue, 32pt bold system font somewhere in my application I also expect that element to follow the same guideline everywhere else. I solve this problem by creating subclasses for every single view element that has a custom style applied to it. So for example:

  • TitleLabel (blue color, 32pt system font)
  • HeadingLabel (blue color, 24pt system font)
  • StandardButton (blue background)
  • DestructiveButton (red background)

Another good thing if you have subclasses and you’re working with autolayout constraints from code, that you can put all your constraint creation logic directly into the subclass itself. Let me show you an example:

import UIKit
-
-class TitleLabel: UILabel {
-
-    override init(frame: CGRect) {
-        super.init(frame: frame)
-
-        self.initialize()
-    }
-
-    required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-
-        self.initialize()
-    }
-
-    init() {
-        super.init(frame: .zero)
-
-        self.initialize()
-    }
-
-    func initialize() {
-        self.translatesAutoresizingMaskIntoConstraints = false
-        self.textColor = App.Colors.title
-        self.font = App.Fonts.title
-    }
-
-    func constraints(in view: UIView) -> [NSLayoutConstraint] {
-        return [
-            self.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16),
-            self.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -16),
-            self.centerYAnchor.constraint(equalTo: view.centerYAnchor),
-        ]
-    }
-}
-

As you can see I only have to set the font & textColor attributes once, so after the view initialization is done, I can be sure that every single instance of TitleLabel will look exactly the same. The usage is pretty simple too, you just have to set the class name in interface builder, or you can simply create the view like this:

// loadView method in a view controller...
-let titleLabel = TitleLabel()
-self.view.addSubview(titleLabel)
-NSLayoutConstraint.activate(titleLabel.constraints(in: self.view))
-

The thing I like the most about this approach is that my constraints are going to be just in the right place, so they won’t bloat my view controller’s loadView method. You can also create multiple constraint variations based on your current situation with extra parameters, so it’s quite scalable for every situation. 👍

View initialization is hard

The downside of this solution is that view initialization is kind of messed up, because of the interface builder support. You have to subclass every single view type (button, label, etc) and literally copy & paste your initialization methods again and again. I already have some articles about this, check the links below. 👇

In order to solve this problem I usually end up by creating a parent class for my own styled views. Here is an example for an abstract base class for my labels:

class Label: UILabel {
-
-    override init(frame: CGRect) {
-        super.init(frame: frame)
-
-        self.initialize()
-    }
-
-    required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-
-        self.initialize()
-    }
-
-    init() {
-        super.init(frame: .zero)
-
-        self.initialize()
-    }
-
-    func initialize() {
-        self.translatesAutoresizingMaskIntoConstraints = false
-    }
-}
-

So from now on I just have to override the initialize method.

class TitleLabel: Label {
-
-    override func initialize() {
-        super.initialize()
-
-        self.font = App.Fonts.title
-        self.textColor = App.Colors.title
-    }
-}
-

See, it’s so much better, because I don’t have to deal with the required view initialization methods anymore, also auto-resizing will be off by default. ❤️

My final takeaway from this lesson is that you should not be afraid of classes and object oriented programming if it comes to the UIKit framework. Protocol oriented programming (also functional programming) is great if you use it in the right place, but since UIKit is quite an OOP framework I believe it’s still better to follow these paradigms instead of choosing some hacky way. 🤪

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/swift-5-and-abi-stability/index.html b/docs/swift-5-and-abi-stability/index.html deleted file mode 100644 index a3da4a8..0000000 --- a/docs/swift-5-and-abi-stability/index.html +++ /dev/null @@ -1,325 +0,0 @@ - - - - - - - - - - - - Swift 5 and ABI stability - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 3 min read - -
-

Swift 5 and ABI stability

-
-

Apple's Swift 5 language version will be a huge milestone for the developer community, let's see what are the possible benefits of it.

-
- -

ABI stability

Everyone talks about that Swift 5 is will have a stable Application Binary Interface, but what exactly is this mysterious ABI thing that’s so important for Swift devs?.

ABI is an interface between two binary program modules.

You can read a well-written definition of ABI on Wikipedia or you can get a brief technical explanation through this reddit thread, but I’m trying to translate the core concepts of the Application Binary Interface to human language. 🤖

ABI is literally a binary communication interface for applications. Just like an API (application programming interface for devs = what was the name of that function?), ABI is a set of rules, so apps and program components like frameworks or libraries can speak the same “binary language”, so they can communicate with each other. 😅

The ABI usually covers the following things:

  • CPU instructions (registers, stack organization, memory access type)
  • sizes, layouts and alignments of data types
  • calling convention (how to call functions, pass arguments, return values)
  • system calls to the OS

So just like with APIs if you change something for example the name of a given method - or a size of a data type if we talk about ABIs - your older API clients will break. This is the exact same case here, older Swift versions are incompatible because the underlying changes in the ABI & API. So to make things work the proper version of Swift dynamic library has to be embedded into your bundle. That means bigger app sizes, but all the apps can run even with different Swift versions. 🤐

As you can see these are pretty nasty low level stuff, usually you don’t have to worry about any of these details, but it’s always good to know what the heck is an ABI in general. Maybe you’ll need this knowledge in one day. 👍

Integrated into the core of the OS

When a language is ABI-stable, that means it can be packaged and linked directly into the operating system itself. Currently if you build a Swift application a Swift dynamic library is going to be embedded to that bundle, in order to support your specific Swift version. This leads to bigger app sizes, and version incompatibility issues. After Swift is going to be an ABI stable language there is no need to package the dylib into apps, so Swift will have a smaller footprint on the system, also you can benefit from the OS provided under-the-hood improvements. 😎

Swift version compatibility

Another big win is version compatiblity. In the past if you had a project that was written in Swift N it was a real pain-in-the-ass to upgrade it to N+1. Especially applies to Swift 2 > Swift 3 migrations. In the future after both the ABI & API are going to be stabilized, you won’t need to upgrade (that much) at all. You can already see this happening, Swift 3 to Swift 4 was a much more easy step than the horrible one I mentioned above. After Swift 5, we can hope that everything is going to be backward compatible, so devs can focus on real tasks instead of migrations. 🙏

Closed-source Swift packages

Developers will be able to create closed source 3rd-party libraries written in Swift and distribute them as pre-compiled frameworks. This one is a HUGE one, because until the ABI stable version of Swift arrives, this is only possible with Objective-C. 🦕

Framework authors can ship the pre-compiled binaries instead of source files, so if you have to integrate multiple external dependencies into your project, clean build times can be significantly faster. This is also a nice advantage, but let’s talk about my personal favorite… 😎

SPM support for appleOS & Xcode

If the Swift language will be part of the core operating system, Apple should definitely provide Swift Package Manager support both for iOS, macOS, tvOS and watchOS. It would be a logical step forward and I can see some signs that points into this direction. Please Apple give the people what they want and sherlock CocoaPods once and for all. The iOS developer community will be a better place without Podfiles. 😅

Xcode should gain a deeply intergrated support for Swift Package Manager. Also it’d be nice to have a package discovery / search option, even it is centralized & controlled by Apple. It’d be truely amazing to have a neat UI to search for packages & integrate them just with one click to my iOS project. Works like magic! 💫

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/swift-abstract-factory-design-pattern/index.html b/docs/swift-abstract-factory-design-pattern/index.html deleted file mode 100644 index f23e3de..0000000 --- a/docs/swift-abstract-factory-design-pattern/index.html +++ /dev/null @@ -1,371 +0,0 @@ - - - - - - - - - - - - Swift abstract factory design pattern - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 1 min read - -
-

Swift abstract factory design pattern

-
-

Let's combine factory method with simple factory voilá: here is the abstract factory design pattern written in Swift language!

-
- -

Abstract factory in Swift

The abstract factory pattern provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete classes.

So abstract factory is there for you to create families of related objects. The implementation usually combines simple factory & factory method principles. Individual objects are created through factory methods, while the whole thing is wrapped in an “abstract” simple factory. Now check the code! 😅

// service protocols
-protocol ServiceFactory {
-    func create() -> Service
-}
-
-protocol Service {
-    var url: URL { get }
-}
-
-// staging
-class StagingService: Service {
-    var url: URL { return URL(string: "https://dev.localhost/")! }
-}
-
-class StagingServiceFactory: ServiceFactory {
-    func create() -> Service {
-        return StagingService()
-    }
-}
-
-// production
-class ProductionService: Service {
-    var url: URL { return URL(string: "https://live.localhost/")! }
-}
-
-class ProductionServiceFactory: ServiceFactory {
-    func create() -> Service {
-        return ProductionService()
-    }
-}
-
-// abstract factory
-class AppServiceFactory: ServiceFactory {
-
-    enum Environment {
-        case production
-        case staging
-    }
-
-    var env: Environment
-
-    init(env: Environment) {
-        self.env = env
-    }
-
-    func create() -> Service {
-        switch self.env {
-        case .production:
-            return ProductionServiceFactory().create()
-        case .staging:
-            return StagingServiceFactory().create()
-        }
-    }
-}
-
-let factory = AppServiceFactory(env: .production)
-let service = factory.create()
-print(service.url)
-

As you can see using an abstract factory will influence the whole application logic, while factory methods have effects only on local parts. Implementation can vary for example you could also create a standalone protocol for the abstract factory, but in this example I wanted to keep things as simple as I could.

Abstract factories are often used to achieve object independence. For example if you have multiple different SQL database connectors (PostgreSQL, MySQL, etc.) written in Swift with a common interface, you could easily switch between them anytime using this pattern. Same logic could be applied for anything with a similar scenario. 🤔

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Event-driven generic hooks for Swift

-
-

In this article I am going to show you how to implement a basic event processing system for your modular Swift application.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Iterator design pattern in Swift

-
-

Learn the iterator design pattern by using some custom sequences, conforming to the IteratorProtocol from the Swift standard library.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Lazy initialization in Swift

-
-

Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Lenses and prisms in Swift

-
-

Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/swift-actors-tutorial-a-beginners-guide-to-thread-safe-concurrency/index.html b/docs/swift-actors-tutorial-a-beginners-guide-to-thread-safe-concurrency/index.html deleted file mode 100644 index 68a8bc2..0000000 --- a/docs/swift-actors-tutorial-a-beginners-guide-to-thread-safe-concurrency/index.html +++ /dev/null @@ -1,486 +0,0 @@ - - - - - - - - - - - - Swift actors tutorial - a beginner's guide to thread safe concurrency - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 9 min read - -
-

Swift actors tutorial - a beginner's guide to thread safe concurrency

-
-

Learn how to use the brand new actor model to protect your application from unwanted data-races and memory issues.

-
- -

Thread safety & data races

Before we dive in to Swift actors, let’s have a simplified recap of computer theory first.

An instance of a computer program is called process. A process contains smaller instructions that are going to be executed at some point in time. These instruction tasks can be performed one after another in a serial order or concurrently. The operating system is using multiple threads to execute tasks in parallel, also schedules the order of execution with the help of a scheduler. 🕣

After a task is being completed on a given thread, the CPU can to move forward with the execution flow. If the new task is associated with a different thread, the CPU has to perform a context switch. This is quite an expensive operation, because the state of the old thread need to be saved, the new one should be restored before we can perform our actual task.

During this context switching a bunch of other oprations can happen on different threads. Since modern CPU architectures have multiple cores, they can handle multiple threads at the same time. Problems can happen if the same resource is being modified at the same time on multiple threads. Let me show you a quick example that produces an unsafe output. 🙉

var unsafeNumber: Int = 0
-DispatchQueue.concurrentPerform(iterations: 100) { i in
-    print(Thread.current)
-    unsafeNumber = i
-}
-
-print(unsafeNumber)
-

If you run the code above multiple times, it’s possible to have a different output each time. This is because the concurrentPerform method runs the block on different threads, some threads have higher priorities than others so the execution order is not guaranteed. You can see this for yourself, by printing the current thread in each block. Some of the number changes happen on the main thread, but others happen on a background thread. 🧵

The main thread is a special one, all the user interface related updates should happen on this one. If you are trying to update a view from a background thread in an iOS application you’ll could get an warning / error or even a crash. If you are blocking the main thread with a long running application your entire UI can become unresponsive, that’s why it is good to have multiple threads, so you can move your computation-heavy operations into background threads.

It’s a very common approach to work with multiple threads, but this can lead to unwanted data races, data corruption or crashes due to memory issues. Unfortunately most of the Swift data types are not thread safe by default, so if you want to achieve thread-safety you usually had to work with serial queues or locks to guarantee the mutual exclusivity of a given variable.

var threads: [Int: String] = [:]
-DispatchQueue.concurrentPerform(iterations: 100) { i in
-    threads[i] = "\(Thread.current)"
-}
-print(threads)
-

The snippet above will crash for sure, since we’re trying to modify the same dictionary from multiple threads. This is called a data-race. You can detect these kind of issues by enabling the Thread Sanitizer under the Scheme > Run > Diagnostics tab in Xcode. 🔨

Now that we know what’s a data race, let’s fix that by using a regular Grand Central Dispatch based approach. We’re going to create a new serial dispatch queue to prevent concurrent writes, this will syncronize all the write operations, but of course it has a hidden cost of switching the context each and every time we update the dictionary.

var threads: [Int: String] = [:]
-let lockQueue = DispatchQueue(label: "my.serial.lock.queue")
-DispatchQueue.concurrentPerform(iterations: 100) { i in
-    lockQueue.sync {
-        threads[i] = "\(Thread.current)"
-    }
-}
-print(threads)
-

This synchronization technique is a quite popular solution, we could create a generic class that hides the internal private storage and the lock queue, so we can have a nice public interface that you can use safely without dealing with the internal protection mechanism. For the sake of simplicity we’re not going to introduce generics this time, but I’m going to show you a simple AtomicStorage implementation that uses a serial queue as a lock system. 🔒

import Foundation
-import Dispatch
-
-class AtomicStorage {
-
-    private let lockQueue = DispatchQueue(label: "my.serial.lock.queue")
-    private var storage: [Int: String]
-    
-    init() {
-        self.storage = [:]
-    }
-        
-    func get(_ key: Int) -> String? {
-        lockQueue.sync {
-            storage[key]
-        }
-    }
-    
-    func set(_ key: Int, value: String) {
-        lockQueue.sync {
-            storage[key] = value
-        }
-    }
-
-    var allValues: [Int: String] {
-        lockQueue.sync {
-            storage
-        }
-    }
-}
-
-let storage = AtomicStorage()
-DispatchQueue.concurrentPerform(iterations: 100) { i in
-    storage.set(i, value: "\(Thread.current)")
-}
-print(storage.allValues)
-

Since both read and write operations are sync, this code can be quite slow since the entire queue has to wait for both the read and write operations. Let’s fix this real quick by changing the serial queue to a concurrent one, and marking the write function with a barrier flag. This way users can read much faster (concurrently), but writes will be still synchronized through these barrier points.

import Foundation
-import Dispatch
-
-class AtomicStorage {
-
-    private let lockQueue = DispatchQueue(label: "my.concurrent.lock.queue", attributes: .concurrent)
-    private var storage: [Int: String]
-    
-    init() {
-        self.storage = [:]
-    }
-        
-    func get(_ key: Int) -> String? {
-        lockQueue.sync {
-            storage[key]
-        }
-    }
-    
-    func set(_ key: Int, value: String) {
-        lockQueue.async(flags: .barrier) { [unowned self] in
-            storage[key] = value
-        }
-    }
-
-    var allValues: [Int: String] {
-        lockQueue.sync {
-            storage
-        }
-    }
-}
-
-let storage = AtomicStorage()
-DispatchQueue.concurrentPerform(iterations: 100) { i in
-    storage.set(i, value: "\(Thread.current)")
-}
-print(storage.allValues)
-

Of course we could speed up the mechanism with dispatch barriers, alternatively we could use an os_unfair_lock, NSLock or a dispatch semaphore to create similar thread-safe atomic objects.

One important takeaway is that even if we are trying to select the best available option by using sync we’ll always block the calling thread too. This means that nothing else can run on the thread that calls synchronized functions from this class until the internal closure completes. Since we’re synchronously waiting for the thread to return we can’t utilize the CPU for other work. ⏳

We can say that there are quite a lot of problems with this approach:

  • Context switches are expensive operations
  • Spawning multiple threads can lead to thread explosions
  • You can (accidentally) block threads and prevent further code execution
  • You can create a deadlock if multiple tasks are waiting for each other
  • Dealing with (completion) blocks and memory references are error prone
  • It’s really easy to forget to call the proper synchronization block

That’s quite a lot of code just to provide thread-safe atomic access to a property. Despite the fact that we’re using a concurrent queue with barriers (locks have problems too), the CPU needs to switch context every time we’re calling these functions from a different thread. Due to the synchronous nature we are blocking threads, so this code is not the most efficient.

Fortunately Swift 5.5 offers a safe, modern and overall much better alternative. 🥳

Introducing Swift actors

Now let’s refactor this code using the new Actor type introduced in Swift 5.5. Actors can protect internal state through data isolation ensuring that only a single thread will have access to the underlying data structure at a given time. Long story short, everything inside an actor will be thread-safe by default. First I’ll show you the code, then we’ll talk about it. 😅

import Foundation
-
-actor AtomicStorage {
-
-    private var storage: [Int: String]
-    
-    init() {
-        self.storage = [:]
-    }
-        
-    func get(_ key: Int) -> String? {
-        storage[key]
-    }
-    
-    func set(_ key: Int, value: String) {
-        storage[key] = value
-    }
-
-    var allValues: [Int: String] {
-        storage
-    }
-}
-
-Task {
-    let storage = AtomicStorage()
-    await withTaskGroup(of: Void.self) { group in
-        for i in 0..<100 {
-            group.async {
-                await storage.set(i, value: "\(Thread.current)")
-            }
-        }
-    }
-    print(await storage.allValues)
-}
-

First of all, actors are reference types, just like classes. They can have methods, properties, they can implement protocols, but they don’t support inheritance.

Since actors are closely related to the newly introduced async/await concurrency APIs in Swift you should be familiar with that concept too if you want to understand how they work.

The very first big difference is that we don’t need to provide a lock mechanism anymore in order to provide read or write access to our private storage property. This means that we can safely access actor properties within the actor using a synchronous way. Members are isolated by default, so there is a guarantee (by the compiler) that we can only access them using the same context.

What’s going on with the new Task API and all the await keywords? 🤔

Well, the Dispatch.concurrentPerform call is part of a parallelism API and Swift 5.5 introduced concurrency instead of parallelism, we have to move away from regular queues and use structured concurrency to perform tasks in parallel. Also the concurrentPerform function is not an asynchronous operation, it’ll block the caller thread until all the work is done within the block.

Working with async/await means that the CPU can work on a different task when awaits for a given operation. Every await call is a potential suspension point, where the function can give up the thread and the CPU can perform other tasks until the awaited function resumes & returns with the necessary value. The new Swift concurrency APIs are built on top a cooperative thread pool, where each CPU core has just the right amount of threads and the suspension & continuation happens “virtually” with the help of the language runtime. This is far more efficient than actual context switching, and also means that when you interact with async functions and await for a function the CPU can work on other tasks instead of blocking the thread on the call side.

So back to the example code, since actors have to protect their internal states, they only allows us to access members asynchronously when you reference from async functions or outside the actor. This is very similar to the case when we had to use the lockQueue.sync to protect our read / write functions, but instead of giving the ability to the system to perform other tasks on the thread, we’ve entirely blocked it with the sync call. Now with await we can give up the thread and allow others to perform operations using it and when the time comes the function can resume.

Inside the task group we can perform our tasks asynchronously, but since we’re accessing the actor function (from an async context / outside the actor) we have to use the await keyword before the set call, even if the function is not marked with the async keyword.

The system knows that we’re referencing the actor’s property using a different context and we have to perform this operation always isolated to eliminate data races. By converting the function to an async call we give the system a chance to perform the operation on the actor’s executor. Later on we’ll be able to define custom executors for our actors, but this feature is not available yet.

Currently there is a global executor implementation (associated with each actor) that enqueues the tasks and runs them one-by-one, if a task is not running (no contention) it’ll be scheduled for execution (based on the priority) otherwise (if the task is already running / under contention) the system will just pick-up the message without blocking.

The funny thing is that this does not necessary means that the exact same thread… 😅

import Foundation
-
-extension Thread {
-    var number: String {
-        "\(value(forKeyPath: "private.seqNum")!)"
-    }
-}
-
-actor AtomicStorage {
-
-    private var storage: [Int: String]
-    
-    init() {
-        print("init actor thread: \(Thread.current.number)")
-        self.storage = [:]
-    }
-        
-    func get(_ key: Int) -> String? {
-        storage[key]
-    }
-    
-    func set(_ key: Int, value: String) {
-        storage[key] = value + ", actor thread: \(Thread.current.number)"
-    }
-
-    var allValues: [Int: String] {
-        print("allValues actor thread: \(Thread.current.number)")
-        return storage
-    }
-}
-
-
-Task {
-    let storage = AtomicStorage()
-    await withTaskGroup(of: Void.self) { group in
-        for i in 0..<100 {
-            group.async {
-                await storage.set(i, value: "caller thread: \(Thread.current.number)")
-            }
-        }
-    }    
-    for (k, v) in await storage.allValues {
-        print(k, v)
-    }
-}
-

Multi-threading is hard, anyway same thing applies to the storage.allValues statement. Since we’re accessing this member from outside the actor, we have to await until the “synchronization happens”, but with the await keyword we can give up the current thread, wait until the actor returns back the underlying storage object using the associated thread, and voilá we can continue just where we left off work. Of course you can create async functions inside actors, when you call these methods you’ll always have to use await, no matter if you are calling them from the actor or outside.

There is still a lot to cover, but I don’t want to bloat this article with more advanced details. I know I’m just scratching the surface and we could talk about non-isolated functions, actor reentrancy, global actors and many more. I’ll definitely create more articles about actors in Swift and cover these topics in the near future, I promise. Swift 5.5 is going to be a great release. 👍

Hopefully this tutorial will help you to start working with actors in Swift. I’m still learning a lot about the new concurrency APIs and nothing is written in stone yet, the core team is still changing names and APIs, there are some proposals on the Swift evolution dashboard that still needs to be reviewed, but I think the Swift team did an amazing job. Thanks everyone. 🙏

Honestly actors feels like magic and I already love them. 😍

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/swift-adapter-design-pattern/index.html b/docs/swift-adapter-design-pattern/index.html deleted file mode 100644 index 55bc93b..0000000 --- a/docs/swift-adapter-design-pattern/index.html +++ /dev/null @@ -1,379 +0,0 @@ - - - - - - - - - - - - Swift adapter design pattern - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 2 min read - -
-

Swift adapter design pattern

-
-

Turn an incompatible object into a target interface or class by using a real world example and the adapter design pattern in Swift.

-
- -

Fist of all let me emphasize that, this is the real world representation of what we’re going to build in this little Swift adapter pattern tutorial:

Picture of a USB Adapter

Adapter is a structural design pattern that allows objects with incompatible interfaces to work together. In other words, it transforms the interface of an object to adapt it to a different object.

So adapter can transform one thing into another, sometimes it’s called wrapper, because it wraps the object and provides a new interface around it. It’s like a software dongle for specific interfaces or legacy classes. (Dongle haters: it’s time to leave the past behind!) 😂

Adapter design pattern implementation

Creating an adapter in Swift is actually a super easy task to do. You just need to make a new object, “box” the old one into it and implement the required interface on your new class or struct. In other words, a wrapper object will be our adapter to implement the target interface by wrapping an other adaptee object. So again:

Adaptee

The object we are adapting to a specific target (e.g. old-school USB-A port).

Adapter

An object that wraps the original one and produces the new requirements specified by some target interface (this does the actual work, aka. the little dongle above).

Target

It is the object we want to use adaptee with (our USB-C socket).

How to use the adapter pattern in Swift?

You can use an adapter if you want to integrate a third-party library in your code, but it’s interface doesn’t match with your requirements. For example you can create a wrapper around an entire SDK or backend API endpoints in order to create a common denominator. 👽

In my example, I’m going to wrap an EKEvent object with an adapter class to implement a brand new protocol. 📆

import Foundation
-import EventKit
-
-// our target protocol
-protocol Event {
-    var title: String { get }
-    var startDate: String { get }
-    var endDate: String { get }
-}
-
-// adapter (wrapper class)
-class EventAdapter {
-
-    private lazy var dateFormatter: DateFormatter = {
-        let dateFormatter = DateFormatter()
-        dateFormatter.dateFormat = "yyyy. MM. dd. HH:mm"
-        return dateFormatter
-    }()
-
-    private var event: EKEvent
-
-    init(event: EKEvent) {
-        self.event = event
-    }
-}
-
-// actual adapter implementation
-extension EventAdapter: Event {
-
-    var title: String {
-        return self.event.title
-    }
-    var startDate: String {
-        return self.dateFormatter.string(from: event.startDate)
-    }
-    var endDate: String {
-        return self.dateFormatter.string(from: event.endDate)
-    }
-}
-
-// let's create an EKEvent adaptee instance
-let dateFormatter = DateFormatter()
-dateFormatter.dateFormat = "MM/dd/yyyy HH:mm"
-
-let calendarEvent = EKEvent(eventStore: EKEventStore())
-calendarEvent.title = "Adapter tutorial deadline"
-calendarEvent.startDate = dateFormatter.date(from: "07/30/2018 10:00")
-calendarEvent.endDate = dateFormatter.date(from: "07/30/2018 11:00")
-
-// now we can use the adapter class as an Event protocol, instead of an EKEvent
-let adapter = EventAdapter(event: calendarEvent)
-// adapter.title
-// adapter.startDate
-// adapter.endDate
-

Another use case is when you have to use several existing final classes or structs but they lack some functionality and you want to build a new target interface on top of them. Sometimes it’s a good choice to implement an wrapper to handle this messy situation. 🤷‍♂️

That’s all about the adapter design pattern. Usually it’s really easy to implement it in Swift - or in any other programming language - but it’s super useful and sometimes unavoidable.

Kids, remember: don’t go too hard on dongles! 😉 #himym

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Event-driven generic hooks for Swift

-
-

In this article I am going to show you how to implement a basic event processing system for your modular Swift application.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Iterator design pattern in Swift

-
-

Learn the iterator design pattern by using some custom sequences, conforming to the IteratorProtocol from the Swift standard library.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Lazy initialization in Swift

-
-

Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Lenses and prisms in Swift

-
-

Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/swift-builder-design-pattern/index.html b/docs/swift-builder-design-pattern/index.html deleted file mode 100644 index 2f08547..0000000 --- a/docs/swift-builder-design-pattern/index.html +++ /dev/null @@ -1,482 +0,0 @@ - - - - - - - - - - - - Swift builder design pattern - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 3 min read - -
-

Swift builder design pattern

-
-

Learn how to implement the builder pattern in Swift to hide the complexity of creating objects with lots of individual properties.

-
- -

How does the builder pattern work?

The builder pattern can be implemented in multiple ways, but that really doesn’t matters if you understand the main goal of the pattern:

The intent of the Builder design pattern is to separate the construction of a complex object from its representation.

So if you have an object with lots of properties, you want to hide the complexity of the initialization process, you could write a builder and construct the object through that. It can be as simple as a build method or an external class that controls the entire construction process. It all depends on the given environment. 🏗

That’s enough theory for now, let’s see the builder pattern in action using dummy, but real-world examples and the powerful Swift programming language! 💪

Simple emitter builder

I believe that SKEmitterNode is quite a nice example. If you want to create custom emitters and set properties programmatically - usually for a SpriteKit game - an emitter builder class like this could be a reasonable solution. 👾

class EmitterBuilder {
-
-    func build() -> SKEmitterNode {
-        let emitter = SKEmitterNode()
-        emitter.particleTexture = SKTexture(imageNamed: "MyTexture")
-        emitter.particleBirthRate = 100
-        emitter.particleLifetime = 60
-        emitter.particlePositionRange = CGVector(dx: 100, dy: 100)
-        emitter.particleSpeed = 10
-        emitter.particleColor = .red
-        emitter.particleColorBlendFactor = 1
-        return emitter
-    }
-}
-
-EmitterBuilder().build()
-

Simple theme builder

Let’s move away from gaming and imagine that you are making a theme engine for your UIKit application which has many custom fonts, colors, etc. a builder could be useful to construct standalone themes. 🔨

struct Theme {
-    let textColor: UIColor?
-    let backgroundColor: UIColor?
-}
-
-class ThemeBuilder {
-
-    enum Style {
-        case light
-        case dark
-    }
-
-    func build(_ style: Style) -> Theme {
-        switch style {
-        case .light:
-            return Theme(textColor: .black, backgroundColor: .white)
-        case .dark:
-            return Theme(textColor: .white, backgroundColor: .black)
-        }
-    }
-}
-
-let builder = ThemeBuilder()
-let light = builder.build(.light)
-let dark = builder.build(.dark)
-"Chained" URL builder
-With this approach you can configure your object through various methods and every single one of them will return the same builder object. This way you can chain the configuration and as a last step build the final product. ⛓
-
-class URLBuilder {
-
-    private var components: URLComponents
-
-    init() {
-        self.components = URLComponents()
-    }
-
-    func set(scheme: String) -> URLBuilder {
-        self.components.scheme = scheme
-        return self
-    }
-
-    func set(host: String) -> URLBuilder {
-        self.components.host = host
-        return self
-    }
-
-    func set(port: Int) -> URLBuilder {
-        self.components.port = port
-        return self
-    }
-
-    func set(path: String) -> URLBuilder {
-        var path = path
-        if !path.hasPrefix("/") {
-            path = "/" + path
-        }
-        self.components.path = path
-        return self
-    }
-
-    func addQueryItem(name: String, value: String) -> URLBuilder  {
-        if self.components.queryItems == nil {
-            self.components.queryItems = []
-        }
-        self.components.queryItems?.append(URLQueryItem(name: name, value: value))
-        return self
-    }
-
-    func build() -> URL? {
-        return self.components.url
-    }
-}
-
-let url = URLBuilder()
-    .set(scheme: "https")
-    .set(host: "localhost")
-    .set(path: "api/v1")
-    .addQueryItem(name: "sort", value: "name")
-    .addQueryItem(name: "order", value: "asc")
-    .build()
-
-

The builder pattern with a director

Let’s meet the director object. As it seems like this little thing decouples the builder from the exact configuration part. So for instance you can make a game with circles, but later on if you change your mind and you’d like to use squares, that’s relatively easy. You just have to create a new builder, and everything else can be the same. 🎬

protocol NodeBuilder {
-    var name: String { get set }
-    var color: SKColor { get set }
-    var size: CGFloat { get set }
-
-    func build() -> SKShapeNode
-}
-
-protocol NodeDirector {
-    var builder: NodeBuilder { get set }
-
-    func build() -> SKShapeNode
-}
-
-class CircleNodeBuilder: NodeBuilder {
-    var name: String = ""
-    var color: SKColor = .clear
-    var size: CGFloat = 0
-
-    func build() -> SKShapeNode {
-        let node = SKShapeNode(circleOfRadius: self.size)
-        node.name = self.name
-        node.fillColor = self.color
-        return node
-    }
-}
-
-class PlayerNodeDirector: NodeDirector {
-
-    var builder: NodeBuilder
-
-    init(builder: NodeBuilder) {
-        self.builder = builder
-    }
-
-    func build() -> SKShapeNode {
-        self.builder.name = "Hello"
-        self.builder.size = 32
-        self.builder.color = .red
-        return self.builder.build()
-    }
-}
-
-let builder = CircleNodeBuilder()
-let director = PlayerNodeDirector(builder: builder)
-let player = director.build()
-

Block based builders

A more swifty approach can be the use of blocks instead of builder classes to configure objects. Of course we could argue on if this is still a builder pattern or not… 😛

extension UILabel {
-
-    static func build(block: ((UILabel) -> Void)) -> UILabel {
-        let label = UILabel(frame: .zero)
-        block(label)
-        return label
-    }
-}
-
-let label = UILabel.build { label in
-    label.translatesAutoresizingMaskIntoConstraints = false
-    label.text = "Hello wold!"
-    label.font = UIFont.systemFont(ofSize: 12)
-}
-

Please note that the builder implementation can vary on the specific use case. Sometimes a builder is combined with factories. As far as I can see almost everyone interpreted it in a different way, but I don’t think that’s a problem. Design patterns are well-made guidelines, but sometimes you have to cross the line.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Event-driven generic hooks for Swift

-
-

In this article I am going to show you how to implement a basic event processing system for your modular Swift application.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Iterator design pattern in Swift

-
-

Learn the iterator design pattern by using some custom sequences, conforming to the IteratorProtocol from the Swift standard library.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Lazy initialization in Swift

-
-

Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Lenses and prisms in Swift

-
-

Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/swift-command-design-pattern/index.html b/docs/swift-command-design-pattern/index.html deleted file mode 100644 index e1ff5a5..0000000 --- a/docs/swift-command-design-pattern/index.html +++ /dev/null @@ -1,396 +0,0 @@ - - - - - - - - - - - - Swift command design pattern - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 1 min read - -
-

Swift command design pattern

-
-

This time I'm going to show you a behavioral pattern. Here is a little example of the command design patten written in Swift.

-
- -

The command pattern can be handy if you’d like to provide a common interface for different actions that will be executed later in time. Usually it’s an object that encapsulates all the information needed to run the underlying action properly.

Commands are often used to handle user interface actions, create undo managers, or manage transactions. Let’s see a command pattern implementation in Swift by creating a command line argument handler with emojis. 💾

#!/usr/bin/env swift
-
-import Foundation
-
-protocol Command {
-    func execute()
-}
-
-class HelpCommand: Command {
-
-    func execute() {
-        Help().info()
-    }
-}
-
-class Help {
-
-    func info() {
-        print("""
-
-             🤖 Commander 🤖
-                  v1.0
-
-        Available commands:
-
-            👉 help      This command
-            👉 ls        List documents
-
-        Bye! 👋
-
-        """)
-    }
-}
-
-class ListCommand: Command {
-
-    func execute() {
-        List().homeDirectoryContents()
-    }
-}
-
-class List {
-
-    func homeDirectoryContents() {
-        let fileManager = FileManager.default
-        guard let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else {
-            print("Could not open documents directory")
-            exit(-1)
-        }
-        do {
-            let fileURLs = try fileManager.contentsOfDirectory(at: documentsURL, includingPropertiesForKeys: nil)
-            print("\n\t📁 Listing documents directory:\n")
-            print(fileURLs.map { "\t\t💾 " + $0.lastPathComponent }.joined(separator: "\n\n") + "\n" )
-        }
-        catch {
-            print(error.localizedDescription)
-            exit(-1)
-        }
-
-    }
-}
-
-class App {
-
-    var commands: [String:Command] = [:]
-
-    init() {
-        self.commands["help"] = HelpCommand()
-        self.commands["ls"] = ListCommand()
-    }
-
-    func run() {
-        let arguments = CommandLine.arguments[1...]
-
-        guard let key = arguments.first, self.commands[key] != nil else {
-            print("Usage: ./command.swift [\(self.commands.keys.joined(separator: "|"))]")
-            exit(-1)
-        }
-
-        self.commands[key]!.execute()
-    }
-}
-
-App().run()
-

If you save this file, can run it by simply typing ./file-name.swift from a terminal window. The Swift compiler will take care of the rest.

Real world use cases for the command design pattern:

+ various button actions
-+ collection / table view selection actions
-+ navigating between controllers
-+ history management / undo manager
-+ transactional behavior
-+ progress management
-+ wizards
-

As you can see this pattern can be applied in multiple areas. Apple even made a specific class for this purpose called NSInvocation, but unfortunately it’s not available in Swift, due to it’s dynamic behavior. That’s not a big deal, you can always make your own protocol & implementation, in most cases you just need one extra class that wraps the underlying command logic. 😛

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Event-driven generic hooks for Swift

-
-

In this article I am going to show you how to implement a basic event processing system for your modular Swift application.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Iterator design pattern in Swift

-
-

Learn the iterator design pattern by using some custom sequences, conforming to the IteratorProtocol from the Swift standard library.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Lazy initialization in Swift

-
-

Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Lenses and prisms in Swift

-
-

Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - -
-
- -
- - - -
- - - - diff --git a/docs/swift-delegate-design-pattern/index.html b/docs/swift-delegate-design-pattern/index.html deleted file mode 100644 index 5430ac3..0000000 --- a/docs/swift-delegate-design-pattern/index.html +++ /dev/null @@ -1,387 +0,0 @@ - - - - - - - - - - - - Swift delegate design pattern - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 1 min read - -
-

Swift delegate design pattern

-
-

The delegate design pattern is a relatively easy way to communicate between two objects through a common interface, protocol in Swift.

-
- -

Implementing delegation in Swift

You’ll need a delegate protocol, a delegator who actually delegates out the tasks and a delegate object that implements the delegate protocol and does the actual work that was requested by the “boss”. Let’s translate this into human.

The client reports a bug. The project manager creates an issue and tells one of the developers to fix the problem asap.

See? That’s delegation. At some point an event happened, so the delegator (manager) utilized an external resource (a developer) using a common interface (issue describing the problem for both party) to do achieve something (fix the 🐛).

To demonstrate how delegation works in real life I made a pretty simple example. I’m going to use a similar approach (because Xcode playgrounds are still freezing every 1-5 minutes) like I did for the command pattern, but the purpose of this one is going to be almost entirely different, because we’re talking about delegation. 😅

#!/usr/bin/env swift
-
-import Foundation
-
-
-protocol InputDelegate {
-
-    var shouldContinueListening: Bool { get }
-
-    func didStartListening()
-    func didReceive(input: String)
-}
-
-
-class InputHandler {
-
-    var delegate: InputDelegate?
-
-    func listen() {
-        self.delegate?.didStartListening()
-
-        repeat {
-            guard let input = readLine() else {
-                continue
-            }
-            self.delegate?.didReceive(input: input)
-        }
-        while self.delegate?.shouldContinueListening ?? false
-    }
-}
-
-
-struct InputReceiver: InputDelegate {
-
-    var shouldContinueListening: Bool {
-        return true
-    }
-
-    func didStartListening() {
-        print("👻 Please be nice and say \"hi\", if you want to leave just tell me \"bye\":")
-    }
-
-    func didReceive(input: String) {
-        switch input {
-        case "hi":
-            print("🌎 Hello world!")
-        case "bye":
-            print("👋 Bye!")
-            exit(0)
-        default:
-            print("🔍 Command not found! Please try again:")
-        }
-    }
-}
-
-let inputHandler = InputHandler()
-let inputReceiver = InputReceiver()
-inputHandler.delegate = inputReceiver
-inputHandler.listen()
-

This is how you can create your own delegate pattern in Swift. You can imagine that Apple is doing the same thing under the hood, with UICollectionViewDataSource, UICollectionViewDelegate etc. You only have to implement the delegate, they’ll provide the protocol and the delegator. 🤔

Weak properties, delegates and classes

Memory management is a very important thing so it’s worth to mention that all the class delegates should be weak properties, or you’ll create a really bad retain cycle. 😱

protocol InputDelegate: class { /*...*/ }
-
-class InputHandler {
-
-    weak var delegate: InputDelegate?
-
-    /*...*/
-}
-
-class InputReceiver: InputDelegate {
-    /*...*/
-}
-

Here is the altered Swift code snippet, but now using a class as the delegate. You just have to change your protocol a little bit and the property inside the delegator. Always use weak delegate variables if you are going to assign a class as a delegate. ⚠️

As you can see delegation is pretty easy, but it can be dangerous. It helps decoupling by providing a common interface that can be used by anyone who implements the delegate (sometimes data source) protocol. There are really amazing articles about delegates, if you’d like to know more about this pattern, you should check them out.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Event-driven generic hooks for Swift

-
-

In this article I am going to show you how to implement a basic event processing system for your modular Swift application.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Iterator design pattern in Swift

-
-

Learn the iterator design pattern by using some custom sequences, conforming to the IteratorProtocol from the Swift standard library.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Lazy initialization in Swift

-
-

Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Lenses and prisms in Swift

-
-

Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/swift-dependency-injection-design-pattern/index.html b/docs/swift-dependency-injection-design-pattern/index.html deleted file mode 100644 index 8d5a38d..0000000 --- a/docs/swift-dependency-injection-design-pattern/index.html +++ /dev/null @@ -1,444 +0,0 @@ - - - - - - - - - - - - Swift dependency injection design pattern - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

Swift dependency injection design pattern

-
-

Want to learn the Dependency Injection pattern using Swift? This tutorial will show you how to write loosely coupled code using DI.

-
- -

First of all I really like this little quote by James Shore:

Dependency injection means giving an object its instance variables. Really. That’s it.

In my opinion the whole story is just a little bit more complicated, but if you tear down the problem to the roots, you’ll realize that implementing the DI pattern can be as simple as giving an object instance variables. No kidding, it’s really a no-brainer, but many developers are over complicating it and using injections at the wrong places. 💉

Learning DI is not about the implementation details, it’s all about how are you going to use the pattern. There are four little variations of dependency injection, let’s go through them by using real world examples that’ll help you to get an idea about when to use dependency injection. Now grab your keyboards! 💻

Dependency Injection basics

As I mentioned before DI is a fancy term for a simple concept, you don’t really need external libraries or frameworks to start using it. Let’s imagine that you have two separate objects. Object A wants to use object B. Say hello to your first dependency.

If you hardcode object B into object A that’s not going to be good, because from that point A can not be used without B. Now scale this up to a ~100 object level. If you don’t do something with this problem you’ll have a nice bowl of spaghetti. 🍝

So the main goal is to create independent objects as much as possible or some say loosely coupled code, to improve reusability and testability. Separation of concerns and decoupling are right terms to use here too, because in most of the cases you should literally separate logical functionalities into standalone objects. 🤐

So in theory both objects should do just one specific thing, and the dependency between them is usually realized through a common descriptor (protocol), without hardcoding the exact instances. Using dependency injection for this purpose will improve your code quality, because dependencies can be replaced without changing the other object’s implementation. That’s good for mocking, testing, reusing etc. 😎

How to do DI in Swift?

Swift is an amazing programming language, with excellent support for both protocol and object oriented principles. It also has great functional capabilities, but let’s ignore that for now. Dependency injection can be done in multiple ways, but in this tutorial I’ll focus on just a few basic ones without any external dependency injection. 😂

Well, let’s start with a protocol, but that’s just because Swift is not exposing the Encoder for the public, but we’ll need something like that for the demos.

protocol Encoder {
-    func encode<T>(_ value: T) throws -> Data where T: Encodable
-}
-extension JSONEncoder: Encoder { }
-extension PropertyListEncoder: Encoder { }
-

Property list and JSON encoders already implement this method we’ll only need to extend our objects to comply for our brand new protocol.

Constructor injection

The most common form of dependency injection is constructor injection or initializer-based injection. The idea is that you pass your dependency through the initializer and store that object inside a (private read-only / immutable) property variable. The main benefit here is that your object will have every dependency - by the time it’s being created - in order to work properly. 🔨

class Post: Encodable {
-
-    var title: String
-    var content: String
-
-    private var encoder: Encoder
-
-    private enum CodingKeys: String, CodingKey {
-        case title
-        case content
-    }
-
-    init(title: String, content: String, encoder: Encoder) {
-        self.title = title
-        self.content = content
-        self.encoder = encoder
-    }
-
-    func encoded() throws -> Data {
-        return try self.encoder.encode(self)
-    }
-}
-
-let post = Post(title: "Hello DI!", content: "Constructor injection", encoder: JSONEncoder())
-
-if let data = try? post.encoded(), let encoded = String(data: data, encoding: .utf8) {
-    print(encoded)
-}
-

You can also give a default value for the encoder in the constructor, but you should fear the bastard injection anti-pattern! That means if the default value comes from another module, your code will be tightly coupled with that one. So think twice! 🤔

Property injection

Sometimes initializer injection is hard to do, because your class have to inherit from a system class. This makes the process really hard if you have to work with views or controllers. A good solution for this situation is to use a property-based injection design pattern. Maybe you can’t have full control over initialization, but you can always control your properties. The only disadvantage is that you have to check if that property is already presented (being set) or not, before you do anything with it. 🤫

class Post: Encodable {
-
-    var title: String
-    var content: String
-
-    var encoder: Encoder?
-
-    private enum CodingKeys: String, CodingKey {
-        case title
-        case content
-    }
-
-    init(title: String, content: String) {
-        self.title = title
-        self.content = content
-    }
-
-    func encoded() throws -> Data {
-        guard let encoder = self.encoder else {
-            fatalError("Encoding is only supported with a valid encoder object.")
-        }
-        return try encoder.encode(self)
-    }
-}
-
-let post = Post(title: "Hello DI!", content: "Property injection")
-post.encoder = JSONEncoder()
-
-if let data = try? post.encoded(), let encoded = String(data: data, encoding: .utf8) {
-    print(encoded)
-}
-

There are lots of property injection patterns in iOS frameworks, delegate patterns are often implemented like this. Also another great benefit is that these properties can be mutable ones, so you can replace them on-the-fly. ✈️

Method injection

If you need a dependency only once, you don’t really need to store it as an object variable. Instead of an initializer argument or an exposed mutable property, you can simply pass around your dependency as a method parameter, this technique is called method injection or some say parameter-based injection. 👍

class Post: Encodable {
-
-    var title: String
-    var content: String
-
-    init(title: String, content: String) {
-        self.title = title
-        self.content = content
-    }
-
-    func encode(using encoder: Encoder) throws -> Data {
-        return try encoder.encode(self)
-    }
-}
-
-let post = Post(title: "Hello DI!", content: "Method injection")
-
-if let data = try? post.encode(using: JSONEncoder()), let encoded = String(data: data, encoding: .utf8) {
-    print(encoded)
-}
-

Your dependency can vary each time this method gets called, it’s not required to keep a reference from the dependency, so it’s just going to be used in a local method scope.

Ambient context

Our last pattern is quite a dangerous one. It should be used only for universal dependencies that are being shared alongside multiple object instances. Logging, analytics or a caching mechanism is a good example for this. 🚧

class Post: Encodable {
-
-    var title: String
-    var content: String
-
-    init(title: String, content: String) {
-        self.title = title
-        self.content = content
-    }
-
-    func encoded() throws -> Data {
-        return try Post.encoder.encode(self)
-    }
-
-
-    private static var _encoder: Encoder = PropertyListEncoder()
-
-    static func setEncoder(_ encoder: Encoder) {
-        self._encoder = encoder
-    }
-
-    static var encoder: Encoder {
-        return Post._encoder
-    }
-}
-
-let post = Post(title: "Hello DI!", content: "Ambient context")
-Post.setEncoder(JSONEncoder())
-
-if let data = try? post.encoded(), let encoded = String(data: data, encoding: .utf8) {
-    print(encoded)
-}
-

Ambient context has some disadvantages. It might fits well in case of cross-cutting concerns, but it creates implicit dependencies and represents a global mutable state. It’s not highly recommended, you should consider the other dependency injection patterns first, but sometimes it can be a right fit for you.

That’s all about dependency injection patterns in a nutshell. If you are looking for more, you should read the following sources, because they’re all amazing. Especially the first one by Ilya Puchka, that’s highly recommended. 😉

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Event-driven generic hooks for Swift

-
-

In this article I am going to show you how to implement a basic event processing system for your modular Swift application.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Iterator design pattern in Swift

-
-

Learn the iterator design pattern by using some custom sequences, conforming to the IteratorProtocol from the Swift standard library.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Lazy initialization in Swift

-
-

Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Lenses and prisms in Swift

-
-

Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/swift-enum-all-values/index.html b/docs/swift-enum-all-values/index.html deleted file mode 100644 index f91e73a..0000000 --- a/docs/swift-enum-all-values/index.html +++ /dev/null @@ -1,352 +0,0 @@ - - - - - - - - - - - - Swift enum all values - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 1 min read - -
-

Swift enum all values

-
-

In this quick tutorial I'll show you how to get all the possible values for a Swift enum type with a generic solution written in Swift.

-
- -

From Swift 4.2 you can simply conform to the CaseIterable protocol, and you’ll get the allCases static property for free. If you are reading this blog post in 2023, you should definitely upgrade your Swift language version to the latest. 🎉

enum ABC: String, CaseIterable {
-    case a, b, c
-}
-
-
-print(ABC.allCases.map { $0.rawValue })
-

If you are targeting below Swift 4.2, feel free to use the following method.

The EnumCollection protocol approach

First we need to define a new EnumCollection protocol, and then we’ll make a protocol extension on it, so you don’t have to write too much code at all.

public protocol EnumCollection: Hashable {
-    static func cases() -> AnySequence<Self>
-    static var allValues: [Self] { get }
-}
-
-public extension EnumCollection {
-
-    public static func cases() -> AnySequence<Self> {
-        return AnySequence { () -> AnyIterator<Self> in
-            var raw = 0
-            return AnyIterator {
-                let current: Self = withUnsafePointer(to: &raw) { $0.withMemoryRebound(to: self, capacity: 1) { $0.pointee } }
-                guard current.hashValue == raw else {
-                    return nil
-                }
-                raw += 1
-                return current
-            }
-        }
-    }
-
-    public static var allValues: [Self] {
-        return Array(self.cases())
-    }
-}
-

From now on you only have to conform your enum types to the EnumCollection protocol and you can enjoy the brand new cases method and allValues property which will contain all the possible values for that given enumeration.

enum Weekdays: String, EnumCollection {
-    case sunday, monday, tuesday, wednesday, thursday, friday, saturday
-}
-
-for weekday in Weekdays.cases() {
-    print(weekday.rawValue)
-}
-
-print(Weekdays.allValues.map { $0.rawValue.capitalized })
-

Note that the base type of the enumeration needs to be Hashable, but that’s not a big deal. However this solution feels like past tense, just like Swift 4, please consider upgrading your project to the latest version of Swift. 👋

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/swift-facade-design-pattern/index.html b/docs/swift-facade-design-pattern/index.html deleted file mode 100644 index 8864aa9..0000000 --- a/docs/swift-facade-design-pattern/index.html +++ /dev/null @@ -1,243 +0,0 @@ - - - - - - - - - - - - Swift facade design pattern - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 1 min read - -
-

Swift facade design pattern

-
-

The facade design pattern is a simplified interface over a complex subsystem. Let me show you a real quick example using Swift.

-
- -

What is a facade?

The name of the facade pattern comes from real life building architecture.

one exterior side of a building, usually the front

In software development this definition can be translated to something like everything that’s outside, hiding all the internal parts. So the main purpose of a facade is to provide a beautiful API over some more complex ugly ones. 😅

Usually the facade design pattern comes handy if you have two or more separate subsystems that needs to work together in order to accomplish some kind of tasks. It can hide the underlying complexity, plus if anything changes inside the hidden methods, the interface of the facade can still remain the same. 👍

A real-life facade pattern example

I promised a quick demo, so let’s imagine an application with a toggle button that turns on or off a specific settings. If the user taps it, we change the underlying settings value in the default storage, plus we also want to play a sound as an extra feedback for the given input. That’s three different things grouped together. 🎶

func toggleSettings() {
-    // change underlying settings value
-    let settingsKey = "my-settings"
-
-    let originalValue = UserDefaults.standard.bool(forKey: settingsKey)
-    let newValue = !originalValue
-
-    UserDefaults.standard.set(newValue, forKey: settingsKey)
-    UserDefaults.standard.synchronize()
-
-    // positive feedback sound
-    AudioServicesPlaySystemSound(1054);
-
-    // update UI
-    self.switchButton.setOn(newValue, animated: true)
-}
-

Congratulations, we’ve just created the most simple facade! If this code seems familiar to you, that means you already have utilized facades in your past.

Of course things can be more complicated, for example if you have a web service and you need to upload some data and an attachment file, you can also write a facade to hide the underlying complexity of the subsystems.

Facades are really easy to create, sometimes you won’t even notice that you are using one, but they can be extremely helpful to hide, decouple or simplify things. If you want to learn more about them, please check the linked articles. 😉

- -
- - - -
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/swift-factory-method-design-pattern/index.html b/docs/swift-factory-method-design-pattern/index.html deleted file mode 100644 index d441326..0000000 --- a/docs/swift-factory-method-design-pattern/index.html +++ /dev/null @@ -1,340 +0,0 @@ - - - - - - - - - - - - Swift factory method design pattern - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 1 min read - -
-

Swift factory method design pattern

-
-

The factory method design pattern is a dedicated non-static method for hiding the creation logic of an object. Let's make it in Swift!

-
- -

Factory method is just a non-static method

Let’s face it, this pattern is just a method usually backed by simple protocols & classes. Start with a really simple example: imagine a class that can create a base URL for your service endpoint. Let’s call it service factory. 😅

class ServiceFactory {
-    func createProductionUrl() -> URL {
-        return URL(string: "https://localhost/")!
-    }
-}
-let factory = ServiceFactory()
-factory.createProductionUrl()
-

You might think, that hey, this is not even close to a factory method pattern, but wait for it… let’s make things a little bit complicated by creating a protocol for the service class and a protocol for returning the URL as well. Now we can implement our base production URL protocol as a separate class and return that specific instance from a production service factory class. Just check the code you’ll get it:

protocol ServiceFactory {
-    func create() -> Service
-}
-
-protocol Service {
-    var url: URL { get }
-}
-
-class ProductionService: Service {
-    var url: URL { return URL(string: "https://localhost/")! }
-}
-
-class ProductionServiceFactory: ServiceFactory {
-    func create() -> Service {
-        return ProductionService()
-    }
-}
-
-let factory = ProductionServiceFactory()
-let request = factory.create()
-

Why did we separated all the logic into two classes and protocols? Please believe me decoupling is a good thing. From now on you could easily write a mocked service with a dummy URL to play around with. Obviously that’d need a matching factory class.

Those mock instances would also implement the service protocols so you could add new types in a relatively painless way without changing the original codebase. The factory method solves one specific problem of a simple factory pattern. If the list - inside the switch-case - becomes too long, maintaining new objects will be hell with just one factory. Factory method solves this by introducing multiple factory objects.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Event-driven generic hooks for Swift

-
-

In this article I am going to show you how to implement a basic event processing system for your modular Swift application.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Iterator design pattern in Swift

-
-

Learn the iterator design pattern by using some custom sequences, conforming to the IteratorProtocol from the Swift standard library.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Lazy initialization in Swift

-
-

Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Lenses and prisms in Swift

-
-

Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/swift-init-patterns/index.html b/docs/swift-init-patterns/index.html deleted file mode 100644 index ee4836a..0000000 --- a/docs/swift-init-patterns/index.html +++ /dev/null @@ -1,525 +0,0 @@ - - - - - - - - - - - - Swift init patterns - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 6 min read - -
-

Swift init patterns

-
-

The ultimate guide how to init your Swift data types, with the help of designated, convenience, failable intitializers and more.

-
- -

What is initialization?

Initialization is the process of preparing an instance of a class, structure, or enumeration for use.

This process is handled through initializers, an initializer is just a special kind of function, usually the init keyword is reserved for them - so you don’t have to use the func keyword - and usually you don’t return any value from an initializer.

Initializing properties

Classes and structures must set all of their stored properties to an appropriate initial value by the time an instance of that class or structure is created.
First imagine a really simple struct, that has only two properties.

struct Point {
-    let x: Int
-    let y: Int
-}
-

Now, the rule above says that we have to init all the properties, so let’s make that by creating our very first init method.

struct Point {
-    let x: Int
-    let y: Int
-
-    init(x: Int, y: Int) {
-        self.x = x
-        self.y = y
-    }
-}
-

It’s just like every other Swift function. Now we’re able to create our first point.

let p1 = Point(x: 1, y: 1)
-

Note that you don’t have to initialize implicitly unwrapped optional properties, and optional properties, if they are variables and not constants.

The same logic applies for classes, you can try it by changing the struct keyword to class. However structs are value types, classes are reference types and this difference will provide us some unique capabilities for both types.

Memberwise initializer (ONLY for structs)

The nice thing about structs is that the compiler will generate a memberwise init for free if you don’t provide your own init method. However there are a quite a few catches. The generated method will contain all the properties (optionals too) except constants with default values, and it will have an internal access type, so it’s not going to be visible from another modules.

The default memberwise initializer for a structure type is considered private if any of the structure’s stored properties are private. Likewise, if any of the structure’s stored properties are file private, the initializer is file private. Otherwise, the initializer has an access level of internal.

struct Point {
-    let x: Int
-    let y: Int
-    var key: Int!
-    let label: String? = "zero"
-}
-let p1 = Point(x: 0, y: 0, key: 0) // provided by the memberwise init
-

Failable initializer

Sometimes things can go wrong, and you don’t want to create bad or invalid objects, for example you’d like filter out the origo from the list of valid points.

struct Point {
-    let x: Int
-    let y: Int
-
-    init?(x: Int, y: Int) { // ? marks that this could fail
-        if x == 0 && y == 0 {
-            return nil
-        }
-        self.x = x
-        self.y = y
-    }
-}
-let p1 = Point(x: 0, y: 0) // nil
-let p2 = Point(x: 1, y: 1) // valid point
-

Enumerations that deliver from a RawRepresentable protocol can be initialized through rawValues, that’s also a failable init pattern:

enum Color: String {
-    case red
-    case blue
-    case yellow
-}
-
-let c1 = Color(rawValue: "orange") // nil, no such case
-let c2 = Color(rawValue: "red") // .red
-

You can also use init! instead of init?, that’ll create an implicitly unwrapped optinal type of the instance. Note that classes can also have failable initializers.

Initializing pure Swift classes

You know classes are native types in the Swift programming language. You don’t even have to import the Foundation framework in order to create a brand new class. Here is the exact same Point object represented by a pure Swift class:

class Point {
-    let x: Int
-    let y: Int
-
-    init(x: Int, y: Int) {
-        self.x = x
-        self.y = y
-    }
-}
-let p1 = Point(x: 1, y: 1)
-

This time we had to provide the init method by ourselves, because classes don’t have memberwise initializers. They are reference types, and inheritance logic, so it’d be more complex to generate memberwise init methods for them.

Default initializer

For Swift classes you will only get an internal default initializer for free if you provide default values for all the stored properties, even for optional ones. In practice it looks something like this:

class Point {
-    let x: Int = 1
-    let y: Int = 1
-}
-let p1 = Point()
-

Or if we follow the previous example:

class Point {
-    let x: Int = 0
-    let y: Int = 0
-    var key: Int!
-    let label: String? = "zero"
-}
-let p1 = Point()
-

This feels so wrong. Why would a point have a key and a label property? It’d be nice to have a child object which could have the extra properties. It’s time to refactor this code with some class inheritance.

Designated initializer

Designated initializers are the primary initializers for a class.

In other words, it’s not marked with the convenience keyword. A class can also have mutliple designated initializers. So let’s continue with our Point class, which is going to be the superclass of our NamedPoint class.

class Point {
-    let x: Int
-    let y: Int
-
-    init(x: Int, y: Int) { // this is the designated initializer
-        self.x = x
-        self.y = y
-    }
-}
-
-class NamedPoint: Point {
-
-    let label: String?
-
-    init(x: Int, y: Int, label: String?) { // designated
-        self.label = label
-
-        super.init(x: x, y: y)
-    }
-
-    init(point: Point, label: String?) { // also designated
-        self.label = label
-        super.init(x: point.x, y: point.y) // delegating up
-    }
-}
-
-let p1 = NamedPoint(x: 1, y: 1, label: "first")
-let p2 = NamedPoint(point: Point(x: 1, y: 1), label: "second")
-

A designated initializer must always call a designated initializer from its immediate superclass, so you have to delegate up the chain. But first we had to initialize all of our properties, by the first rule of initialization. So this means that the Swift language has a two-phase initialization process.

Two-Phase Initialization

  1. Every stored property is assigned an intial value by the class that introduced it.
  2. Each class is given the opportunity to customize it’s stored properies.

So by these rules, first we had to init the label property, then delegate up and only after then we gave the opportunity to do other things.

Convenience initializer

They are initializers used to make initialization a bit easier.

So for example in our previous case if we could have an initializers for points where x and y are equal numbers. That’d be pretty handy in some cases.

class Point {
-    let x: Int
-    let y: Int
-
-    init(x: Int, y: Int) {
-        self.x = x
-        self.y = y
-    }
-    convenience init(z: Int) {
-        self.init(x: z, y: z) // we're calling the designated init
-    }
-}
-let p1 = Point(z: 1)
-

A convenience initializer must call another initializer from the same class and ultimately call a designated initializer. Stucts can also have “convenience” initializer like init methods, but you don’t have to write out the keyword, actually those init methods are slightly differnet, you can just call out from one to another, that’s why it looks like the same.

struct Point {
-    let x: Int
-    let y: Int
-
-    init(x: Int, y: Int) {
-        self.x = x
-        self.y = y
-    }
-
-    init(z: Int) {
-        self.init(x: z, y: z)
-    }
-}
-
-var p1 = Point(z: 1)
-

Required initializer

If you mark an initializer required in your class, all the direct - you have to mark as required in every level - subclasses of that class have to implement it too.

class Point {
-    let x: Int
-    let y: Int
-
-    required init(x: Int, y: Int) {
-        self.x = x
-        self.y = y
-    }
-}
-
-class NamedPoint: Point {
-    let label: String?
-
-    required init(x: Int, y: Int) {
-        self.label = nil
-
-        super.init(x: x, y: y)
-    }
-}
-
-let p1 = NamedPoint(x: 1, y: 1)
-

Override initializer

In Swift initializers are not inherited for subclasses by default. If you want to provide the same initializer for a subclass that the parent class already has, you have to use the override keyword.

class Point {
-    let x: Int
-    let y: Int
-
-    init(x: Int, y: Int) {
-        self.x = x
-        self.y = y
-    }
-}
-
-class NamedPoint: Point {
-    let label: String?
-
-    override init(x: Int, y: Int) {
-        self.label = nil
-
-        super.init(x: x, y: y)
-    }
-}
-
-let p1 = NamedPoint(x: 1, y: 1)
-

There are two rules of init inheritance, here is the first…

If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.

…and the second:

If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.

Deinitialization

A deinitializer is called immediately before a class instance is deallocated.

So if you want to do some manual cleanup when your class is being terminated, this is the method that you are looking for. You don’t have to deal with memory management in most of the cases, because ARC will do it for you.

class Point {
-    let x: Int
-    let y: Int
-
-    init(x: Int, y: Int) {
-        self.x = x
-        self.y = y
-    }
-
-    deinit {
-        print("Point is clenaed up.")
-    }
-}
-
-var p1: Point? = Point(x: 1, y: 1)
-p1 = nil //deinit is being called
-
- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Event-driven generic hooks for Swift

-
-

In this article I am going to show you how to implement a basic event processing system for your modular Swift application.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Iterator design pattern in Swift

-
-

Learn the iterator design pattern by using some custom sequences, conforming to the IteratorProtocol from the Swift standard library.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Lazy initialization in Swift

-
-

Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Lenses and prisms in Swift

-
-

Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/swift-object-pool-design-pattern/index.html b/docs/swift-object-pool-design-pattern/index.html deleted file mode 100644 index 317aa6f..0000000 --- a/docs/swift-object-pool-design-pattern/index.html +++ /dev/null @@ -1,363 +0,0 @@ - - - - - - - - - - - - Swift object pool design pattern - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 1 min read - -
-

Swift object pool design pattern

-
-

In this quick tutorial I'll explain & show you how to implement the object pool design pattern using the Swift programming language.

-
- -

A generic object pool in Swift

The object pool pattern is a creational design pattern. The main idea behind it is that first you create a set of objects (a pool), then you acquire & release objects from the pool, instead of constantly creating and releasing them. 👍

Why? Performance improvements. For example the Dispatch framework uses an object pool pattern to give pre-created queues for the developers, because creating a queue (with an associated thread) is an relatively expensive operation.

Another use case of the object pool pattern is workers. For example you have to download hundreds of images from the web, but you’d like to download only 5 simultaneously you can do it with a pool of 5 worker objects. Probably it’s going to be a lot cheaper to allocate a small number of workers (that’ll actually do the download task), than create a new one for every single image download request. 🖼

What about the downsides of this pattern? There are some. For example if you have workers in your pool, they might contain states or sensitive user data. You have to be very careful with them aka. reset everything. Also if you are running in a multi-threaded environment you have to make your pool thread-safe.

Here is a simple generic thread-safe object pool class:

import Foundation
-
-class Pool<T> {
-
-    private let lockQueue = DispatchQueue(label: "pool.lock.queue")
-    private let semaphore: DispatchSemaphore
-    private var items = [T]()
-
-    init(_ items: [T]) {
-        self.semaphore = DispatchSemaphore(value: items.count)
-        self.items.reserveCapacity(items.count)
-        self.items.append(contentsOf: items)
-    }
-
-    func acquire() -> T? {
-        if self.semaphore.wait(timeout: .distantFuture) == .success, !self.items.isEmpty {
-            return self.lockQueue.sync {
-                return self.items.remove(at: 0)
-            }
-        }
-        return nil
-    }
-
-    func release(_ item: T) {
-        self.lockQueue.sync {
-            self.items.append(item)
-            self.semaphore.signal()
-        }
-    }
-}
-
-
-let pool = Pool<String>(["a", "b", "c"])
-
-let a = pool.acquire()
-print("\(a ?? "n/a") acquired")
-let b = pool.acquire()
-print("\(b ?? "n/a") acquired")
-let c = pool.acquire()
-print("\(c ?? "n/a") acquired")
-
-DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + .seconds(2)) {
-    if let item = b {
-        pool.release(item)
-    }
-}
-
-print("No more resource in the pool, blocking thread until...")
-let x = pool.acquire()
-print("\(x ?? "n/a") acquired again")
-

As you can see the implementation is just a few lines. You have the thread safe array of the generic pool items, a dispatch semaphore that’ll block if there are no objects available in the pool, and two methods in order to actually use the object pool.

In the sample you can see that if there are no more objects left in the pool, the current queue will be blocked until a resource is being freed & ready to use. So watch out & don’t block the main thread accidentally! 😉

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Event-driven generic hooks for Swift

-
-

In this article I am going to show you how to implement a basic event processing system for your modular Swift application.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Iterator design pattern in Swift

-
-

Learn the iterator design pattern by using some custom sequences, conforming to the IteratorProtocol from the Swift standard library.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Lazy initialization in Swift

-
-

Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Lenses and prisms in Swift

-
-

Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/swift-on-the-server-in-2020/index.html b/docs/swift-on-the-server-in-2020/index.html deleted file mode 100644 index aaecdde..0000000 --- a/docs/swift-on-the-server-in-2020/index.html +++ /dev/null @@ -1,359 +0,0 @@ - - - - - - - - - - - - Swift on the Server in 2020 - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 8 min read - -
-

Swift on the Server in 2020

-
-

Why choose Swift as a backend language in 2020? What are the available frameworks to build your server? Let me guide you.

-
- -

Swift is everywhere

Swift is a modern, interactive, type-safe programming language with performance in mind. It is one of the fastest growing programming languages in the industry. Swift has gained so much attraction over the last few years, some people love it, others might hate it, but one thing is for sure:

Swift is here to stay for a very long time.

Swift is as easy to use as a scripting language, without sacrificing any performance. This means C-like performance in most of the cases, which should be more than enough for most of the people.

In the beginning Swift was considered as a replacement for Objective-C for Apple platforms, but don’t get fooled. Swift is a generic purpose programming language, so you can build anything with it. It runs on embedded systems, of course you can make iOS apps and great desktop class applications using Swift, you can use it to write great Machine Learning algorithms (TensorFlow), build backend servers, even experimental operating systems, but let me just stop here. ✋🏻

Swift is everywhere, it has changed a lot, but now we can consider it as a mature programming language. There are still some missing / annoying things, it’d be great to have more (lightweight) tools (e.g. you still can’t run tests for packages without installing Xcode), but we are slowly getting there. Apple will build a new generation of hardware devices using Apple Silicon, they’ve put a lot of effort to rewrite the underlying software components (I bet there is plenty of Swift code already), so I’d say this is just the beginning of the story. 🚀

Swift Server Work Group (SSWG)

The SSWG is on the right track. In the beginning I think they dictated a very slow pace, but now it seems like they know how to work together in an efficient way. It is worth to mention that this group has some very talented people from big companies, such as Apple, Amazon, MongoDB or the Vapor community. They are responsible for priorizing “what needs to be done” for the server community, they run an incubation process for backend related packages and channel feedbacks to the Swift Core Team so they can quickly address the underlying issues in the language itself.

Unfortunately in the very end of last year, IBM announced that they are moving away from Swift on the server, but it turns out that the community is going to be able to save and maintain the Kitura web framework. Anyway, Vapor 4 is almost ready if you are looking for an alternative solution. 🙈

Later on this year Tom Doron, member of the Swift Core Team and the Swift Server Work Group, announced that more Linux distributions are going to be supported. This is a great news, you can now download official Swift releases for seven different platform. Nightly and official Docker images are also available for Swift, this was announced by Mishal Shah on the Swift Forums.

Language stability

As Swift evolved more and more great features were added to the language. Those implementations also pushed forward server side projects. When Swift was open sourced back in 2015 (it became available for Linux) the standard library, Foundation were quite buggy and many language features that we have today were completely missing. Fortunately this changed a lot. I’d say Swift 5 is the first version of the language that was stable enough to build a reliable backend server. 🤖

With Swift 5.0 ABI stability has arrived, so finally we were able to build an app without the entire standard library and runtime included in the binary. We still had to wait about half a year for module stability, that arrived with the release of Swift 5.1 in late 2019. If you are interested in developing a framework or a library, you should also read about Library Evolution in Swift.

Swift Package Manager

The SPM library is here with us for quite a long time now, but in the beginning we weren’t able to use it for AppleOS platforms. Xcode 11 finally brought us a complete integration and developers started to use SPM to integrate third party dependencies with iOS apps. Sorry that I have to say this, but currently SPM is half-baked. We will only be able to ship binary dependencies and resources when Swift 5.3 will be released. In the beginning SPM was clearly designed to help boosting open source Swift packages, libraries and tools mostly on the server side.

Honestly I think this approach was quite a success, the project clearly had some issues in the beginning, but eventually SPM will become a de-facto tool for most of us. I’m quite sure that the authors are putting tremendous effort to make this project amazing and they need time to solve the hard parts under the hood (dependency resolution, etc.), so we (regular Swift developers) can integrate our projects with external packages in a pleasant way. 🙏

What do I miss from SPM? Well, I’d like to be able to update just one package in Xcode instead of refreshing the entire dependency tree. When you work with multiple dependencies this could take a while (especially when a dependency is quite big). Ok, ok, this is more like an Xcode feature, but here is another one: I’d like to be able to build and distrubute dynamic libraries via SPM. This way framework authors could provide a dynamic version that could be reused in multiple packages. I’m not the only one with this particular issue. Please help us. 😢

Swift packages for backend developers

Anyway, SPM is a great way to distribute Swift packages and I’m really glad that we have so many options to build a backend. Let me show you some of the most inspiring open source projects that you can use to develop your server application. Most of these libraries are backed by Apple, so they won’t go anywhere, we can be sure that such a big organization will update and support them. Apple is using this stuff to build up the infrastructure for some cloud based platforms. ☁️

SwiftNIO

SwiftNIO is a cross-platform asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. In other words it’s an extremely performant low-level network framework that you can use to build your own server or client using a non-blocking approach.

It’s like Netty, but written for Swift.

You can find plenty of great tutorials, for example how to build a text modifying server or a practical guide for asynchronous problems even about how to make a clone called “microexpress” of the famous express web framework from the Node.js world.

You’ll also find great documentation about SwiftNIO, but I have to repeat myself, it is a very low level framework, so if don’t have prior network programming experience maybe it’s better to choose a high level framework such as Vapor or even a CMS written in Swift to begin with.

AsyncHTTPClient

If you are looking for a HTTP client library, the AsyncHTTPClient package can be a great candidate. The framework uses a non-blocking asynchronous model for request methods, it can also follow redirects, supports streaming body download, TLS and cookie parsing.

Swift AWS Lambda Runtime

A few months ago the Swift AWS Lambda Runtime package was introduced via the official Swift blog. If you want to develop serverless functions using the AWS Lambda service and the Swift programming language you have to take a closer look on this package. Fabian Fett wrote a great tutorial about getting started with Swift on AWS Lambda, also there is a WWDC20 session video about using Swift on AWS Lambda with Xcode. Honestly I had no time to play with this library, because I was mostly focusing on my CMS, but I can’t wait to go serverless using Swift. 🤔

AWS SDK Swift

The AWS SDK Swift library provides access to all AWS services. The 5th major version is almost feature complete, Adam Fowler recently made a blog post about the latest changes. Personally I was using this package to store images on AWS S3 and it worked like a charm. The only downside of having such a huge library is that it takes a pretty long time for SPM to fetch it as a dependency (I was only using S3, but still I had to load the entire package). Anyway, if your infrastructure highly depends on Amazon Web Services and you have to access most of the available solutions through Swift, this is the framework that you should pick. 😎

Swift Service Lifecycle

The Swift Service Lifecycle package is an elegant way to manage your server. It provides a basic mechanism to cleanly start up and shut down backend apps so you can free resources before exiting. It also provides a signal based shutdown hook, so you can listen to specific events.

For more info you should read the introduction blog post or the readme on GitHub.

Swift Cluster Membership

Apple recently introduced the Swift Cluster Membership repository. Honestly I don’t know much about the SWIM protocol, but it seems like it’s important of you want to build and manage a lifecycle of a distributed system. This library aims to help building clustered mutli-node environments with the help of Swift. For me, this is a completely new area, but I think it’s definitely an interesting stuff and I want to learn a lot about more this in the future. 🤓

Backtrace, Crypto, Metrics, Log and more…

Here are a few other libraries that you can utilize when you build a backend server using Swift.

The first one helps you to print a crash backtrace when your app fails.

Crypto is a cross-platform Swift implementation of Apple’s CryptoKit framework. It is quite a young project announced by Cory Benfield at the dotSwift conference, but it the project already features most of the functionalities from CryptoKit.

If you have a server side or cross platform Swift application you might want to measure some of your code. The Swift Metrics API package is a great way to emit such records.

There is an offical Logging API package for Swift that you can use to persist log messages in files or simply print out various messages to the console using a standardized way.

In the beginning of the article I mentioned that SSWG has an incubation process for server related packages. If you have a specific need, it is always a good idea to check the status of the currently available projects on the official Swift programming language website. 🔍

Vapor

Vapor is the most popular web framework written in Swift. If you want to get started with Vapor 4 you should definitely take a look at my Practical Server Side Swift book, or you can use all the FREE resources on my blog.

The future of Swift (on the server)

As you can see the server side Swift infrastructure is evolving real quick. Swift is available on more and more platforms (Windows support is coming next), plus the language itself is on a good way and it has the potential to “fulfill the prophecy”. 🌎 💪 😅

Apart from the missing parts, such as the long awaited (pun intended) async / await feature, on the long term Swift 6 is definitely going to be a huge milestone. Don’t expect that this will happen anytime soon, we still need a 5.3 release before and who knows, maybe Swift 5.4 and more.

So back to the original question…

Why choose Swift in 2020 as a backend language?

I know better: why choose Swift as your main language? Well, Swift is modern, fast and safe. It can run on many platforms and it has a great learning curve. Swift has a bright future not because of Apple, but because the huge community that loves (and sometimes hate) using it.

You don’t need an expensive Apple device to start learning Swift. You can build your own apps using a PC with Linux. You can even get started using a small & cheap Raspberry PI, a SwiftIO machine or maybe this online Swift playground can do the job. 💡

I don’t want to compare Swift to another languages, let’s just say I think of it as a love child of JavaScript & C. I hope this analogy pretty much explains it (in a good way). 😂

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/swift-package-manager-tutorial/index.html b/docs/swift-package-manager-tutorial/index.html deleted file mode 100644 index 8cd8888..0000000 --- a/docs/swift-package-manager-tutorial/index.html +++ /dev/null @@ -1,424 +0,0 @@ - - - - - - - - - - - - Swift Package Manager tutorial - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

Swift Package Manager tutorial

-
-

Learn how to use the Swift Package Manager to handle external dependencies, create your library or app on macOS and Linux.

-
- -

Swift Package Manager basics

First of all, please check your Swift version on your device before we jump in this tutorial will only work with the latest toolchain, so you’ll need Swift 5.2 or newer.

Apple Swift version 5.2.2 (swiftlang-1103.0.32.6 clang-1103.0.32.51)
-Target: x86_64-apple-darwin19.4.0
-

Creating apps

All the hard work is done by the swift package command. You can enter that into a terminal window and see the available sub-commands. To generate a new package you should go with the init command, if you don’t provide a type flag, by default it’ll create a library, but this time we’d like to make an executable application.

swift package init --type executable
-swift build
-swift run my-app
-

The compiler can build your source files with the help of the swift build command. The executable file is going to be placed somewhere under the .build directory, if you run the newly created application with the swift run my-app command, you should see the basic Hello, world! message.

Congratulations for your first command line Swift application!

Now you should do some actual coding. Usually your swift source files should be under the Sources directory, however you might want to create some reusable parts for your app. So let’s prepare for that scenario by starting a brand new library.

Making a library

We start with the init command, but this time we don’t specify the type. We actually could enter swift package init --type library but that’s way too may words to type. Also because we’re making a library, the SPM tool is going to provide us some basic tests, let’s run them too with the swift test command. 😜

swift package init
-swift test
-# swift test --help
-# swift test --filter <test-target>.<test-case>/<test>
-

If you check the file structure now you won’t find a main.swift file inside the source folder, but instead of this you’ll get an example unit test under the Tests directory.

Now know the basics. You have an example application and a library, so let’s connect them together with the help of the Swift Package Manager Manifest API!

The Manifest API - Package.swift

Every SPM bundle has a Package.swift manifest file inside of it. In this manifest file you can define all your dependencies, targets and even the exact source files for your project. In this section I’ll teach you the basics of the manifest file.

Tool version

First of all if you want to support the new manifest file format (aka. Swift 4 version), you have to set the swift-tools-version as comment in your manifest file.

// swift-tools-version:5.2
-

Now you’re ready to work with the brand new manifest API.

Dependencies

Let’s just add our library as a dependency for the main application first by creating a new package dependency inside the Package.swift file. The first argument is a package url string, which can be a local file path or a remote url (usually a github repo link). Note that you should add your dependency to the targets as well. Usually the specific name of a package is defined inside the library manifest file.

// swift-tools-version:5.2
-import PackageDescription
-
-let package = Package(
-    name: "my-app",
-    dependencies: [
-        .package(url: "../my-lib", .branch("master")),
-    ],
-    targets: [
-        .target(name: "my-app", dependencies: [
-            .product(name: "my-lib", package: "my-lib"),
-        ]),
-    ]
-)
-

Now if you run swift build you’ll fail to build your sources. That’s because the SPM only works with git repositories. This means you have to create a repository for your library. Let’s move to the directory of the library and run the following commands.

git init
-git add .
-git commit -m 'initial'
-

You should also note that we specified the branch in the package dependencies. You can use version numbers, or even commit hashes too. All the available options are well written inside the manifest API redesign proposal document.

Now let’s go back to the application directory and update the dependencies with the swift package update command. This time it’s going to be able to fetch, clone and finally resolve our dependency.

You can build and run, however we’ve forgot to set the access level of our struct inside our library to public, so nothing is going to be visible from that API.

public struct my_lib {
-    public var text = "Hello, World!"
-
-    public init() {}
-}
-

Let’s do some changes and commit them into the library’s main branch.

git add .
-git commit -m 'access level fix'
-

You’re ready to use the lib in the app, change the main.swift file like this.

import my_lib
-
-print(my_lib().text)
-

Update the dependencies again, and let’s do a release build this time.

swift package update
-swift build -c release
-swift run -c release
-

With the -c or --configuration flag you can make a release build.

Products and targets

By default the SPM works with the following target directories:

Regular targets: package root, Sources, Source, src, srcs. Test targets: Tests, package root, Sources, Source, src, srcs.

This means, that if you create .swift files inside these folders, those sources will be compiled or tested, depending on the file location. Also the generated manifest file contains only one build target (like Xcode targets), but sometimes you want to create multiple apps or libraries from the same bundle. Let’s change our Package.swift file a little bit, and see how can we make a brand new target.

// swift-tools-version:5.2
-import PackageDescription
-
-let package = Package(
-    name: "my-app",
-    dependencies: [
-        .package(url: "../my-lib", .branch("master")),
-        .package(url: "https://github.com/kylef/Commander", from: "0.8.0"),
-    ],
-    targets: [
-        .target(name: "my-app", dependencies: [
-            .product(name: "my-lib", package: "my-lib"),
-        ]),
-        .target(name: "my-cmd", dependencies: [
-            .product(name: "Commander", package: "Commander"),
-        ], path: "./Sources/my-cmd", sources: ["main.swift"]),
-    ]
-)
-

We just created a new dependency from GitHub, and a brand new target which will contain only the main.swift file from the Sources/my-cmd directory. Now let’s create this directory and add the source code for the new app.

import Foundation
-import Commander
-
-let main = command { (name:String) in
-    print("Hello, \(name.capitalized)!")
-}
-
-main.run()
-

Build the project with swift build and run the newly created app with one extra name parameter. Hopefully you’ll see something like this.

swift run my-cmd guest
-# Hello, Guest!
-

So we just made a brand new executable target, however if you’d like to expose your targets for other packages, you should define them as products as well. If you open the manifest file for the library, you’ll see that there is a product defined from the library target. This way the package manager can link the product dependencies based on the given product name.

You can define static or dynamic libraries, however it is recommended to use automatic so the SPM can decide appropriate linkage.

// swift-tools-version:5.2
-import PackageDescription
-
-let package = Package(
-    name: "my-lib-package",
-    products: [
-        .library(name: "my-lib", targets: ["my-lib"]),
-        //.library(name: "my-lib", type: .static, targets: ["my-lib"]),
-        //.library(name: "my-lib", type: .dynamic, targets: ["my-lib"]),
-    ],
-    dependencies: [
-        // .package(url: /* package url */, from: "1.0.0"),
-    ],
-    targets: [
-        .target(name: "my-lib", dependencies: []),
-        .testTarget(name: "my-libTests", dependencies: ["my-lib"]),
-    ]
-)
-

Deployment target, other build flags

Sometimes you’ll need to specify a deployment target for your package. Now this is possible with the Swift Package Manager (it was buggy a log time ago), you just have to provide some extra arguments for the compiler, during the build phase.

swift build -Xswiftc "-target" -Xswiftc "x86_64-apple-macosx10.12"
-

Also if you would like to define build flags, that’s possible too.

swift build -Xswiftc "-D" -Xswiftc "DEBUG"
-

Now in your source code you can check for the existence of the DEBUG flag.

#if DEBUG
-    print("debug mode")
-#endif
-

If you want to know more about the build process, just type swift build --help and you’ll see your available options for the build command.

This was SPM in a nutshell. Actually we have covered more than just the basics, we deep-dived a little into the Swift Package Manager, now you must be familiar with targets, products and most of the available commands, but there is always more to learn. So if you want to know even more about this amazing tool, you should check the Swift evolution dashboard for more info. 😉

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginner's guide to Swift package manager command plugins

-
-

Learn how to create command plugins for the Swift Package Manager to execute custom actions using SPM and other tools.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

How to create a Swift package collection?

-
-

In this tutorial I'm going to show you how to create your own package collection from your favorite Swift libraries.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Introduction to SPM artifact bundles

-
-

In this tutorial I'm going to show you how to use the new binary target related artifact bundle using the Swift package manager.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Swift Package Manager tutorial

-
-

Learn how to use the Swift Package Manager to handle external dependencies, create your library or app on macOS and Linux.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/swift-prototype-design-pattern/index.html b/docs/swift-prototype-design-pattern/index.html deleted file mode 100644 index 0ca3f20..0000000 --- a/docs/swift-prototype-design-pattern/index.html +++ /dev/null @@ -1,367 +0,0 @@ - - - - - - - - - - - - Swift prototype design pattern - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 1 min read - -
-

Swift prototype design pattern

-
-

The prototype design pattern is used to create clones of a base object, so let's see some practical examples written in Swift.

-
- -

This is also a creational design pattern, it is useful when you have a very basic configuration for an object and you’d like to give (clone) those predefined values to another one. Basically you’re making clones from a prototype objects. 😊😊😊

This approach has some benefits, one is for example that you don’t have to subclass, but you can configure clones individually. This also means that you can remove a bunch of boilerplate (configuration) code if you are going to use prototypes. 🤔

class Paragraph {
-
-    var font: UIFont
-    var color: UIColor
-    var text: String
-
-    init(font: UIFont = UIFont.systemFont(ofSize: 12),
-         color: UIColor = .darkText,
-         text: String = "") {
-
-        self.font = font
-        self.color = color
-        self.text = text
-    }
-
-    func clone() -> Paragraph {
-        return Paragraph(font: self.font, color: self.color, text: self.text)
-    }
-}
-
-let base = Paragraph()
-
-let title = base.clone()
-title.font = UIFont.systemFont(ofSize: 18)
-title.text = "This is the title"
-
-let first = base.clone()
-first.text = "This is the first paragraph"
-
-let second = base.clone()
-second.text = "This is the second paragraph"
-

As you can see the implementation is just a few lines of code. You only need a default initializer and a clone method. Everything will be pre-configured for the prototype object in the init method and you can make your clones using the clone method, but that’s pretty obvious at this point… 🤐

Let’s take a look at one more example:

class Paragraph {
-
-    var font: UIFont
-    var color: UIColor
-    var text: String
-
-    init(font: UIFont = UIFont.systemFont(ofSize: 12),
-         color: UIColor = .darkText,
-         text: String = "") {
-
-        self.font = font
-        self.color = color
-        self.text = text
-    }
-
-    func clone() -> Paragraph {
-        return Paragraph(font: self.font, color: self.color, text: self.text)
-    }
-}
-
-let base = Paragraph()
-
-let title = base.clone()
-title.font = UIFont.systemFont(ofSize: 18)
-title.text = "This is the title"
-
-let first = base.clone()
-first.text = "This is the first paragraph"
-
-let second = base.clone()
-second.text = "This is the second paragraph"
-

The prototype design pattern is also helpful if you are planning to have snapshots of a given state. For example in a drawing app, you could have a shape class as a proto, you can start adding paths to it, and at some point at time you could create a snapshot from it. You can continue to work on the new object, but this will give you the ability to return to a saved state at any point of time in the future. 🎉

That’s it about the prototype design pattern in Swift, in a nuthsell. 🐿

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Event-driven generic hooks for Swift

-
-

In this article I am going to show you how to implement a basic event processing system for your modular Swift application.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Iterator design pattern in Swift

-
-

Learn the iterator design pattern by using some custom sequences, conforming to the IteratorProtocol from the Swift standard library.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Lazy initialization in Swift

-
-

Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Lenses and prisms in Swift

-
-

Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - -
-
- -
- - - -
- - - - diff --git a/docs/swift-simple-factory-design-pattern/index.html b/docs/swift-simple-factory-design-pattern/index.html deleted file mode 100644 index d42db5b..0000000 --- a/docs/swift-simple-factory-design-pattern/index.html +++ /dev/null @@ -1,371 +0,0 @@ - - - - - - - - - - - - Swift simple factory design pattern - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 1 min read - -
-

Swift simple factory design pattern

-
-

This time let's talk about the simple factory design pattern to encapsulate object creation in a really simple way using Swift.

-
- -

Simple factory implementation using switch-case

The goal of this pattern is to encapsulate something that can often vary. Imagine a color palette for an application. You might have to change the colors according to the latest habit of the designer on a daily basis. I’d be really inconvenient if you had to search & replace every single instance of the color code by hand. So let’s make a simple factory in Swift that can return colors based on a given style. 🎩

class ColorFactory {
-
-    enum Style {
-        case text
-        case background
-    }
-
-    func create(_ style: Style) -> UIColor {
-        switch style {
-        case .text:
-            return .black
-        case .background:
-            return .white
-        }
-    }
-}
-
-
-let factory = ColorFactory()
-let textColor = factory.create(.text)
-let backgroundColor = factory.create(.background)
-

This can be really useful, especially if it comes to a complicated object initialization process. You can also define a protocol and return various instance types that implement the required interface using a switch case block. 🚦

protocol Environment {
-    var identifier: String { get }
-}
-
-class DevEnvironment: Environment {
-    var identifier: String { return "dev" }
-}
-
-class LiveEnvironment: Environment {
-    var identifier: String { return "live" }
-}
-
-class EnvironmentFactory {
-
-    enum EnvType {
-        case dev
-        case live
-    }
-
-    func create(_ type: EnvType) -> Environment {
-        switch type {
-        case .dev:
-            return DevEnvironment()
-        case .live:
-            return LiveEnvironment()
-        }
-    }
-}
-
-let factory = EnvironmentFactory()
-let dev = factory.create(.dev)
-print(dev.identifier)
-

So, a few things to remember about the simple factory design pattern:

+ it helps loose coupling by separating init & usage logic 🤔
-+ it's just a wrapper to encapsulate things that can change often 🤷‍♂️
-+ simple factory can be implemented in Swift using an enum and a switch-case
-+ use a protocol if you are planning to return different objects (POP 🎉)
-+ keep it simple 🏭
-

This pattern separates the creation from the actual usage and moves the responsibility to a specific role, so if something changes you only have to modify the factory. You can leave all your tests and everything else completely untouched. Powerful and simple! 💪

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Event-driven generic hooks for Swift

-
-

In this article I am going to show you how to implement a basic event processing system for your modular Swift application.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Iterator design pattern in Swift

-
-

Learn the iterator design pattern by using some custom sequences, conforming to the IteratorProtocol from the Swift standard library.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Lazy initialization in Swift

-
-

Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Lenses and prisms in Swift

-
-

Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/swift-singleton-design-pattern/index.html b/docs/swift-singleton-design-pattern/index.html deleted file mode 100644 index 6a47ecf..0000000 --- a/docs/swift-singleton-design-pattern/index.html +++ /dev/null @@ -1,420 +0,0 @@ - - - - - - - - - - - - Swift singleton design pattern - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 4 min read - -
-

Swift singleton design pattern

-
-

Singleton is the most criticized design pattern of all time. Learn the proper way of using Swift singleton classes inside iOS projects.

-
- -

Everyone is bullying on the poor singleton pattern, most of the people call it anti-pattern. But what exactly is a singleton class and why is it so bad?

What is a singleton?

It’s a very popular and commonly adopted pattern because of simplicity. A singleton class can only have exactly one instance through the entire application lifecycle. That single instance is only accessible through a static property and the initialized object is usally shared globally. It’s like a global variable. 🌏

Global variables and states

Singletons have bad reputation because they share global mutable states. The global keyword is always feared even in the circle of experienced developers. Global states & variables are the hotbed of side effects. Global variables can be accessed from anywhere of your program so your classes that use them will become stateful, unsecure, tight coupled and hard to debug. It’s not a good practice to share states alongside objects through this way for obvious reasons. 🤮

Side effects

You should scope and isolate your variables as much as you can and minimize the statefullness of your code. This will eliminate side effects, make your code more secure to use. Consider the following example:

var global = 0
-
-// method is written by someone else
-func square(_ x: Int) -> Int {
-    global = x
-    return x * x
-}
-
-global = 1;
-var result = square(5)
-result += global //we assume that global is 1
-print(result) //wtf 30 it should be 26
-

The square method is written by someone else, who wanted to store the input in the same global variable for some reason. Now when you call that function you won’t be avare of this, until you look at his code. Imagine this kind of issues inside of a project with lots of oop classes written by multiple code authors… good luck with the army of BUGS! 🐛🐛🐛

The secret life of a singleton object

Singletons are created once and live forever, they work almost exactly like global variables and that’s why you have to be extremely careful with them. You should only manage those states with singletons that lasts for the complete lifecycle of the app. For example user-specific sessions are usually bad practices and you should rethink your design. Also Swift is not thread safe by default, so if you are working with singletons you have to be prepared for multi-threading issues as well. But if they are so problematic, shouldn’t we simply avoid them entirely? The answer is no. 🚫

When to use a singleton class?

For example UIApplication is most likely a singleton because there should be only one application instance, and it should live until you shut it down. That makes just the perfect example for a singleton. Another use case can be a Logger class. It’s safe to use a singleton because your application won’t behave any different if a logger is turned on or not. Noone else will own or manage the logger and you’ll only pass information into the logger, so states can’t be messed up. Conclusion: a console or a logger class is quite an acceptable scenario for the usage of the singleton pattern. 👏

Console.default.notice("Hello I'm a singleton!")
-

There are a lots of “singletonish” (not everything is a true singleton object) use cases in Apple frameworks, here is a short list, so you can have a little inspiration:

+ HTTPCookieStorage.shared
-+ URLCredentialStorage.shared
-+ URLSessionConfiguration.default
-+ URLSession.shared
-+ FileManager.default
-+ Bundle.main
-+ UserDefaults.standard
-+ NotificationCenter.default
-+ UIScreen.main
-+ UIDevice.current
-+ UIApplication.shared
-+ MPMusicPlayerController.systemMusicPlayer
-+ GKLocalPlayer.localPlayer()
-+ SKPaymentQueue.default()
-+ WCSession.default
-+ CKContainer.default()
-+ etc.
-

I’ve seen lots of manager classes implemented as singletons, such as network, location or core data managers, but those objects usually shouldn’t be singletons, simply because it can be more than one of them. 💩

Singleton pattern can be very useful, but it should be used with caution

If you want to turn something into a singleton, ask yourself these questions:

Will anything else own, manage or be responsible for it?
Is there going to be exactly one instance?

  • Will it be a global state variable?
  • Should I really use a globally shared object?
  • Should live through the whole app lifecycle?
  • Is there any alternatives for it?

If the answers is clearly a yes for everything above, then you can “safely” use a singleton or a global variable to store your data. 🎉🎉🎉

How to create a singleton in Swift?

It’s really easy to make a singleton object in Swift, but please always think twice and consider alternatives before you apply this design pattern.

class Singleton {
-
-    static let shared = Singleton()
-
-    private init() {
-        // don't forget to make this private
-    }
-}
-let singleton = Singleton.shared
-

Nowadays I’m always creating one specific singleton object, that’s called App. This way I can hook up every application related global state properties into that one singleton. The naming convention also helps me to reevaluate what goes into it. 💡

How to eliminate singletons?

If there is other way you should go with that in ~90% of the cases. The most common alternative solution for singletons is dependency injection. First you should abstract the singleton methods into a protocol, then you can use the singleton as the default implementation if it’s still needed. Now you can inject the singleton or a refactored object into the right place. This way your code can be tested with mocked objects of the protocol, even ignoring the singleton itself. 😎

typealias DataCompletionBlock = (Data?) -> Void
-
-// 1. abstract away the required functions
-protocol Session {
-    func make(request: URLRequest, completionHandler: @escaping DataCompletionBlock)
-}
-
-// 2. make your "singleton" conform to the protocol
-extension URLSession: Session {
-
-    func make(request: URLRequest, completionHandler: @escaping DataCompletionBlock) {
-        let task = self.dataTask(with: request) { data, _, _ in
-            completionHandler(data)
-        }
-        task.resume()
-    }
-}
-
-class ApiService {
-
-    var session: Session
-
-    // 3. using dependency injection with the "singleton" object
-    init(session: Session = URLSession.shared) {
-        self.session = session
-    }
-
-    func load(_ request: URLRequest, completionHandler: @escaping DataCompletionBlock) {
-        self.session.make(request: request, completionHandler: completionHandler)
-    }
-}
-
-// 4. create mock object
-
-class MockedSession: Session {
-
-    func make(request: URLRequest, completionHandler: @escaping DataCompletionBlock) {
-        completionHandler("Mocked data response".data(using: .utf8))
-    }
-}
-
-// 5. write your tests
-func test() {
-    let api = ApiService(session: MockedSession())
-    let request = URLRequest(url: URL(string: "https://localhost/")!)
-    api.load(request) { data in
-        print(String(data: data!, encoding: .utf8)!)
-    }
-}
-test()
-

As you can see the singleton pattern is very easy to implement, but it’s really hard to make a decision about it’s application forms. I’m not saying that it’s an anti-pattern, because it’s clearly not, but take care if you are planning to deal with singletons. 😉

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Event-driven generic hooks for Swift

-
-

In this article I am going to show you how to implement a basic event processing system for your modular Swift application.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Iterator design pattern in Swift

-
-

Learn the iterator design pattern by using some custom sequences, conforming to the IteratorProtocol from the Swift standard library.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Lazy initialization in Swift

-
-

Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Lenses and prisms in Swift

-
-

Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/swift-static-factory-design-pattern/index.html b/docs/swift-static-factory-design-pattern/index.html deleted file mode 100644 index 28ae90a..0000000 --- a/docs/swift-static-factory-design-pattern/index.html +++ /dev/null @@ -1,396 +0,0 @@ - - - - - - - - - - - - Swift static factory design pattern - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 2 min read - -
-

Swift static factory design pattern

-
-

In this article I'll teach you about the static factory design pattern and show some use cases using the Swift programming language.

-
- -

Real world static factory pattern examples

Named constructors

The first good thing about the static factory pattern is that every static factory method can have a name. Apple uses this pattern in their UIColor class implementation to create named colors like .red, .yellow, etc. Please note that the implementation in Swift is not really a method, but a static property, which returns the actual instance.

public extension TimeInterval {
-    public static var second: TimeInterval { return 1 }
-    public static var minute: TimeInterval { return 60 }
-    public static var hour: TimeInterval { return 3_600 }
-    public static var day: TimeInterval { return 86_400 }
-    public static var week: TimeInterval { return 604_800 }
-}
-

If it’s so hard to memorize how many seconds is a day or a week why don’t we create a named initializer for it. See? TimeInterval.week is much better than 604_800. 😅

Cached objects

The next good thing about the static factory pattern is that it can support caching for the sake of better memory usage. This way you can limit the number of objects created if you are initializing it through the static constructor (aka. static factory method). 🏭

class Service {
-
-    var name: String
-
-    // MARK: - cache
-
-    private static var cache: [String:Service] = [:]
-
-    private static func cached(name: String) -> Service {
-        if Service.cache[name] == nil {
-            Service.cache[name] = Service(named: name)
-        }
-        return Service.cache[name]!
-    }
-
-    // MARK: - static factory
-
-    static var local: Service {
-        return Service.cached(name: "local")
-    }
-
-    static var remote: Service {
-        return Service.cached(name: "remote")
-    }
-
-    // MARK: - init
-
-    init(named name: String) {
-        self.name = name
-    }
-}
-

Local initialization scope

Another good thing about static factory methods that you can limit the initialization of a class to a private scope level. In other words object creation will only be available through the static factory method. You just have to make the init method private.

public final class Service {
-
-    var name: String
-
-    private init(name: String) {
-        self.name = name
-    }
-
-    public static var local: Service {
-        return Service(name: "local")
-    }
-
-    public static var remote: Service {
-        return Service(name: "remote")
-    }
-}
-

Note that you can restrict subclassing with the final & static keyword. If you want to allow subclassing you should remove final and use the class keyword instead of the static for the properties, this way subclasses can override factory methods. 🤔

Statically return anything

Static factory can also return subtypes of a given object, but why don’t we take this even one step further? You can also return any kind of type from a static method, I know this seems like a cheat, because I’m not creating an instance of UIColor here, but I believe that it’s worth to mention this method here, because it’s closely related to static factories. This technique can be really useful sometimes. 😛

extension UIColor {
-
-    private static func image(with color: UIColor) -> UIImage {
-        let rect = CGRect(x: 0, y: 0, width: 1, height: 1)
-        UIGraphicsBeginImageContext(rect.size)
-        let context = UIGraphicsGetCurrentContext()!
-        context.setFillColor(color.cgColor)
-        context.fill(rect)
-        let img = UIGraphicsGetImageFromCurrentImageContext()
-        UIGraphicsEndImageContext()
-        return img!
-    }
-
-    static var redImage: UIImage {
-        return UIColor.image(with: .red)
-    }
-}
-
- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Event-driven generic hooks for Swift

-
-

In this article I am going to show you how to implement a basic event processing system for your modular Swift application.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Iterator design pattern in Swift

-
-

Learn the iterator design pattern by using some custom sequences, conforming to the IteratorProtocol from the Swift standard library.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Lazy initialization in Swift

-
-

Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Lenses and prisms in Swift

-
-

Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/swift-structured-concurrency-tutorial/index.html b/docs/swift-structured-concurrency-tutorial/index.html deleted file mode 100644 index 52fff4c..0000000 --- a/docs/swift-structured-concurrency-tutorial/index.html +++ /dev/null @@ -1,542 +0,0 @@ - - - - - - - - - - - - Swift structured concurrency tutorial - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 9 min read - -
-

Swift structured concurrency tutorial

-
-

Learn how to work with the Task object to perform asynchronous operations in a safe way using the new concurrency APIs in Swift.

-
- -

Introducing structured concurrency in Swift

In my previous tutorial we’ve talked about the new async/await feature in Swift, after that I’ve created a blog post about thread safe concurrency using actors, now it is time to get started with the other major concurrency feature in Swift, called structured concurrency. 🔀

What is structured concurrency? Well, long story short, it’s a new task-based mechanism that allows developers to perform individual task items in concurrently. Normally when you await for some piece of code you create a potential suspension point. If we take our number calculation example from the async/await article, we could write something like this:

let x = await calculateFirstNumber()
-let y = await calculateSecondNumber()
-let z = await calculateThirdNumber()
-print(x + y + z)
-

I’ve already mentioned that each line is being executed after the previous line finishes its job. We create three potential suspension points and we await until the CPU has enough capacity to execute & finish each task. This all happens in a serial order, but sometimes this is not the behavior that you want.

If a calculation depends on the result of the previous one, this example is perfect, since you can use x to calculate y, or x & y to calculate z. What if we’d like to run these tasks in parallel and we don’t care the individual results, but we need all of them (x,y,z) as fast as we can? 🤔

async let x = calculateFirstNumber()
-async let y = calculateSecondNumber()
-async let z = calculateThirdNumber()
-
-let res = await x + y + z
-print(res)
-

I already showed you how to do this using the async let bindings proposal, which is a kind of a high level abstraction layer on top of the structured concurrency feature. It makes ridiculously easy to run async tasks in parallel. So the big difference here is that we can run all of the calculations at once and we can await for the result “group” that contains both x, y and z.

Again, in the first example the execution order is the following:

  • await for x, when it is ready we move forward
  • await for y, when it is ready we move forward
  • await for z, when it is ready we move forward
  • sum the already calculated x, y, z numbers and print the result

We could describe the second example like this

  • Create an async task item for calculating x
  • Create an async task item for calculating y
  • Create an async task item for calculating z
  • Group x, y, z task items together, and await sum the results when they are ready
  • print the final result

As you can see this time we don’t have to wait until a previous task item is ready, but we can execute all of them in parallel, instead of the regular sequential order. We still have 3 potential suspension points, but the execution order is what really matters here. By executing tasks parallel the second version of our code can be way faster, since the CPU can run all the tasks at once (if it has free worker thread / executor). 🧵

At a very basic level, this is what structured concurrency is all about. Of course the async let bindings are hiding most of the underlying implementation details in this case, so let’s move a bit down to the rabbit hole and refactor our code using tasks and task groups.

await withTaskGroup(of: Int.self) { group in
-    group.async {
-        await calculateFirstNumber()
-    }
-    group.async {
-        await calculateSecondNumber()
-    }
-    group.async {
-        await calculateThirdNumber()
-    }
-
-    var sum: Int = 0
-    for await res in group {
-        sum += res
-    }
-    print(sum)
-}
-

According to the current version of the proposal, we can use tasks as basic units to perform some sort of work. A task can be in one of three states: suspended, running or completed. Task also support cancellation and they can have an associated priority.

Tasks can form a hierarchy by defining child tasks. Currently we can create task groups and define child items through the group.async function for parallel execution, this child task creation process can be simplified via async let bindings. Children automatically inherit their parent tasks’s attributes, such as priority, task-local storage, deadlines and they will be automatically cancelled if the parent is cancelled. Deadline support is coming in a later Swift release, so I won’t talk more about them.

A task execution period is called a job, each job is running on an executor. An executor is a service which can accept jobs and arranges them (by priority) for execution on available thread. Executors are currently provided by the system, but later on actors will be able to define custom ones.

That’s enough theory, as you can see it is possible to define a task group using the withTaskGroup or the withThrowingTaskGroup methods. The only difference is that the later one is a throwing variant, so you can try to await async functions to complete. ✅

A task group needs a ChildTaskResult type as a first parameter, which has to be a Sendable type. In our case an Int type is a perfect candidate, since we’re going to collect the results using the group. You can add async task items to the group that returns with the proper result type.

We can gather individual results from the group by awaiting for the the next element (await group.next()), but since the group conforms to the AsyncSequence protocol we can iterate through the results by awaiting for them using a standard for loop. 🔁

That’s how structured concurrency works in a nutshell. The best thing about this whole model is that by using task hierarchies no child task will be ever able to leak and keep running in the background by accident. This a core reason for these APIs that they must always await before the scope ends. (thanks for the suggestions @ktosopl). ❤️

Let me show you a few more examples…

Waiting for dependencies

If you have an async dependency for your task items, you can either calculate the result upfront, before you define your task group or inside a group operation you can call multiple things too.

import Foundation
-
-func calculateFirstNumber() async -> Int {
-    await withCheckedContinuation { c in
-        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
-            c.resume(with: .success(42))
-        }
-    }
-}
-
-func calculateSecondNumber() async -> Int {
-    await withCheckedContinuation { c in
-        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
-            c.resume(with: .success(6))
-        }
-    }
-}
-
-func calculateThirdNumber(_ input: Int) async -> Int {
-    await withCheckedContinuation { c in
-        DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
-            c.resume(with: .success(9 + input))
-        }
-    }
-}
-
-func calculateFourthNumber(_ input: Int) async -> Int {
-    await withCheckedContinuation { c in
-        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
-            c.resume(with: .success(69 + input))
-        }
-    }
-}
-
-@main
-struct MyProgram {
-    
-    static func main() async {
-
-        let x = await calculateFirstNumber()
-        await withTaskGroup(of: Int.self) { group in
-            group.async {
-                await calculateThirdNumber(x)
-            }
-            group.async {
-                let y = await calculateSecondNumber()
-                return await calculateFourthNumber(y)
-            }
-            
-
-            var result: Int = 0
-            for await res in group {
-                result += res
-            }
-            print(result)
-        }
-    }
-}
-

It is worth to mention that if you want to support a proper cancellation logic you should be careful with suspension points. This time I won’t get into the cancellation details, but I’ll write a dedicated article about the topic at some point in time (I’m still learning this too… 😅).

Tasks with different result types

If your task items have different return types, you can easily create a new enum with associated values and use it as a common type when defining your task group. You can use the enum and box the underlying values when you return with the async task item functions.

import Foundation
-
-func calculateNumber() async -> Int {
-    await withCheckedContinuation { c in
-        DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
-            c.resume(with: .success(42))
-        }
-    }
-}
-
-func calculateString() async -> String {
-    await withCheckedContinuation { c in
-        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
-            c.resume(with: .success("The meaning of life is: "))
-        }
-    }
-}
-
-@main
-struct MyProgram {
-    
-    static func main() async {
-        
-        enum TaskSteps {
-            case first(Int)
-            case second(String)
-        }
-
-        await withTaskGroup(of: TaskSteps.self) { group in
-            group.async {
-                .first(await calculateNumber())
-            }
-            group.async {
-                .second(await calculateString())
-            }
-
-            var result: String = ""
-            for await res in group {
-                switch res {
-                case .first(let value):
-                    result = result + String(value)
-                case .second(let value):
-                    result = value + result
-                }
-            }
-            print(result)
-        }
-    }
-}
-

After the tasks are completed you can switch the sequence elements and perform the final operation on the result based on the wrapped enum value. This little trick will allow you to run all kind of tasks with different return types to run parallel using the new Tasks APIs. 👍

Unstructured and detached tasks

As you might have noticed this before, it is not possible to call an async API from a sync function. This is where unstructured tasks can help. The most important thing to note here is that the lifetime of an unstructured task is not bound to the creating task. They can outlive the parent, and they inherit priorities, task-local values, deadlines from the parent. Unstructured tasks are being represented by a task handle that you can use to cancel the task.

import Foundation
-
-func calculateFirstNumber() async -> Int {
-    await withCheckedContinuation { c in
-        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
-            c.resume(with: .success(42))
-        }
-    }
-}
-
-@main
-struct MyProgram {
-    
-    static func main() {
-        Task(priority: .background) {
-            let handle = Task { () -> Int in
-                print(Task.currentPriority == .background)
-                return await calculateFirstNumber()
-            }
-            
-            let x = await handle.get()
-            print("The meaning of life is:", x)
-            exit(EXIT_SUCCESS)
-        }
-        dispatchMain()
-    }
-}
-

You can get the current priority of the task using the static currentPriority property and check if it matches the parent task priority (of course it should match it). ☺️

So what’s the difference between unstructured tasks and detached tasks? Well, the answer is quite simple: unstructured task will inherit the parent context, on the other hand detached tasks won’t inherit anything from their parent context (priorities, task-locals, deadlines).

@main
-struct MyProgram {
-    
-    static func main() {
-        Task(priority: .background) {
-            Task.detached {
-                /// false -> Task.currentPriority is unspecified
-                print(Task.currentPriority == .background)
-                let x = await calculateFirstNumber()
-                print("The meaning of life is:", x)
-                exit(EXIT_SUCCESS)
-            }
-        }
-        dispatchMain()
-    }
-}
-

You can create a detached task by using the detached method, as you can see the priority of the current task inside the detached task is unspecified, which is definitely not equal with the parent priority. By the way it is also possible to get the current task by using the withUnsafeCurrentTask function. You can use this method too to get the priority or check if the task is cancelled. 🙅‍♂️

@main
-struct MyProgram {
-    
-    static func main() {
-        Task(priority: .background) {
-            Task.detached {
-                withUnsafeCurrentTask { task in
-                    print(task?.isCancelled ?? false)
-                    print(task?.priority == .unspecified)
-                }
-                let x = await calculateFirstNumber()
-                print("The meaning of life is:", x)
-                exit(EXIT_SUCCESS)
-            }
-        }
-        dispatchMain()
-    }
-}
-

There is one more big difference between detached and unstructured tasks. If you create an unstructured task from an actor, the task will execute directly on that actor and NOT in parallel, but a detached task will be immediately parallel. This means that an unstructured task can alter internal actor state, but a detached task can not modify the internals of an actor. ⚠️

You can also take advantage of unstructured tasks in task groups to create more complex task structures if the structured hierarchy won’t fit your needs.

Task local values

There is one more thing I’d like to show you, we’ve mentioned task local values quite a lot of times, so here’s a quick section about them. This feature is basically an improved version of the thread-local storage designed to play nice with the structured concurrency feature in Swift.

Sometimes you’d like to carry on custom contextual data with your tasks and this is where task local values come in. For example you could add debug information to your task objects and use it to find problems more easily. Donny Wals has an in-depth article about task local values, if you are interested more about this feature, you should definitely read his post. 💪

So in practice, you can annotate a static property with the @TaskLocal property wrapper, and then you can read this metadata within an another task. From now on you can only mutate this property by using the withValue function on the wrapper itself.

import Foundation
-
-enum TaskStorage {
-    @TaskLocal static var name: String?
-}
-
-@main
-struct MyProgram {
-    
-    static func main() async {
-        await TaskStorage.$name.withValue("my-task") {
-            let t1 = Task {
-                print("unstructured:", TaskStorage.name ?? "n/a")
-            }
-            
-            let t2 = Task.detached {
-                print("detached:", TaskStorage.name ?? "n/a")
-            }
-            /// runs in parallel
-            _ = await [t1.value, t2.value]
-        }
-    }
-}
-

Tasks will inherit these local values (except detached) and you can alter the value of task local values inside a given task as well, but these changes will be only visible for the current task & child tasks. To sum this up, task local values are always tied to a given task scope.

As you can see structured concurrency in Swift is quite a lot to digest, but once you understand the basics everything comes nicely together with the new async/await features and Tasks you can easily construct jobs for serial or parallel execution. Anyway, I hope you enjoyed this article. 🙏

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/swift-visitor-design-pattern/index.html b/docs/swift-visitor-design-pattern/index.html deleted file mode 100644 index 5d3630e..0000000 --- a/docs/swift-visitor-design-pattern/index.html +++ /dev/null @@ -1,438 +0,0 @@ - - - - - - - - - - - - Swift visitor design pattern - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 3 min read - -
-

Swift visitor design pattern

-
-

The visitor design pattern in Swift allows us to add new features to an existing group of objects without altering the original code.

-
- -

A basic visitor example

The visitor design pattern is one of the behavioral patterns, it is used to extend an object with a given functionality without actually modifying it. Sounds cool, right? Actually this pattern is what gives SwiftUI superpowers, let me show you how it works.

open class View {}
-
-final class FirstView: View {}
-final class SecondView: View {}
-final class ThirdView: View {}
-
-struct HeightVisitor {
-    func visit(_ view: FirstView) -> Float { 16 }
-    func visit(_ view: SecondView) -> Float { 32 }
-    func visit(_ view: ThirdView) -> Float { 64 }
-}
-
-protocol AcceptsHeightVisitor {
-    func accept(_ visitor: HeightVisitor) -> Float
-}
-
-extension FirstView: AcceptsHeightVisitor {
-    func accept(_ visitor: HeightVisitor) -> Float { visitor.visit(self) }
-}
-
-extension SecondView: AcceptsHeightVisitor {
-    func accept(_ visitor: HeightVisitor) -> Float { visitor.visit(self) }
-}
-
-extension ThirdView: AcceptsHeightVisitor {
-    func accept(_ visitor: HeightVisitor) -> Float { visitor.visit(self) }
-}
-
-let visitor = HeightVisitor()
-let view1: AcceptsHeightVisitor = FirstView()
-let view2: AcceptsHeightVisitor = SecondView()
-let view3: AcceptsHeightVisitor = ThirdView()
-
-
-print(view1.accept(visitor))
-print(view2.accept(visitor))
-print(view3.accept(visitor))
-

First we define our custom view classes, this will help to visualize how the pattern works. Next we define the actual HeightVisitor object, which can be used to calculate the height for each view type (FirstView, SecondView, ThirdView). This way we don’t have to alter these views, but we can define a protocol AcceptsHeightVisitor, and extend our classes to accept this visitor object and calculate the result using a self pointer. 👈

On the call side we can initiate a new visitor instance and simply define the views using the protocol type, this way it is possible to call the accept visitor method on the views and we can calculate the height for each type without altering the internal structure of these classes.

A generic visitor

We can also make this pattern more generic by creating a Swift protocol with an associated type.

open class View {}
-
-final class FirstView: View {}
-final class SecondView: View {}
-final class ThirdView: View {}
-
-struct HeightVisitor {
-    func visit(_ view: FirstView) -> Float { 16 }
-    func visit(_ view: SecondView) -> Float { 32 }
-    func visit(_ view: ThirdView) -> Float { 64 }
-}
-
-protocol Visitor {
-    associatedtype R
-    func visit<O>(_ object: O) -> R
-}
-
-protocol AcceptsVisitor {
-    func accept<V: Visitor>(_ visitor: V) -> V.R
-}
-
-extension AcceptsVisitor {
-    func accept<V: Visitor>(_ visitor: V) -> V.R { visitor.visit(self) }
-}
-
-extension FirstView: AcceptsVisitor {}
-extension SecondView: AcceptsVisitor {}
-extension ThirdView: AcceptsVisitor {}
-
-extension HeightVisitor: Visitor {
-
-    func visit<O>(_ object: O) -> Float {
-        if let o = object as? FirstView {
-            return visit(o)
-        }
-        if let o = object as? SecondView {
-            return visit(o)
-        }
-        if let o = object as? ThirdView {
-            return visit(o)
-        }
-        fatalError("Visit method unimplemented for type \(O.self)")
-    }
-}
-
-let visitor = HeightVisitor()
-let view1: AcceptsVisitor = FirstView()
-let view2: AcceptsVisitor = SecondView()
-let view3: AcceptsVisitor = ThirdView()
-
-print(view1.accept(visitor))
-print(view2.accept(visitor))
-print(view3.accept(visitor))
-
-// this will crash for sure...
-// class FourthView: View {}
-// extension FourthView: AcceptsVisitor {}
-// FourthView().accept(visitor)
-

You can use the generic Visitor protocol to define the visitor and the AcceptsVisitor protocol to easily extend your objects to accept a generic visitor type. If you choose this approach you still have to implement the generic visit method on the Visitor, cast the object type and call the type specific visit method. This way we moved the visit call logic into the visitor. 🙃

Since the views already conforms to the AcceptsVisitor protocol, we can easily extend them with other visitors. For example we can define a color visitor like this:

struct ColorVisitor: Visitor {
-    func visit(_ view: FirstView) -> String { "red" }
-    func visit(_ view: SecondView) -> String { "green" }
-    func visit(_ view: ThirdView) -> String { "blue" }
-    
-    func visit<O>(_ object: O) -> String {
-        if let o = object as? FirstView {
-            return visit(o)
-        }
-        if let o = object as? SecondView {
-            return visit(o)
-        }
-        if let o = object as? ThirdView {
-            return visit(o)
-        }
-        fatalError("Visit method unimplemented for type \(O.self)")
-    }
-}
-
-let visitor = ColorVisitor()
-let view1: AcceptsVisitor = FirstView()
-let view2: AcceptsVisitor = SecondView()
-let view3: AcceptsVisitor = ThirdView()
-
-print(view1.accept(visitor))
-print(view2.accept(visitor))
-print(view3.accept(visitor))
-

As you can see it’s pretty nice that we can achieve this kind of dynamic object extension logic through visitors. If you want to see a practical UIKit example, feel free to take a look at this article. Under the hood SwiftUI heavily utilizes the visitor pattern to achieve some magical TupleView & ViewBuilder related stuff. This pattern is so cool, I highly recommend to learn more about it. 💪

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Event-driven generic hooks for Swift

-
-

In this article I am going to show you how to implement a basic event processing system for your modular Swift application.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Iterator design pattern in Swift

-
-

Learn the iterator design pattern by using some custom sequences, conforming to the IteratorProtocol from the Swift standard library.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Lazy initialization in Swift

-
-

Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Lenses and prisms in Swift

-
-

Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.

- - -
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/swiftnio-tutorial-the-echo-server/index.html b/docs/swiftnio-tutorial-the-echo-server/index.html deleted file mode 100644 index f03cbcd..0000000 --- a/docs/swiftnio-tutorial-the-echo-server/index.html +++ /dev/null @@ -1,466 +0,0 @@ - - - - - - - - - - - - SwiftNIO tutorial - The echo server - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 7 min read - -
-

SwiftNIO tutorial - The echo server

-
-

This is a beginner's guide to learn the basics of the SwiftNIO network app framework by building a basic TCP echo server.

-
- -

Intoducing SwiftNIO

If you used a high level web framework, such as Vapor, in the past, you might had some interaction with event loops or promises. Well, these fundamental building blocks are part of a low level network framework, called SwiftNIO, which I’m going to talk about in this tutorial.

Don’t worry if you haven’t heard about event loops or non-blocking IO just yet, I’ll try to explain everything in this guide, so hopefully you’ll understand everything even if you are a complete beginner to this topic. Let’s start with some basics about networks and computers.

Let’s talk about TCP/IP

It all started on January 1st, 1983. The internet was born (as some say) and people started to officially use the internet protocol suite (TCP/IP) to communicate between devices. If you don’t know much about TCP/IP and you are curious about the underlying parts, you can read a few other articles, but in a nutshell this model allows us to talk with remote computers easily. 💬

Let’s say that you have two machines, connected by the network. How do they communicate with each other? Well, just like when you send a regular letter, first you have to specify the address of the recipient. In order to send a message to another computer, you have to know its digital address too. This digital address is called IP address and it looks like this: 127.0.0.1.

So you’ve got the address, but sometimes this is not enough, because a building can have multiple apartments and you have to specify the exact letterbox in order to reach the actual person. This can happen with computers too, the letterbox is called port number and the full address of the target can be created by combining the IP address and the port number (we call this full address as a network socket address or simply socket, e.g. 127.0.0.1:80). 💌

After you’ve specified the exact address, you’ll need someone to actually deliver the letter containing your message. The postal delivery service can transfer your letter, there are two ways to send it over to the recipient. The first solution is to simply send it without knowing much about the delivery status, the digital version of this approach is called User Datagram Protocol (UDP).

The other (more reliable) method is to get a receipt about the delivery, this way you can make sure that the letter actually arrived and the recipient got it. Although, the postman can open your letter and alter your message, but it’ll be still delivered and you’ll get a notification about this. When you communicate through the network, this method is called Transmission Control Protocol (TCP).

Ok, that’s more than enough network theory, I know it’s a high level abstraction and not entirely accurate, but hopefully you’ll get the basic idea. Now let’s talk about what happens inside the machine and how we can place an actual digital letterbox in front of the imaginary house. 📪

The basic building blocks of SwiftNIO

What do you do if you expect a letter? Apart from the excitement, most people constantly check their mailboxes to see if it’s already there or not. They are listening for the noises of the postman, just like computer programs listen on a given port to check if some data arrived or not. 🤓

What happens if a letter arrives? First of all you have to go and get it out from the mailbox. In order to get it you have to walk through the hallway or down the stairs or you can ask someone else to deliver the letter for you. Anyway, should get the letter somehow first, then based on the envelope you can perform an action. If it looks like a spam, you’ll throw it away, but if it’s an important letter you’ll most likely open it, read the contents and send back an answer as soon as possible. Let’s stick with this analogy, and let me explain this again, but this time using SwiftNIO terms.

Channel

A Channel connects the underlying network socket with the application’s code. The channel’s responsibility is to handle inbound and outbound events, happening through the socket (or file descriptor). In other words, it’s the channel that connects the mailbox with you, you should imagine it as the hallway to the mailbox, literally the messages are going travel to you via a channel. 📨

ChannelPipeline

The ChannelPipeline describes a set of actions about how to handle the letters. One possible version is to make a decision based on the envelope, you’ll throw it away if it looks like a spam, or open it if it looks like a formal letter, it’s also an action if you respond to the letter. Actions are called as channel handlers in SwiftNIO. In short: a pipeline is a predefined sequence of handlers.

ChannelHandler

The ChannelHandler is the action that you can perform when you open the letter. The channel handler has an input and an output type, which you can use to read the message using the input and respond to it using the output. Okay, just two more important terms, bear with me for a second, I’m going to show you some real examples afterwards. 🐻

EventLoop

The EventLoop works just like a run loop or a dispatch queue. What does this mean?

The event loop is an object that waits for events (usually I/O related events, such as “data received”) to happen and then fires some kind of callback when they do.

The modern CPUs have a limited number of cores, apps will most likely associate one thread (of execution) per core. Switching between thread contexts is also inefficient. What happens when an event has to wait for something and a thread becomes available for other tasks? In SwiftNIO the event loop will receive the incoming message, process it, and if it has to wait for something (like a file or database read) it’ll execute some other tasks in the meantime. When the IO operation finishes it’ll switch back to the task and it’ll call back to your code when it’s time. Or something like this, but the main takeaway here is that your channel handler is always going to be associated with exactly one event loop, this means actions will be executed using the same context.

EventLoopGroup

The EventLoopGroup manages threads and event loops. The MultiThreadedEventLoopGroup is going to balance out client over the available threads (event loops) this way the application is going to be efficient and every thread will handle just about the same amount of clients.

Other components

There are some other SwiftNIO components, we could talk more about Futures, Promises and the ByteBuffer type, but I suppose this was more than enough theory for now, so I’m not going to dive into these kind of objects, but spare them for upcoming articles. 😇

Building an echo server using SwiftNIO

You can start by creating a new executable Swift package, using the Swift Package Manager. Next you have to add SwiftNIO as a package dependency inside the Package.swift file.

// swift-tools-version:5.7
-import PackageDescription
-
-let package = Package(
-    name: "echo-server",
-    platforms: [
-       .macOS(.v10_15),
-    ],
-    dependencies: [
-        .package(
-            url: "https://github.com/apple/swift-nio",
-            from: "2.0.0"
-        ),
-    ],
-    targets: [
-        .executableTarget(
-            name: "Server",
-            dependencies: [
-                .product(
-                    name: "NIO",
-                    package: "swift-nio"
-                )
-            ]
-        ),
-    ]
-)
-

The next step is to alter the main project file, we can easily create the SwiftNIO based TCP server by using the ServerBootstrap object. First we have to instantiate a MultiThreadedEventLoopGroup with a number of threads, using the CPU cores in the system.

Then we configure the server by adding some channel options. You don’t have to know much about these just yet, the interesting part is inside the childChannelInitializer block. We create the actual channel pipeline there. Our pipeline will consist of two handlers, the first one is the built-in BackPressureHandler, the second one is going to be our custom made EchoHandler object.

If you are interested in the available ChannelOptions, you can take a look at the NIO source code, it also contains some very good docs about these things. The final step is to bind the server bootstrap object to a given host and port, and wait for incoming connections. 🧐

import NIO
-
-@main
-public struct Server {
-    
-    public static func main() throws {
-        let eventLoopGroup = MultiThreadedEventLoopGroup(
-            numberOfThreads: System.coreCount
-        )
-
-        defer {
-            try! eventLoopGroup.syncShutdownGracefully()
-        }
-
-        let serverBootstrap = ServerBootstrap(
-            group: eventLoopGroup
-        )
-        .serverChannelOption(
-            ChannelOptions.backlog,
-            value: 256
-        )
-        .serverChannelOption(
-            ChannelOptions.socketOption(.so_reuseaddr),
-            value: 1
-        )
-        .childChannelInitializer { channel in
-            channel.pipeline.addHandlers([
-                BackPressureHandler(),
-                EchoHandler(),
-            ])
-        }
-        .childChannelOption(
-            ChannelOptions.socketOption(.so_reuseaddr),
-            value: 1
-        )
-        .childChannelOption(
-            ChannelOptions.maxMessagesPerRead,
-            value: 16
-        )
-        .childChannelOption(
-            ChannelOptions.recvAllocator,
-            value: AdaptiveRecvByteBufferAllocator()
-        )
-
-        let defaultHost = "127.0.0.1" // or ::1 for IPv6
-        let defaultPort = 8888
-
-        let channel = try serverBootstrap.bind(
-            host: defaultHost,
-            port: defaultPort
-        )
-        .wait()
-
-        print("Server started and listening on \(channel.localAddress!)")
-        try channel.closeFuture.wait()
-        print("Server closed")
-    }
-}
-

As I mentioned this, in order to handle an event happening on the channel we have can create a custom ChannelInboundHandler object. Inside the channelRead function it is possible to unwrap the inbound data into a ByteBuffer object and write the input message onto the output as a wrapped NIOAny object.

Challenge: write a server that can print colorful messages. Hint: building a text modifying server.

import NIO
-
-final class EchoHandler: ChannelInboundHandler {
-
-    typealias InboundIn = ByteBuffer
-    typealias OutboundOut = ByteBuffer
-
-    func channelRead(
-        context: ChannelHandlerContext,
-        data: NIOAny
-    ) {
-        let input = self.unwrapInboundIn(data)
-        guard
-            let message = input.getString(at: 0, length: input.readableBytes)
-        else {
-            return
-        }
-        
-        var buff = context.channel.allocator.buffer(capacity: message.count)
-        buff.writeString(message)
-        context.write(wrapOutboundOut(buff), promise: nil)
-    }
-
-
-    func channelReadComplete(
-        context: ChannelHandlerContext
-    ) {
-        context.flush()
-    }
-
-    func errorCaught(
-        context: ChannelHandlerContext,
-        error: Error
-    ) {
-        print(error)
-
-        context.close(promise: nil)
-    }
-}
-

If you run the app and connect to it using the telnet 127.0.0.1 8888 command you can enter some text and the server will echo it back to you. Keep in mind that this is a very simple TCP server, without HTTP, but it is possible to write express-like HTTP servers, JSON API servers, even a game backend and many other cool and crazy performant stuff using SwiftNIO. I hope this tutorial will help you to get started with SwiftNIO, I’m also learning a lot about the framework lately, so please forgive me (or even correct me) if I missed / messed up something. 😅

So again: SwiftNIO a (low-level) non-blocking event-driven network application framework for high performance protocol servers & clients. It’s like Netty, but written for Swift.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/table-joins-in-fluent-4/index.html b/docs/table-joins-in-fluent-4/index.html deleted file mode 100644 index 8c5433e..0000000 --- a/docs/table-joins-in-fluent-4/index.html +++ /dev/null @@ -1,505 +0,0 @@ - - - - - - - - - - - - Table joins in Fluent 4 - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

Table joins in Fluent 4

-
-

In this quick tutorial I'm going to show you how to join and query database models using the Fluent ORM framework in Vapor 4.

-
- -

Database models

Fluent is a Swift ORM framework written for Vapor. You can use models to represent rows in a table, migrations to create the structure for the tables and you can define relations between the models using Swift property wrappers. That’s quite a simple way of representing parent, child or sibling connections. You can “eager load” models through these predefined relation properties, which is great, but sometimes you don’t want to have static types for the relationships.

I’m working on a modular CMS and I can’t have hardcoded relationship properties inside the models. Why? Well, I want to be able to load modules at runtime, so if module A depends from module B through a relation property then I can’t compile module A independently. That’s why I dropped most of the cross-module relations, nevertheless I have to write joined queries. 😅

Customer model

In this example we are going to model a simple Customer-Order-Product relation. Our customer model will have a basic identifier and a name. Consider the following:

final class CustomerModel: Model, Content {
-    static let schema = "customers"
-    
-    @ID(key: .id) var id: UUID?
-    @Field(key: "name") var name: String
-
-    init() { }
-
-    init(id: UUID? = nil, name: String) {
-        self.id = id
-        self.name = name
-    }
-}
-

Nothing special, just a basic Fluent model.

Order model

Customers will have a one-to-many relationship to the orders. This means that a customer can have multiple orders, but an order will always have exactly one associated customer.

final class OrderModel: Model, Content {
-    static let schema = "orders"
-    
-    @ID(key: .id) var id: UUID?
-    @Field(key: "date") var date: Date
-    @Field(key: "customer_id") var customerId: UUID
-
-    init() { }
-
-    init(id: UUID? = nil, date: Date, customerId: UUID) {
-        self.id = id
-        self.date = date
-        self.customerId = customerId
-    }
-}
-

We could take advantage of the @Parent and @Child property wrappers, but this time we are going to store a customerId reference as a UUID type. Later on we are going to put a foreign key constraint on this relation to ensure that referenced objects are valid identifiers.

Product model

The product model, just like the customer model, is totally independent from anything else. 📦

final class ProductModel: Model, Content {
-    static let schema = "products"
-    
-    @ID(key: .id) var id: UUID?
-    @Field(key: "name") var name: String
-
-    init() { }
-
-    init(id: UUID? = nil, name: String) {
-        self.id = id
-        self.name = name
-    }
-}
-

We can create a property with a @Sibling wrapper to express the relationship between the orders and the products, or use joins to query the required data. It really doesn’t matter which way we go, we still need a cross table to store the related product and order identifiers.

OrderProductModel

We can describe a many-to-many relation between two tables using a third table.

final class OrderProductModel: Model, Content {
-    static let schema = "order_products"
-    
-    @ID(key: .id) var id: UUID?
-    @Field(key: "order_id") var orderId: UUID
-    @Field(key: "product_id") var productId: UUID
-    @Field(key: "quantity") var quantity: Int
-
-    init() { }
-
-    init(id: UUID? = nil, orderId: UUID, productId: UUID, quantity: Int) {
-        self.id = id
-        self.orderId = orderId
-        self.productId = productId
-        self.quantity = quantity
-    }
-}
-

As you can see we can store extra info on the cross table, in our case we are going to associate quantities to the products on this relation right next to the product identifier.

Migrations

Fortunately, Fluent gives us a simple way to create the schema for the database tables.

struct InitialMigration: Migration {
-
-    func prepare(on db: Database) -> EventLoopFuture<Void> {
-        db.eventLoop.flatten([
-            db.schema(CustomerModel.schema)
-                .id()
-                .field("name", .string, .required)
-                .create(),
-            db.schema(OrderModel.schema)
-                .id()
-                .field("date", .date, .required)
-                .field("customer_id", .uuid, .required)
-                .foreignKey("customer_id", references: CustomerModel.schema, .id, onDelete: .cascade)
-                .create(),
-            db.schema(ProductModel.schema)
-                .id()
-                .field("name", .string, .required)
-                .create(),
-            db.schema(OrderProductModel.schema)
-                .id()
-                .field("order_id", .uuid, .required)
-                .foreignKey("order_id", references: OrderModel.schema, .id, onDelete: .cascade)
-                .field("product_id", .uuid, .required)
-                .foreignKey("product_id", references: ProductModel.schema, .id, onDelete: .cascade)
-                .field("quantity", .int, .required)
-                .unique(on: "order_id", "product_id")
-                .create(),
-        ])
-    }
-
-    func revert(on db: Database) -> EventLoopFuture<Void> {
-        db.eventLoop.flatten([
-            db.schema(OrderProductModel.schema).delete(),
-            db.schema(CustomerModel.schema).delete(),
-            db.schema(OrderModel.schema).delete(),
-            db.schema(ProductModel.schema).delete(),
-        ])
-    }
-}
-

If you want to avoid invalid data in the tables, you should always use the foreign key and unique constraints. A foreign key can be used to check if the referenced identifier exists in the related table and the unique constraint will make sure that only one row can exists from a given field.

Joining database tables using Fluent 4

We have to run the InitialMigration script before we start using the database. This can be done by passing a command argument to the backend application or we can achieve the same thing by calling the autoMigrate() method on the application instance.

For the sake of simplicity I’m going to use the wait method instead of async Futures & Promises, this is fine for demo purposes, but in a real-world server application you should never block the current event loop with the wait method.

This is one possible setup of our dummy database using an SQLite storage, but of course you can use PostgreSQL, MySQL or even MariaDB through the available Fluent SQL drivers. 🚙

public func configure(_ app: Application) throws {
-
-    app.databases.use(.sqlite(.file("db.sqlite")), as: .sqlite)
-
-    app.migrations.add(InitialMigration())
-
-    try app.autoMigrate().wait()
-
-    let customers = [
-        CustomerModel(name: "Bender"),
-        CustomerModel(name: "Fry"),
-        CustomerModel(name: "Leela"),
-        CustomerModel(name: "Hermes"),
-        CustomerModel(name: "Zoidberg"),
-    ]
-    try customers.create(on: app.db).wait()
-    
-    let products = [
-        ProductModel(name: "Hamburger"),
-        ProductModel(name: "Fish"),
-        ProductModel(name: "Pizza"),
-        ProductModel(name: "Beer"),
-    ]
-    try products.create(on: app.db).wait()
-
-    // Bender + pizza & beer
-    let order = OrderModel(date: Date(), customerId: customers[0].id!)
-    try order.create(on: app.db).wait()
-
-    let beerProduct = OrderProductModel(orderId: order.id!, productId: products[3].id!, quantity: 6)
-    try beerProduct.create(on: app.db).wait()
-    let pizzaProduct = OrderProductModel(orderId: order.id!, productId: products[2].id!, quantity: 1)
-    try pizzaProduct.create(on: app.db).wait()
-}
-

We have created 5 customers (Bender, Fry, Leela, Hermes, Zoidberg), 4 products (Hamburger, Fish, Pizza, Beer) and one new order for Bender containing 2 products (6 beers and 1 pizza). 🤖

Inner join using one-to-many relations

Now the question is: how can we get the customer data based on the order?

let orders = try OrderModel
-    .query(on: app.db)
-    .join(CustomerModel.self, on: \OrderModel.$customerId == \CustomerModel.$id, method: .inner)
-    .all()
-    .wait()
-
-for order in orders {
-    let customer = try order.joined(CustomerModel.self)
-    print(customer.name)
-    print(order.date)
-}
-

The answer is pretty simple. We can use an inner join to fetch the customer model through the order.customerId and customer.id relation. When we iterate through the models we can ask for the related model using the joined method.

Joins and many to many relations

Having a customer is great, but how can I fetch the associated products for the order? We can start the query with the OrderProductModel and use a join using the ProductModel plus we can filter by the order id using the current order.

for order in orders {
-    //...
-
-    let orderProducts = try OrderProductModel
-        .query(on: app.db)
-        .join(ProductModel.self, on: \OrderProductModel.$productId == \ProductModel.$id, method: .inner)
-        .filter(\.$orderId == order.id!)
-        .all()
-        .wait()
-
-    for orderProduct in orderProducts {
-        let product = try orderProduct.joined(ProductModel.self)
-        print(product.name)
-        print(orderProduct.quantity)
-    }
-}
-

We can request the joined model the same way as we did it for the customer. Again, the very first parameter is the model representation of the joined table, next you define the relation between the tables using the referenced identifiers. As a last parameter you can specify the type of the join.

Inner join vs left join

There is a great SQL tutorial about joins on w3schools.com, I highly recommend reading it. The main difference between an inner join and a left join is that an inner join only returns those records that have matching identifiers in both tables, but a left join will return all the records from the base (left) table even if there are no matches in the joined (right) table.

There are many different types of SQL joins, but inner and left join are the most common ones. If you want to know more about the other types you should read the linked article. 👍

Summary

Table joins are really handy, but you have to be careful with them. You should always use proper foreign key and unique constraints. Also consider using indexes on some rows when you work with joins, because it can improve the performance of your queries. Speed can be an important factor, so never load more data from the database than you actually need.

There is an issue on GitHub about the Fluent 4 API, and another one about querying specific fields using the .field method. Long story short, joins can be great and we need better docs. 🙉

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/tags/design-pattern/index.html b/docs/tags/design-pattern/index.html deleted file mode 100644 index fa3e97f..0000000 --- a/docs/tags/design-pattern/index.html +++ /dev/null @@ -1,594 +0,0 @@ - - - - - - - - - - - - Design pattern - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
- -
- Design pattern -

Design pattern

-

- -
- - - -

20 posts

- -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Lenses and prisms in Swift

-
-

Beginner's guide about optics in Swift. Learn how to use lenses and prisms to manipulate objects using a functional approach.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Swift visitor design pattern

-
-

The visitor design pattern in Swift allows us to add new features to an existing group of objects without altering the original code.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Event-driven generic hooks for Swift

-
-

In this article I am going to show you how to implement a basic event processing system for your modular Swift application.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Modules and hooks in Swift

-
-

Learn how to extend your application with new functionalities using a loosely coupled modular plugin system written in Swift.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Swift init patterns

-
-

The ultimate guide how to init your Swift data types, with the help of designated, convenience, failable intitializers and more.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Lazy initialization in Swift

-
-

Learn how to use lazy properties in Swift to improve performance, avoid optionals or just to make the init process more clean.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift object pool design pattern

-
-

In this quick tutorial I'll explain & show you how to implement the object pool design pattern using the Swift programming language.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Iterator design pattern in Swift

-
-

Learn the iterator design pattern by using some custom sequences, conforming to the IteratorProtocol from the Swift standard library.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Swift adapter design pattern

-
-

Turn an incompatible object into a target interface or class by using a real world example and the adapter design pattern in Swift.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Swift dependency injection design pattern

-
-

Want to learn the Dependency Injection pattern using Swift? This tutorial will show you how to write loosely coupled code using DI.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift delegate design pattern

-
-

The delegate design pattern is a relatively easy way to communicate between two objects through a common interface, protocol in Swift.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift command design pattern

-
-

This time I'm going to show you a behavioral pattern. Here is a little example of the command design patten written in Swift.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift prototype design pattern

-
-

The prototype design pattern is used to create clones of a base object, so let's see some practical examples written in Swift.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift abstract factory design pattern

-
-

Let's combine factory method with simple factory voilá: here is the abstract factory design pattern written in Swift language!

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift factory method design pattern

-
-

The factory method design pattern is a dedicated non-static method for hiding the creation logic of an object. Let's make it in Swift!

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift simple factory design pattern

-
-

This time let's talk about the simple factory design pattern to encapsulate object creation in a really simple way using Swift.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Swift static factory design pattern

-
-

In this article I'll teach you about the static factory design pattern and show some use cases using the Swift programming language.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Swift builder design pattern

-
-

Learn how to implement the builder pattern in Swift to hide the complexity of creating objects with lots of individual properties.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Swift singleton design pattern

-
-

Singleton is the most criticized design pattern of all time. Learn the proper way of using Swift singleton classes inside iOS projects.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

UIKit init patterns

-
-

Learn about the initialization process of the two well known classes in UIKit. Say hello to UIViewcontroller, and UIView init patterns.

- - -
-
-
- -
- - - -
- - - - diff --git a/docs/tags/hummingbird/index.html b/docs/tags/hummingbird/index.html deleted file mode 100644 index cde1f69..0000000 --- a/docs/tags/hummingbird/index.html +++ /dev/null @@ -1,237 +0,0 @@ - - - - - - - - - - - - Hummingbird - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
- -
- Hummingbird -

Hummingbird

-

- -
- - - -

3 posts

- -
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Hummingbird routing and requests

-
-

Beginner's guide to learn all about routing and request handling using the Hummingbird server-side Swift framework.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to server-side Swift using the Hummingbird framework

-
-

Learn about Swift on the server by creating a simple application using the brand new HTTP server library called: Hummingbird.

- - -
-
-
- -
- - - -
- - - - diff --git a/docs/tags/index.html b/docs/tags/index.html deleted file mode 100644 index ffaac39..0000000 --- a/docs/tags/index.html +++ /dev/null @@ -1,223 +0,0 @@ - - - - - - - - - - - - Tags - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -

Tags


Browse posts by tags.

- -
- -
-
- Design pattern -

Design pattern

-

20 posts

-
-
- Hummingbird -

Hummingbird

-

3 posts

-
-
- Server -

Server

-

34 posts

-
-
- Swift -

Swift

-

40 posts

-
-
- Swift Package Manager -

Swift Package Manager

-

4 posts

-
-
- SwiftUI -

SwiftUI

-

0 posts

-
-
- Tooling -

Tooling

-

7 posts

-
-
- UIKit -

UIKit

-

23 posts

-
-
- VIPER -

VIPER

-

5 posts

-
-
- Vapor -

Vapor

-

30 posts

-
-
- Xcode -

Xcode

-

3 posts

-
-
- iOS -

iOS

-

19 posts

-
- -
-
- -
- - - -
- - - - diff --git a/docs/tags/ios/index.html b/docs/tags/ios/index.html deleted file mode 100644 index 4d8ae29..0000000 --- a/docs/tags/ios/index.html +++ /dev/null @@ -1,590 +0,0 @@ - - - - - - - - - - - - iOS - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
- -
- iOS -

iOS

-

- -
- - - -

19 posts

- -
-
- - -
- - Tibor Bödecs - -
-
- - · 12 min read -
- -

Progressive Web Apps on iOS

-
-

This is a beginner's guide about creating PWAs for iOS including custom icons, splash screens, safe area and dark mode support.

- -
- iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 13 min read -
- -

What are the best practices to learn iOS / Swift in 2020?

-
-

Are you learning iOS development? Looking for Swift best practices? This is the right place to start your journey as a mobile application developer.

- -
- iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Picking and playing videos in Swift

-
-

Learn how to record or select a video file using a video picker controller and the AVPlayer class, written entirely in Swift 5.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

URLSession and the Combine framework

-
-

Learn how to make HTTP requests and parse the response using the brand new Combine framework with foundation networking.

- -
- iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Uniquely identifying views

-
-

Learn how to use string based UIView identifiers instead of tags. If you are tired of tagging views, check out these alternative solutions.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Styling by subclassing

-
-

Learn how to design and build reusable user interface elements by using custom view subclasses from the UIKit framework in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Picking images with UIImagePickerController in Swift 5

-
-

Learn how to get an image from the photo library or directly from the camera by using the UIImagePickerController class in Swift 5.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

UICollectionView data source and delegates programmatically

-
-

In this quick UIKit tutorial I'll show you how to create a simple UICollectionView without Interface Builder, but only using Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Mastering iOS auto layout anchors programmatically from Swift

-
-

Looking for best practices of using layout anchors? Let's learn how to use the iOS autolayout system in the proper way using Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

How to use iCloud drive documents?

-
-

Learn how to sync files and data through a shared iCloud drive folder using the latest version of Swift programming language.

- -
- iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

iOS custom transition tutorial in Swift

-
-

In this tutorial, you'll learn how to replace the push, pop and modal animations with custom transitions & percent driven interactions.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Networking examples for appleOS

-
-

Learn how to use Bonjour, with UDP/TCP sockets, streams and how to communicate through CoreBluetooth or the watch APIs.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

UICollectionView cells with circular images plus rotation support

-
-

Learn how to make rounded corners for UIImageView items wrapped inside collection view cells, with rotation support.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Self sizing cells with rotation support

-
-

How to make self sizing cells in Swift both for table & collection views supporting orientation changes and dynamic font types.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

iOS Auto Layout tutorial programmatically

-
-

In this great iOS Auto Layout tutorial I'll teach you how to support rotation, use constraints, work with layers, animate corner radius.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

The ultimate Combine framework tutorial in Swift

-
-

Get started with the brand new declarative Combine framework in practice using Swift. I'll teach you all the goodies from zero to hero.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Top 20 iOS libraries written in Swift

-
-

I gathered the best open source Swift frameworks on github that will help you to speed up mobile application development in 2019.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

UIKit init patterns

-
-

Learn about the initialization process of the two well known classes in UIKit. Say hello to UIViewcontroller, and UIView init patterns.

- - -
-
-
- -
- - - -
- - - - diff --git a/docs/tags/ipados/index.html b/docs/tags/ipados/index.html deleted file mode 100644 index f26568f..0000000 --- a/docs/tags/ipados/index.html +++ /dev/null @@ -1,197 +0,0 @@ - - - - - - - - - - - - iPadOS - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
- -
- iPadOS -

iPadOS

-

- -
- - - -

1 posts

- -
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Networking examples for appleOS

-
-

Learn how to use Bonjour, with UDP/TCP sockets, streams and how to communicate through CoreBluetooth or the watch APIs.

- - -
-
-
- -
- - - -
- - - - diff --git a/docs/tags/macos/index.html b/docs/tags/macos/index.html deleted file mode 100644 index 59caa90..0000000 --- a/docs/tags/macos/index.html +++ /dev/null @@ -1,240 +0,0 @@ - - - - - - - - - - - - macOS - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
- -
- macOS -

macOS

-

- -
- - - -

3 posts

- -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

How to build macOS apps using only the Swift Package Manager?

-
-

In this article we're going to create a macOS application without ever touching an Xcode project file, but only working with SPM.

- -
- macOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Networking examples for appleOS

-
-

Learn how to use Bonjour, with UDP/TCP sockets, streams and how to communicate through CoreBluetooth or the watch APIs.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

How to launch a macOS app at login?

-
-

In this tutorial I'll show you how to launch a completely sandboxed macOS application on system startup written in Swift.

- -
- Tooling - macOS -
-
-
-
- -
- - - -
- - - - diff --git a/docs/tags/server/index.html b/docs/tags/server/index.html deleted file mode 100644 index 766cc22..0000000 --- a/docs/tags/server/index.html +++ /dev/null @@ -1,919 +0,0 @@ - - - - - - - - - - - - Server - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
- -
- Server -

Server

-

- -
- - - -

34 posts

- -
- -
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Hummingbird routing and requests

-
-

Beginner's guide to learn all about routing and request handling using the Hummingbird server-side Swift framework.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to server-side Swift using the Hummingbird framework

-
-

Learn about Swift on the server by creating a simple application using the brand new HTTP server library called: Hummingbird.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Running and testing async Vapor commands

-
-

In this article I'll show you how to build asynchronous Vapor commands and how to test them using ConsoleKit.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

The abstract Vapor service factory design pattern

-
-

In this tutorial I'm going to show you how you can create an abstract driver-based component for the Vapor framework.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

SwiftNIO tutorial - The echo server

-
-

This is a beginner's guide to learn the basics of the SwiftNIO network app framework by building a basic TCP echo server.

- -
- Server -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

The repository pattern for Vapor 4

-
-

In this article I'm going to talk about the repository design pattern and give you a few Fluent ORM tips for your Vapor 4 app.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

How to write HTML in Swift?

-
-

This tutorial is all about rendering HTML docs using a brand new DSL library called SwiftHtml and the Vapor web framework.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

The future of server side Swift

-
-

What's going to happen with Swift on the Server in 2022? Distributed actors, Vapor 5, some predictions and wishes.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Building a global storage for Vapor

-
-

This tutorial is about a shared global storage that you can implement using a common design pattern in Vapor 4.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

Beginner's guide to the async/await concurrency API in Vapor & Fluent

-
-

Learn how to convert your existing EventLoopFuture based Vapor server app using the new async/await Swift feature.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

Declarative unit tests for Vapor

-
-

Learn how to test your server side Swift backend app in a declarative style using a lightweight library called Spec.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

How to design type safe RESTful APIs using Swift & Vapor?

-
-

Learn to make proper data transfer objects for CRUD operations and integrate them both into the client and server side API layer.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

File upload API server in Vapor 4

-
-

Learn how to build a very simple file upload API server using Vapor 4 and URLSession upload task on the client side.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

File upload using Vapor 4

-
-

Learn how to implement a basic HTML file upload form using the Leaf template engine and Vapor, all written in Swift of course.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

Swift on the Server in 2020

-
-

Why choose Swift as a backend language in 2020? What are the available frameworks to build your server? Let me guide you.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

How to store keys in env files?

-
-

In this tutorial I'll show you how to save and load secret keys as base64 encoded strings using dotenv files in Vapor 4.

- -
- Server - Tooling - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Table joins in Fluent 4

-
-

In this quick tutorial I'm going to show you how to join and query database models using the Fluent ORM framework in Vapor 4.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 13 min read -
- -

Websockets for beginners using Vapor 4 and Vanilla JavaScript

-
-

Learn how to create a websocket server using Swift & Vapor. Multiplayer game development using JavaScript in the browser.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 11 min read -
- -

Sign in with Apple using Vapor 4

-
-

A complete tutorial for beginners about how to implement the Sign in with Apple authentication service for your website.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

Server side Swift projects inside Docker using Vapor 4

-
-

Learn how to setup Vapor 4 projects inside a Docker container. Are you completely new to Docker? This article is just for you.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 16 min read -
- -

All about authentication in Vapor 4

-
-

Learn how to implement a user login mechanism with various auth methods using sessions, JWTs, written in Swift only.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

The anatomy of Vapor commands

-
-

Learn how to build and run your existing Vapor apps using various command line arguments, flags and environments.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

How to use middlewares in Vapor 4?

-
-

Learn how to create middlewares for a Vapor based server side Swift application to handle common routing functionalities.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

How to write Swift scripts using the new Command API in Vapor 4?

-
-

Shell scripts are essentials on the server side. Learn how to build Swift scripts for your backend apps using property wrappers.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 15 min read -
- -

Get started with the Fluent ORM framework in Vapor 4

-
-

Learn how to use the Fluent ORM framework. Migrations, schemas, relations powered by PostgreSQL, written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

How to set up pgSQL for Fluent 4?

-
-

This is a tutorial for beginners about using PostgreSQL. I'll show you how to automatically backup and restore the database.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 18 min read -
- -

Beginner's guide to Server side Swift using Vapor 4

-
-

Learn how to build and host your very first backend application using Vapor 4 and the brief history of server side Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

What's new in Vapor 4?

-
-

Vapor is the most popular server side Swift web application framework. This time we'll cover what's new in Vapor 4.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 13 min read -
- -

Asynchronous validation for Vapor

-
-

Learn how to validate input data using an async technique. Unified request validation API for your server side Swift app.

- -
- Server - Vapor -
-
-
-
- -
- - - -
- - - - diff --git a/docs/tags/swift-package-manager/index.html b/docs/tags/swift-package-manager/index.html deleted file mode 100644 index 4099475..0000000 --- a/docs/tags/swift-package-manager/index.html +++ /dev/null @@ -1,255 +0,0 @@ - - - - - - - - - - - - Swift Package Manager - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
- -
- Swift Package Manager -

Swift Package Manager

-

- -
- - - -

4 posts

- -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Introduction to SPM artifact bundles

-
-

In this tutorial I'm going to show you how to use the new binary target related artifact bundle using the Swift package manager.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginner's guide to Swift package manager command plugins

-
-

Learn how to create command plugins for the Swift Package Manager to execute custom actions using SPM and other tools.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

How to create a Swift package collection?

-
-

In this tutorial I'm going to show you how to create your own package collection from your favorite Swift libraries.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Swift Package Manager tutorial

-
-

Learn how to use the Swift Package Manager to handle external dependencies, create your library or app on macOS and Linux.

- - -
-
-
- -
- - - -
- - - - diff --git a/docs/tags/swift/index.html b/docs/tags/swift/index.html deleted file mode 100644 index 84daab1..0000000 --- a/docs/tags/swift/index.html +++ /dev/null @@ -1,1014 +0,0 @@ - - - - - - - - - - - - Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
- -
- Swift -

Swift

-

Swift is a powerful programming language by Apple, used for building both server-side applications and user interfaces with SwiftUI. Explore server-side frameworks like Vapor and Hummingbird, and learn best practices for creating robust backend services.

- -
- - - -

40 posts

- -
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Running tasks in parallel

-
-

Learn how to run tasks in parallel using the old-school tools and frameworks plus the new structured concurrency API in Swift.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Easy multipart file upload for Swift

-
-

Let me show you how to create HTTP requests using multipart (form data) body without a third party library. Simple solution.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Beginner's guide to Swift arrays

-
-

Learn how to manipulate arrays in Swift like a pro. This tutorial covers lots of useful array related methods, tips and tricks.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

How to use a Swift library in C

-
-

In this tutorial, we're going to build a C app by importing a Swift library and talk a bit about the Swift / C Interoperability in general.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Building tree data structures in Swift

-
-

This tutorial is about showing the pros and cons of various Swift tree data structures using structs, enums and classes.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Practical guide to binary operations using the UInt8 type in Swift

-
-

Introduction to the basics of signed number representation and some practical binary operation examples in Swift using UInt8.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

How to build better command line apps and tools using Swift?

-
-

These tips will help you to create amazing CLI tools, utility apps, server side projects or terminal scripts using the Swift language.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Swift structured concurrency tutorial

-
-

Learn how to work with the Task object to perform asynchronous operations in a safe way using the new concurrency APIs in Swift.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Swift actors tutorial - a beginner's guide to thread safe concurrency

-
-

Learn how to use the brand new actor model to protect your application from unwanted data-races and memory issues.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Introduction to async/await in Swift

-
-

Beginners guide to the new async/await API's in Swift 5.5. Interacting with sync code, structured concurrency, async let.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

Dynamic libraries and code replacements in Swift

-
-

How to load a dynamic library and use native method swizzling in Swift? This article is all about the magic behind SwiftUI previews.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

Unsafe memory pointers in Swift

-
-

Learn how to use raw pointer references, interact with unsafe pointers and manually manage memory addresses in Swift.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

Memory layout in Swift

-
-

Start learning about how Swift manages, stores and references various data types and objects using a memory safe approach.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

How to use C libraries in Swift?

-
-

Learn how to use system libraries and call C code from Swift. Interoperability between the Swift language and C for beginners.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

Building static and dynamic Swift libraries using the Swift compiler

-
-

This tutorial is all about emitting various Swift binaries without the Swift package manager, but only using the Swift compiler.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

The Swift compiler for beginners

-
-

Learn how to build executable files using the swiftc command, meet the build pipeline, compilers and linkers under the hood.

- -
- Swift - Tooling -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Event-driven generic hooks for Swift

-
-

In this article I am going to show you how to implement a basic event processing system for your modular Swift application.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Getting started with SwiftIO

-
-

SwiftIO is an electronic circuit board that runs Swift on the bare metal. It can control sensors, displays, lights, motors and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Logging for beginners in Swift

-
-

Learn how to print variables to the debug console using different functions such as print, dump, NSLog and the unified os.log API.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

How to define strings, use escaping sequences and interpolations?

-
-

As a beginner it can be hard to understand String interpolation and escaping sequences, in this tutorial I'll teach you the basics.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Building and loading dynamic libraries at runtime in Swift

-
-

Learn how to create a plugin system using dynamic libraries and the power of Swift, aka. modular frameworks on the server-side.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

What's new in Swift 5.3?

-
-

Swift 5.3 is going to be an exciting new release. This post is a showcase of the latest Swift programming language features.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

The Swift package manifest file

-
-

This article is a complete Swift Package Manager cheatsheet for the package manifest file, using the latest Swift 5.2 tools version.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 12 min read -
- -

How to download files with URLSession using Combine Publishers and Subscribers?

-
-

Learn how to load a remote image into an UIImageView asynchronously using URLSessionDownloadTask and the Combine framework in Swift.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

Promises in Swift for beginners

-
-

Everything you ever wanted to know about futures and promises. The beginner's guide about asynchronous programming in Swift.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

How to use the result type to handle errors in Swift 5?

-
-

From this tutorial you can learn how to utilize the do-try-catch syntax with the brand new result type to handle errors in Swift.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Swift 5 and ABI stability

-
-

Apple's Swift 5 language version will be a huge milestone for the developer community, let's see what are the possible benefits of it.

- -
- Swift - Tooling -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Generating random numbers in Swift

-
-

Learn everything what you'll ever need to generate random values in Swift using the latest methods and covering some old techniques.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 11 min read -
- -

Ultimate Grand Central Dispatch tutorial in Swift

-
-

Learn the principles of multi-threading with the GCD framework in Swift. Queues, tasks, groups everything you'll ever need I promise.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

How to parse JSON in Swift using Codable protocol?

-
-

In this Swift tutorial, I'd like to give you an example about getting and parsing JSON data using URLSession and Codable protocol.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 11 min read -
- -

Deep dive into Swift frameworks

-
-

Learn everything about Swift modules, libraries, packages, closed source frameworks, command line tools and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

How to call C code from Swift

-
-

Interacting with C libraries from the Swift language is really amazing, from this post can learn the most of C interoperability.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

How to make a Swift framework?

-
-

Creating a Swift framework shouldn't be hard. This tutorial will help you making a universal framework for complex projects.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Swift enum all values

-
-

In this quick tutorial I'll show you how to get all the possible values for a Swift enum type with a generic solution written in Swift.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

Everything about public and private Swift attributes

-
-

Have you ever heard about Swift language attributes? In this article I'm trying to gather all the @ annotations and their meanings.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 12 min read -
- -

Result builders in Swift

-
-

If you want to make a result builder in Swift, this article will help you to deal with the most common cases when creating a DSL.

- -
- Swift -
-
-
-
- -
- - - -
- - - - diff --git a/docs/tags/swiftui/index.html b/docs/tags/swiftui/index.html deleted file mode 100644 index 5700ba7..0000000 --- a/docs/tags/swiftui/index.html +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - SwiftUI - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
- -
- SwiftUI -

SwiftUI

-

- -
- - - -

0 posts

- - Empty. -
- -
- - - -
- - - - diff --git a/docs/tags/tooling/index.html b/docs/tags/tooling/index.html deleted file mode 100644 index b6ac46c..0000000 --- a/docs/tags/tooling/index.html +++ /dev/null @@ -1,325 +0,0 @@ - - - - - - - - - - - - Tooling - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
- -
- Tooling -

Tooling

-

- -
- - - -

7 posts

- -
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Utilizing Makefiles for Swift projects

-
-

In this tutorial I'll show you how to use Makefiles for server-side Swift projects to help running utility tasks in a more simple way.

- -
- Tooling -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

The Swift compiler for beginners

-
-

Learn how to build executable files using the swiftc command, meet the build pipeline, compilers and linkers under the hood.

- -
- Swift - Tooling -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Custom working directory in Xcode

-
-

Learn how to set a custom working directory in Xcode to solve one of the most common beginner issue when using Vapor.

- -
- Tooling - Xcode -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

How to store keys in env files?

-
-

In this tutorial I'll show you how to save and load secret keys as base64 encoded strings using dotenv files in Vapor 4.

- -
- Server - Tooling - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Swift 5 and ABI stability

-
-

Apple's Swift 5 language version will be a huge milestone for the developer community, let's see what are the possible benefits of it.

- -
- Swift - Tooling -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

How to launch a macOS app at login?

-
-

In this tutorial I'll show you how to launch a completely sandboxed macOS application on system startup written in Swift.

- -
- Tooling - macOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Conventions for Xcode

-
-

Learn how to organize your codebase. If you are struggling with Xcode project structure, files, naming conventions, read this.

- -
- Tooling - Xcode -
-
-
-
- -
- - - -
- - - - diff --git a/docs/tags/tvos/index.html b/docs/tags/tvos/index.html deleted file mode 100644 index 5aca29a..0000000 --- a/docs/tags/tvos/index.html +++ /dev/null @@ -1,197 +0,0 @@ - - - - - - - - - - - - tvOS - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
- -
- tvOS -

tvOS

-

- -
- - - -

1 posts

- -
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Networking examples for appleOS

-
-

Learn how to use Bonjour, with UDP/TCP sockets, streams and how to communicate through CoreBluetooth or the watch APIs.

- - -
-
-
- -
- - - -
- - - - diff --git a/docs/tags/uikit/index.html b/docs/tags/uikit/index.html deleted file mode 100644 index 0a44cc2..0000000 --- a/docs/tags/uikit/index.html +++ /dev/null @@ -1,669 +0,0 @@ - - - - - - - - - - - - UIKit - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
- -
- UIKit -

UIKit

-

- -
- - - -

23 posts

- -
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

Working with diffable data sources and table views using UIKit

-
-

In this tutorial we're going to build a screen to allow single and multiple selections using diffable data source and a table view.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

UIKit - loadView vs viewDidLoad

-
-

When to use these methods? Common questions and answers about the iOS view hierarchy including memory management.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Picking and playing videos in Swift

-
-

Learn how to record or select a video file using a video picker controller and the AVPlayer class, written entirely in Swift 5.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Uniquely identifying views

-
-

Learn how to use string based UIView identifiers instead of tags. If you are tired of tagging views, check out these alternative solutions.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Styling by subclassing

-
-

Learn how to design and build reusable user interface elements by using custom view subclasses from the UIKit framework in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Picking images with UIImagePickerController in Swift 5

-
-

Learn how to get an image from the photo library or directly from the camera by using the UIImagePickerController class in Swift 5.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

UITableView tutorial in Swift

-
-

This guide is made for beginners to learn the foundations of the UITableView class programmatically with auto layout in Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

UICollectionView data source and delegates programmatically

-
-

In this quick UIKit tutorial I'll show you how to create a simple UICollectionView without Interface Builder, but only using Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Mastering iOS auto layout anchors programmatically from Swift

-
-

Looking for best practices of using layout anchors? Let's learn how to use the iOS autolayout system in the proper way using Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

UIColor best practices in Swift

-
-

Learn what are color models, how to convert hex values to UIColor and back, generate random colors, where to find beautiful palettes.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

iOS custom transition tutorial in Swift

-
-

In this tutorial, you'll learn how to replace the push, pop and modal animations with custom transitions & percent driven interactions.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 12 min read -
- -

Ultimate UICollectionView guide with iOS examples written in Swift

-
-

Learn how to use UICollectionView, with highly reusable UIKit components and some MVVM pattern without the going nuts with index path calculations.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

UICollectionView cells with circular images plus rotation support

-
-

Learn how to make rounded corners for UIImageView items wrapped inside collection view cells, with rotation support.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Self sizing cells with rotation support

-
-

How to make self sizing cells in Swift both for table & collection views supporting orientation changes and dynamic font types.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

iOS Auto Layout tutorial programmatically

-
-

In this great iOS Auto Layout tutorial I'll teach you how to support rotation, use constraints, work with layers, animate corner radius.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

How to create reusable views for modern collection views?

-
-

A quick intro to modern collection views using compositional layout, diffable data source and reusable view components.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

The ultimate Combine framework tutorial in Swift

-
-

Get started with the brand new declarative Combine framework in practice using Swift. I'll teach you all the goodies from zero to hero.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Top 20 iOS libraries written in Swift

-
-

I gathered the best open source Swift frameworks on github that will help you to speed up mobile application development in 2019.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

UIKit init patterns

-
-

Learn about the initialization process of the two well known classes in UIKit. Say hello to UIViewcontroller, and UIView init patterns.

- - -
-
-
- -
- - - -
- - - - diff --git a/docs/tags/vapor/index.html b/docs/tags/vapor/index.html deleted file mode 100644 index c832aa6..0000000 --- a/docs/tags/vapor/index.html +++ /dev/null @@ -1,832 +0,0 @@ - - - - - - - - - - - - Vapor - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
- -
- Vapor -

Vapor

-

- -
- - - -

30 posts

- -
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Running and testing async Vapor commands

-
-

In this article I'll show you how to build asynchronous Vapor commands and how to test them using ConsoleKit.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

The abstract Vapor service factory design pattern

-
-

In this tutorial I'm going to show you how you can create an abstract driver-based component for the Vapor framework.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

The repository pattern for Vapor 4

-
-

In this article I'm going to talk about the repository design pattern and give you a few Fluent ORM tips for your Vapor 4 app.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

How to write HTML in Swift?

-
-

This tutorial is all about rendering HTML docs using a brand new DSL library called SwiftHtml and the Vapor web framework.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

The future of server side Swift

-
-

What's going to happen with Swift on the Server in 2022? Distributed actors, Vapor 5, some predictions and wishes.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Building a global storage for Vapor

-
-

This tutorial is about a shared global storage that you can implement using a common design pattern in Vapor 4.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

Beginner's guide to the async/await concurrency API in Vapor & Fluent

-
-

Learn how to convert your existing EventLoopFuture based Vapor server app using the new async/await Swift feature.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

Declarative unit tests for Vapor

-
-

Learn how to test your server side Swift backend app in a declarative style using a lightweight library called Spec.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

How to design type safe RESTful APIs using Swift & Vapor?

-
-

Learn to make proper data transfer objects for CRUD operations and integrate them both into the client and server side API layer.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

File upload API server in Vapor 4

-
-

Learn how to build a very simple file upload API server using Vapor 4 and URLSession upload task on the client side.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

File upload using Vapor 4

-
-

Learn how to implement a basic HTML file upload form using the Leaf template engine and Vapor, all written in Swift of course.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

Swift on the Server in 2020

-
-

Why choose Swift as a backend language in 2020? What are the available frameworks to build your server? Let me guide you.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

How to store keys in env files?

-
-

In this tutorial I'll show you how to save and load secret keys as base64 encoded strings using dotenv files in Vapor 4.

- -
- Server - Tooling - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Table joins in Fluent 4

-
-

In this quick tutorial I'm going to show you how to join and query database models using the Fluent ORM framework in Vapor 4.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 13 min read -
- -

Websockets for beginners using Vapor 4 and Vanilla JavaScript

-
-

Learn how to create a websocket server using Swift & Vapor. Multiplayer game development using JavaScript in the browser.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 11 min read -
- -

Sign in with Apple using Vapor 4

-
-

A complete tutorial for beginners about how to implement the Sign in with Apple authentication service for your website.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

Server side Swift projects inside Docker using Vapor 4

-
-

Learn how to setup Vapor 4 projects inside a Docker container. Are you completely new to Docker? This article is just for you.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 16 min read -
- -

All about authentication in Vapor 4

-
-

Learn how to implement a user login mechanism with various auth methods using sessions, JWTs, written in Swift only.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

The anatomy of Vapor commands

-
-

Learn how to build and run your existing Vapor apps using various command line arguments, flags and environments.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

How to use middlewares in Vapor 4?

-
-

Learn how to create middlewares for a Vapor based server side Swift application to handle common routing functionalities.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

How to write Swift scripts using the new Command API in Vapor 4?

-
-

Shell scripts are essentials on the server side. Learn how to build Swift scripts for your backend apps using property wrappers.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 15 min read -
- -

Get started with the Fluent ORM framework in Vapor 4

-
-

Learn how to use the Fluent ORM framework. Migrations, schemas, relations powered by PostgreSQL, written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

How to set up pgSQL for Fluent 4?

-
-

This is a tutorial for beginners about using PostgreSQL. I'll show you how to automatically backup and restore the database.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 18 min read -
- -

Beginner's guide to Server side Swift using Vapor 4

-
-

Learn how to build and host your very first backend application using Vapor 4 and the brief history of server side Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

What's new in Vapor 4?

-
-

Vapor is the most popular server side Swift web application framework. This time we'll cover what's new in Vapor 4.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 13 min read -
- -

Asynchronous validation for Vapor

-
-

Learn how to validate input data using an async technique. Unified request validation API for your server side Swift app.

- -
- Server - Vapor -
-
-
-
- -
- - - -
- - - - diff --git a/docs/tags/viper/index.html b/docs/tags/viper/index.html deleted file mode 100644 index 1447d29..0000000 --- a/docs/tags/viper/index.html +++ /dev/null @@ -1,276 +0,0 @@ - - - - - - - - - - - - VIPER - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
- -
- VIPER -

VIPER

-

- -
- - - -

5 posts

- -
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

How to write services for VIPER?

-
-

Not everything is a VIPER module. In this article I'll show you how do I separate the service layer from the modules, using Swift.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

How to build SwiftUI apps using VIPER?

-
-

In this tutorial I'll show you how to combine SwiftUI with the VIPER architecture in a real world iOS application example.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Mastering the VIPER architecture

-
-

Learn how to master the VIPER architectural design pattern, with some protocol oriented programming techniques using Swift.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

VIPER best practices for iOS developers

-
-

In this tutorial I'm going to show you a complete guide about how to build a VIPER based iOS application, written entirely in Swift.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

The ultimate VIPER architecture tutorial

-
-

Learn how to write scalable iOS code using the VIPER architecture with some MVVM and MVC tricks and coordinators in mind.

- -
- VIPER -
-
-
-
- -
- - - -
- - - - diff --git a/docs/tags/visionos/index.html b/docs/tags/visionos/index.html deleted file mode 100644 index bd331a9..0000000 --- a/docs/tags/visionos/index.html +++ /dev/null @@ -1,197 +0,0 @@ - - - - - - - - - - - - visionOS - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
- -
- visionOS -

visionOS

-

- -
- - - -

1 posts

- -
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Networking examples for appleOS

-
-

Learn how to use Bonjour, with UDP/TCP sockets, streams and how to communicate through CoreBluetooth or the watch APIs.

- - -
-
-
- -
- - - -
- - - - diff --git a/docs/tags/watchos/index.html b/docs/tags/watchos/index.html deleted file mode 100644 index 9fc8ee9..0000000 --- a/docs/tags/watchos/index.html +++ /dev/null @@ -1,218 +0,0 @@ - - - - - - - - - - - - watchOS - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
- -
- watchOS -

watchOS

-

- -
- - - -

2 posts

- -
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

Networking examples for appleOS

-
-

Learn how to use Bonjour, with UDP/TCP sockets, streams and how to communicate through CoreBluetooth or the watch APIs.

- - -
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

ClockKit complications cheatsheet

-
-

ClockKit families and templates, there are so many of them. It's a little bit time consuming if you are looking for the right one.

- -
- watchOS -
-
-
-
- -
- - - -
- - - - diff --git a/docs/tags/xcode/index.html b/docs/tags/xcode/index.html deleted file mode 100644 index dc905b0..0000000 --- a/docs/tags/xcode/index.html +++ /dev/null @@ -1,236 +0,0 @@ - - - - - - - - - - - - Xcode - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
- -
- Xcode -

Xcode

-

- -
- - - -

3 posts

- -
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Custom working directory in Xcode

-
-

Learn how to set a custom working directory in Xcode to solve one of the most common beginner issue when using Vapor.

- -
- Tooling - Xcode -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 1 min read -
- -

Awesome native Xcode extensions

-
-

This is the biggest and the best collection of the currently available natively created source editor extensions for Xcode.

- -
- Xcode -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Conventions for Xcode

-
-

Learn how to organize your codebase. If you are struggling with Xcode project structure, files, naming conventions, read this.

- -
- Tooling - Xcode -
-
-
-
- -
- - - -
- - - - diff --git a/docs/the-abstract-vapor-service-factory-design-pattern/index.html b/docs/the-abstract-vapor-service-factory-design-pattern/index.html deleted file mode 100644 index 842986d..0000000 --- a/docs/the-abstract-vapor-service-factory-design-pattern/index.html +++ /dev/null @@ -1,359 +0,0 @@ - - - - - - - - - - - - The abstract Vapor service factory design pattern - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 4 min read - -
-

The abstract Vapor service factory design pattern

-
-

In this tutorial I'm going to show you how you can create an abstract driver-based component for the Vapor framework.

-
- -

I’ve written several articles about factory design patterns on my blog and this time I’d like to talk about a special one, which you can encounter if you work with Vapor. Here’s a little recap about my factory design pattern blog posts, all written in Swift:

Now let’s dive in to the “Fluent pattern”. In order to understand this architecture, first we should examine the related Swift packages first. There is the FluentKit library and several Fluent database driver implementations (SQLite, PostgreSQL, MySQL, etc.), all based on the FluentKit product. Also there is one package that connects Fluent with Vapor, this one is simply called: Fluent. 📀

  • FluentKit - contains the abstract interface (without Vapor, using SwiftNIO)
  • Fluent[xy]Driver - contains the implementation defined in FluentKit
  • Fluent - connects FluentKit with Vapor, by extending Vapor

This is the base structure, the FluentKit library provides the following abstract interfaces, which you have to implement if you want to create your own driver implementation. Unfortunately you won’t be able to find proper documentation for these interfaces, so I’ll explain them a bit:

  • Database - Query execution and transaction related functions
  • DatabaseContext - Holds the config, logger, event loop, history and page size limit
  • DatabaseDriver - A factory interface to create and shutdown Database instances
  • DatabaseID - A unique ID to store database configs, drivers and instances
  • DatabaseError - A generic database related error protocol
  • DatabaseConfiguration - A protocol to create DatabaseDriver objects
  • DatabaseConfigurationFactory - A box-like object to hide driver related stuff
  • Databases - Shared config, driver and running instance storage

As you can see there are many protocols involved in this architecture, but I’ll try to walk you through the entire driver creation flow and hopefully you’ll be able to understand how the pieces are related, and how can build your own drivers or even Vapor components based on this.

Fluent is written as a service for Vapor using the underlying shared storage object, this is what stores a reference to the Databases instance. This object has two hash maps, for storing configurations and running driver instances using the DatabaseID as a key for both. 🔑

When you ask for a driver, the Databases object will check if that driver exists, if yes, it’ll simply return it and story over. The interesting part happens when the driver does not exists yet in the Databases storage. First the system will check for a pre-registered driver implementation.

app.databases.use(.sqlite(.file("db.sqlite")), as: .sqlite)
-

This line above registers a new driver configuration for the shared Databases. The .sqlite() method is a static function on the DatabaseConfigurationFactory which creates a new SQLite specific configuration and hides it using the init(make:) call. The SQLite related configuration implements the DatabaseConfiguration protocol, so it can be used as a valid config when the system creates the actual database context.

The config object is also responsible for creating the specific driver object using the Databases object if needed. At this point we’ve got a configuration and a driver instance registered in the databases storage. What happens if someone asks for a database instance?

Depending on the context, you can ask for a Database implementation through the app.db or req.db properties. This is defined in the FluentProvider code and behind the scenes everything can be traced back to the Databases class. Since you only want to have a single shared storage for all the drivers, but you also want to avoid the singleton pattern, you should hook this service up to the Application class. This is how the Vapor folks did it anyway. 🤓

let db: Database = req.db
-let db: Database = req.db(.sqlite)
-
-let db: Database = app.db
-let db: Database = app.db(.sqlite)
-

When you ask for a database, or a database with an explicit identifier, you are essentially calling a make method inside the Databases class, which is going look for a registered configuration and a driver implementation using the hashes and it’ll call the driver’s make method and pass around the logger, the event loop and the current database configuration as a database context object.

We can say that after you ask for an abstract Database driver, a new DatabaseDriver instance reference (associated with a given DatabaseID) will be stored inside the Databases class and it’ll always make you a new Database reference with the current DatabaseContext. If the driver already exists, then it’ll be reused, but you still get new Database references (with the associated context) every time. So, it is important to note that there is only one DatabaseDriver instance per configuration / database identifier, but it can create multiple Database objects. 🤔

Ok, I know, it’s quite complicated, but here’s an oversimplified version in Swift:

final class Databases {
-    var configs: [DatabaseID: DatabaseConfiguration] = [:]
-    var drivers: [DatabaseID: DatabaseDriver] = [:]
-
-    func make(
-        _ id: DatabaseID,
-        logger: Logger,
-        on eventLoop: EventLoop
-    ) -> Database {
-        let config = configs[id]!
-
-        if drivers[id] == nil {
-            drivers[id] = config.make(self)
-        }
-        let context = DatabaseContext(config, logger, eventLoop)
-        return drivers[id]!.make(context)
-    }
-
-    func use(_ config: DatabaseConfiguration, for id: DatabaseID) {
-        configs[id] = config
-    }
-}
-

And the Vapor service extension could be interpreted somewhat like this:

extension Application {
-
-    var databases: Databases {
-        get {
-            if storage[DatabasesKey.self] == nil {
-                storage[DatabasesKey.self] = .init()
-            }
-            return storage[DatabasesKey.self]
-        }
-        set {
-            self.storage[MyConfigurationKey.self] = newValue
-        }
-    }
-
-    var db: Database {
-        databases.make(
-            .default, 
-            logger: logger, 
-            eventLoop: eventLoopGroup.next()
-        )
-    }
-}
-

You can apply the same principles and create an extension over the Request object to access a Database instance. Of course there’s a lot more happening under the hood, but the purpose of this article is to get a basic overview of this pattern, so I’m not going into those details now. 🙃

Honestly I really like this approach, because it’s elegant and it can completely hide driver specific details through these abstractions. I followed the exact same principles when I created the Liquid file storage driver for Vapor and learned a lot during the process. Although, you should note that not everything is a good candidate for being implemented an “abstract Vapor service factory” design pattern (or whatever we call this approach). Anyway, I really hope that this quick tutorial will help you to create your own Vapor components, if needed. 🤷‍♂️

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - -
-
- -
- - - -
- - - - diff --git a/docs/the-anatomy-of-vapor-commands/index.html b/docs/the-anatomy-of-vapor-commands/index.html deleted file mode 100644 index 6943fb0..0000000 --- a/docs/the-anatomy-of-vapor-commands/index.html +++ /dev/null @@ -1,400 +0,0 @@ - - - - - - - - - - - - The anatomy of Vapor commands - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 3 min read - -
-

The anatomy of Vapor commands

-
-

Learn how to build and run your existing Vapor apps using various command line arguments, flags and environments.

-
- -

The Vapor toolbox

The very first thing I want to show you (again) is the Vapor toolbox command line application. It’s a nice little convenient tool for initializing new Vapor applications from scratch. You can use it to build, run, update, test and even deploy (to Heroku) your project.

# create & run a new project
-vapor new myProject
-cd myProject
-vapor build
-vapor run
-

Personally I’m not using it too much, except when I create a new project. I’d love to generate additional “boilerplate” code for controllers, models using the toolbox, but unfortunately this feature is not implemented yet. The loopback-cli is a great example tho… 🙏

You can run vapor --help to see all the available commands.

Serve

Every server needs to listen for incoming requests on some port. The serve command starts the Vapor application and fires up the HTTP server. You can specify the hostname and the port using some additional flags. The bind flag combines the hostname and port flags into one, they both have short and long versions, feel free to pick your favorite command format. 😉

# by default Vapor runs the serve command
-swift run Run
-
-# the serve command starts the server
-swift run Run serve
-swift run Run serve --hostname "localhost" --port 8080
-swift run Run serve -h "localhost" -p 8080
-swift run Run serve --bind "localhost:8080"
-swift run Run serve -b "localhost:8080"
-

You should know that this is the default command, so if you simply run your app without any arguments, the serve command will be executed behind the scenes. 💀

Migrate

When you work with databases using Fluent, you need a schema first. You can only populate the database with actual data after the main structure exists. This process is called migration. You’ll also have to migrate your database if you change something in your Fluent code (for example you introduce a new field for a model). You can perform a migration by running:

# run Fluent migrations
-swift run Run migrate
-
-# run migrations without the confirmation
-swift run Run migrate --auto-migrate
-
-# revert last migration
-swift run Run migrate --revert
-

The cli will show you what needs to be done in order to keep your DB up-to-date. You can double check everything one more time before you proceed, or you can skip the entire confirmation dialog by using the --auto-migrate option. Be extremely careful with auto migrations! ⚠️

Log levels

You might have noticed that there are a bunch of Vapor messages in your console. Well, the good news is that you can filter them by log level. There are two ways of doing this. The first option is to provide a log flag with one of the following values:

  • trace
  • debug
  • info
  • notice
  • warning
  • error
  • critical

The --log flag has no short variant, don’t try to use -l.

If you want to trace, debug and info logs, you can run the app like this:

# set log level
-swift run Run --log notice
-

The second option is to set a LOG_LEVEL variable before you run the app.

# set log level using a variable
-LOG_LEVEL=notice swift run Run
-
-# set log level using an exported environmental variable
-export LOG_LEVEL=notice
-swift run Run
-# unset log level
-unset LOG_LEVEL
-

The exported variable will be around until you close the terminal window or you remove it.

Environment

Every Vapor application can run in development or production mode. The default mode is development, but you can explicitly set this using the command line:

# .env.development
-DB_URL="postgres://myuser:mypass@localhost:5432/mydb"
-
-# run in development mode using the .env.development file
-swift run Run --env development
-swift run Run -e dev
-
-# .env
-DB_URL="postgres://realuser:realpass@localhost:5432/realdb"
-
-# run in production mode using the .env file
-swift run Run --env production
-swift run Run -e prod
-

It is possible to store environmental variables in a dot env file. The .env.development file will be loeaded in development mode and the .env file in production mode. You can also use the .env.testing file for the test environment.

You can also override environmental variables with a local variable, like the way we defined the LOG_LEVEL before. So let’s say if you have a DB_URL in your production .env file, but you still want to use the dev database, you can run Vapor like this:

DB_URL="postgres://myuser:mypass@localhost:5432/mydb" swift run Run --env production
-

Environment variables are super cool, you should play around with them to get familiar.

Routes

This is very handy command to quickly display all the connected endpoints that your app has.

# prints all the routes information
-swift run Run routes
-
-# +-----+----+
-# | GET    | /              |
-# +-----+----+
-# | GET    | /hello/        |
-# +-----+----+
-# | GET    | /todos         |
-# +-----+----+
-# | POST   | /todos         |
-# +-----+----+
-# | DELETE | /todos/:todoID |
-# +-----+----+
-

If you need more info about how routing works in Vapor 4, you should check the official docs.

Boot

Honestly: I’ve never used the boot command before, but it’s there. 🤷‍♂️

# boots the app providers & exists
-swift run Run boot
-

Can somebody tell me a use case for this?

Custom commands

It is possible to write your custom commands using the brand new Command API in Vapor 4. If you are interested in writing Swift scripts, you should continue reading the linked article. 📚

There are lots of other Swift compiler flags (e.g. -Xswiftc -g to make Backtrace.print() work) that you can use during the build process. If you are interested in those please let me know and maybe I’ll make an article about it in the not so distant future.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/the-future-of-server-side-swift/index.html b/docs/the-future-of-server-side-swift/index.html deleted file mode 100644 index b296968..0000000 --- a/docs/the-future-of-server-side-swift/index.html +++ /dev/null @@ -1,395 +0,0 @@ - - - - - - - - - - - - The future of server side Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

The future of server side Swift

-
-

What's going to happen with Swift on the Server in 2022? Distributed actors, Vapor 5, some predictions and wishes.

-
- -

The new Swift concurrency model

One of the greatest thing about Swift 5.5 is definitely the new concurrency model, which introduced quite a lot of new features and APIs. The implementation of the async / await proposal allows us completely eliminate the need of unnecessary closures and completion handlers. Actors are great for isolating data, they can prevent data races and protect you from unwanted memory issues too. With the structured concurrency features we’re able to define tasks, we can form dependencies between them and they even have built-in cancellation support.

With these features added we can say that Swift is a great language for writing concurrent code, but what’s missing? Well, of course there is always room for improvements and in this case I have some features that I’d love to see coming to Swift. 🤔

For example currently there is no way to define an executor object for an actor. This would be a great addition for SwiftNIO and many more server side related projects, because it’d heavily reduce the overhead of context switching. An actor with a custom executor could have an event loop and this way it would be possible to ensure that all the future calls are tied to the exact same event loop.

The other thing I’d like to mention is called distributed actors, this feature is definitely going to come to Swift in the near future. Distributed actors allow developers to scale their programs beyond a single process or node, this means that your code can run on multiple processes or even multiple machines by taking advantage of location transparency. Honestly, I don’t know much about distributed actors yet, but I can imagine that this is going to be a game-changer feature. 😍

I know this is just the beginning of a new era, but still the new concurrency model change quite a lot about how we build our programs. Async / await is extremely powerful and as we move forward and learn more about actors our Swift apps will get even better, through the built-in safety features that they provide. Building reliable apps is a must and I really like this direction that we’re heading.

On the road to Vapor 5

Vapor 4 is amazing, but what are the next steps for the web framework? You can find out a little bit more about the future of Vapor by joining the official discord server, there is a vapor-5 channel where people already started to throw in ideas about the next major release.

Personally, I’d like to see some minor changes about Vapor, but I’d like to see a major API redesign for Fluent. Currently Fluent Models are working like repositories and they also provide the structural definition for the database schemas. Sorry to say, but I hate this approach. I believe that the schema definition should be completely separated from the queried models. For example:

import Vapor
-import Fluent
-
-struct TodoCreate: Codable {
-    let name: String
-    let isCompleted: Bool
-}
-
-struct TodoList: Codable {
-    let id: UUID
-    let name: String
-    let isCompleted: Bool
-}
-
-struct TodoSchema: DatabaseSchema {
-
-    var name: String = "todos"
-
-    var definition = Definition {
-        Migration(id: "v1") {
-            Process {
-                CreateSchema(name) {
-                    Field(type: .id)
-                    Field(type: .string, .required, key: "name")
-                    Field(type: .bool, .required, key: "isComplete")
-                    // Unique(on: "title")
-                }
-            }
-            Revert {
-                DeleteSchema(name)
-            }
-        }
-        Migration(id: "seed") {
-            Process {
-                CreateRecords(schema: name) {
-                    TodoCreate(name: "foo", isComplete: true)
-                }
-            }
-            Revert {
-                DeleteRecords(schema: name)
-            }
-        }
-    }
-}
-
-struct TodoRepository: DatabaseRepository {
-    typealias Create = TodoCreate
-    typealias List = TodoList
-}
-
-extension TodoList: Content {}
-
-func someAsyncRequestHandler(_ req: Request) async throws -> [TodoList] {
-    let object = TodoCreate(name: "bar", isCompleted: false)
-    try await TodoRepository.create(object, on: req.db) 
-    return try await TodoRepository.findAll(on: req.db) 
-}
-

As you can see instead of mixing up the Model definition with migration related info this way the schema definition could have its own place and the database repository could take care of all the querying and record alteration features. It would be nice to have a DSL-like approach for migrations, since I don’t see any benefits of passing around that stupid database pointer. 😅

Maybe you think, hey you’re crazy this idea is stupid, but still my real-world experience is that I need something like this in the future, so yeah, hopefully the core team will see this post and get some inspiration for their future work. Maybe it’s too late and they don’t want to include such drastic changes, but who knows, I can still hope & wish for such things, right?

My other secret wish is the ability to dynamically reset a Vapor app, because in order to enable and disable a module I’d have to remove all the registered routes, middlewares, commands and migrations from the system. Currently this is just partially possible, but I really hope that the core team will provide some kind of open API that’d let me do this.

import Vapor
-
-public extension Application {
-    func reset() {
-        app.middleware.storage = []
-        app.routes.all = []
-        app.migrations.storage = [:]
-        app.commands.commands = [:]
-    }
-}
-
-try app.reset()
-

If this was possible I could load a dylib and provide a proper install, update, delete mechanism through a module manager. This would allow Feather CMS to open a module store and install extensions with just a single click, that’d be HUGE, so please give me this API. 🙏

Anyway, these are just my wishes, Vapor 5 will be a great release I’m quite sure about that, one more additional thing is that I’d like to see is to reduce the size of the core library (opt-out from websockets, console and multipart libs?, merge async-kit with the core?), it’d be nice to completely drop event loop future based APIs and drop the Async* prefixes. That’s all I’d like to see.

Feather CMS

So, after a bit more than one and a half year of development, now I’m getting ready to release an actual version of my content management system. I’ve had several ups and downs, personal issues during this period of time, but I never stopped thinking about Feather. 🪶

The main idea and purpose is to provide a reliable type-safe modular CMS, written entirely in Swift. The long term goal is to build a dynamic module system, just like the Wordpress plugin ecosystem and I’d be able to install and remove components with just a single click, without the need of recompiling the code. This is why I’ve researched so much about dylibs and frameworks. This is the reason why I’m using hook functions and why I’m trying to encapsulate everything inside a module. The good news is that modules will have public API libraries so the server side code can be shared with clients (mostly iOS, but the API code can be easily converted into another languages).

What are the problems that Feather tries to solve?

  • There is no easy to use backend (API) system for mobile apps.
  • Building admin interfaces on top of a set of APIs is a pain in the ass.
  • API definitions are not shared with the client at all (leads to issues)
  • Backend developers don’t update API docs properly (or they don’t write it at all)
  • There is no API / CMS with proper user permission & role management
  • Swift is resource (low memory footprint) and cost effective on the server

Hopefully with Feather I’ll be able to tackle a few of these issues from the list. Please remember, that this is just my point of view, of course there are many great examples out there and I’ve seen properly written systems using node.js, golang or PHP. I don’t mind using other technologies, I’m a heavy Wordpress user and I like JavaScript too, but I can also see the potential in Swift. 💪

I’d love to see a future where more and more people could use backends written in Swift, maybe even using Feather CMS. I know that changing things will take time and I also know that people don’t like changes, but I really hope that they’ll realize the importance of Swift.

We are living in a world where resources are limited and by using a more efficient language we could lower our ecological footprint. With the current chip shortage, we should really thik about this. The M1 CPU and Swift could take over the servers and we could drastically reduce the cost that we have to pay for our backend infrastructures. In 10 years I really wish to look back to this period of time as the beginning of the server side Swift era, but who knows, we’ll see. 🤐

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/the-repository-pattern-for-vapor-4/index.html b/docs/the-repository-pattern-for-vapor-4/index.html deleted file mode 100644 index b34b740..0000000 --- a/docs/the-repository-pattern-for-vapor-4/index.html +++ /dev/null @@ -1,568 +0,0 @@ - - - - - - - - - - - - The repository pattern for Vapor 4 - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 7 min read - -
-

The repository pattern for Vapor 4

-
-

In this article I'm going to talk about the repository design pattern and give you a few Fluent ORM tips for your Vapor 4 app.

-
- -

Fluent is essentially broken

The more I use the Fluent ORM framework the more I realize how hard it is to work with it. I’m talking about a particular design issue that I also mentioned in the future of server side Swift article. I really don’t like the idea of property wrappers and abstract database models.

What’s the problem with the current database model abstraction? First of all, the optional ID property is confusing. For example you don’t have to provide an identifier when you insert a record, it can be an nil value and the ORM system can create a unique identifier (under the hood using a generator) for you. So why do we have an id for create operations at all? Yes, you might say that it is possible to specify a custom identifier, but honestly how many times do we need that? If you want to identify a record that’s going to be something like a key, not an id field. 🙃

Also this optional property can cause some other issues, when using fluent you can require an id, which is a throwing operation, alternatively you can unwrap the optional property if you’re sure that the identifier already exists, but this is not a safe approach at all.

My other issue is related to initializers, if you define a custom model you always have to provide an empty init() {} method for it, otherwise the compiler will complain, because models have to be classes. BUT WHY? IMHO the reason relates to this issue: you can query the database models using the model itself. So the model acts like a repository that you can use to query the fields, and it also represents the the record itself. Isn’t this against the clean principles? 🤔

Okay, one last thing. Property wrappers, field keys and migrations. The core members at Vapor told us that this approach will provide a safe way to query my models and I can be sure that field keys won’t be messed up, but I’m actually struggling with versioning in this case. I had to introduce a v1, v2, vN structure both for the field keys and the migration, which actually feels a bit worse than using raw strings. It is over-complicated for sure, and it feels like the schema definition is mixed up with the actual query mechanism and the model layer as well.

Sorry folks, I really appreciate the effort that you’ve put into Fluent, but these issues are real and I know that you can fix them on the long term and make the developer experience a lot better.

How to make Fluent a bit better?

On the short term I’m trying to fix these issues and fortunately there is a nice approach to separate the query mechanism from the model layer. It is called the repository pattern and I’d like to give a huge credit to 0xTim again, because he made a cool answer on StackOverlow about this topic.

Anyway, the main idea is that you wrap the Request object into a custom repository, it’s usually a struct, then you only call database related queries inside this specific object. If we take a look at at the default project template (you can generate one by using the vapor toolbox), we can easily create a new repository for the Todo models.

import Vapor
-import Fluent
-
-struct TodoRepository {
-    var req: Request
-    
-    /// initialize the repository with a request object
-    init(req: Request) {
-        self.req = req
-    }
-    
-    /// query the Todo models using the req.db property
-    func query() -> QueryBuilder<Todo> {
-        Todo.query(on: req.db)
-    }
-    
-    /// query the models and filter by an identifier
-    func query(_ id: Todo.IDValue) -> QueryBuilder<Todo> {
-        query().filter(\.$id == id)
-    }
-    
-    /// query the models and filter by multiple identifiers
-    func query(_ ids: [Todo.IDValue]) -> QueryBuilder<Todo> {
-        query().filter(\.$id ~~ ids)
-    }
-
-    /// list all the available Todo items
-    func list() async throws -> [Todo] {
-        try await query().all()
-    }
-    
-    /// get one Todo item by an identifier if it exists
-    func get(_ id: Todo.IDValue) async throws -> Todo? {
-        try await get([id]).first
-    }
-
-    /// get the list of the Todo items by multiple identifiers
-    func get(_ ids: [Todo.IDValue]) async throws -> [Todo] {
-        try await query(ids).all()
-    }
-
-    /// create a Todo model and return the updated model (with an id)
-    func create(_ model: Todo) async throws -> Todo {
-        try await model.create(on: req.db)
-        return model
-    }
-    
-    /// update a Todo model
-    func update(_ model: Todo) async throws -> Todo {
-        try await model.update(on: req.db)
-        return model
-    }
-
-    /// delete a Todo item based on the identifier
-    func delete(_ id: Todo.IDValue) async throws {
-        try await delete([id])
-    }
-
-    /// delete multiple Todo items based on id values
-    func delete(_ ids: [Todo.IDValue]) async throws {
-        try await query(ids).delete()
-    }
-}
-

That’s how we are can manipulate Todo models, from now on you don’t have to use the static methods on the model itself, but you can use an instance of the repository to alter your database rows. The repository can be hooked up to the Request object by using a common pattern. The most simple way is to return a service every time you need it.

import Vapor
-
-extension Request {
-    
-    var todo: TodoRepository {
-        .init(req: self)
-    }
-}
-

Of course this is a very basic solution and it pollutes the namespace under the Request object, I mean, if you have lots of repositories this can be a problem, but first let me show you how to refactor the controller by using this simple method. 🤓

import Vapor
-
-struct TodoController: RouteCollection {
-
-    func boot(routes: RoutesBuilder) throws {
-        let todos = routes.grouped("todos")
-        todos.get(use: index)
-        todos.post(use: create)
-        todos.group(":todoID") { todo in
-            todo.delete(use: delete)
-        }
-    }
-
-    func index(req: Request) async throws -> [Todo] {
-        try await req.todo.list()
-    }
-
-    func create(req: Request) async throws -> Todo {
-        let todo = try req.content.decode(Todo.self)
-        return try await req.todo.create(todo)
-    }
-
-    func delete(req: Request) async throws -> HTTPStatus {
-        guard let id = req.parameters.get("todoID", as: Todo.IDValue.self) else {
-            throw Abort(.notFound)
-        }
-        try await req.todo.delete(id)
-        return .ok
-    }
-}
-

As you can see this way we were able to eliminate the Fluent dependency from the controller, and we can simply call the appropriate method using the repository instance. Still if you want to unit test the controller it is not possible to mock the repository, so we have to figure out something about that issue. First we need some new protocols.

public protocol Repository {
-    init(_ req: Request)
-}
-
-public protocol TodoRepository: Repository {
-    func query() -> QueryBuilder<Todo>
-    func query(_ id: Todo.IDValue) -> QueryBuilder<Todo>
-    func query(_ ids: [Todo.IDValue]) -> QueryBuilder<Todo>
-    func list() async throws -> [Todo]
-    func get(_ ids: [Todo.IDValue]) async throws -> [Todo]
-    func get(_ id: Todo.IDValue) async throws -> Todo?
-    func create(_ model: Todo) async throws -> Todo
-    func update(_ model: Todo) async throws -> Todo
-    func delete(_ ids: [Todo.IDValue]) async throws
-    func delete(_ id: Todo.IDValue) async throws
-}
-

Next we’re going to define a shared repository registry using the Application extension. This registry will allow us to register repositories for given identifiers, we’ll use the RepositoryId struct for this purpose. The RepositoryRegistry will be able to return a factory instance with a reference to the required request and registry service, this way we’re going to be able to create an actual Repository based on the identifier. Of course this whole ceremony can be avoided, but I wanted to come up with a generic solution to store repositories under the req.repository namespace. 😅

public struct RepositoryId: Hashable, Codable {
-
-    public let string: String
-    
-    public init(_ string: String) {
-        self.string = string
-    }
-}
-
-public final class RepositoryRegistry {
-
-    private let app: Application
-    private var builders: [RepositoryId: ((Request) -> Repository)]
-
-    fileprivate init(_ app: Application) {
-        self.app = app
-        self.builders = [:]
-    }
-
-    fileprivate func builder(_ req: Request) -> RepositoryFactory {
-        .init(req, self)
-    }
-    
-    fileprivate func make(_ id: RepositoryId, _ req: Request) -> Repository {
-        guard let builder = builders[id] else {
-            fatalError("Repository for id `\(id.string)` is not configured.")
-        }
-        return builder(req)
-    }
-    
-    public func register(_ id: RepositoryId, _ builder: @escaping (Request) -> Repository) {
-        builders[id] = builder
-    }
-}
-
-public struct RepositoryFactory {
-    private var registry: RepositoryRegistry
-    private var req: Request
-    
-    fileprivate init(_ req: Request, _ registry: RepositoryRegistry) {
-        self.req = req
-        self.registry = registry
-    }
-
-    public func make(_ id: RepositoryId) -> Repository {
-        registry.make(id, req)
-    }
-}
-
-public extension Application {
-
-    private struct Key: StorageKey {
-        typealias Value = RepositoryRegistry
-    }
-    
-    var repositories: RepositoryRegistry {
-        if storage[Key.self] == nil {
-            storage[Key.self] = .init(self)
-        }
-        return storage[Key.self]!
-    }
-}
-
-public extension Request {
-    
-    var repositories: RepositoryFactory {
-        application.repositories.builder(self)
-    }
-}
-

As a developer you just have to come up with a new unique identifier and extend the RepositoryFactory with your getter for your own repository type.

public extension RepositoryId {
-    static let todo = RepositoryId("todo")
-}
-
-public extension RepositoryFactory {
-
-    var todo: TodoRepository {
-        guard let result = make(.todo) as? TodoRepository else {
-            fatalError("Todo repository is not configured")
-        }
-        return result
-    }
-}
-

We can now register the FluentTodoRepository object, we just have to rename the original TodoRepository struct and conform to the protocol instead.

// repository file
-public struct FluentTodoRepository: TodoRepository {
-    var req: Request
-    
-    public init(_ req: Request) {
-        self.req = req
-    }
-    
-    func query() -> QueryBuilder<Todo> {
-        Todo.query(on: req.db)
-    }
-
-    // ... same as before
-}
-
-// configure.swift
-app.repositories.register(.todo) { req in
-    FluentTodoRepository(req)
-}
-

We’re going to be able to get the repository through the req.repositories.todo property. You don’t have to change anything else inside the controller file.

import Vapor
-
-struct TodoController: RouteCollection {
-
-    func boot(routes: RoutesBuilder) throws {
-        let todos = routes.grouped("todos")
-        todos.get(use: index)
-        todos.post(use: create)
-        todos.group(":todoID") { todo in
-            todo.delete(use: delete)
-        }
-    }
-
-    func index(req: Request) async throws -> [Todo] {
-        try await req.repositories.todo.list()
-    }
-
-    func create(req: Request) async throws -> Todo {
-        let todo = try req.content.decode(Todo.self)
-        return try await req.repositories.todo.create(todo)
-    }
-
-    func delete(req: Request) async throws -> HTTPStatus {
-        guard let id = req.parameters.get("todoID", as: Todo.IDValue.self) else {
-            throw Abort(.notFound)
-        }
-        try await req.repositories.todo.delete(id)
-        return .ok
-    }
-}
-

The best part of this approach is that you can simply replace the FluentTodoRepository with a MockTodoRepository for testing purposes. I also like the fact that we don’t pollute the req.* namespace, but every single repository has its own variable under the repositories key.

You can come up with a generic DatabaseRepository protocol with an associated database Model type, then you could implement some basic features as a protocol extension for the Fluent models. I’m using this approach and I’m quite happy with it so far, what do you think? Should the Vapor core team add better support for repositories? Let me know on Twitter. ☺️

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/the-swift-compiler-for-beginners/index.html b/docs/the-swift-compiler-for-beginners/index.html deleted file mode 100644 index eb8f68e..0000000 --- a/docs/the-swift-compiler-for-beginners/index.html +++ /dev/null @@ -1,430 +0,0 @@ - - - - - - - - - - - - The Swift compiler for beginners - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 6 min read - -
-

The Swift compiler for beginners

-
-

Learn how to build executable files using the swiftc command, meet the build pipeline, compilers and linkers under the hood.

-
- -

Compiling Swift source files

The most basic scenario is when you want to build and run a single Swift file. Let’s create a main.swift file somewhere on your disk and print out a simple “Hello world!” text.

print("Hello world!")
-

We don’t even need to import the Foundation framework, Swift has quite a lot built-in language functions and the print function is part of the Swift standard library.

The standard library provides a “base layer” of functionality for writing Swift applications, on the other hand the Foundation framework gives you OS independent extra functions, core utilities (file management, localization, etc.) and more.

So, how do we turn our print function into an executable file that we can run? The Swift compiler (swiftc command) can compile (translate human readable code into machine code) Swift source files into binary executable files that you can run. 🔨

# compile the `main.swift` source file into a `main` binary file
-swiftc main.swift 
-
-# run the `main` executable, prints "Hello world!"
-./main
-

This is the most basic example, you can also specify the name of the output file by using the -o parameter. Of course this is an optional parameter, by default the compiler will use the basename of the Swift source that you are trying to build, that’s why we were able to run the executable with the ./main command in the previous example.

swiftc main.swift -o hello
-./hello
-

There are lots of other flags and arguments that you can use to control the compilation process, you can check the available options with the -h or --help flag.

swiftc -h
-

Don’t worry you don’t have to understand any of those, we’ll cover some of the compiler flags in this tutorial, others in a more advanced article. 😉

Swift compiler flags

Sometimes you might want to create custom flags and compile parts of your code if that flag is present. The most common one is the DEBUG flag. You can define all kinds of compiler flags through the -D argument, here’s a quick main.swift example file.

#if(DEBUG)
-    print("debug mode")
-#endif
-print("Hello world!")
-

Now if you run the swiftc command it will only print “Hello world!” again, but if we add a new special parameter.

swiftc main.swift -D DEBUG
-./main
-
-# or we can run this as a one-liner
-swiftc main.swift -D DEBUG && ./main
-

This time the “debug mode” text will be also printed out. Swift compiler flags can only be present or absent, but you can also use other flags to change source compilation behavior. 🐞

Mutliple Swift sources

What happens if you have multiple Swift source files and you want to compile them to a single binary? Let me show you an example real quick. Consider the following point.swift file:

struct Point {
-    let x: Int
-    let y: Int
-}
-

Now in the main.swift file, you can actually use this newly defined Point struct. Please note that these files are both located under the same namespace, so you don’t have to use the import keyword, you can use the struct right away, it’s an internal object.

#if(DEBUG)
-    print("debug mode")
-#endif
-let p = Point(x: 4, y: 20)
-
-print("Hello world!", p.x, p.y)
-

We can compile multiple sources by simply listing them one after other when using the swiftc command, the order of the files doesn’t matter, the compiler is smart enough, so it can figure out the object dependencies between the listed sources.

swiftc point.swift main.swift -o point-app
-# prints: Hello world! 4 20
-./point-app
-

You can also use the find command to list all the Swift sources in a given directory (even with a maximum search depth), and pass the output to the swiftc command. 🔍

swiftc `find . -name "*.swift" -maxdepth 1` -o app-name
-
-# alternatively
-find . -name "*.swift" -maxdepth 1 | xargs swiftc -o app-name
-

The xargs command is also handy, if you don’t like to evaluate shell commands through the backtick syntax (```) you can use it to pass one command output to another as an argument.

Under the hood of swiftc

I just mentioned that the compiler is smart enough to figure out object dependencies, but how does swiftc actually works? Well, we can see the executed low-level instructions if we compile our source files using the verbose -v flag. Let’s do so and examine the output.

swiftc -D DEBUG point.swift main.swift -o point-app
-
-# swiftc -v -D DEBUG point.swift main.swift -o point-app && ./point-app
-# Apple Swift version 5.3.2 (swiftlang-1200.0.45 clang-1200.0.32.28)
-# Target: arm64-apple-darwin20.3.0
-
-# /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift \
-#   -frontend \
-#   -c \
-#   -primary-file point.swift main.swift \
-#   -target arm64-apple-darwin20.3.0 \
-#   -Xllvm -aarch64-use-tbi \
-#   -enable-objc-interop \
-#   -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/#Developer/SDKs/MacOSX11.1.sdk \
-#   -color-diagnostics \
-#   -D DEBUG \
-#   -target-sdk-version 11.1 \
-#   -module-name main \
-#   -o /var/folders/7d/m4wk_5195mvgt9sf8j8541n80000gn/T/point-99f33d.o
-
-# /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift \
-#   -frontend \
-#   -c point.swift \
-#   -primary-file main.swift \
-#   -target arm64-apple-darwin20.3.0 \
-#   -Xllvm -aarch64-use-tbi \
-#   -enable-objc-interop \
-#   -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
-#   -color-diagnostics \
-#   -D DEBUG \
-#   -target-sdk-version 11.1 \
-#   -module-name main \
-#   -o /var/folders/7d/m4wk_5195mvgt9sf8j8541n80000gn/T/main-e09eef.o
-
-# /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld \
-#   /var/folders/7d/m4wk_5195mvgt9sf8j8541n80000gn/T/point-99f33d.o \
-#   /var/folders/7d/m4wk_5195mvgt9sf8j8541n80000gn/T/main-e09eef.o \
-#   /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/clang/lib/darwin/libclang_rt.osx.a \
-#   -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
-#   -lobjc \
-#   -lSystem \
-#   -arch arm64 \
-#   -L /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx \
-#   -L /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk/usr/lib/swift \
-#   -platform_version macos 11.0.0 11.1.0 \
-#   -no_objc_category_merging \
-#   -o point-app
-

You might think, this is a mess, I reformatted the output a bit, so we can walk through the steps of the Swift source compilation process.

When you compile a program code with multiple sources, each and every source needs to be converted to machine code (compiler), then those converted files needs to be put together (linker), this way we can get our final executable file. This entire process is called build pipeline and you should definitely read the linked article if you want to know more about it. 👍

The swiftc command calls the “real Swift compiler” (swift -frontend) to turn every single swift file into an object file (.o). Every command, function, (class, object etc.) that you write when you create a Swift file needs to be resolved. This is because your machine needs to look up the actual implementation of the components in your codebase. For example when you call the print(“Hello world!”) line, the print function needs to be resolved to an actual system call, the function itself is located somewhere inside an SDK that is usually shipped with your operating system.

Where exactly? For the compiler, it doesn’t matter. The Software Development Kit (SDK) usually contains interfaces (header files or module maps) for specific functionalities. The compiler only needs the interface to build byte code from source files, the compiler doesn’t cares about the implementation details. The compiler trusts the interface and builds intermediate object files for a given platform using the flags and other parameters that we don’t care about for now. 🙃

This is what happens in the first two section. The swift command turns the point.swift file into a temporary point.o file, then it does the exact same thing with the main.swift file. If you take a closer look, apart from the long paths, it’s a pretty simple command with just a few arguments:

swift \
-   -frontend \
-   -c point.swift \
-   -primary-file main.swift \
-   -target arm64-apple-darwin20.3.0 \
-   -Xllvm -aarch64-use-tbi \
-   -enable-objc-interop \
-   -sdk MacOSX11.1.sdk \
-   -color-diagnostics \
-   -D DEBUG \
-   -target-sdk-version 11.1 \
-   -module-name main \
-   -o main.o
-

As you can see we just tell Swift to turn our primary input file into an intermediate output file. Of course the whole story is way more complicated involving the LLVM compiler infrastructure, there is a great article about a brief overview of the Swift compiler, that you should read if you want more details about the phases and tools, such as the parser, analyzer etc. 🤔

Compilers are complicated, for now it’s more than enough if you take away this one simple thing about the Swift compiler: it turns your source files into intermediate object files.

Before we could run our final program code, those temporary object files needs to be combined together into a single executable. This is what linkers can do, they verify object files and resolve underlying dependencies by linking together various dependencies.

Dependencies can be linked together in a static or dynamic way. For now lets just stay that static linking means that we literally copy & paste code into the final binary file, on the other hand dynamic linking means that libraries will be resolved at runtime. I have a pretty detailed article about Swift frameworks and related command line tools that you can use to examine them.

In our case the linker command is ld and we feed it with our object files.

ld \
-    point.o \
-    main.o \
-    libclang_rt.osx.a \
-   -syslibroot MacOSX11.1.sdk \
-   -lobjc \
-   -lSystem \
-   -arch arm64 \
-   -L /usr/lib/swift/macosx \
-   -L /MacOSX11.1.sdk/usr/lib/swift \
-   -platform_version macos 11.0.0 11.1.0 \
-   -no_objc_category_merging \
-   -o point-app
-

I know, there are plenty of unknown flags involved here as well, but in 99% of the cases you don’t have to directly interact with these things. This whole article is all about trying to understand the “dark magic” that produces games, apps and all sort of fun things for our computers, phones and other type of gadgets. These core components makes possible to build amazing software. ❤️

Just remember this about the linker (ld command): it will use the object files (prepared by the compiler) and it’ll create the final product (library or executable) by combining every resource (object files and related libraries) together.

It can be real hard to understand these things at first sight, and you can live without them, build great programs without ever touching the compiler or the linker. Why bother? Well, I’m not saying that you’ll become a better developer if you start with the basics, but you can extend your knowledge with something that you use on a daily basis as a computer programmer. 💡

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/the-swift-package-manifest-file/index.html b/docs/the-swift-package-manifest-file/index.html deleted file mode 100644 index c461257..0000000 --- a/docs/the-swift-package-manifest-file/index.html +++ /dev/null @@ -1,477 +0,0 @@ - - - - - - - - - - - - The Swift package manifest file - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 7 min read - -
-

The Swift package manifest file

-
-

This article is a complete Swift Package Manager cheatsheet for the package manifest file, using the latest Swift 5.2 tools version.

-
- -

If you want to learn how to use the Swift Package Manager you should read my other article, because that is more like an introduction for those who have never worked with SPM yet.

Package types

There are multiple package types that you can create with the swift package init command. You can specify the --type flag with the following values: empty, library, executable, system-module, manifest. You can also define a custom package name through the --name flag.

  • The empty package will create the default file structure without the sample code files.
  • The library type will create a reusable library product template.
  • The executable type will create a Swift application with an executable product definition in the package and a main.swift file as a starting point.
  • The system-module type will create a wrapper around a system provided package, such as libxml, we’ll talk about this later on.
  • The manifest type will only create a Package.swift file without anything else.

The Package manifest file

Every single SPM project has this special file inside of it called Package.swift. I already wrote a post about how the package manager and the Swift toolchain works behind the scenes, this time we’re going to focus only on the manifest file itself. Let’s get started. 📦

Every single Package.swift file begins with a special comment line where you have to define the version of the used Swift tools. The latest version is quite different from the older ones.

// swift-tools-version:5.2
-

Next you have to import the PackageDescription framework in order to define your Swift package. This framework contains the package manifest structure as Swift objects.

import PackageDescription
-

That’s it now you are ready to describe the package itself. Oh by the way you can change the version of the used tools, you can read more about this in the Package Manager usage docs.

Package

A package is just a bunch of Swift (or other) files. The manifest file is the description of what and how to build from those sources. Every single package should have a name, but this is not enough to actually generate something from it. You can only have exactly one package definition inside the file. This is the shortest and most useless one that you can create. 🙈

let package = Package(name: "myPackage")
-

The package name is going to be used when you are importing packages as dependencies, so name your pacages carefully. If you choose a reserved name by a system framework there can be issues with linking. If there’s a conflict you have to use static linking instead of dynamic. If you generate a project via the swift package generate-xcodeproj command that project will try to link everything dynamically, but if you open the Package.swift file using Xcode 11, the dependencies will be linked statically if this was not set explicitly in the product definition section.

Platform

A platform is basically an operating system with a given version that you can support.

let package = Package(
-    name: "myPackage",
-    platforms: [
-        .iOS(.v13),         //.v8 - .v13
-        .macOS(.v10_15),    //.v10_10 - .v10_15
-        .tvOS(.v13),        //.v9 - .v13
-        .watchOS(.v6),      //.v2 - .v6
-    ]
-)
-

When you add a platform you are putting a constraint on it via the required version. Every single dependency should match the requirement of the main package platforms. Long story short if you need to add support for Apple platforms, you should specify a platform flag with a supported version, otherwise SPM will use the oldest deployment target based on the installed SDK, except for macOS, that’s going to be v10_10. Every package has Linux support by default, you can’t add such restrictions yet, but maybe this will change in the near future, also Windows is coming.

Product

A package can have one or more final products (build artifacts). Currently there are two types of build products: executables and libraries. The executable is a binary that can be executed, for example this can be a command line application. A library is something that others can use, it is basically the public API product representation on your targets.

// swift-tools-version:5.2
-import PackageDescription
-
-let package = Package(name: "myPackage", products: [
-    .library(name: "myPackageLib", targets: ["myPackageLib"]),
-    .library(name: "myPackageStaticLib", type: .static, targets: ["myPackageLib"]),
-    .library(name: "myPackageDynLib", type: .dynamic, targets: ["myPackageLib"]),
-    .executable(name: "myPackageCli", targets: ["myPackage"])
-], targets: [
-    .target(name: "myPackageLib"),
-    .target(name: "myPackageCli"),
-])
-

If the library type is unspecified, the Package Manager will automatically choose it based on the client’s preference. As I mentioned this earlier generated Xcode projects prefer dynamic linking, but if you simply open the manifest file the app will be statically linked.

Dependency

Packages can rely on other packages. You can define your dependencies by specifying a local path or a repository URL with a given version tag. Adding a dependency into this section is not enough to use it in your targets. You also have to add the product provided by the package at the target level.

let package = Package(
-    name: "myPackage",
-    dependencies: [
-        .package(path: "/local/path/to/myOtherPackage"),
-        .package(url: "<git-repository-url>", from: "1.0.0"),
-        .package(url: "<git-repository-url>", .branch("dev")),
-        .package(url: "<git-repository-url>", .exact("1.3.2")),
-        .package(url: "<git-repository-url>", .revision("<hash>")),
-        .package(url: "<git-repository-url>", .upToNextMajor(from: "1.0.0")),
-        .package(url: "<git-repository-url>", .upToNextMinor(from: "1.0.0")),
-        .package(url: "<git-repository-url>", "1.0.0"..<"1.3.0"),
-    ]
-)
-

The URL can be a GitHub URL, fortunately you can add private repositories as well by using an ssh key based authentication. Just use the git@github.com:BinaryBirds/viper-kit.git URL format, instead of the HTTP based, if you want to add private packages. 🤫

Target

A target is something that you can build, in other words it’s a build target that can result in a library or an executable. You should have at least one target in your project file otherwise you can’t build anything. A target should always have a name, every other settings is optional.

Settings

There are many settings that you can use to configure your target. Targets can depend on other targets or products defined in external packages. A target can have a custom location, you can specify this by setting the path attribute. Also you can exclude source files from the target or explicitly define the sources you want to use. Targets can have their own public headers path and you can provide build settings both for the C, C++ and the Swift language, and compiler flags.

.target(name: "myPackage",
-        dependencies: [
-            .target(name: "other"),
-            .product(name: "package", package: "package-kit")
-        ],
-        path: "./Sources/myPackage",
-        exclude: ["foo.swift"],
-        sources: ["main.swift"],
-        publicHeadersPath: "./Sources/myPackage/headers",
-        cSettings: [
-            .define("DEBUG"),
-            .define("DEBUG", .when(platforms: [.iOS, .macOS, .tvOS, .watchOS], configuration: .debug)),
-            .define("DEBUG", to: "yes-please", .when(platforms: [.iOS], configuration: .debug)),
-            .headerSearchPath(""),
-            .headerSearchPath("", .when(platforms: [.android, .linux, .windows], configuration: .release)),
-            .unsafeFlags(["-D EXAMPLE"]),
-            .unsafeFlags(["-D EXAMPLE"], .when(platforms: [.iOS], configuration: .debug)),
-        ],
-        cxxSettings: [
-            // same as cSettings
-        ],
-        swiftSettings: [
-            .define("DEBUG"),
-            .define("DEBUG", .when(platforms: [.iOS, .macOS, .tvOS, .watchOS], configuration: .debug)),
-            .unsafeFlags(["-D EXAMPLE"]),
-            .unsafeFlags(["-D EXAMPLE"], .when(platforms: [.iOS], configuration: .debug)),
-        ],
-        linkerSettings: [
-            .linkedFramework("framework"),
-            .linkedLibrary("framework", .when(platforms: [.iOS], configuration: .debug)),
-            .linkedLibrary("library"),
-            .linkedLibrary("library", .when(platforms: [.macOS], configuration: .release)),
-            .unsafeFlags(["-L example"]),
-            .unsafeFlags(["-L example"], .when(platforms: [.linux], configuration: .release)),
-        ]),
-

As you can see you can define preprocessor macros for every single language. You can use the safe cases for basic stuff, but there is an unsafeFlags case for the reckless ones. The nice thing is that you can support a platform condition filter including build configuration to every single settings as the last param.

Available platforms are:

  • .iOS
  • .macOS
  • .watchOS
  • .tvOS
  • .android
  • .linux
  • .windows

The build configuration can be .debug or .release

Test targets

Test targets are used to define test suites. They can be used to unit test other targets using the XCTest framework. They look like exactly the same as regular targets.

.testTarget(name: String,
-    dependencies: [Target.Dependency],
-    path: String?,
-    exclude: [String],
-    sources: [String]?,
-    cSettings: [CSetting]?,
-    cxxSettings: [CXXSetting]?,
-    swiftSettings: [SwiftSetting]?,
-    linkerSettings: [LinkerSetting]?)
-

I think the only difference between a target and a test target is that you can run a test target using the swift test command, but from a structural point of view, they are basically the same.

Package configs and system libraries

You can wrap an existing system library using Swift, the beauty of this is that you can use packages written in C, CPP or other languages. I’ll show you a quick example through the amazing Kanna(鉋) - XML/HTML parser repository. I’m using this tool a lot, thanks for making it Atsushi Kiwaki. 🙏

// https://github.com/tid-kijyun/Kanna/tree/master/Modules
-#if swift(>=5.2) && !os(Linux)
-let pkgConfig: String? = nil
-#else
-let pkgConfig = "libxml-2.0"
-#endif
-
-#if swift(>=5.2)
-let providers: [SystemPackageProvider] = [
-    .apt(["libxml2-dev"])
-]
-#else
-let providers: [SystemPackageProvider] = [
-    .apt(["libxml2-dev"]),
-    .brew(["libxml2"])
-]
-#endif
-
-let package = Package(name: "Kanna",
-pkgConfig: "",
-providers: [
-  .apt(["libsqlite-dev"]),
-  .brew(["sqlite3"])
-],
-products: [
-  .library(name: "Kanna", targets: ["Kanna"])
-],
-targets: [
-.target(name: "myPackage"),
-.systemLibrary(name: "libxml2",
-               path: "Modules",
-               pkgConfig: pkgConfig,
-               providers: providers)
-])
-

There is a module definition file at the Modules directory. You’ll need a module.modulemap file to export a given library, you can read more about Modules on the LLVM website.

module libxml2 [system] {
-    link "xml2"
-    umbrella header "libxml2-kanna.h"
-    export *
-    module * { export * }
-}
-

You can define your own umbrella header and tell the system what to import.

#import <libxml2/libxml/HTMLtree.h>
-#import <libxml2/libxml/xpath.h>
-#import <libxml2/libxml/xpathInternals.h>
-

I barely use system libraries, but this is a good reference point. Anyways, if you need to wrap a system library I assume that you’ll have the required knowledge to make it happen. 😅

Language settings

You can also specify the list of Swift verisons that the package is compatible with. If you are creating a package that contains C or C++ code you can tell the compiler to use a specific language standard during the build process.

//supported Swift versions
-swiftLanguageVersions: [.v4, .v4_2, .v5, .version("5.1")],
-
-//.c89, .c90, .iso9899_1990, .iso9899_199409, .gnu89, .gnu90, .c99, .iso9899_1999, .gnu99, .c11, .iso9899_2011, .gnu11
-cLanguageStandard: .c11,
-
-//.cxx98, .cxx03, .gnucxx98, .gnucxx03, .cxx11, .gnucxx11, .cxx14, .gnucxx14, .cxx1z, .gnucxx1z
-cxxLanguageStandard: .gnucxx11)
-

You can see all the currently available options in the comments. I don’t know how many of you use these directives, but personally I never had to work with them. I’m not writing too much code from the C language family nowadays, but it’s still good that SPM has this option built-in. 👍

Summary

The Swift Package Manager is not the perfect tool just yet, but it’s on a good track to become the de facto standard by slowly replacing CocoaPods and Carthage. There are still some missing features that are essentials for most of the developers. Don’t worry, SPM will improve a lot in the near future. For example the binary dependency and resource support is coming alongside Swift 5.3. You can track the package evolution process on the official Swift Evolution dashboard.

You can read more about the Package Manager on the official Swift website, but it’s quite obsolate. The documentation on Apple’s website is also very old, but still useful. There is a good read me file on GitHub about the usage of the Swift Package Manager, but nothing is updated frequently. 😢

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/the-ultimate-combine-framework-tutorial-in-swift/index.html b/docs/the-ultimate-combine-framework-tutorial-in-swift/index.html deleted file mode 100644 index 9700f8b..0000000 --- a/docs/the-ultimate-combine-framework-tutorial-in-swift/index.html +++ /dev/null @@ -1,566 +0,0 @@ - - - - - - - - - - - - The ultimate Combine framework tutorial in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 7 min read - -
-

The ultimate Combine framework tutorial in Swift

-
-

Get started with the brand new declarative Combine framework in practice using Swift. I'll teach you all the goodies from zero to hero.

-
- -

What is Combine?

Customize handling of asynchronous events by combining event-processing operators. - Apple’s Combine Framework

In other words, it allows you to write functional reactive code in a declarative way using Swift. Functional reactive programming (FRP) is a special paradigm used to deal with asynchronous code. It’s a special kind of functional programming, where you are working with async streams of values. So basically you can process and transform values over time using functional methods like map, flatMap, etc. Combine is the “native” Swift implementation of this programming paradigm, made by Apple.

Publishers, Operators, Subscribers

I already made a brief networking example of using Combine, which is good if you’re just looking for a simple code snippet to simplify your URLSession requests. Allow me to grab one example and paste it here again, I’ll show you why… 🤔

private var cancellable: AnyCancellable?
-//...
-self.cancellable = URLSession.shared.dataTaskPublisher(for: url)
-.map { $0.data }
-.decode(type: [Post].self, decoder: JSONDecoder())
-.replaceError(with: [])
-.eraseToAnyPublisher()
-.sink(receiveValue: { posts in
-    print(posts.count)
-})
-//...
-self.cancellable?.cancel()
-

The most important thing here is the new dataTaskPublisher method. It creates Publisher that can send (aka. publish) sequences of values over time.

Moving forward to the next few lines we can see examples of various Operator functions (map, decode, replaceError, ereaseToAnyPublisher). They are special functional methods and they always return a Publisher. By using operators you can chain a bunch of publishers together, this gives us that nice declarative syntax that I mentioned before. Functional programming is awesome! 😎

The final member of the Combine family is the Subscriber. Since we can publish all sort of things, we can assume that on the other end of the publisher chain, there will be some sort of object that’s going to use our final result. Staying with our current example, the sink method is a built-in function that can connect a publisher to a subscriber. You’ll learn the other one later on… hint: assign.

Benefits of using the Combine framework

I believe that Combine is a huge leap forward and everyone should learn it. My only concern is that you can only use it if you are targeting iOS 13 or above, but this will fade away (in a blink) with time, just like it was with collection and stack views.

Do you remember iOS 6? Yeah, next up: iOS 14!!!

Anyway, there are a bunch of goodies that Combine will bring you:

  • Simplified asynchronous code - no more callback hells
  • Declarative syntax - easier to read and maintain code
  • Composable components - composition over inheritance & reusability
  • Multi-platform - except on Linux, we’re good with SwiftNIO’s approach
  • Cancellation support - it was always an issue with Promises
  • Multithreading - you don’t have to worry about it (that much)
  • Built-in memory management - no more bags to carry on

This is the future of aysnc programming on Apple plaftorms, and it’s brighter than it was ever before. This is one of the biggest updates since the completely revamped GCD framework API in Swift. Oh, by the way you might ask the question…

GCD vs Combine vs Rx vs Promises

My advice is to stay with your current favorite solution for about one year (but only if you are happy with it). Learn Combine and be prepared to flip the switch, if the time comes, but if you are just starting a new project and you can go with iOS13+ then I suggest to go with Combine only. You will see how amazing it is to work with this framework, so I if you are still not convinced, it’s time to…

Learn Combine by example

Since there are some great articles & books about using Combine, I decided to gather only those practical examples and patterns here that I use on a regular basis.

Built-in publishers

There are just a few built-in publishers in the Foundation framework, but I think the number will grow rapidly. These are the ones that I used mostly to simplify my code:

Timer

You can use Combine to get periodic time updates through a publisher:

var cancellable: AnyCancellable?
-
-// start automatically
-cancellable = Timer.publish(every: 1, on: .main, in: .default)
-.autoconnect()
-.sink {
-    print($0)
-}
-
-// start manually
-let timerPublisher = Timer.publish(every: 1.0, on: RunLoop.main, in: .default)
-cancellable = timerPublisher
-.sink {
-    print($0)
-}
-
-// start publishing time
-let cancellableTimerPublisher = timerPublisher.connect()
-// stop publishing time
-//cancellableTimerPublisher.cancel()
-
-// cancel subscription
-//cancellable?.cancel()
-

You can start & stop the publisher any time you need by using the connect method.

Combine has built-in support for cancellation. Both the sink and the assign methods are returning an object that you can store for later and you can call the cancel method on that AnyCancellable object to stop execution.

NotificationCenter

You can also subscribe to notifications by using publishers.

extension Notification.Name {
-    static let example = Notification.Name("example")
-}
-
-class ViewController: UIViewController {
-
-    var cancellable: AnyCancellable?
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        self.cancellable = NotificationCenter.Publisher(center: .default, name: .example, object: nil)
-        .sink { notification in
-            print(notification)
-        }
-
-        //post notification
-        NotificationCenter.default.post(name: .example, object: nil)
-    }
-}
-

If you save the cancellable object as a stored property you can retain the subscription until you call the cancel method. Make sure you don’t make extra retain cycles, so if you need self inside the sink block, always use aweak or unowned reference.

URLSession

I’m not going to repeat myself here again, because I already made a complete tutorial about how to use URLSession with the Combine framework, so please click the link if you want to learn more about it.

That’s it about built-in publishers, let’s take a look at…

Published variables

Property Wrappers are a brand new feature available from Swift 5.1. Combine comes with one new wrapper called @Published, which can be used to attach a Publisher to a single property. If you mark the property as @Published, you can subscribe to value changes and you can also use these variables as bindings.

import UIKit
-import Combine
-
-class ViewController: UIViewController {
-
-    @IBOutlet weak var textLabel: UILabel!
-    @IBOutlet weak var actionButton: UIButton!
-
-    @Published var labelValue: String? = "Click the button!"
-
-    var cancellable: AnyCancellable?
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        self.cancellable = self.$labelValue.receive(on: DispatchQueue.main)
-                                           .assign(to: \.text, on: self.textLabel)
-
-    }
-
-    @IBAction func actionButtonTouched(_ sender: UIButton) {
-        self.labelValue = "Hello World!"
-    }
-}
-

By using the $ sign and the assign function we can create a binding and subscribe to value changes, so if the labelValue property changes, it’ll be assigned to the text property of the textLabel variable. In other words, the actual text of the label will be updated on the user interface. Also you only want to get updates on the main queue, since we’re doing UI related stuff. You can use the receive operator for this.

Custom publishers

Creating a custom publisher is not so hard that you might think, but honestly I never had to make one for myself yet. Still there are some really nice use-cases where building a custom publisher is the right way to go. Antoine v.d. SwiftLee has a great tutorial about how to create a custom combine publisher to extend UIKit, you should definitely check that out if you want to learn more about custom publishers.

Subjects

A subject can be used to transfer values between publishers and subscribers.

let subject = PassthroughSubject<String, Never>()
-
-let anyCancellable = subject
-.sink { value in
-    print(value)
-}
-
-// sending values to the subject
-subject.send("Hello")
-
-// subscribe a subject to a publisher
-let publisher = Just("world!")
-publisher.subscribe(subject)
-
-anyCancellable.cancel()
-
-
-// sending errors
-enum SubjectError: LocalizedError {
-    case unknown
-}
-let errorSubject = PassthroughSubject<String, Error>()
-errorSubject.send(completion: .failure(SubjectError.unknown))
-

You can send values or errors to the subject manually or you can subscribe a publisher to a subject. They are extremely useful if you’d like to make a Combine-like interface for a traditional delegate pattern based API. Consider the following example as a very basic starting point, but I hope you’ll get the idea. 💡

class LocationPublisher: NSObject {
-
-    let subject = PassthroughSubject<[CLLocation], Error>()
-
-    //...
-}
-
-extension LocationPublisher: CLLocationManagerDelegate {
-
-    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
-        self.subject.send(locations)
-    }
-
-    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
-        self.subject.send(completion: .failure(error))
-    }
-}
-

Futures and promises

I already have a tutorial for beginners about promises in Swift, if you need to understand the reasoning behind these types, please read that article first.

Combine has it’s own future / promise implementation, which is surprisingly well-made. I use them very often if I have an async callback block, I usually transform that function into a promisified version (returning a publisher), by using a future.

func asyncMethod(completion: ((String) -> Void)) {
-    //...
-}
-
-func promisifiedAsyncMethod() -> AnyPublisher<String, Never> {
-    Future<String, Never> { promise in
-        asyncMethod { value in
-            promise(.success(value))
-        }
-    }
-    .eraseToAnyPublisher()
-}
-

Just

Just is made from a generic result type and a Never failure type. It just provides you a single value, then it will terminate. It’s quite useful if you want to fallback to a default value, or you just want to return a value.

let just = Just<String>("just a value")
-
-just.sink(receiveCompletion: { _ in
-
-}) { value in
-    print(value)
-}
-

Schedulers

You can add a delay to a publisher by using a scheduler, for example if you’d like to add a 1 second delay, you can use the following snippet:

return Future<String, Error> { promise in
-    promise(.success("example"))
-}
-.delay(for: .init(1), scheduler: RunLoop.main)
-.eraseToAnyPublisher()
-

Error handling

As I mentioned before the Never type is indicates no errors, but what happens if a publisher returns an actual error? Well, you can catch that error, or you can transform the error type into something else by using the mapError operator.

// error handling in sink
-errorPublisher
-.sink(receiveCompletion: { completion in
-    switch completion {
-    case .finished:
-        break
-    case .failure(let error):
-        fatalError(error.localizedDescription)
-    }
-}, receiveValue: { value in
-    print(value)
-})
-
-
-// mapError, catch
-_ = Future<String, Error> { promise in
-    promise(.failure(NSError(domain: "", code: 0, userInfo: nil)))
-}
-.mapError { error in
-    //transform the error if needed
-    return error
-}
-.catch { error in
-    Just("fallback")
-}
-.sink(receiveCompletion: { _ in
-
-}, receiveValue: { value in
-    print(value)
-})
-

Of course this is just the tip of the iceberg, you can assert errors and many more, but I hardly use them on a daily basis. Usually I handle my errors in the sink block.

Debugging

You can use the handleEvents operator to observe emitted events, the other option is to put breakpoints into your chain. There are a few helper methods in order to do this, you should read this article about debugging Combine if you want to know more. 👍

// handle events
-.handleEvents(receiveSubscription: { subscription in
-
-}, receiveOutput: { output in
-
-}, receiveCompletion: { completion in
-
-}, receiveCancel: {
-
-}, receiveRequest: { request in
-
-})
-
-// breakpoints
-.breakpoint()
-
-.breakpoint(receiveSubscription: { subscription in
-    true
-}, receiveOutput: { output in
-    true
-}, receiveCompletion: { completion in
-    true
-})
-
-.breakpointOnError()
-

Groups and dependencies

I have examples for both cases in my other article about Combine & URLSession, so please go and read that if you’d like to learn how to zip together two publishers.

Conclusion

Combine is a really nice framework, you should definitively learn it eventually. It’s also a good opportunity to refactor your legacy / callback-based code into a nice modern declarative one. You can simply transform all your old-school delegates into publishers by using subjects. Futures and promises can help you to move away from callback blocks and prefer publishers instead. There are plenty of good resources about Combine around the web, also the official documentation is real good. 📖

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/the-ultimate-viper-architecture-tutorial/index.html b/docs/the-ultimate-viper-architecture-tutorial/index.html deleted file mode 100644 index 1c28f56..0000000 --- a/docs/the-ultimate-viper-architecture-tutorial/index.html +++ /dev/null @@ -1,381 +0,0 @@ - - - - - - - - - - - - The ultimate VIPER architecture tutorial - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 9 min read - -
-

The ultimate VIPER architecture tutorial

-
-

Learn how to write scalable iOS code using the VIPER architecture with some MVVM and MVC tricks and coordinators in mind.

-
- -

Swift design patterns and iOS architectures

A software design pattern is basically a generic template of how to solve a particular - but usually local - situation. Achitectural patterns have bigger impact on the whole codebase, they are high level generic templates. Please remember one thing:

there is no such thing as a bad architecture

The weapon of choice only depends on the situation, but you know everything is relative. Let’s walk through all the iOS design patterns and architectures real quick and start learning VIPER. 🐍

Swift design patterns

Let’s start with the basics, right? If we don’t get into UIKit, we can find that there are many design patterns invented, maybe you know some of them already. But hey, since we don’t have that much time and I’d like to talk about VIPER, let’s check out the basic principle of building UIKit apps using the MVC pattern.

MVC

The Model-View-Controller (Massive-View-Controller) pattern is a basic concept. You have usually a huge UIViewController subclass that controls all the views and collects every model that needed to be displayed for the end user. For example you call an API endpoint using URLSession or Alamofire from the controller, do the response data validation and formatting then you implement your table or collection view delegates on the view controller, so basically all the application logic goes inside that single overstuffed miserable view controller class. Does this ring a bell for you? 🙄

MVVM

After realizing the problem, the first thing that you can do is outsourcing the data transforming or binding part to a separate class. This is how the smart people at Microsoft invented the Model-View-ViewModel architecture pattern. Now you’re one step closer, your data models and the views can have their “get together” on a whole new level inside shiny new files far-far away from controller land. However this pattern will not clean up all the leftovers inside the view controller. Remember that you still have to feed the view controller with data, handle all the different states.

MVP

What if we move out all the data loading and presentation stuff from the view controller and put it into a new class magically called the Presenter? Sounds good, the view controller can own the new presenter instance and we can live happily ever after. Come on people we should really rename this to the Most Valuable Pattern ever! 😉

The Coordinator pattern

Say hello to The coordinator by Soroush Khanlou. Or should I simply call this as the Inverse Model View Presenter pattern? Look, here is the deal, coordinators are on a whole new level inside of this evolution progress, but they also have too much to do. It’s against the Single Responsibility principle, because now you have to manage the presentation context, the data storage, the routing and all the different states inside coordinators or sub-coordinators… but, finally your view controller is free from all the leftover baggage and it can focus directly on it’s job, which is? 🙃

To be fucking dumb.

Presenting views using UIKit related stuff, and forwarding events.

I don’t hate the design patters from above, I’m just simply trying to point out (in a funny / sarcastic way) why VIPER was born on the first place. 😅

Are you still with me? 😬

The VIPER architecture

First of all DO NOT believe that VIPER is bad, just because someone misused it. I think it’s a freaking amazing architecture! You just have to learn it properly, which is hard, because of the lack of good tutorials. Everyone is comparing architectures, but that’s not what people should do. As far as I can see, an MVP is good for a small app with a few screens, you should never use VIPER for those apps. The real problem starts if you app grows and more and more components get into the game.

If you are planning to write a small app, just start with MVC. Later on you can fix the massive view controller problem with MVVM, but if you want to take it one level further you can always use MVP or the coordinator pattern to keep maintainability. Which is completely fine, until you realize one day that your code is stuffed with utility classes, managers, handlers and all the nonsense objects. Sounds familiar? 😅

As I mentioned this before there is no such thing as a bad architecture. There are only bad choices, which lead us to hardly maintainable codebases. So let me guide you through the most useful design pattern that you’ll ever want to know in order to write truly scalable iOS applications: VIPER with module builders = VIPER(B)

Understanding VIPER

The VIPER architecture is based on the single responsibility principle (S.O.L.I.D.) which leads us to the theory of a clean architecture. The core components or let’s say layers of a VIPERB module are the following ones:

View

It’s the interface layer, which means UIKit files, mostly UIViewController subclasses and all the other stuff. Views don’t do anything that’s related to business logic, they are just a presentation and event forwarding layer which is used by the presenter. Because the view is just a pure view controller, you can use MVVM principles or data managers to make your project even more concise.

Interactor

The interactor is responsible for retrieving data from the model layer, and its implementation is completely independent of the user interface. It’s important to remember that data managers (network, database, sensor) are not part of VIPER, so they are treated as separate components (services), coming outside from the VIPER module land and they can be injected as dependencies for interactors.

The Interactor can prepare or transform data, that’s coming from the service layer. For example it can do some sorting or filtering before asking the proper network service implementation to request or save the data. But remember that the Interactor doesn’t know the view, so it has no idea how the data should be prepared for the view, that’s the role of the Presenter. 🙄

Presenter

UIKit independent class that prepares the data in the format required by the view and take decisions based on UI events from the view, that’s why sometimes it’s referred as an event handler. It’s the core class of a VIPER module, because it also communicates with the Interactor and calls the router for wire-framing (aka. to present a new module or dismiss the current one).

It’s the only class that communicates with almost all the other components. That’s the ONLY job of the presenter, so it should not know anything about UIKit or low level data models. Basically it’s the heart of the application, or some would say it’s the place where all the business logic gets implemented. 💜

Entity

Plain model classes used mostly by the interactor. Usually I’m defining them outside the VIPER module structure (in the service layer), because these entities are shared across the system. We could separate them by module, but usually I don’t like that approach because e.g. all the CoreData models can be generated into one place. Same thing applies if you are using Swagger or a similar tool.

Router

The navigation logic of the application using UIKit classes. For example if you are using the same iPhone views in a iPad application, the only thing that might change is how the router builds up the structure. This allows you to keep everything else, but the Router untouched. It also listens for navigation flow changes from the presenter, so it’ll display the proper screen if needed. Also if you need to open an external URL call UIApplication.shared.openURL(URL) inside the Router because that’s also a routing action, the same logic applies for social media sharing using UIActivityViewController.

Also if you have to pass data between VIPER modules it feels like a right place to do this in the router. I usually communicate between two module using a delegate pattern, so I picked up this habit of calling delegate functions in the router. 📲

Builder

Some people are using the router to build the whole module, but I don’t like that approach. That’s why I’m always using a separate module builder class. It’s only responsibility is to build the complete module by using dependency injection for all the external services. It can also build mock or other versions of the same module. That’s quite helpful if it comes to unit testing. Totally makes sense. 👍

NOT everything is a VIPER module

For example if you want to create a generic subclass from a UIViewWhatever please don’t try to stuff that into the components above. You should create a place outside your VIPER modules folder and put it there. There will be some use cases with specific classes that are better not to be VIPERized! 😉

Services and application specific code

I usually have 3 separate layers in my applications. Modules, services, and app. All the VIPER modules are sitting inside the Modules folder. Everything that’s network or data related goes to the Services folder (API service, core data service, location service, etc.) and later on gets used in the module builder depending the current environment (for example mock implementation for testing). All the remaining stuff like view subclassess, and other UI related objects, app specific styling or design wise things are placed inside the App directory.

How to write VIPER code?

I can’t emphasize enough how important is to learn this architecture before you start using it. I believe that things can go real bad if someone misunderstands VIPER and start putting view logic in a presenter for example. If you had a previous bad experience with VIPER, think about this quote: don’t blame the tool, blame the carpenter (just as Ilya Puchka wisely said on a twitter conversation). 🔨

Every single component will just get into the right place if you follow the rules of VIPER.

Module generation

Never start to create a VIPER module by hand, you should always use a code generator, because (unfortunately) you’ll need lots of boilerplate code for each module. That seems quite unfortunate at first sight, but this is what gives the true power of this architecture. All members of your developer team will know where to look for if a specific issue occurs. If it’s a view issue, you have to fix the view, if it comes to a navigation problem then it’s a router problem.

There are many existing code generator solutions (one of the famous is Generamba), but I made my own little Swift tool for generating VIPER modules.

Naming conventions

Protocols are defined for almost every VIPER component. Every protocol will be prefixed with the module name, and it won’t have any other suffix except from the layer name (like MyModuleRouter, MyModulePresenter).

Default implementation is used for the basic scenario, every protocol implementation follows the ModuleName+Default+Layer naming convention. So for example MyModuleDefaultRouter or MyModuleDefaultPresenter.

Inter-module communication using delegates

The flow is something like this:

Router / Presenter

The presenter can send events for the router using the router protocol definition.

Presenter / Interactor

The interactor can notify the presenter through the presenter’s interface, and the presenter can call the interactor using the defined methods inside the interactor protocol.

Presenter / View

The view usually has setter methods to update it’s contents defined on the view protocol. It can also notify the presenter of incoming or load events through the presenter protocol.

Data transfer between modules

Imagine a list, you select an item and go to a new controller scene. You have to pass at least a unique identifier between VIPER modules to make this possible.

It’s usually done somewhat like this:

  • The view calls the didSelect method on the presenter with the id
  • The presenter forwards the id to the router using the routeFor(id) method
  • The router calls the builder to build a new module using the id
  • The builder builds the new module using the id
  • The router presents the new module using it’s view (controller)
  • The new module passes the id for everyone who needs it (router, presenter)
  • The new module’s presenter gets the id
  • The new module’s interactor loads the data and gives it for the presenter
  • The new module’s presenter gives the data for the view and presents it
  • Detail screen appears with proper data.

If you are presenting a controller modally you can also pass the original router as a delegate, so you’ll be able to close it properly if it’s needed. 😎

Memory management

Long story short:

  • The builder holds no-one.
  • The router keeps a weak reference of the view and the presenter.
  • The presenter holds the router and the interactor strongly
  • The interactor keeps a weak reference of the presenter
  • The view keeps a strong reference of the presenter
  • UIKit holds the views.

You should check this in the provided example, no leaks - I hope so - everything gets released nice and smoothly after you go back or dismiss a module. 🤞

Final conclusion: should I learn VIPER?

Although VIPER is highly criticized because of it’s complexity, all I can say it’s worth the effort to learn its principles properly. You’ll see that there are way more benefits of using VIPER instead of ignoring it.

Advantages

  • Simplicity - for large teams on complex projects
  • Scalability - simultaneous work seamlessly
  • Reusability - decoupled app components based on roles
  • Consistency - module skeletons, separation of concerns
  • Clarity - Single responsibilities (SOLID)
  • Testability - separated small classes, TDD, better code coverage
  • Interfaces - module independence, well defined scopes
  • Bug fixing - easier to track issues, locate bugs and problems
  • Source control - smaller files, less conflicts, cleaner code
  • Easy - codebase looks similar, faster to read others work

Drawbacks

  • Verbosity - many files per module
  • Complexity - many protocols and delegates
  • On-boarding - lack of proper VIPER knowledge
  • Engagement - VIPER is bad, because it’s complex, meh!

I made a follow-up article about VIPER best practices that I’ve learn along the journey, you can find the sample repository on GitHub. I hope that these tutorials will help you to learn this architecture better, if you have any questions, feel free to contact me. 👨‍💻

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

How to build SwiftUI apps using VIPER?

-
-

In this tutorial I'll show you how to combine SwiftUI with the VIPER architecture in a real world iOS application example.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

How to write services for VIPER?

-
-

Not everything is a VIPER module. In this article I'll show you how do I separate the service layer from the modules, using Swift.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Mastering the VIPER architecture

-
-

Learn how to master the VIPER architectural design pattern, with some protocol oriented programming techniques using Swift.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

The ultimate VIPER architecture tutorial

-
-

Learn how to write scalable iOS code using the VIPER architecture with some MVVM and MVC tricks and coordinators in mind.

- -
- VIPER -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/top-20-ios-libraries-of-2019/index.html b/docs/top-20-ios-libraries-of-2019/index.html deleted file mode 100644 index 895f82c..0000000 --- a/docs/top-20-ios-libraries-of-2019/index.html +++ /dev/null @@ -1,392 +0,0 @@ - - - - - - - - - - - - Top 20 iOS libraries written in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 2 min read - -
-

Top 20 iOS libraries written in Swift

-
-

I gathered the best open source Swift frameworks on github that will help you to speed up mobile application development in 2019.

-
- -

Sometimes it’s just better to use a 3rd-party framework instead of reinventing the wheel, but there are some important questions that you should ask first:

  • do I really need a library?
  • what should I use?
  • is it going to be supported?
  • what if it’s buggy? 🐛

Adding a dependency to your project can also lead to a technical debt. Don’t be lazy, learn the underlying technology first (or at least read about it or ask someone who actually knows about it) and if you’re sure that the framework is a good choice, then give it a chance. In this list I’ve tried to collect future proof, trusted and well-known iOS libraries used by most of the developer community. ⭐️

Selection criteria:

  • the framework has to be written in Swift
  • the library should not be design specific (there is cocoacontrols for that)
  • it should be a runtime framework not a toolkit (aka. import XY)
  • should have some package manager support (Carthage, CocoaPods, SPM)
  • it has to support the latest major version of Swift
  • must have at least 1000 stars on GitHub

Connecting to…

Alamofire

Alamofire is an HTTP networking library written in Swift.

Kingfisher

Kingfisher is a powerful, pure-Swift library for downloading and caching images from the web. It provides you a chance to use a pure-Swift way to work with remote images in your next app.

Starscream

Starscream is a conforming WebSocket (RFC 6455) client library in Swift.

📦 Server side Swift

Listening…

Vapor

Vapor is a web framework for Swift. It provides a beautifully expressive and easy to use foundation for your next website, API, or cloud project.

SwiftNIO

SwiftNIO is a cross-platform asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.

🔨 Reactive Programming

Streams, observers, etc…

ReactiveCocoa

ReactiveSwift offers composable, declarative and flexible primitives that are built around the grand concept of streams of values over time. These primitives can be used to uniformly represent common Cocoa and generic programming patterns that are fundamentally an act of observation.

RxSwift

Rx is a generic abstraction of computation expressed through Observable interface. This is a Swift version of Rx.

🦋 Animation

UIView.animated…

Hero

Hero is a library for building iOS view controller transitions. It provides a declarative layer on top of the UIKit’s cumbersome transition APIs—making custom transitions an easy task for developers.

Spring

A library to simplify iOS animations in Swift.

📐 Auto layout helpers

Anchors vs…

SnapKit

SnapKit is a DSL to make Auto Layout easy on both iOS and OS X.

TinyConstraints

TinyConstraints is the syntactic sugar that makes Auto Layout sweeter for human use.

❌ Testing

TDD FTW…

Quick

Quick is a behavior-driven development framework for Swift and Objective-C. Inspired by RSpec, Specta, and Ginkgo.

Nimble

Use Nimble to express the expected outcomes of Swift or Objective-C expressions. Inspired by Cedar.

⚙️ Utilities

Did I miss something?

PromiseKit

PromiseKit is a thoughtful and complete implementation of promises for any platform that has a swiftc.

CryptoSwift

CryptoSwift is a growing collection of standard and secure cryptographic algorithms implemented in Swift.

SwiftDate

SwiftDate is the definitive toolchain to manipulate and display dates and time zones on all Apple platform and even on Linux and Swift Server Side frameworks like Vapor or Kitura.

SwiftyBeaver

Convenient logging during development & release in Swift 2, 3 & 4

Swinject

Swinject is a lightweight dependency injection framework for Swift.

SwiftyJSON

SwiftyJSON makes it easy to deal with JSON data in Swift.

If you are looking for more Swift libraries you can always explore the top Swift repositories on GitHub, and please remember: always connect your dependencies through a package manager. 😉

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/uicollectionview-cells-with-circular-images-plus-rotation-support/index.html b/docs/uicollectionview-cells-with-circular-images-plus-rotation-support/index.html deleted file mode 100644 index 43f8bdf..0000000 --- a/docs/uicollectionview-cells-with-circular-images-plus-rotation-support/index.html +++ /dev/null @@ -1,457 +0,0 @@ - - - - - - - - - - - - UICollectionView cells with circular images plus rotation support - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 3 min read - -
-

UICollectionView cells with circular images plus rotation support

-
-

Learn how to make rounded corners for UIImageView items wrapped inside collection view cells, with rotation support.

-
- -

Circular cells inside a collection view

Achieving the goal is relatively easy, but if you don’t know what’s going on in the background it’s going to be harder than you would think first. So let’s create a new project add a storyboard with a UICollectionViewController, drag a UIImageView inside the cell, resize it, add some constraints, set the cell identifier.

Image view

It should look something like the image above. Nothing special just a simple UI for our example application. Now search for some random image, add it to the project and let’s do some real coding. First I’ll show you the little trick inside of the cell subclass.

class Cell: UICollectionViewCell {
-
-    @IBOutlet weak var imageView: UIImageView!
-
-    override var bounds: CGRect {
-        didSet {
-            layoutIfNeeded()
-        }
-    }
-
-    override func awakeFromNib() {
-        super.awakeFromNib()
-
-        imageView.layer.masksToBounds = true
-    }
-
-    override func layoutSubviews() {
-        super.layoutSubviews()
-
-        setCircularImageView()
-    }
-
-    func setCircularImageView() {
-        imageView.layer.cornerRadius = CGFloat(
-            roundf(Float(imageView.frame.size.width / 2.0))
-        )
-    }
-}
-

Can you see it? Yes, you should override the bounds property. As the next step we have to write the controller class with some basic data source for the collection view and with the proper support for the rotation methods. 🤓

class ViewController: UICollectionViewController {
-
-    override func collectionView(
-        _ collectionView: UICollectionView,
-        numberOfItemsInSection section: Int
-    ) -> Int {
-        30
-    }
-
-    override func collectionView(
-        _ collectionView: UICollectionView,
-        cellForItemAt indexPath: IndexPath
-    ) -> UICollectionViewCell {
-
-        let cell = collectionView.dequeueReusableCell(
-            withReuseIdentifier: "Cell", 
-            for: indexPath
-        ) as! Cell
-
-        cell.imageView.image = UIImage(named: "Example.jpg")
-        cell.imageView.backgroundColor = .lightGray
-
-        return cell
-    }
-
-    override func traitCollectionDidChange(
-        _ previousTraitCollection: UITraitCollection?
-    ) {
-        super.traitCollectionDidChange(previousTraitCollection)
-
-        guard
-            let previousTraitCollection = previousTraitCollection,
-            traitCollection.verticalSizeClass != previousTraitCollection.verticalSizeClass ||
-            traitCollection.horizontalSizeClass != previousTraitCollection.horizontalSizeClass
-        else {
-            return
-        }
-
-        collectionView?.collectionViewLayout.invalidateLayout()
-        collectionView?.reloadData()
-    }
-
-    override func viewWillTransition(
-        to size: CGSize, 
-        with coordinator: UIViewControllerTransitionCoordinator
-    ) {
-        super.viewWillTransition(to: size, with: coordinator)
-
-        collectionView?.collectionViewLayout.invalidateLayout()
-
-        coordinator.animate(alongsideTransition: { context in
-
-        }, completion: { context in
-            collectionView?.collectionViewLayout.invalidateLayout()
-
-            collectionView?.visibleCells.forEach { cell in
-                guard let cell = cell as? Cell else {
-                    return
-                }
-                cell.setCircularImageView()
-            }
-        })
-    }
-}
-
-extension ViewController: UICollectionViewDelegateFlowLayout {
-
-    func collectionView(
-        _ collectionView: UICollectionView,
-        layout collectionViewLayout: UICollectionViewLayout,
-        sizeForItemAt indexPath: IndexPath
-    ) -> CGSize {
-        .init(
-            width: collectionView.frame.size.width/3.0 - 8,
-            height: collectionView.frame.size.width/3.0 - 8
-        )
-    }
-}
-

If you are familiar with collection views, you might ask why am I doing this tutorial? It’s so simple. It just works, right? No, actually without the overridden bounds property the example would look something like this on the left side. 😢

Circular images

Funny, huh? The image on the right side is the actual result with the overridden bounds, that’s the expected behavior. Scrolling and rotation is going to be really strange if you don’t override bounds and you don’t reset the cornerRadius property for the visible views. You might ask: but why? 🤔

Layers, springs & struts and some explanation

Apple still has “Springs & Struts” based code inside of UIKit. This means that frame and bound calculations are happening in the underlying system and the constraint system is trying to work hard as well to figure out the proper measures.

“Springs & Struts” needs to die!

While there is an init(frame:) method, or a required init(coder:) these layout things will suck as hell. I really like Interface Builder, but until we can not get a fine tool to create great user interfaces IB is going to be just another layer of possible bugs.

This issue won’t even be there if you create the cell from code only using auto layout constraints or layout anchors! It’s because IB creates the cell based on the frame you gave in while you designed your prototype. But if you forget init(frame:) and you just create a new UIImageView instance and let auto layout do the hard work, the layout system will solve everything else. Check this.

class Cell: UICollectionViewCell {
-
-    weak var imageView: UIImageView!
-
-    required init?(coder aDecoder: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
-
-    override init(frame: CGRect) {
-        super.init(frame: frame)
-
-        translatesAutoresizingMaskIntoConstraints = false
-
-        let imageView = UIImageView()
-        imageView.translatesAutoresizingMaskIntoConstraints = false
-        addSubview(imageView)
-        imageView = imageView
-
-        imageView.topAnchor.constraint(equalTo: topAnchor)
-        imageView.bottomAnchor.constraint(equalTo: bottomAnchor)
-        imageView.leadingAnchor.constraint(equalTo: leadingAnchor)
-        imageView.trailingAnchor.constraint(equalTo: trailingAnchor)
-    }
-
-    override func layoutSubviews() {
-        super.layoutSubviews()
-
-        imageView.layer.masksToBounds = true
-        imageView.layer.cornerRadius = CGFloat(
-            roundf(Float(imageView.frame.size.width/2.0))
-        )
-    }
-}
-

Obviously you have to write more code, register your cell class manually inside the controller class and you also have to override the layoutSubviews method inside the cell, but it’ll work as it is expected. 🙄

collectionView?.register(Cell.self, forCellWithReuseIdentifier: "Cell")
-

Anyway, after you register the programmatically created cell you’ll have a nice way of displaying circular images. Using this technique is quite tricky, but it definitely works in every case. You can download the example from The.Swift.Dev. tutorials on GitHub.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/uicollectionview-data-source-and-delegates-programmatically/index.html b/docs/uicollectionview-data-source-and-delegates-programmatically/index.html deleted file mode 100644 index 499240a..0000000 --- a/docs/uicollectionview-data-source-and-delegates-programmatically/index.html +++ /dev/null @@ -1,454 +0,0 @@ - - - - - - - - - - - - UICollectionView data source and delegates programmatically - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 1 min read - -
-

UICollectionView data source and delegates programmatically

-
-

In this quick UIKit tutorial I'll show you how to create a simple UICollectionView without Interface Builder, but only using Swift.

-
- -

UICollectionViewCell programmatically

If you’d like to add views to your cell, you should use the init(frame:) method, and set up your view hierarchy there. Instead of awakeFromNib you should style your views in the init method as well. You can reset everything inside the usual prepareForReuse method. As you can see by using anchors sometimes it’s worth to ditch IB entirely. 🎉

class Cell: UICollectionViewCell {
-
-    static var identifier: String = "Cell"
-
-    weak var textLabel: UILabel!
-
-    override init(frame: CGRect) {
-        super.init(frame: frame)
-
-        let textLabel = UILabel(frame: .zero)
-        textLabel.translatesAutoresizingMaskIntoConstraints = false
-        contentView.addSubview(textLabel)
-        NSLayoutConstraint.activate([
-            contentView.centerXAnchor.constraint(equalTo: textLabel.centerXAnchor),
-            contentView.centerYAnchor.constraint(equalTo: textLabel.centerYAnchor),
-        ])
-        self.textLabel = textLabel
-        reset()
-    }
-
-    required init?(coder aDecoder: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
-
-    override func prepareForReuse() {
-        super.prepareForReuse()
-        
-        reset()
-    }
-
-    func reset() {
-        textLabel.textAlignment = .center
-    }
-}
-

UICollectionView programmatically

Creating collection view controllers using only Swift code requires only a few additional lines. You can implement loadView and create your UICollectionView object there. Store a weak reference of it inside the controller, and the rest is the same.

class ViewController: UIViewController {
-
-    weak var collectionView: UICollectionView!
-
-    var data: [Int] = Array(0..<10)
-
-    override func loadView() {
-        super.loadView()
-
-        let collectionView = UICollectionView(
-            frame: .zero, 
-            collectionViewLayout: UICollectionViewFlowLayout()
-        )
-        collectionView.translatesAutoresizingMaskIntoConstraints = false
-        view.addSubview(collectionView)
-        NSLayoutConstraint.activate([
-            view.topAnchor.constraint(equalTo: collectionView.topAnchor),
-            view.bottomAnchor.constraint(equalTo: collectionView.bottomAnchor),
-            view.leadingAnchor.constraint(equalTo: collectionView.leadingAnchor),
-            view.trailingAnchor.constraint(equalTo: collectionView.trailingAnchor),
-        ])
-        collectionView = collectionView
-    }
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        collectionView.dataSource = self
-        collectionView.delegate = self
-        collectionView.register(Cell.self, forCellWithReuseIdentifier: Cell.identifier)
-        collectionView.alwaysBounceVertical = true
-        collectionView.backgroundColor = .white
-    }
-}
-
-extension ViewController: UICollectionViewDataSource {
-
-    func collectionView(
-        _ collectionView: UICollectionView,
-        numberOfItemsInSection section: Int
-    ) -> Int {
-        data.count
-    }
-
-    func collectionView(
-        _ collectionView: UICollectionView,
-        cellForItemAt indexPath: IndexPath
-    ) -> UICollectionViewCell {
-        let cell = collectionView.dequeueReusableCell(
-            withReuseIdentifier: Cell.identifier, 
-            for: indexPath
-        ) as! Cell
-
-        let data = data[indexPath.item]
-        cell.textLabel.text = String(data)
-        return cell
-    }
-}
-
-extension ViewController: UICollectionViewDelegate {
-
-    func collectionView(
-        _ collectionView: UICollectionView, 
-        didSelectItemAt indexPath: IndexPath
-    ) {
-
-    }
-}
-
-extension ViewController: UICollectionViewDelegateFlowLayout {
-
-    func collectionView(
-        _ collectionView: UICollectionView,
-        layout collectionViewLayout: UICollectionViewLayout,
-        sizeForItemAt indexPath: IndexPath
-    ) -> CGSize {
-        .init(width: collectionView.bounds.width, height: 44)
-    }
-
-    func collectionView(
-        _ collectionView: UICollectionView,
-        layout collectionViewLayout: UICollectionViewLayout,
-        insetForSectionAt section: Int
-    ) -> UIEdgeInsets {
-        .init(top: 0, left: 0, bottom: 0, right: 0) //.zero
-    }
-
-    func collectionView(
-        _ collectionView: UICollectionView,
-        layout collectionViewLayout: UICollectionViewLayout,
-        minimumInteritemSpacingForSectionAt section: Int
-    ) -> CGFloat {
-        0
-    }
-
-    func collectionView(
-        _ collectionView: UICollectionView,
-        layout collectionViewLayout: UICollectionViewLayout,
-        minimumLineSpacingForSectionAt section: Int
-    ) -> CGFloat {
-        0
-    }
-}
-

That was easy. Anchors are really powerful, Interface Builder is extremely helpful, but sometimes it’s just faster to create your views from code. The choice is yours, but please don’t be afraid of coding user interfaces! 😅

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/uicolor-best-practices-in-swift/index.html b/docs/uicolor-best-practices-in-swift/index.html deleted file mode 100644 index 72f0b4c..0000000 --- a/docs/uicolor-best-practices-in-swift/index.html +++ /dev/null @@ -1,482 +0,0 @@ - - - - - - - - - - - - UIColor best practices in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 6 min read - -
-

UIColor best practices in Swift

-
-

Learn what are color models, how to convert hex values to UIColor and back, generate random colors, where to find beautiful palettes.

-
- -

What are color models and color spaces?

A color model is a method of describing a color.

  • RGB - Red+Green+Blue
  • HSB - Hue+Saturation+Brightness

There are several other color models, but if you are dealing with iOS colors you should be familiar with these two above. Usually you are going to work with the RGBA & HSBA color models which are basically the same as above extended with the alpha channel where the letter A stands for that. 😉

A color space is the set of colors which can be displayed or reproduced in a medium (whether stored, printed or displayed). For example, sRGB is a particular set of intensities for red, green and blue and defines the colors that can be reproduced by mixing those ranges of red, green and blue.

Enough from the theory, let’s do some color magic! 💫💫💫

How to work with UIColor objects using RGBA and HSBA values in Swift?

Do you remember the old Paint program from old-school Windows times?

I’ve used Microsoft Paint a lot, and I loved it. 😅

Back then without any CS knowledge I was always wondering about the numbers between 0 and 255 that I had to pick. If you are working with RGB colors you usually define your color the same way, except that in iOS the values are between 0 and 1, but that’s just a different representation of the fraction of 255.

So you can make a color with RGB codes using the same logic.

UIColor(
-    red: CGFloat(128)/CGFloat(255),
-    green: CGFloat(128)/CGFloat(255),
-    blue: CGFloat(128)/CGFloat(255),
-    alpha: 1
-)
-// this is just about the same gray color but it's more readable
-UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 1)
-

See? Pretty easy, huh? 👍

Alternatively you can use HSB values, almost the same logic applies for those values, except that hue goes from 0 ’til 360 (because of the actual color wheel), however saturation and brightness are measured in a “percent like” format 0-100, so you have to think about these numbers if you map them to floating point values.

UIColor(hue: CGFloat(120)/CGFloat(360), saturation: 0.5, brightness: 0.5, alpha: 1)
-UIColor(hue: 0.3, saturation: 0.5, brightness: 0.5, alpha: 1)
-

Now let’s reverse the situation and let me show you how to get back these components from an actual UIColor instance with the help of an extension.

public extension UIColor {
-    public var rgba: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
-        var r: CGFloat = 0
-        var g: CGFloat = 0
-        var b: CGFloat = 0
-        var a: CGFloat = 0
-        self.getRed(&r, green: &g, blue: &b, alpha: &a)
-        return (r, g, b, a)
-    }
-
-    public var hsba: (hue: CGFloat, saturation: CGFloat, brightness: CGFloat, alpha: CGFloat) {
-        var h: CGFloat = 0
-        var s: CGFloat = 0
-        var b: CGFloat = 0
-        var a: CGFloat = 0
-        self.getHue(&h, saturation: &s, brightness: &b, alpha: &a)
-        return (h, s, b, a)
-    }
-}
-

So here it is how to read the red, green blue slash hue saturation brightness and alpha components from a UIColor. With this little neat extension you can simply get the component values and use them through their proper names.

UIColor.yellow.rgba.red
-UIColor.yellow.hsba.hue
-

How to convert HEX colors to RGB and vica versa for UIColor objects in Swift?

iOS developer 101 course, first questions:

  • How the fuck can I create a UIColor from a hex string?
  • How to convert a hex color to a UIColor?
  • How to use a hext string to make a UIColor?

Ok, maybe these are not the first questions, but it’s definitely inside common ones. The answer is pretty simple: through an extension. I have a really nice solution for your needs, which will handle most of the cases like using only 1, 2, 3 or 6 hex values.

public extension UIColor {
-
-    public convenience init(hex: Int, alpha: CGFloat = 1.0) {
-        let red = CGFloat((hex & 0xFF0000) >> 16) / 255.0
-        let green = CGFloat((hex & 0xFF00) >> 8) / 255.0
-        let blue = CGFloat((hex & 0xFF)) / 255.0
-
-        self.init(red: red, green: green, blue: blue, alpha: alpha)
-    }
-
-    public convenience init(hex string: String, alpha: CGFloat = 1.0) {
-        var hex = string.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
-
-        if hex.hasPrefix("#") {
-            let index = hex.index(hex.startIndex, offsetBy: 1)
-            hex = String(hex[index...])
-        }
-
-        if hex.count < 3 {
-            hex = "\(hex)\(hex)\(hex)"
-        }
-
-        if hex.range(of: "(^[0-9A-Fa-f]{6}$)|(^[0-9A-Fa-f]{3}$)", options: .regularExpression) != nil {
-            if hex.count == 3 {
-
-                let startIndex = hex.index(hex.startIndex, offsetBy: 1)
-                let endIndex = hex.index(hex.startIndex, offsetBy: 2)
-
-                let redHex = String(hex[..<startIndex])
-                let greenHex = String(hex[startIndex..<endIndex])
-                let blueHex = String(hex[endIndex...])
-
-                hex = redHex + redHex + greenHex + greenHex + blueHex + blueHex
-            }
-
-            let startIndex = hex.index(hex.startIndex, offsetBy: 2)
-            let endIndex = hex.index(hex.startIndex, offsetBy: 4)
-            let redHex = String(hex[..<startIndex])
-            let greenHex = String(hex[startIndex..<endIndex])
-            let blueHex = String(hex[endIndex...])
-
-            var redInt: CUnsignedInt = 0
-            var greenInt: CUnsignedInt = 0
-            var blueInt: CUnsignedInt = 0
-
-            Scanner(string: redHex).scanHexInt32(&redInt)
-            Scanner(string: greenHex).scanHexInt32(&greenInt)
-            Scanner(string: blueHex).scanHexInt32(&blueInt)
-
-            self.init(red: CGFloat(redInt) / 255.0,
-                      green: CGFloat(greenInt) / 255.0,
-                      blue: CGFloat(blueInt) / 255.0,
-                      alpha: CGFloat(alpha))
-        }
-        else {
-            self.init(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0)
-        }
-    }
-
-    var hexValue: String {
-        var color = self
-
-        if color.cgColor.numberOfComponents < 4 {
-            let c = color.cgColor.components!
-            color = UIColor(red: c[0], green: c[0], blue: c[0], alpha: c[1])
-        }
-        if color.cgColor.colorSpace!.model != .rgb {
-            return "#FFFFFF"
-        }
-        let c = color.cgColor.components!
-        return String(format: "#%02X%02X%02X", Int(c[0]*255.0), Int(c[1]*255.0), Int(c[2]*255.0))
-    }
-}
-

Here is how you can use it with multiple input variations:

let colors = [
-    UIColor(hex: "#cafe00"),
-    UIColor(hex: "cafe00"),
-    UIColor(hex: "c"),
-    UIColor(hex: "ca"),
-    UIColor(hex: "caf"),
-    UIColor(hex: 0xcafe00),
-]
-let values = colors.map { $0.hexValue }
-print(values) //["#CAFE00", "#CAFE00", "#CCCCCC", "#CACACA", "#CCAAFF", "#CAFE00"]
-

As you can see I’ve tried to replicate the behavior of the CSS rules, so you will have the freedom of less characters if a hext string is like #ffffff (you can use just f, because # is optional). Also you can provide integers as well, that’s just a simple “overloaded” convenience init method.

Also .hexValue will return the string representation of a UIColor instance. 👏👏👏

How to generate a random UIColor in Swift?

This is also a very common question for beginners, I don’t really want to waste the space here by deep explanation, arc4random() is just doing it’s job and the output is a nice randomly generated color.

public extension UIColor {
-    public static var random: UIColor {
-        let max = CGFloat(UInt32.max)
-        let red = CGFloat(arc4random()) / max
-        let green = CGFloat(arc4random()) / max
-        let blue = CGFloat(arc4random()) / max
-
-        return UIColor(red: red, green: green, blue: blue, alpha: 1.0)
-    }
-}
-

How to create a 1x1 pixel big UIImage object with a single solid color in Swift?

I’m using this trick to set the background color of a UIButton object. The reason for this is state management. If you press the button the background image will be darker, so there will be a visual feedback for the user. However by setting the background color directly of a UIButton instance won’t work like this, and the color won’t change at all on the event. 👆

public extension UIColor {
-    public var imageValue: UIImage {
-        let rect = CGRect(origin: .zero, size: CGSize(width: 1, height: 1))
-        UIGraphicsBeginImageContext(rect.size)
-        let context = UIGraphicsGetCurrentContext()!
-        context.setFillColor(self.cgColor)
-        context.fill(rect)
-        let newImage = UIGraphicsGetImageFromCurrentImageContext()
-        UIGraphicsEndImageContext()
-        return newImage!
-    }
-}
-

The snippet above will produce a 1x1 pixel image object from the source color. You can use that anywere, but here is my example with a button background:

button.setBackgroundImage(UIColor.red.imageValue, for: .normal)
-

Online color palettes

You can’t find the right color? No problem, these links will help you to choose the proper one and to get some inspiration. Also if you are looking for flat UI colors or material design colors these are the right links where you should head first.

A personal thing of mine: dear designers, please never ever try to use material design principles for iOS apps. Thank you. HIG

Convert colors online

Finally there are some great online color converter tools, if you are looking for a great one, you should try these first.

Managing UIColors

If your app target is iOS 11+ you can use asset catalogs to organize your color palettes, but if you need to go below iOS 11, I’d suggest to use an enum or struct with static UIColor properties. Nowadays I’m usually doing something like this.

class App {
-    struct Color {
-        static var green: UIColor { return UIColor(hex: 0x4cd964) }
-        static var yellow: UIColor { return UIColor(hex: 0xffcc00) }
-        static var red: UIColor { return UIColor(hex: 0xff3b30) }
-    }
-
-    /* ... */
-}
-
-App.Color.yellow
-

Usually I’m grouping together fonts, colors etc inside structs, but this is just one way of doing things. You can also use something like R.swift or anything that you prefer.

That’s it for now, I think I’ve covered most of the basic questions about UIColor.

Feel free to contact me if you have a topic or suggestion that you’d like to see covered here in the blog. I’m always open for new ideas. 😉

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/uikit-init-patterns/index.html b/docs/uikit-init-patterns/index.html deleted file mode 100644 index 43e0091..0000000 --- a/docs/uikit-init-patterns/index.html +++ /dev/null @@ -1,435 +0,0 @@ - - - - - - - - - - - - UIKit init patterns - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 2 min read - -
-

UIKit init patterns

-
-

Learn about the initialization process of the two well known classes in UIKit. Say hello to UIViewcontroller, and UIView init patterns.

-
- -

UIViewController init

Actually UIViewController intialization is pretty straightforward. You only have to override a few methods if you want to be in full control. It depends on the circumstances which init will be called, if you are using a storyboard, init(coder) is the one that you are looking for. If you are trying to initiate your controller from an external nib file, init(nib,bundle) is going to be called. You also have a third option, you can initialize a controller programmatically from code. Long story short, in order to make a sane init process, you have to deal with all this stuff.

Let me introduce two patterns for UIViewControllers, the first one is just a common init function that gets called in every case that could initialize a controller.

import UIKit
-
-class ViewController: UIViewController {
-
-    override init(
-        nibName nibNameOrNil: String?, 
-        bundle nibBundleOrNil: Bundle?
-    ) {
-        super.init(
-            nibName: nibNameOrNil, 
-            bundle: nibBundleOrNil
-        )
-
-        self.initialize()
-    }
-
-    required init?(
-        coder aDecoder: NSCoder
-    ) {
-        super.init(coder: aDecoder)
-
-        self.initialize()
-    }
-
-    init() {
-        super.init(nibName: nil, bundle: nil)
-
-        self.initialize()
-    }
-
-    func initialize() {
-        //do your stuff here
-    }
-}
-

You can also hide the init(nib:bundle) and init(coder) methods from the future subclasses. You don’t have to override init(nib:bundle) and you can mark the init(coder) as a convenience initializer. It seems like a little bit hacky solution and I don’t like it too much, but it does the job.

import UIKit
-
-class ViewController: UIViewController {
-
-    init() {
-        super.init(nibName: nil, bundle: nil)
-
-        self.initialize()
-    }
-
-    required convenience init?(coder aDecoder: NSCoder) {
-        self.init(coder: aDecoder)
-
-        self.initialize()
-    }
-
-    func initialize() {
-        //do your stuff here
-    }
-}
-
-class MyFutureViewController: ViewController {
-
-    override init() {
-        super.init()
-    }
-}
-let vc = MyFutureViewController()
-

UIView init

I usually create a common initializer for UIViews to make the init process more pleasant. I also set the translate autoresizing mask property to false in that initializer method, because it’s 2017 and noone uses springs & struts anymore, right?

import UIKit
-
-class View: UIView {
-
-    init() {
-        super.init(frame: .zero)
-
-        self.initialize()
-    }
-
-    override init(frame: CGRect) {
-        super.init(frame: frame)
-
-        self.initialize()
-    }
-
-    required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-
-        self.initialize()
-    }
-
-    func initialize() {
-        self.translatesAutoresizingMaskIntoConstraints = false
-    }
-}
-

It’s also nice to have some autolayout helpers, and if you want to initialize a view from a nib file, it’s really good to have some convenience method around.

import UIKit
-
-extension UIView {
-
-    public convenience init(autolayout: Bool) {
-        self.init(frame: .zero)
-
-        self.translatesAutoresizingMaskIntoConstraints = !autolayout
-    }
-
-    public static func create(autolayout: Bool = true) -> Self {
-        let _self = self.init()
-        let view  = _self as UIView
-        view.translatesAutoresizingMaskIntoConstraints = !autolayout
-        return _self
-    }
-
-    public static func createFromNib(
-        owner: Any? = nil, 
-        options: [AnyHashable: Any]? = nil
-    ) -> UIView {
-        return Bundle.main.loadNibNamed(
-            String(describing: self), 
-            owner: owner, 
-            options: options
-        )?.last as! UIView
-    }
-}
-let view = UIView(autolayout: true)
-

Using these snippets, it’s really easy to maintain a sane init process for all the UIKit classes, because most of them ared derived from these two “primary” classes.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/uikit-loadview-vs-viewdidload/index.html b/docs/uikit-loadview-vs-viewdidload/index.html deleted file mode 100644 index 672b315..0000000 --- a/docs/uikit-loadview-vs-viewdidload/index.html +++ /dev/null @@ -1,409 +0,0 @@ - - - - - - - - - - - - UIKit - loadView vs viewDidLoad - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

UIKit - loadView vs viewDidLoad

-
-

When to use these methods? Common questions and answers about the iOS view hierarchy including memory management.

-
- -

Weak, unowned or strong subviews?

I’ve got quite a lot of emails and tweets about this topic, so I decided to write about it, because it is really hard to find a proper answer for this question on the internet. There are some great posts and programming guides, some some articles are a bit older, still many people are asking the weak vs strong IBOutlet question even on the official forums, but noone really explains the reasons, even on the forums they only recommend this WWDC session video. So what’s going on here? 🤔

I did a little research on the topic and the very first thing that we should state is this: Apple removed the viewDidUnload method in iOS6 and from that version the iOS view controller lifecycle changed a bit. If you don’t know much about the lifecycle methods (demystified), you should read this article. This was quite a big change and Apple also touched their internal view management. Before iOS6 it was a common practice to define weak subviews. Because they had a strong reference to it and they were not releasing it unless you removed it from the view hierarchy.

This was about 10 years ago. Now why are we still afraid of strong subviews? The number one reason was the addSubview method. The documentation states that it’ll create a strong reference, which automatically triggered my brain and I defined my views as weak pointers, since they’re going have a strong reference to their parents. Seems reasonable, right? 🧠

Weak subviews

Well, the problem is that if you want to define a weak variable we have to use an optional, but I don’t like the idea of using an optional variable since the view is going to be always there, it’s part of the view hierarchy at some point in, it’s not going anywhere. It’s only going to be “destroyed” when my view controller is deallocated. Should I declare it as an implicitly unwrapped optional?!? Maybe.

import UIKit
-
-class ViewController: UIViewController {
-
-    weak var foo: UILabel! // this can be problematic
-    weak var bar: UILabel? // this is safe, but meh...
-    
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        // this will crash your app.
-        foo.removeFromSuperview()
-        foo.text = "crash"
-    }
-}
-

Actually you can go wrong with unwrapped weak pointers, because if you remove your view from the view hiearchy at some point in time before the view controller deallocation then your weak pointer will be nil. In this case there won’t be any more strong references and your view will be deallocated right away, so if it’s an implicitly unwrapped optional, then we have a trouble. Your app will crash if you try to access the property, because it’s going to have a nil value.

So yes you can use implicitly unwrapped optional variables to store subviews, but only if you are sure that you are not going to remove it from the hiearchy. This also means that you don’t trust Apple’s view management system, which is fine, there can be bugs, but honestly this is quite a crucial feature and it has been around for a decade by now. 🙃

The other alternative is to use a regular weak optional variable, but in that case you’ll always have to check if it’s nil or not, which is going to be a pain in the ass, but at least you’re going to be safe for sure. Personal opinion: it won’t worth the effort at all and I never stored views like this.

Strong subviews

My recommendation is to trust Apple and define your subviews as strong properties. Okay, this can also be problematic if you have other strong references to the same stuff, but in general if the view controller has the only reference to that given subview you should be totally fine.

Since it’s a strong property you also have to initialize the view, but that’s not a big deal. You can always initialize a view with a .zero frame and that’s it. Alternatively you can create a subclass with a regular init() method, that’s even better, becuase you are going to use auto layout for sure and this way can set the translatesAutoresizingMaskIntoConstraints property in one go.

import UIKit
-
-class Label: UILabel {
-    
-    init() {
-        super.init(frame: .zero)
-
-        self.translatesAutoresizingMaskIntoConstraints = false
-    }
-    
-    @available(*, unavailable)
-    required init?(coder: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
-    
-    deinit {
-        print("deinit Label")
-    }
-}
-
-class ViewController: UIViewController {
-
-    // strong view pointers for the win! 😅
-    var foo: Label = .init()
-    var bar: UILabel = .init(frame: .zero)
-    
-    override func viewDidLoad() {
-        super.viewDidLoad()
-        
-    }
-    
-    deinit {
-        print("deinit ViewController")
-    }
-    
-}
-

By implementing a custom deinit method or even better, by creating a symbolic breakpoint you can easily detect retain cycles and fix memory issues. I made some tests and I can confirm you don’t have to be afraid of strong views, both the viewcontroller and the view is going to be deallocated if it’s needed. 👻

Unowned subviews

Unowned and weak are more or less equivalent, I’d say that you won’t need to define views as unowned references, because they can be problematic if it comes to initialization. It’s usually better to have a weak reference and check for nil values, but of course there can be some cases where you might need an unowned subview reference.

Using loadView and viewDidLoad

The loadView method can be used to create your own views manually. You should never call this method directly, but it’s save to override it. The other thing that you should not is that if you are using this method to override the root view, then you shouldn’t call super.loadView().

import UIKit
-
-class ViewController: UIViewController {
-    
-    override func loadView() {
-        view = UIView(frame: .zero)
-
-        // super.loadView() // no need for this
-            
-    }
-}
-

In every other case when you just want to add views to the view hierarchy, it’s completely fine to call the super method. I’m usually implementing this method to setup views and constraints.

import UIKit 
-
-class ViewController: UIViewController {
-
-    var foo: Label = .init()
-    
-    override func loadView() {
-        super.loadView()
-        
-        view.addSubview(foo)
-        
-        NSLayoutConstraint.activate([
-            view.centerXAnchor.constraint(equalTo: foo.centerXAnchor),
-            view.leadingAnchor.constraint(equalTo: foo.leadingAnchor),
-            view.trailingAnchor.constraint(equalTo: foo.trailingAnchor),
-            foo.heightAnchor.constraint(equalToConstant: 44),
-        ])
-    }
-}
-

This way I can be sure that every single view is ready by the time the viewDidLoad method is called. It is possible to configure views inside the loadView method too, but I prefer to keep the hierarchy setup there and I place everything else inside the viewDidLoad function. I mean controller related stuff only, like setting up navigation bar buttons and things like this.

As I mentioned this in my previous article, I prefer to use subclasses to configure my views, I also move layout constraints there (as a function that returns them based on some parameters) to keep the view controller clean. Inside the viewDidLoad method I can perform additional user interface related actions, but that’s it I don’t use it for adding or styling views anymore.

Conclusion

Based on my current knowledge, here is what I recommend for modern UIKit developers:

  • Define your subviews as strong properties
  • Always check for leaks, implement deinit, use breakpoints or instruments
  • Use weak / unowned references if you have to break retain cycles
  • Add views to the hierarchy in the loadView method
  • Use subclasses for styling views, make them reusable
  • Define layout constraint getters on the view subclass, activate them inside loadView
  • Perform remaining UI related operations in the viewDidLoad function

That’s it. I’m not saying this is the perfect approach, but for me it’s definitely the way to go forward with UIKit. I know for sure that many people are still working with the framework and it is here to stay for a long time. I hope these tips will help you to understand UIKit just a little bit better. ☺️

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/uitableview-tutorial-in-swift/index.html b/docs/uitableview-tutorial-in-swift/index.html deleted file mode 100644 index 2f27056..0000000 --- a/docs/uitableview-tutorial-in-swift/index.html +++ /dev/null @@ -1,727 +0,0 @@ - - - - - - - - - - - - UITableView tutorial in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 10 min read - -
-

UITableView tutorial in Swift

-
-

This guide is made for beginners to learn the foundations of the UITableView class programmatically with auto layout in Swift.

-
- -

How to create a table view programmatically?

Let’s jump straight into the coding part, but first: start Xcode, create a new iOS single view app project, enter some name & details for the project as usual, use Swift and finally open the ViewController.swift file right away. Now grab your keyboard! ⌨️

Pro tip: use Cmd+Shift+O to quickly jump between files

I’m not going to use interface builder in this tutorial, so how do we create views programmatically? There is a method called loadView that’s where you should add custom views to your view hierarchy. You can option+click the method name in Xcode & read the discussion about loadView method, but let me summarize the whole thing.

We’ll use a weak property to hold a reference to our table view. Next, we override the loadView method & call super, in order to load the controller’s self.view property with a view object (from a nib or a storyboard file if there is one for the controller). After that we assign our brand new view to a local property, turn off system provided layout stuff, and insert our table view into our view hierarchy. Finally we create some real constraints using anchors & save our pointer to our weak property. Easy! 🤪

class ViewController: UIViewController {
-
-    weak var tableView: UITableView!
-
-    override func loadView() {
-        super.loadView()
-
-        let tableView = UITableView(frame: .zero, style: .plain)
-        tableView.translatesAutoresizingMaskIntoConstraints = false
-        self.view.addSubview(tableView)
-        NSLayoutConstraint.activate([
-        self.view.safeAreaLayoutGuide.topAnchor.constraint(equalTo: tableView.topAnchor),
-            self.view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: tableView.bottomAnchor),
-            self.view.leadingAnchor.constraint(equalTo: tableView.leadingAnchor),
-            self.view.trailingAnchor.constraint(equalTo: tableView.trailingAnchor),
-        ])
-        self.tableView = tableView
-    }
-}
-

Always use auto layout anchors to specify view constraints, if you don’t know how to use them, check my layout anchors tutorial, it’s takes only about 15 minutes to learn this API, and you won’t regret it. It’s an extremely useful tool for any iOS developer! 😉

You might ask: should I use weak or strong properties for view references? I’d say in most of the cases if you are not overriding self.view you should use weak! The view hierarchy will hold your custom view through a strong reference, so there is no need for stupid retain cycles & memory leaks. Trust me! 🤥

UITableViewDataSource basics

Okay, we have an empty table view, let’s display some cells! In order to fill our table view with real data, we have to conform to the UITableViewDataSource protocol. Through a simple delegate pattern, we can provide various information for the UITableView class, so it’ll to know how much sections and rows will be needed, what kind of cells should be displayed for each row, and many more little details.

Another thing is that UITableView is a really efficient class. It’ll reuse all the cells that are currently not displayed on the screen, so it’ll consume way less memory than a UIScrollView, if you have to deal with hundreds or thousands of items. To support this behavior we have to register our cell class with a reuse identifier, so the underlying system will know what kind of cell is needed for a specific place. ⚙️

class ViewController: UIViewController {
-
-    var items: [String] = [
-        "👽", "🐱", "🐔", "🐶", "🦊", "🐵", "🐼", "🐷", "💩", "🐰",
-        "🤖", "🦄", "🐻", "🐲", "🦁", "💀", "🐨", "🐯", "👻", "🦖",
-    ]
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "UITableViewCell")
-
-        self.tableView.dataSource = self
-    }
-}
-
-extension ViewController: UITableViewDataSource {
-
-    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
-        return self.items.count
-    }
-
-    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
-        let cell = tableView.dequeueReusableCell(withIdentifier: "UITableViewCell", for: indexPath)
-        let item = self.items[indexPath.item]
-        cell.textLabel?.text = item
-        return cell
-    }
-}
-

After adding a few lines of code to our view controller file, the table view is now able to display a nice list of emojis! We are using the built-in UITableViewCell class from UIKit, which comes really handy if you are good to go with the “iOS-system-like” cell designs. We also conformed to the data source protocol, by telling how many items are in our section (currently there is only one section), and we configured our cell inside the famous cell for row at indexPath delegate method. 😎

Customizing table view cells

UITableViewCell can provide some basic elements to display data (title, detail, image in different styles), but usually you’ll need custom designed cells. Here is a basic template of a custom cell subclass, I’ll explain all the methods after the code.

class MyCell: UITableViewCell {
-
-    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
-        super.init(style: style, reuseIdentifier: reuseIdentifier)
-
-        self.initialize()
-    }
-
-    required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-
-        self.initialize()
-    }
-
-    func initialize() {
-
-    }
-    /*
-    override func awakeFromNib() {
-        super.awakeFromNib()
-
-    }
-    */
-    override func prepareForReuse() {
-        super.prepareForReuse()
-
-    }
-}
-

The init(style:reuseIdentifier) method is a great place to override the cell style property if you are going to use the default UITableViewCell programmatically, but with different styles (there is no option to set cellStyle after the cell was initialized). For example if you need a .value1 styled cell, just pass the argument directly to the super call. This way you can benefit from the 4 predefined cell styles.

You’ll also have to implement init(coder:), so you should create a common initialize() function where you’ll be able to add your custom views to the view hierarchy, like we did in the loadView method above. If you are using xib files & IB, you can use the awakeFromNib method to add extra style to your views through the standard @IBOutlet properties (or add extra views to the hierarchy as well). 👍

The last method that we have to talk about is prepareForReuse. As I mentioned before cells are being reused so if you want to reset some properties, like the background of a cell, you can do it here. This method will be called before the cell is going to be reused.

Let’s make two new cell subclasses to play around with.

class DetailCell: UITableViewCell {
-
-    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
-        super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
-
-        self.initialize()
-    }
-
-    required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-
-        self.initialize()
-    }
-
-    func initialize() {
-        // nothing to do here :)
-    }
-
-    override func prepareForReuse() {
-        super.prepareForReuse()
-
-        self.textLabel?.text = nil
-        self.detailTextLabel?.text = nil
-        self.imageView?.image = nil
-    }
-}
-

Our custom cell will have a big image background plus a title label in the center of the view with a custom sized system font. Also I’ve added the Swift logo as an asset to the project, so we can have a nice demo image. 🖼

class CustomCell: UITableViewCell {
-
-    weak var coverView: UIImageView!
-    weak var titleLabel: UILabel!
-
-    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
-        super.init(style: style, reuseIdentifier: reuseIdentifier)
-
-        self.initialize()
-    }
-
-    required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-
-        self.initialize()
-    }
-
-    func initialize() {
-        let coverView = UIImageView(frame: .zero)
-        coverView.translatesAutoresizingMaskIntoConstraints = false
-        self.contentView.addSubview(coverView)
-        self.coverView = coverView
-
-        let titleLabel = UILabel(frame: .zero)
-        titleLabel.translatesAutoresizingMaskIntoConstraints = false
-        self.contentView.addSubview(titleLabel)
-        self.titleLabel = titleLabel
-
-        NSLayoutConstraint.activate([
-            self.contentView.topAnchor.constraint(equalTo: self.coverView.topAnchor),
-            self.contentView.bottomAnchor.constraint(equalTo: self.coverView.bottomAnchor),
-            self.contentView.leadingAnchor.constraint(equalTo: self.coverView.leadingAnchor),
-            self.contentView.trailingAnchor.constraint(equalTo: self.coverView.trailingAnchor),
-
-            self.contentView.centerXAnchor.constraint(equalTo: self.titleLabel.centerXAnchor),
-            self.contentView.centerYAnchor.constraint(equalTo: self.titleLabel.centerYAnchor),
-        ])
-
-        self.titleLabel.font = UIFont.systemFont(ofSize: 64)
-    }
-
-    override func prepareForReuse() {
-        super.prepareForReuse()
-
-        self.coverView.image = nil
-    }
-}
-

That’s it, let’s start using these new cells. I’ll even tell you how to set custom height for a given cell, and how to handle cell selection properly, but first we need to get to know with another delegate protocol. 🤝

Basic UITableViewDelegate tutorial

This delegate is responsible for lots of things, but for now we’re going to cover just a few interesting aspects, like how to handle cell selection & provide a custom cell height for each items inside the table. Here is a quick sample code.

class ViewController: UIViewController {
-
-    override func viewDidLoad() {
-            super.viewDidLoad()
-
-            self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "UITableViewCell")
-            self.tableView.register(DetailCell.self, forCellReuseIdentifier: "DetailCell")
-            self.tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
-
-            self.tableView.dataSource = self
-            self.tableView.delegate = self
-    }
-}
-extension ViewController: UITableViewDataSource {
-
-    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
-        let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
-        let item = self.items[indexPath.item]
-        cell.titleLabel.text = item
-        cell.coverView.image = UIImage(named: "Swift")
-        return cell
-    }
-}
-
-extension ViewController: UITableViewDelegate {
-
-    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
-        return 128
-    }
-
-    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
-        tableView.deselectRow(at: indexPath, animated: true)
-        let item = self.items[indexPath.item]
-
-        let alertController = UIAlertController(title: item, message: "is in da house!", preferredStyle: .alert)
-        let action = UIAlertAction(title: "Ok", style: .default) { _ in }
-        alertController.addAction(action)
-        self.present(alertController, animated: true, completion: nil)
-    }
-}
-

As you can see I’m registering my brand new custom cell classes in the viewDidLoad method. I also changed the code inside the cellForRowAt indexPath method, so we can use the CustomCell class instead of UITableViewCells. Don’t be afraid of force casting here, if something goes wrong at this point, your app should crash. 🙃

There are two delegate methods that we are using here. In the first one, we have to return a number and the system will use that height for the cells. If you want to use different cell height per row, you can achieve that too by checking indexPath property or anything like that. The second one is the handler for the selection. If someone taps on a cell, this method will be called & you can perform some action.

An indexPath has two interesting properties: section & item (=row)

Multiple sections with headers and footers

It’s possible to have multiple sections inside the table view, I won’t go too much into the details, because it’s pretty straightforward. You just have to use indexPaths in order to get / set / return the proper data for each section & cell.

import UIKit
-
-class ViewController: UIViewController {
-
-    weak var tableView: UITableView!
-
-    var placeholderView = UIView(frame: .zero)
-    var isPullingDown = false
-
-    enum Style {
-        case `default`
-        case subtitle
-        case custom
-    }
-
-    var style = Style.default
-
-    var items: [String: [String]] = [
-        "Originals": ["👽", "🐱", "🐔", "🐶", "🦊", "🐵", "🐼", "🐷", "💩", "🐰","🤖", "🦄"],
-        "iOS 11.3": ["🐻", "🐲", "🦁", "💀"],
-        "iOS 12": ["🐨", "🐯", "👻", "🦖"],
-    ]
-
-    override func loadView() {
-        super.loadView()
-
-        let tableView = UITableView(frame: .zero, style: .plain)
-        tableView.translatesAutoresizingMaskIntoConstraints = false
-        self.view.addSubview(tableView)
-        NSLayoutConstraint.activate([
-            self.view.safeAreaLayoutGuide.topAnchor.constraint(equalTo: tableView.topAnchor),
-            self.view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: tableView.bottomAnchor),
-            self.view.leadingAnchor.constraint(equalTo: tableView.leadingAnchor),
-            self.view.trailingAnchor.constraint(equalTo: tableView.trailingAnchor),
-        ])
-        self.tableView = tableView
-    }
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "UITableViewCell")
-        self.tableView.register(DetailCell.self, forCellReuseIdentifier: "DetailCell")
-        self.tableView.register(CustomCell.self, forCellReuseIdentifier: "CustomCell")
-
-        self.tableView.dataSource = self
-        self.tableView.delegate = self
-        self.tableView.separatorStyle = .singleLine
-        self.tableView.separatorColor = .lightGray
-        self.tableView.separatorInset = .zero
-
-        self.navigationItem.rightBarButtonItem = .init(barButtonSystemItem: .refresh, target: self, action: #selector(self.toggleCells))
-    }
-
-    @objc func toggleCells() {
-
-        switch self.style {
-        case .default:
-            self.style = .subtitle
-        case .subtitle:
-            self.style = .custom
-        case .custom:
-            self.style = .default
-        }
-
-        DispatchQueue.main.async {
-            self.tableView.reloadData()
-        }
-    }
-
-    // MARK: - helpers
-
-    func key(for section: Int) -> String {
-        let keys = Array(self.items.keys).sorted { first, last -> Bool in
-            if first == "Originals" {
-                return true
-            }
-            return first < last
-        }
-        let key = keys[section]
-        return key
-    }
-
-    func items(in section: Int) -> [String] {
-        let key = self.key(for: section)
-        return self.items[key]!
-    }
-
-    func item(at indexPath: IndexPath) -> String {
-        let items = self.items(in: indexPath.section)
-        return items[indexPath.item]
-    }
-}
-
-extension ViewController: UITableViewDataSource {
-
-    func numberOfSections(in tableView: UITableView) -> Int {
-        return self.items.keys.count
-    }
-
-    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
-        return self.items(in: section).count
-    }
-
-    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
-        let item = self.item(at: indexPath)
-        let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
-        cell.titleLabel.text = item
-        cell.coverView.image = UIImage(named: "Swift")
-        return cell
-    }
-
-    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
-        return self.key(for: section)
-    }
-
-}
-
-extension ViewController: UITableViewDelegate {
-
-    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
-        return 128
-    }
-
-    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
-        tableView.deselectRow(at: indexPath, animated: true)
-
-        let item = self.item(at: indexPath)
-        let alertController = UIAlertController(title: item, message: "is in da house!", preferredStyle: .alert)
-        let action = UIAlertAction(title: "Ok", style: .default) { _ in }
-        alertController.addAction(action)
-        self.present(alertController, animated: true, completion: nil)
-    }
-}
-

Although there is one interesting addition in the code snippet above. You can have a custom title for every section, you just have to add the titleForHeaderInSection data source method. Yep, it looks like shit, but this one is not about perfect UIs. 😂

However if you are not satisfied with the layout of the section titles, you can create a custom class & use that instead of the built-in ones. Here is how to do a custom section header view. Here is the implementation of the reusable view:

class HeaderView: UITableViewHeaderFooterView {
-
-    weak var titleLabel: UILabel!
-
-    override init(reuseIdentifier: String?) {
-        super.init(reuseIdentifier: reuseIdentifier)
-
-        self.initialize()
-    }
-
-    required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-
-        self.initialize()
-    }
-
-    func initialize() {
-        let titleLabel = UILabel(frame: .zero)
-        titleLabel.translatesAutoresizingMaskIntoConstraints = false
-        self.contentView.addSubview(titleLabel)
-        self.titleLabel = titleLabel
-
-        NSLayoutConstraint.activate([
-            self.contentView.centerXAnchor.constraint(equalTo: self.titleLabel.centerXAnchor),
-            self.contentView.centerYAnchor.constraint(equalTo: self.titleLabel.centerYAnchor),
-        ])
-
-        self.contentView.backgroundColor = .black
-        self.titleLabel.font = UIFont.boldSystemFont(ofSize: 16)
-        self.titleLabel.textAlignment = .center
-        self.titleLabel.textColor = .white
-    }
-}
-

There is only a few things left to do, you have to register your header view, just like you did it for the cells. It’s exactly the same way, except that there is a separate registration “pool” for the header & footer views. Lastly you have to implement two additional, but relatively simple (and familiar) delegate methods.

// This goes to viewDidLoad, but I don't want to embedd that much code... :)
-// self.tableView.register(HeaderView.self, forHeaderFooterViewReuseIdentifier: "HeaderView")
-
-extension ViewController: UITableViewDelegate {
-
-    /* ... */
-
-    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
-        return 32
-    }
-
-    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
-        let view = tableView.dequeueReusableHeaderFooterView(withIdentifier: "HeaderView") as! HeaderView
-        view.titleLabel.text = self.key(for: section)
-        return view
-    }
-}
-

Footers works exactly the same as headers, you just have to implement the corresponding data source & delegate methods in order to support them.

You can even have multiple cells in the same table view based on the row or section index or any specific business requirement. I’m not going to demo this here, because I have a way better solution for mixing and reusing cells inside the CoreKit framework. It’s already there for table views as well, plus I already covered this idea in my ultimate collection view tutorial post. You should check that too. 🤓

Section titles & indexes

Ok, if your brain is not melted yet, I’ll show you two more little things that can be interesting for beginners. The first one is based on two additional data source methods and it’s a very pleasant addition for long lists. (I prefer search bars!) 🤯

extension ViewController: UITableViewDataSource {
-    /* ... */
-
-    func sectionIndexTitles(for tableView: UITableView) -> [String]? {
-        return ["1", "2", "3"]
-    }
-
-    func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
-        return index
-    }
-}
-

If you are going to implement these methods above you can have a little index view for your sections in the right side of the table view, so the end-user will be able to quickly jump between sections. Just like in the official contacts app. 📕

Selection vs highlight

Cells are highlighted when you are holding them down with your finger. Cell is going to be selected if you release your finger from the cell.

Don’t over-complicate this. You just have to implement two methods in you custom cell class to make everything work. I prefer to deselect my cells right away, if they’re not for example used by some sort of data picker layout. Here is the code:

class CustomCell: UITableViewCell {
-
-    /* ... */
-
-    override func setSelected(_ selected: Bool, animated: Bool) {
-        self.coverView.backgroundColor = selected ? .red : .clear
-    }
-
-    override func setHighlighted(_ highlighted: Bool, animated: Bool) {
-        self.coverView.backgroundColor = highlighted ? .blue : .clear
-    }
-}
-

As you can see, it’s ridiculously easy, but most of the beginners don’t know how to do this. Also they usually forget to reset cells before the reusing logic happens, so the list keeps messing up cell states. Don’t worry too much about these problems, they’ll go away as you’re going to be more experienced with the UITableView APIs.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/ultimate-grand-central-dispatch-tutorial-in-swift/index.html b/docs/ultimate-grand-central-dispatch-tutorial-in-swift/index.html deleted file mode 100644 index cddf4fd..0000000 --- a/docs/ultimate-grand-central-dispatch-tutorial-in-swift/index.html +++ /dev/null @@ -1,679 +0,0 @@ - - - - - - - - - - - - Ultimate Grand Central Dispatch tutorial in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 11 min read - -
-

Ultimate Grand Central Dispatch tutorial in Swift

-
-

Learn the principles of multi-threading with the GCD framework in Swift. Queues, tasks, groups everything you'll ever need I promise.

-
- -

GCD concurrency tutorial for beginners

The Grand Central Dispatch (GCD, or just Dispatch) framework is based on the underlying thread pool design pattern. This means that there are a fixed number of threads spawned by the system - based on some factors like CPU cores - they’re always available waiting for tasks to be executed concurrently. 🚦

Creating threads on the run is an expensive task so GCD organizes tasks into specific queues, and later on the tasks waiting on these queues are going to be executed on a proper and available thread from the pool. This approach leads to great performance and low execution latency. We can say that the Dispatch framework is a very fast and efficient concurrency framework designed for modern multi-core hardware and needs.

Concurrency, multi-tasking, CPU cores, parallelism and threads

A processor can run tasks made by you programmatically, this is usually called coding, developing or programming. The code executed by a CPU core is a thread. So your app is going to create a process that is made up from threads. 🤓

In the past a processor had one single core, it could only deal with one task at a time. Later on time-slicing was introduced, so CPU’s could execute threads concurrently using context switching. As time passed by processors gained more horse power and cores so they were capable of real multi-tasking using parallelism. ⏱

Nowadays a CPU is a very powerful unit, it’s capable of executing billions of tasks (cycles) per second. Because of this high availability speed Intel introduced a technology called hyper-threading. They divided CPU clock cycles between (usually two) processes running at the same time, so the number of available threads essentially doubled. 📈

As you can see concurrent execution can be achieved with various techniques, but you don’t need to care about that much. It’s up to the CPU architecture how it solves concurrency, and it’s the operating system’s task how much thread is going to be spawned for the underlying thread pool. The GCD framework will hide all the complexity, but it’s always good to understand the basic principles. 👍

Synchronous and asynchronous execution

Each work item can be executed either synchronously or asynchronously.

Have you ever heard of blocking and non-blocking code? This is the same situation here. With synchronous tasks you’ll block the execution queue, but with async tasks your call will instantly return and the queue can continue the execution of the remaining tasks (or work items as Apple calls them). 🚧

Synchronous execution

When a work item is executed synchronously with the sync method, the program waits until execution finishes before the method call returns.

Your function is most likely synchronous if it has a return value, so func load() -> String is going to probably block the thing that runs on until the resources is completely loaded and returned back.

Asynchronous execution

When a work item is executed asynchronously with the async method, the method call returns immediately.

Completion blocks are a good sing of async methods, for example if you look at this method func load(completion: (String) -> Void) you can see that it has no return type, but the result of the function is passed back to the caller later on through a block.

This is a typical use case, if you have to wait for something inside your method like reading the contents of a huge file from the disk, you don’t want to block your CPU, just because of the slow IO operation. There can be other tasks that are not IO heavy at all (math operations, etc.) those can be executed while the system is reading your file from the physical hard drive. 💾

With dispatch queues you can execute your code synchronously or asynchronously. With synchronous execution the queue waits for the work, with async execution the code returns immediately without waiting for the task to complete. ⚡️

Dispatch queues

As I mentioned before, GCD organizes task into queues, these are just like the queues at the shopping mall. On every dispatch queue, tasks will be executed in the same order as you add them to the queue - FIFO: the first task in the line will be executed first - but you should note that the order of completion is not guaranteed. Tasks will be completed according to the code complexity. So if you add two tasks to the queue, a slow one first and a fast one later, the fast one can finish before the slower one. ⌛️

Serial and concurrent queues

There are two types of dispatch queues. Serial queues can execute one task at a time, these queues can be utilized to synchronize access to a specific resource. Concurrent queues on the other hand can execute one or more tasks parallel in the same time. Serial queue is just like one line in the mall with one cashier, concurrent queue is like one single line that splits for two or more cashiers. 💰

Main, global and custom queues

The main queue is a serial one, every task on the main queue runs on the main thread.

Global queues are system provided concurrent queues shared through the operating system. There are exactly four of them organized by high, default, low priority plus an IO throttled background queue.

Custom queues can be created by the user. Custom concurrent queues always mapped into one of the global queues by specifying a Quality of Service property (QoS). In most of the cases if you want to run tasks in parallel it is recommended to use one of the global concurrent queues, you should only create custom serial queues.

System provided queues

  • Serial main queue
  • Concurrent global queues
  • high priority global queue
  • default priority global queue
  • low priority global queue
  • global background queue (IO throttled)

Custom queues by quality of service

  • userInteractive (UI updates) -> serial main queue
  • userInitiated (async UI related tasks) -> high priority global queue
  • default -> default priority global queue
  • utility -> low priority global queue
  • background -> global background queue
  • unspecified (lowest) -> low priority global queue

Enough from the theory, let’s see how to use the Dispatch framework in action! 🎬

How to use the DispatchQueue class in Swift?

Here is how you can get all the queues from above using the brand new GCD syntax available from Swift 3. Please note that you should always use a global concurrent queue instead of creating your own one, except if you are going to use the concurrent queue for locking with barriers to achieve thread safety, more on that later. 😳

How to get a queue?

import Dispatch
-
-DispatchQueue.main
-DispatchQueue.global(qos: .userInitiated)
-DispatchQueue.global(qos: .userInteractive)
-DispatchQueue.global(qos: .background)
-DispatchQueue.global(qos: .default)
-DispatchQueue.global(qos: .utility)
-DispatchQueue.global(qos: .unspecified)
-
-DispatchQueue(
-    label: "com.theswiftdev.queues.serial"
-)
-
-DispatchQueue(
-    label: "com.theswiftdev.queues.concurrent", 
-    attributes: .concurrent
-)
-

So executing a task on a background queue and updating the UI on the main queue after the task finished is a pretty easy one using Dispatch queues.

DispatchQueue.global(qos: .background).async {
-    // do your job here
-
-    DispatchQueue.main.async {
-        // update ui here
-    }
-}
-

Sync and async calls on queues

There is no big difference between sync and async methods on a queue. Sync is just an async call with a semaphore (explained later) that waits for the return value. A sync call will block, on the other hand an async call will immediately return. 🎉

let q = DispatchQueue.global()
-
-let text = q.sync {
-    return "this will block"
-}
-print(text)
-
-q.async {
-    print("this will return instantly")
-}
-

Basically if you need a return value use sync, but in every other case just go with async. DEADLOCK WARNING: you should never call sync on the main queue, because it’ll cause a deadlock and a crash. You can use this snippet if you are looking for a safe way to do sync calls on the main queue / thread. 👌

Don’t call sync on a serial queue from the serial queue’s thread!

Delay execution

You can simply delay code execution using the Dispatch framework.

DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) {
-    //this code will be executed only after 2 seconds have been passed
-}
-

Perform concurrent loop

Dispatch queue simply allows you to perform iterations concurrently.

DispatchQueue.concurrentPerform(iterations: 5) { (i) in
-    print(i)
-}
-

Debugging

Oh, by the way it’s just for debugging purpose, but you can return the name of the current queue by using this little extension. Do not use in production code!!!

extension DispatchQueue {
-    static var currentLabel: String {
-        .init(validatingUTF8: __dispatch_queue_get_label(nil))!
-    }
-}
-//print(DispatchQueue.currentLabel)
-

Using DispatchWorkItem in Swift

DispatchWorkItem encapsulates work that can be performed. A work item can be dispatched onto a DispatchQueue and within a DispatchGroup. A DispatchWorkItem can also be set as a DispatchSource event, registration, or cancel handler.

So you just like with operations by using a work item you can cancel a running task. Also work items can notify a queue when their task is completed.

var workItem: DispatchWorkItem?
-workItem = DispatchWorkItem {
-    for i in 1..<6 {
-        guard let item = workItem, !item.isCancelled else {
-            print("cancelled")
-            break
-        }
-        sleep(1)
-        print(String(i))
-    }
-}
-
-workItem?.notify(queue: .main) {
-    print("done")
-}
-
-
-DispatchQueue.global().asyncAfter(
-    deadline: .now() + .seconds(2)
-) {
-    workItem?.cancel()
-}
-DispatchQueue.main.async(execute: workItem!)
-// you can use perform to run on the current queue instead of queue.async(execute:)
-//workItem?.perform()
-

Concurrent tasks with DispatchGroups

So you need to perform multiple network calls in order to construct the data required by a view controller? This is where DispatchGroup can help you. All of your long running background task can be executed concurrently, when everything is ready you’ll receive a notification. Just be careful you have to use thread-safe data structures, so always modify arrays for example on the same thread! 😅

func load(delay: UInt32, completion: () -> Void) {
-    sleep(delay)
-    completion()
-}
-
-let group = DispatchGroup()
-
-group.enter()
-load(delay: 1) {
-    print("1")
-    group.leave()
-}
-
-group.enter()
-load(delay: 2) {
-    print("2")
-    group.leave()
-}
-
-group.enter()
-load(delay: 3) {
-    print("3")
-    group.leave()
-}
-
-group.notify(queue: .main) {
-    print("done")
-}
-

Note that you always have to balance out the enter and leave calls on the group. The dispatch group also allows us to track the completion of different work items, even if they run on different queues.

let group = DispatchGroup()
-let queue = DispatchQueue(
-    label: "com.theswiftdev.queues.serial"
-)
-let workItem = DispatchWorkItem {
-    print("start")
-    sleep(1)
-    print("end")
-}
-
-queue.async(group: group) {
-    print("group start")
-    sleep(2)
-    print("group end")
-}
-DispatchQueue.global().async(
-    group: group, 
-    execute: workItem
-)
-
-// you can block your current queue and wait until the group is ready
-// a better way is to use a notification block instead of blocking
-//group.wait(timeout: .now() + .seconds(3))
-//print("done")
-
-group.notify(queue: .main) {
-    print("done")
-}
-

One more thing that you can use dispatch groups for: imagine that you’re displaying a nicely animated loading indicator while you do some actual work. It might happens that the work is done faster than you’d expect and the indicator animation could not finish. To solve this situation you can add a small delay task so the group will wait until both of the tasks finish. 😎

let queue = DispatchQueue.global()
-let group = DispatchGroup()
-let n = 9
-for i in 0..<n {
-    queue.async(group: group) {
-        print("\(i): Running async task...")
-        sleep(3)
-        print("\(i): Async task completed")
-    }
-}
-group.wait()
-print("done")
-

Semaphores

A [semaphore](https://en.wikipedia.org/wiki/Semaphore_(programming) is simply a variable used to handle resource sharing in a concurrent system. It’s a really powerful object, here are a few important examples in Swift.

How to make an async task to synchronous?

The answer is simple, you can use a semaphore (bonus point for timeouts)!

enum DispatchError: Error {
-    case timeout
-}
-
-func asyncMethod(completion: (String) -> Void) {
-    sleep(2)
-    completion("done")
-}
-
-func syncMethod() throws -> String {
-
-    let semaphore = DispatchSemaphore(value: 0)
-    let queue = DispatchQueue.global()
-
-    var response: String?
-    queue.async {
-        asyncMethod { r in
-            response = r
-            semaphore.signal()
-        }
-    }
-    semaphore.wait(timeout: .now() + 5)
-    guard let result = response else {
-        throw DispatchError.timeout
-    }
-    return result
-}
-
-let response = try? syncMethod()
-print(response)
-

Lock / single access to a resource

If you want to avoid race condition you are probably going to use mutual exclusion. This could be achieved using a semaphore object, but if your object needs heavy reading capability you should consider a dispatch barrier based solution. 😜

class LockedNumbers {
-
-    let semaphore = DispatchSemaphore(value: 1)
-    var elements: [Int] = []
-
-    func append(_ num: Int) {
-        self.semaphore.wait(timeout: DispatchTime.distantFuture)
-        print("appended: \(num)")
-        self.elements.append(num)
-        self.semaphore.signal()
-    }
-
-    func removeLast() {
-        self.semaphore.wait(timeout: DispatchTime.distantFuture)
-        defer {
-            self.semaphore.signal()
-        }
-        guard !self.elements.isEmpty else {
-            return
-        }
-        let num = self.elements.removeLast()
-        print("removed: \(num)")
-    }
-}
-
-let items = LockedNumbers()
-items.append(1)
-items.append(2)
-items.append(5)
-items.append(3)
-items.removeLast()
-items.removeLast()
-items.append(3)
-print(items.elements)
-

Wait for multiple tasks to complete

Just like with dispatch groups, you can also use a semaphore object to get notified if multiple tasks are finished. You just have to wait for it…

let semaphore = DispatchSemaphore(value: 0)
-let queue = DispatchQueue.global()
-let n = 9
-for i in 0..<n {
-    queue.async {
-        print("run \(i)")
-        sleep(3)
-        semaphore.signal()
-    }
-}
-print("wait")
-for i in 0..<n {
-    semaphore.wait()
-    print("completed \(i)")
-}
-print("done")
-

Batch execution using a semaphore

You can create a thread pool like behavior to simulate limited resources using a dispatch semaphore. So for example if you want to download lots of images from a server you can run a batch of x every time. Quite handy. 🖐

print("start")
-let sem = DispatchSemaphore(value: 5)
-for i in 0..<10 {
-    DispatchQueue.global().async {
-        sem.wait()
-        sleep(2)
-        print(i)
-        sem.signal()
-    }
-}
-print("end")
-

The DispatchSource object

A dispatch source is a fundamental data type that coordinates the processing of specific low-level system events.

Signals, descriptors, processes, ports, timers and many more. Everything is handled through the dispatch source object. I really don’t want to get into the details, it’s quite low-level stuff. You can monitor files, ports, signals with dispatch sources. Please just read the official Apple docs. 📄

I’d like to make only one example here using a dispatch source timer.

let timer = DispatchSource.makeTimerSource()
-timer.schedule(deadline: .now(), repeating: .seconds(1))
-timer.setEventHandler {
-    print("hello")
-}
-timer.resume()
-

Thread-safety using the dispatch framework

Thread safety is an inevitable topic if it comes to multi-threaded code. In the beginning I mentioned that there is a thread pool under the hood of GCD. Every thread has a run loop object associated with it, you can even run them by hand. If you create a thread manually a run loop will be added to that thread automatically.

let t = Thread {
-    print(Thread.current.name ?? "")
-     let timer = Timer(timeInterval: 1, repeats: true) { t in
-         print("tick")
-     }
-     RunLoop.current.add(timer, forMode: .defaultRunLoopMode)
-
-    RunLoop.current.run()
-    RunLoop.current.run(mode: .commonModes, before: Date.distantPast)
-}
-t.name = "my-thread"
-t.start()
-
-//RunLoop.current.run()
-

You should not do this, demo purposes only, always use GCD queues!

Queue != Thread

A GCD queue is not a thread, if you run multiple async operations on a concurrent queue your code can run on any available thread that fits the needs.

Thread safety is all about avoiding messed up variable states

Imagine a mutable array in Swift. It can be modified from any thread. That’s not good, because eventually the values inside of it are going to be messed up like hell if the array is not thread safe. For example multiple threads are trying to insert values to the array. What happens? If they run in parallel which element is going to be added first? Now this is why you need sometimes to create thread safe resources.

Serial queues

You can use a serial queue to enforce mutual exclusivity. All the tasks on the queue will run serially (in a FIFO order), only one process runs at a time and tasks have to wait for each other. One big downside of the solution is speed. 🐌

let q = DispatchQueue(label: "com.theswiftdev.queues.serial")
-
-q.async() {
-  // writes
-}
-
-q.sync() {
-  // reads
-}
-

Concurrent queues using barriers

You can send a barrier task to a queue if you provide an extra flag to the async method. If a task like this arrives to the queue it’ll ensure that nothing else will be executed until the barrier task have finished. To sum this up, barrier tasks are sync (points) tasks for concurrent queues. Use async barriers for writes, sync blocks for reads. 😎

let q = DispatchQueue(label: "com.theswiftdev.queues.concurrent", attributes: .concurrent)
-
-q.async(flags: .barrier) {
-  // writes
-}
-
-q.sync() {
-  // reads
-}
-

This method will result in extremely fast reads in a thread safe environment. You can also use serial queues, semaphores, locks it all depends on your current situation, but it’s good to know all the available options isn’t it? 🤐

A few anti-patterns

You have to be very careful with deadlocks, race conditions and the readers writers problem. Usually calling the sync method on a serial queue will cause you most of the troubles. Another issue is thread safety, but we’ve already covered that part. 😉

let queue = DispatchQueue(label: "com.theswiftdev.queues.serial")
-
-queue.sync {
-    // do some sync work
-    queue.sync {
-        // this won't be executed -> deadlock!
-    }
-}
-
-//What you are trying to do here is to launch the main thread synchronously from a background thread before it exits. This is a logical error.
-//https://stackoverflow.com/questions/49258413/dispatchqueue-crashing-with-main-sync-in-swift?rq=1
-DispatchQueue.global(qos: .utility).sync {
-    // do some background task
-    DispatchQueue.main.sync {
-        // app will crash
-    }
-}
-

The Dispatch framework (aka. GCD) is an amazing one, it has such a potential and it really takes some time to master it. The real question is that what path is going to take Apple in order to embrace concurrent programming into a whole new level? Promises or async / await, maybe something entirely new, let’s hope that we’ll see something in Swift 6.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/ultimate-uicollectionview-guide-with-ios-examples-written-in-swift/index.html b/docs/ultimate-uicollectionview-guide-with-ios-examples-written-in-swift/index.html deleted file mode 100644 index 1abc488..0000000 --- a/docs/ultimate-uicollectionview-guide-with-ios-examples-written-in-swift/index.html +++ /dev/null @@ -1,727 +0,0 @@ - - - - - - - - - - - - Ultimate UICollectionView guide with iOS examples written in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 12 min read - -
-

Ultimate UICollectionView guide with iOS examples written in Swift

-
-

Learn how to use UICollectionView, with highly reusable UIKit components and some MVVM pattern without the going nuts with index path calculations.

-
- -

Anatomy of the UICollectionView class

If you’re not familiar with UICollectionView, I’d suggest to get familiar with this class immediately. They’re the basic building blocks for many apps provided by Apple and other third party developers. It’s like UITableView on steroids. Here is a quick intro about how to work with them through IB and Swift code. 💻

Layout

You might have noticed that I have a love for metal music. In this tutorial we’re going to build an Apple Music catalog like look from ground zero using only the mighty UICollectionView class. Headers, horizontal and vertical scrolling, circular images, so basically almost everything that you’ll ever need to build great user interfaces. 🤘🏻

How to make a UICollectionView using Interface Builder (IB) in Xcode?

The short & honest answer: you shouldn’t use IB!

If you still want to use IB, here is a real quick tutorial for absolutely beginners:

Section

The main steps of creating your first UICollectionView based screen are these:

  • Drag a UICollectionView object to your view controller
  • Set proper constraints on the collection view
  • Set dataSource & delegate of the collection view
  • Prototype your cell layout inside the controller
  • Add constraints to your views inside the cell
  • Set prototype cell class & reuse identifier
  • Do a little coding:
import UIKit
-
-class MyCell: UICollectionViewCell {
-    @IBOutlet weak var textLabel: UILabel!
-}
-
-class ViewController: UIViewController {
-
-    @IBOutlet weak var collectionView: UICollectionView!
-
-    override func viewDidLayoutSubviews() {
-        super.viewDidLayoutSubviews()
-
-        if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
-            flowLayout.itemSize = CGSize(
-                width: collectionView.bounds.width,
-                height: 120
-            )
-        }
-    }
-}
-
-extension ViewController: UICollectionViewDataSource {
-
-    func numberOfSections(
-        in collectionView: UICollectionView
-    ) -> Int {
-        1
-    }
-
-    func collectionView(
-        _ collectionView: UICollectionView, 
-        numberOfItemsInSection section: Int
-    ) -> Int {
-        10
-    }
-
-    func collectionView(
-        _ collectionView: UICollectionView, 
-        cellForItemAt indexPath: IndexPath
-    ) -> UICollectionViewCell {
-        let cell = collectionView.dequeueReusableCell(
-            withReuseIdentifier: "MyCell", 
-            for: indexPath
-        ) as! MyCell
-
-        cell.textLabel.text = String(indexPath.row + 1)
-        return cell
-    }
-}
-
-extension ViewController: UICollectionViewDelegate {
-
-    func collectionView(
-        _ collectionView: UICollectionView, 
-        didSelectItemAt indexPath: IndexPath
-    ) {
-        print(indexPath.item + 1)
-    }
-}
-

In a nutshell, the data source will provide all the required data about how to populate the collection view, and the delegate will handle user events, such as tapping on a cell. You should have a clear understanding about the data source and delegate methods, so feel free to play with them for a little while. ⌨️

How to setup a UICollectionView based screen programmatically?

As you might have noticed cells are the core components of a collection view. They are derived from reusable views, this means that if you have a list of 1000 elements, there won’t be a thousand cells created for every element, but only a few that fills the size of the screen and when you scroll down the list these items are going to be reused to display your elements. This is only because of memory considerations, so unlike UIScrollView the UICollectionView (and UITableView) class is a really smart and efficient one, but this is also the reason why you have to prepare (reset the contents of) the cell every time before you display your actual data. 😉

Initialization is also handled by the system, but it’s worth to mention that if you are working with Interface Builder, you should do your customization inside the awakeFromNib method, but if you are using code, init(frame:) is your place.

import UIKit
-
-class MyCell: UICollectionViewCell {
-
-    weak var textLabel: UILabel!
-
-    override init(frame: CGRect) {
-        super.init(frame: frame)
-
-        let textLabel = UILabel(frame: .zero)
-        textLabel.translatesAutoresizingMaskIntoConstraints = false
-        contentView.addSubview(textLabel)
-        NSLayoutConstraint.activate([
-            textLabel.topAnchor.constraint(
-                equalTo: contentView.topAnchor
-            ),
-            textLabel.bottomAnchor.constraint(
-                equalTo: contentView.bottomAnchor
-            ),
-            textLabel.leadingAnchor.constraint(
-                equalTo: contentView.leadingAnchor
-            ),
-            textLabel.trailingAnchor.constraint(
-                equalTo: contentView.trailingAnchor
-            ),
-        ])
-        self.textLabel = textLabel
-
-        contentView.backgroundColor = .lightGray
-        textLabel.textAlignment = .center
-    }
-
-    required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-
-        fatalError("Interface Builder is not supported!")
-    }
-
-    override func awakeFromNib() {
-        super.awakeFromNib()
-
-        fatalError("Interface Builder is not supported!")
-    }
-
-    override func prepareForReuse() {
-        super.prepareForReuse()
-
-        textLabel.text = nil
-    }
-}
-

Next we have to implement the view controller which is responsible for managing the collection view, we’re not using IB so we have to create it manually by using Auto Layout anchors - like for the textLabel in the cell - inside the loadView method. After the view hierarchy is ready to rock, we also set the data source and delegate plus register our cell class for further reuse. Note that this is done automatically by the system if you are using IB, but if you prefer code you have to do it by calling the proper registration method. You can register both nibs and classes.

import UIKit
-
-class ViewController: UIViewController {
-
-    weak var collectionView: UICollectionView!
-
-    override func loadView() {
-        super.loadView()
-
-        let collectionView = UICollectionView(
-            frame: .zero, 
-            collectionViewLayout: UICollectionViewFlowLayout()
-        )
-        collectionView.translatesAutoresizingMaskIntoConstraints = false
-        view.addSubview(collectionView)
-        NSLayoutConstraint.activate([
-            collectionView.topAnchor.constraint(
-                equalTo: view.topAnchor
-            ),
-            collectionView.bottomAnchor.constraint(
-                equalTo: view.bottomAnchor
-            ),
-            collectionView.leadingAnchor.constraint(
-                equalTo: view.leadingAnchor
-            ),
-            collectionView.trailingAnchor.constraint(
-                equalTo: view.trailingAnchor
-            ),
-        ])
-        self.collectionView = collectionView
-    }
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        collectionView.backgroundColor = .white
-        collectionView.dataSource = self
-        collectionView.delegate = self
-        collectionView.register(
-            MyCell.self,
-            forCellWithReuseIdentifier: "MyCell"
-        )
-    }
-}
-
-extension ViewController: UICollectionViewDataSource {
-
-    func numberOfSections(
-        in collectionView: UICollectionView
-    ) -> Int {
-        1
-    }
-
-    func collectionView(
-        _ collectionView: UICollectionView, 
-        numberOfItemsInSection section: Int
-    ) -> Int {
-        10
-    }
-
-    func collectionView(
-        _ collectionView: UICollectionView, 
-        cellForItemAt indexPath: IndexPath
-    ) -> UICollectionViewCell {
-        let cell = collectionView.dequeueReusableCell(
-            withReuseIdentifier: "MyCell", 
-            for: indexPath
-        ) as! MyCell
-
-        cell.textLabel.text = String(indexPath.row + 1)
-        return cell
-    }
-}
-
-extension ViewController: UICollectionViewDelegate {
-
-    func collectionView(
-        _ collectionView: UICollectionView, 
-        didSelectItemAt indexPath: IndexPath
-    ) {
-        print(indexPath.row + 1)
-    }
-}
-
-extension ViewController: UICollectionViewDelegateFlowLayout {
-
-    func collectionView(
-        _ collectionView: UICollectionView,
-        layout collectionViewLayout: UICollectionViewLayout,
-        sizeForItemAt indexPath: IndexPath
-    ) -> CGSize {
-        .init(
-            width: collectionView.bounds.size.width - 16, 
-            height: 120
-        )
-    }
-    func collectionView(
-        _ collectionView: UICollectionView,
-        layout collectionViewLayout: UICollectionViewLayout,
-        minimumLineSpacingForSectionAt section: Int
-    ) -> CGFloat {
-        8
-    }
-
-    func collectionView(
-        _ collectionView: UICollectionView,
-        layout collectionViewLayout: UICollectionViewLayout,
-        minimumInteritemSpacingForSectionAt section: Int
-    ) -> CGFloat {
-        0
-    }
-
-    func collectionView(
-        _ collectionView: UICollectionView,
-        layout collectionViewLayout: UICollectionViewLayout,
-        insetForSectionAt section: Int
-    ) -> UIEdgeInsets {
-        .init(top: 8, left: 8, bottom: 8, right: 8)
-    }
-}
-

This time you should pay some attention on the flow layout delegate methods. You can use these methods to provide metrics for the layout system. The flow layout will display all the cells based on these numbers and sizes. sizeForItemAt is responsible for the cell size, minimumInteritemSpacingForSectionAt is the horizontal padding, minimumLineSpacingForSectionAt is the vertical padding, and insetForSectionAt is for the margin of the collection view section.

Using supplementary elements (section headers and footers)

So in this section I’m going to both use storyboards, nibs and some Swift code. This is my usual approach for a few reasons. Although I love making constraints from code, most people prefer visual editors, so all the cells are created inside nibs. Why nibs? Because if you have multiple collection views this is “almost” the only nice way to share cells between them.

You can create section footers exactly the same way as you do headers, so that’s why this time I’m only going to focus on headers, because literally you only have to change one word in order to use footers. ⚽️

Cell

You just have to create two xib files, one for the cell and one for the header. Please note that you could use the exact same collection view cell to display content in the section header, but this is a demo so let’s just go with two distinct items. You don’t even have to set the reuse identifier from IB, because we have to register our reusable views inside the source code, so just set the cell class and connect your outlets.

Cell and supplementary element registration is slightly different for nibs.

let cellNib = UINib(nibName: "Cell", bundle: nil)
-self.collectionView.register(
-    cellNib, 
-    forCellWithReuseIdentifier: "Cell"
-)
-
-let sectionNib = UINib(nibName: "Section", bundle: nil)
-self.collectionView.register(
-    sectionNib, 
-    forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, 
-    withReuseIdentifier: "Section"
-)
-

Implementing the data source for the section header looks like this.

func collectionView(
-    _ collectionView: UICollectionView,
-    viewForSupplementaryElementOfKind kind: String,
-    at indexPath: IndexPath
-) -> UICollectionReusableView {
-
-    guard kind == UICollectionView.elementKindSectionHeader else {
-        return UICollectionReusableView()
-    }
-    let view = collectionView.dequeueReusableSupplementaryView(
-        ofKind: kind, 
-        withReuseIdentifier: "Section", 
-        for: indexPath
-    ) as! Section
-
-    view.textLabel.text = String(indexPath.section + 1)
-    return view
-}
-

Providing the size for the flow layout delegate is also pretty straightforward, however sometimes I don’t really get the naming conventions by Apple. Once you have to switch a kind, and the other time there are exact methods for specific kinds. 🤷‍♂️

func collectionView(
-    _ collectionView: UICollectionView,
-    layout collectionViewLayout: UICollectionViewLayout,
-    referenceSizeForHeaderInSection section: Int
-) -> CGSize {
-    .init(
-        width: collectionView.bounds.size.width, 
-        height: 64
-    )
-}
-

Starting from iOS9 section headers and footers can be pinned to the top or bottom of the visible bounds of the collection view.

if let flowLayout = self.collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
-    flowLayout.sectionHeadersPinToVisibleBounds = true
-}
-

That’s it, now you know how to build basic layouts with collection view.

What about complex cases, like using multiple kinds of cells in the same collection view? Things can get pretty messy with index paths, so that’s why I re-invented something better based on a technique how to build advanced user interfaces with collection views showcased by Apple back at WWDC 2014.

My CollectionView based UI framework

Now you know the basics, so why don’t we get straight to the point? I’ll show you my best practice of building great user interfaces by using my MVVM architecture based CollectionView micro framework.

CollectionView + ViewModel pattern = ❤️ .

I’ll explain the components real quick and after that you’ll learn how to use them to build up the Apple music-ish layout that I was talking about in the beginning. 🎶

Grid system

The first problem with collection views is the size calculation. You have to provide the size (width & height) for each cell inside your collection view.

  • if everything has a fixed size inside your collection view, you can just set the size properties on the flow layout itself
  • if you need dynamic sizes per item, you can implement the flow layout delegate aka. UICollectionViewDelegateFlowLayout (why is the delegate word in the middle of the name???) and return the exact sizes for the layout system
  • if you need even more control you can create a new layout subclass derived from CollectionView(Flow)Layout and do all the size calculations there

Thats good, but still you have to mess with index paths, trait collections, frames and many more in order to have a simple 2, 4, n column layout that adapts on every device. This is the reason why I’ve created a really basic grid system for size calculation. With my grid class you can just set the number of columns and get back the size for x amount of columns, “just like” in web based css grid systems. 🕸

Cell reuse

Registering and reusing cells should and can be automated in a type safe manner. You just want to use the cell, and you shouldn’t care about reuse identifiers and cell registration at all. I’ve made a couple helper methods in order to make the progress more pleasant. Reuse identifiers are derived from the name of the cell classes, so you dont’t have to worry about anymore. This is a practice that most of the developers use.

View model

view model = cell (view) + data (model)

Filling up “template” cell with real data should be the task of a view model. This is where MVVM comes into play. I’ve made a generic base view model class, that you should subclass. With the help of a protocol, you can use various cells in a single collection view without going crazy of the row & section calculations and you can focus on one simple task: connecting view with models. 😛

Section

section = header + footer + cells

I’m trying to emphasize that you don’t want to mess with index paths, you just want to put your data together and that’s it. In the past I’ve struggled more than enough with “unnecessary index path math”, so I’ve made the section object as a simple container to wrap headers, footers and all the items inside of the section. The result? Generic data source class that can be used with multiple cells without any row or section index calculations. 👏👏👏

Source

So in order to make all the things I’ve mentioned above work, I needed to implement the collection view delegate, data source, and flow layout delegate methods. That’s how my source class was born. Everything is implemented here, and I’m using sections, view models the grid system to build up collection views. But hey, enough from this theory, let’s see it in practice. 👓

CollectionView framework example application

How to make a any list or grid layout hassle free? Well, as a first step just add my CollectionView framework as a dependency. Don’t worry you won’t regret it, plus it supports Xcode 11 already, so you can use the Swift Package Manager, straight from the file menu to integrate this package.

Tip: just add the @_exported import CollectionView line in the AppDelegate file, then you I don’t have to worry about importing the framework file-by-file.

Step 1. Make the cell.

This step is identical with the regular setup, except that your cell have to be a subclass of my Cell class. Add your own cell and do everything as you would do normally.

import UIKit
-
-class AlbumCell: Cell {
-
-    @IBOutlet weak var textLabel: UILabel!
-    @IBOutlet weak var detailTextLabel: UILabel!
-    @IBOutlet weak var imageView: UIImageView!
-
-    override func awakeFromNib() {
-        super.awakeFromNib()
-
-        self.textLabel.font = UIFont.systemFont(ofSize: 12, weight: .bold)
-        self.textLabel.textColor = .black
-
-        self.detailTextLabel.font = UIFont.systemFont(ofSize: 12, weight: .bold)
-        self.detailTextLabel.textColor = .darkGray
-
-        self.imageView.layer.cornerRadius = 8
-        self.imageView.layer.masksToBounds = true
-    }
-
-    override func reset() {
-        super.reset()
-
-        self.textLabel.text = nil
-        self.detailTextLabel.text = nil
-        self.imageView.image = nil
-    }
-}
-

Step 2. Make a model

Just pick a model object. It can be anything, but my approach is to make a new struct or class with a Model suffix. This way I know that models are referencing the collection view models inside my reusable components folder.

import Foundation
-
-struct AlbumModel {
-    let artist: String
-    let name: String
-    let image: String
-}
-

Step 3. Make the view model.

Now instead of configuring the cell inside the delegate, or in a configure method somewhere, let’s make a real view model for the cell & the data model that’s going to be represented via the view.

import UIKit
-
-class AlbumViewModel: ViewModel<AlbumCell, AlbumModel> {
-
-    override func updateView() {
-        self.view?.textLabel.text = self.model.artist
-        self.view?.detailTextLabel.text = self.model.name
-        self.view?.imageView.image = UIImage(named: self.model.image)
-    }
-
-    override func size(grid: Grid) -> CGSize {
-        if
-            (self.collectionView.traitCollection.userInterfaceIdiom == .phone &&
-             self.collectionView.traitCollection.verticalSizeClass == .compact) ||
-            self.collectionView?.traitCollection.userInterfaceIdiom == .pad
-        {
-            return grid.size(
-                for: self.collectionView, 
-                ratio: 1.2, 
-                items: grid.columns / 4, 
-                gaps: grid.columns - 1
-            )
-        }
-        if grid.columns == 1 {
-            return grid.size(for: self.collectionView, ratio: 1.1)
-        }
-        return grid.size(
-            for: self.collectionView, 
-            ratio: 1.2, 
-            items: grid.columns / 2,
-            gaps: grid.columns - 1
-        )
-    }
-}
-

Step 4. Setup your data source.

Now, use your real data and populate your collection view using the view models.

let grid = Grid(columns: 1, margin: UIEdgeInsets(all: 8))
-self.collectionView.source = .init(grid: grid, [
-    [
-        HeaderViewModel(.init(title: "Albums"))
-        AlbumViewModel(self.album)
-    ],
-])
-self.collectionView.reloadData()
-

Step 5. 🍺🤘🏻🎸

Congratulations you’re done with your first collection view. With just a few lines of code you have a ROCK SOLID code that will help you out in most of the situations! 😎

This is just the tip of the iceberg! 🚢

Horizontal scrolling inside vertical scrolling

What if we make a cell that contains a collection view and we use the same method like above? A collection view containing a collection view… UICollectionViewception!!! 😂

It’s completely possible, and really easy to do, the data that feeds the view model will be a collection view source object, and you’re done. Effortless, magical and super nice to implement, also included in the example app.

Sections with artists & circular images

Multiple sections? No problem, circular images? That’s also a piece of cake, if you had read my previous tutorial about circular collection view cells, you’ll know how to do it, but please check out the source code from GitLab and see it for yourself in action.

Callbacks and actions

User events can be handled very easy, because view models can have delegates or callback blocks, it only depends on you which one you prefer. The example contains an onSelect handler, which is super nice and built-in to the framework. 😎

Dynamic cell sizing re-imagined

I also had a tutorial about collection view self sizing cell support, but to be honest I’m not a big fan of Apple’s official method. After I’ve made the grid system and started using view models, it was more easy to calculate cell heights by myself, with about 2 lines of extra code. I believe that’s worth it, because self sizing cells are a little buggy if it comes to auto rotation.

Rotation support, adaptivity

Don’t worry about that too much, you can simply change the grid or check trait collections inside the view model if you want. I’d say almost everything can be done right out of the box. My collection view micro framework is just a lightweight wrapper around the official collection view APIs. That’s the beauty of it, feel free to do whatever you want and use it in a way that YOU personally prefer. 📦

Now go, grab the sample code and listen to some metal! 🤘🏻

What if I told you… one more thing: SwiftUI

These are some original quotes of mine back from April, 2018:

If you like this method that’s cool, but what if I told you that there is more? Do you want to use the same pattern everywhere? I mean on iOS, tvOS, macOS and even watchOS. Done deal! I’ve created everything inside the CoreKit framework. UITableViews, WKInterfaceTables are supported as well.

Well, I’m a visionary, but SwiftUI was late 1 year, it arrived in 2019:

I really believe that Apple this year will approach the next generation UIKit / AppKit / UXKit frameworks (written in Swift of course) somewhat like this. I’m not talking about the view model pattern, but about the same API on every platform thinking. Anyway, who knows this for sue, we’ll see… #wwdc18 🤔

If someone from Apple reads this, please explain me why the hell is SwiftUI still an abstraction layer above UIKit/ AppKit instead of a refactored AppleKit UI framework that finally unifies every single API? For real, why? Still don’t get it. 🤷‍♂️

Anyway, we’re going in to the same direction guys, year-by-year I delete more and more self-written “3rd-party” code, so you’re doing great progress there! 🍎

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/uniquely-identifying-views/index.html b/docs/uniquely-identifying-views/index.html deleted file mode 100644 index 4356de3..0000000 --- a/docs/uniquely-identifying-views/index.html +++ /dev/null @@ -1,361 +0,0 @@ - - - - - - - - - - - - Uniquely identifying views - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 1 min read - -
-

Uniquely identifying views

-
-

Learn how to use string based UIView identifiers instead of tags. If you are tired of tagging views, check out these alternative solutions.

-
- -

First approach: accessibility to the rescue!

Long story short, I was quite tired of tagging views with stupid number values, so I looked for a better alternative solution to fix my problem. As it turned out, there is a property called accessibilityIdentifier that can do the job.

extension UIView {
-
-    var id: String? {
-        get {
-            return self.accessibilityIdentifier
-        }
-        set {
-            self.accessibilityIdentifier = newValue
-        }
-    }
-
-    func view(withId id: String) -> UIView? {
-        if self.id == id {
-            return self
-        }
-        for view in self.subviews {
-            if let view = view.view(withId: id) {
-                return view
-            }
-        }
-        return nil
-    }
-}
-

I made a simple extension around the UIView class, so now I can use a proper string value to uniquely identify any view object in the view hierarchy. It’s quite a nice solution, now I can name my views in a really nice way. As a gratis storing the name under the accessibilityIdentifier will benefit your UI tests. 😉

Second approach: using enums

The main idea is to have an Int based enum for every view identifier, so basically you can use the tag property to store the enum’s rawValue. It’s still not so nice as the one above, but it’s way more safe than relying on pure integers. 😬

enum ViewIdentifier: Int {
-    case submitButton
-}
-
-extension UIView {
-
-    var identifier: ViewIdentifier? {
-        set {
-            if let value = newValue {
-                self.tag = value.rawValue
-            }
-        }
-        get {
-            return ViewIdentifier(rawValue: self.tag)
-        }
-    }
-
-    func view(withId id: ViewIdentifier) -> UIView? {
-        return self.viewWithTag(id.rawValue)
-    }
-}
-

Honestly I just came up with the second approach right after I copy & pasted the first snippet to this article, but what the heck, maybe someone else will like it. 😂

If you have a better solution for this problem, feel free to share it with me.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/unsafe-memory-pointers-in-swift/index.html b/docs/unsafe-memory-pointers-in-swift/index.html deleted file mode 100644 index e2d7a6e..0000000 --- a/docs/unsafe-memory-pointers-in-swift/index.html +++ /dev/null @@ -1,491 +0,0 @@ - - - - - - - - - - - - Unsafe memory pointers in Swift - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 8 min read - -
-

Unsafe memory pointers in Swift

-
-

Learn how to use raw pointer references, interact with unsafe pointers and manually manage memory addresses in Swift.

-
- -

Pointers in Swift

What’s is a pointer? A pointer is a variable that stores the memory address of a referenced object. As I mentioned this in my previous article, about the memory layout of various objects in Swift, a memory address is just a hexadecimal representation of a data located somewhere in the memory. You use instances of various unsafe pointer types to access data of a specific type in memory.

Why do we want to use these kind of pointers at the first place? By default you don’t have to write unsafe Swift code, and in most of the cases you can live without unsafe memory pointers. These pointers come handy if you have to interoperate with other “unsafe” languages, such as C. There are other low level or legacy APIs that require the use of manual memory management, but you shouldn’t be afraid, once you get familiar with unsafe Swift pointer types you’ll know a lot more about how memory works and you’ll see how thin is the layer between C libraries and Swift. 😱

What kind of pointers are there? In order to understand pointer types better in Swift, let’s get back to C just for a second. Consider the following C code example:

#include <stdio.h>
-
-int main () {
-
-    int x = 20;
-    int* xPointer = &x;
-
-    printf("x address: `%p`\n", &x);
-    printf("x value: `%u`\n", x);
-    printf("pointer address: `%p`\n", &xPointer);
-    printf("pointer reference: `%p`\n", xPointer); // equals the address of x
-    printf("pointer reference value: `%u`\n", *xPointer);
-
-    *xPointer = 420;
-    printf("x value: `%u`\n", x);
-    printf("pointer reference value: `%u`\n", *xPointer);
-
-    x = 69;
-    printf("x value: `%u`\n", x);
-    printf("pointer reference value: `%u`\n", *xPointer);
-
-    return 0;
-}
-

You can save this code snippet using the main.c name, then compile & run it using the clang main.c -o main && ./main command. It will provide a quite similar output.

$ clang main.c -o main && ./main
-x address: `0x16b0c7a38`
-x value: `20`
-pointer address: `0x16b0c7a30`
-pointer reference: `0x16b0c7a38`
-pointer reference value: `20`
-pointer value `20`
-tib@~: clang main.c -o main && ./main
-x address: `0x16d52fa38`
-x value: `20`
-pointer address: `0x16d52fa30`
-pointer reference: `0x16d52fa38`
-pointer reference value: `20`
-x value: `420`
-pointer reference value: `420`
-x value: `69`
-pointer reference value: `69`
-

So what’s going on here? Well, we simply created an integer variable and a pointer variable with an integer type. We used the address of our x variable (&x) to associate our pointer with the memory address of x. Now both variables points to the same memory address.

We can confirm this by logging the memory address of both variables. We can also alter the value of x by updating the referenced value of the pointer (we can use the * character for this) or go with the usual make x = something line. We’ve simply logged the changed values to confirm that the pointer value update also changed the value of x. We could say that xPointer is just a reference to x.

Now how do we achieve the same thing in Swift? First we have to learn how to define a pointer type. Here’s a quick list of all of the unsafe pointer objects available in the Swift standard library:

You might have noticed a pattern here: Unsafe|[Mutable][Raw][Buffer]Pointer[].

Unsafe pointers are just direct memory addresses. Everything that is mutable can be changed, in other words you can write to that address. Raw means that there is no associated (generic, T) type to the given pointer, it’s just a blob of raw bytes. Buffers are batches (collections) of pointers.

Don’t worry if these types are quite confusing for you right now, it’ll all make sense in a few minutes. Let’s get back to our original C sample code and port it to Swift real quick.

var x: Int = 20
-var xPointer: UnsafeMutablePointer<Int> = .init(&x)
-
-print("x address:", UnsafeRawPointer(&x));
-print("x value:", x);
-print("pointer address:", UnsafeRawPointer(&xPointer));
-print("pointer reference:", xPointer);
-print("pointer reference value:", xPointer.pointee);
-
-
-xPointer.pointee = 420;
-print("x value:", x);
-print("pointer reference value:", xPointer.pointee);
-
-x = 69;
-print("x value:", x);
-print("pointer reference value:", xPointer.pointee);
-

We’ve created an UnsafeMutablePointer reference to our x value, this is basically an int* type if we go back to the C example. We can use the same ampersand (&) character to create pointers from variables. We’ve created a typed mutable pointer, since we’d like to change the value of the referenced integer object (through the pointee property) later on.

To print the memory address of a variable we can simply use an UnsafeRawPointer type, because we don’t really care about the underlying “pointee” value, but we just need the address of the reference. If you print a pointer type the debug description will contain the underlying memory address of the referenced object. In this case the address of x and xPointer. 🤔

Working with typed pointers in Swift

How can we store some values at “unsafe” memory addresses in Swift? The most simple way is that we start with a generic mutable pointer. We can allocate pointers using the required capacity, since we’re working with unsafe memory, we also have to deallocate memory after we’ve finished using it. We also have to manually initialize pointer reference values, unsafe pointers can already contain some sort of leftover data, so the safe approach is to initialize them with a new default value.

let numbers = [4, 8, 15, 16, 23, 42]
-
-let pointer = UnsafeMutablePointer<Int>.allocate(capacity: numbers.count)
-pointer.initialize(repeating: 0, count: numbers.count)
-defer {
-    pointer.deinitialize(count: numbers.count)
-    pointer.deallocate()
-}
-
-for (index, value) in numbers.enumerated() {
-    pointer.advanced(by: index).pointee = value
-}
-
-print(pointer.advanced(by: 5).pointee); //42
-
-let bufferPointer = UnsafeBufferPointer(start: pointer, count: numbers.count) // UnsafeBufferPointer<Int>
-for (index, value) in bufferPointer.enumerated() {
-    print(index, "-", value)
-}
-
-/// change values using a mutable buffer pointer
-let bufferPointer = UnsafeMutableBufferPointer(start: pointer, count: numbers.count)
-for (index, _) in bufferPointer.enumerated() {
-    bufferPointer[index] = index + 1
-}
-

After we have the allocated memory storage, we can set the appropriate pointee values, since we’ve allocated the pointer with a capacity of six integer values, we can store up to 6 numbers using this pointer. You can use the advanced(by:) method (pointer arithmetic (pointer + 5).pointee = 42) works as well) to move to the next address and set the pointee value of it.

The very last thing I’d like to let you know is that you can use a typed buffer pointer to iterate through these number references. You can think of buffer pointers as an array of pointer references. It is possible to enumerate through pointer values and indexes directly. You can update buffer pointer values by using the subscript syntax on a mutable buffer pointer. 💡

We already talked about the UnsafePointer, UnsafeMutablePointer, UnsafeRawPointer, UnsafeBufferPointer and UnsafeMutableBufferPointer type let’s dive in to raw pointers.

Memory management using raw pointers

Typed pointers provide some kind of safety if it comes to pointers, but how do we work with raw pointers? We’ve already seen how easy is to print out an address of a given value type using an UnsafeRawPointer reference, now it’s time to connect the dots and allocate some unsafe raw memory. If you need to know more about memory layout in Swift, please read my previous article.

First of all, we’ll need to know how much memory to allocate. We can use the MemoryLayout struct to get info about a value type. We can use the stride and the number of items to count how much byte is required to store our data type using a raw memory storage.

let numbers = [4, 8, 15, 16, 23, 42]
-
-let stride = MemoryLayout<Int>.stride
-let alignment = MemoryLayout<Int>.alignment
-let byteCount = stride * numbers.count
-
-let pointer = UnsafeMutableRawPointer.allocate(byteCount: byteCount, alignment: alignment)
-defer {
-    pointer.deallocate()
-}
-  
-for (index, value) in numbers.enumerated() {
-    pointer.advanced(by: stride * index).storeBytes(of: value, as: Int.self)
-}
-  
-//print(pointer.advanced(by: stride * 5).load(as: Int.self)) // 42
-
-let bufferPointer = UnsafeRawBufferPointer(start: pointer, count: byteCount)
-for index in 0..<numbers.count {
-    let value = bufferPointer.load(fromByteOffset: stride * index, as: Int.self)
-    print(index, "-", value)
-}
-

After we’ve allocated the required space, we can use the pointer and the advanced(by:) method to store byte values of a given type (storeBytes(of:as:)) as raw bytes. We can load a given type using the load(as:) method. It is worth to mention that if the memory does not contain a value that can be represented as the given type, incompatible value types can crash your app. ☠️

Anyway, if you stored multiple values using a pointer you can use the raw buffer collection to iterate through them and load back the types as values from a given byte offset. If you enumerate through a raw byte buffer you can also print the byte representation for the pointer.

If you want to know more about how to Safely manage pointers in Swift, I highly recommend watching the linked WWDC video. It’s a fresh one, the sample code is compatible with Swift 5. 💪

Memory binding can be dangerous

You can use the bindMemory and the asssumingMemoryBound methods to convert a raw pointer to a typed pointer. The first will actually bind the memory to a given type, but the second function just returns a referenced pointer assuming it’s already bound to the specified type. You can read more about the key differences here or check the original UnsafeRawPointer API proposal.

let stride = MemoryLayout<Int>.stride
-let alignment = MemoryLayout<Int>.alignment
-let count = 1
-let byteCount = stride * count
-
-let rawPointer = UnsafeMutableRawPointer.allocate(byteCount: byteCount, alignment: alignment)
-defer {
-    rawPointer.deallocate()
-}
-let pointer = rawPointer.bindMemory(to: Int.self, capacity: count)
-//let pointer = rawPointer.assumingMemoryBound(to: Int.self)
-pointer.initialize(repeating: 0, count: count)
-defer {
-    pointer.deinitialize(count: count)
-}
-
-pointer.pointee = 42
-print(rawPointer.load(as: Int.self))
-rawPointer.storeBytes(of: 69, toByteOffset: 0, as: Int.self)
-print(pointer.pointee)
-

Binding memory can be dangerous, there are a few rules that you should follow:

  • Never return the pointer from a withUnsafeBytes call
  • Only bind to one type at a time
  • Be careful with off-by-one errors

This article lists the issues that can happen if you re-bind a memory address.

// don't do this, use withMemoryRebound instead...
-let badPointer = rawPointer.bindMemory(to: Bool.self, capacity: count)
-print(badPointer.pointee) // true, but that's not what we expect, right?
- 
-pointer.withMemoryRebound(to: Bool.self, capacity: count) { boolPointer in
-    print(boolPointer.pointee) // false
-}
-
-// never return the pointer variable inside the block
-withUnsafeBytes(of: &pointer.pointee) { pointer -> Void in
-    for byte in pointer {
-        print(byte)
-    }
-    // don't return pointer ever
-}
-
-// off-by-one error...
-let bufferPointer = UnsafeRawBufferPointer(start: pointer, count: byteCount + 1)
-for byte in bufferPointer {
-    print(byte) // ...the last byte will be problematic
-}
-

I also recommend checking this article about memory management and byte computation in Swift. It is also possible to copy or move a memory to a given destination using the assign(from:count:) or moveAssign(from:count:) methods. You can read more about these functions here.

Opaque and managed Swift pointers

If unsafe pointers weren’t just enough, you should know that Swift has a few other pointer types.

As Vadim Bulavin describes this in his article, with the help of the Unmanaged type you can bypass Automatic Reference Counting (ARC) that is otherwise enforced to every Swift class. The other case is to convert objects between opaque pointers back and forth.

class MyPoint {
-
-    let x: Int
-    let y: Int
-
-    init(x: Int, y: Int) {
-        self.x = x
-        self.y = y
-    }
-
-    deinit {
-        print("deinit", x, y)
-    }
-}
-
-let unmanaged = Unmanaged.passRetained(MyPoint(x: 4, y: 20))
-unmanaged.release()
-
-_ = Unmanaged.passUnretained(MyPoint(x: 6, y: 9))
-
-let opaque = Unmanaged.passRetained(MyPoint(x: 1, y: 0)).toOpaque()
-Unmanaged<MyPoint>.fromOpaque(opaque).release()
-

Opaque pointers are used when you have to work with incomplete C data structures which cannot be represented in Swift. For example if you have a struct that contains a pointer type, that variable is going to be imported to Swift as an OpaquePointer type when interacting with C code.

ManagedBufferPointer and the ManagedBuffer type allows you to implement your own copy-on-write data structure. This way you can achieve the exact same behavior as the built-in array, set or dictionary types have. Russ Bishop has a great post related to this topic.

AutoreleasingUnsafeMutablePointer is a pointer that points to an Objective-C reference that doesn’t own its target. you can read more about it here by Keith Harrison

The CVaListPointer is a simple wrapper around a C va_list pointer.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/urlsession-and-the-combine-framework/index.html b/docs/urlsession-and-the-combine-framework/index.html deleted file mode 100644 index cded6b1..0000000 --- a/docs/urlsession-and-the-combine-framework/index.html +++ /dev/null @@ -1,493 +0,0 @@ - - - - - - - - - - - - URLSession and the Combine framework - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 4 min read - -
-

URLSession and the Combine framework

-
-

Learn how to make HTTP requests and parse the response using the brand new Combine framework with foundation networking.

-
- -

This is going to be a really short, but hopefully very useful tutorial about how I started to utilize the Combine framework to slowly replace my Promise library. 🤫

API & data structure

First of all we’re going to need some kind of API to connect, as usual I’m going to use my favorite JSONPlaceholder service with the following data models:

enum HTTPError: LocalizedError {
-    case statusCode
-    case post
-}
-
-struct Post: Codable {
-
-    let id: Int
-    let title: String
-    let body: String
-    let userId: Int
-}
-
-struct Todo: Codable {
-
-    let id: Int
-    let title: String
-    let completed: Bool
-    let userId: Int
-}
-

Nothing special so far, just some basic Codable elements, and a simple error, because hell yeah, we want to show some error if something fails. ❌

The traditional way

Doing an HTTP request in Swift is pretty easy, you can use the built-in shared URLSession with a simple data task, and voilá there’s your response. Of course you might want to check for valid status code and if everything is fine, you can parse your response JSON by using the JSONDecoder object from Foundation.

//somewhere in viewDidLoad
-let url = URL(string: "https://jsonplaceholder.typicode.com/posts")!
-
-let task = URLSession.shared.dataTask(with: url) { data, response, error in
-    if let error = error {
-        fatalError("Error: \(error.localizedDescription)")
-    }
-    guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
-        fatalError("Error: invalid HTTP response code")
-    }
-    guard let data = data else {
-        fatalError("Error: missing response data")
-    }
-
-    do {
-        let decoder = JSONDecoder()
-        let posts = try decoder.decode([Post].self, from: data)
-        print(posts.map { $0.title })
-    }
-    catch {
-        print("Error: \(error.localizedDescription)")
-    }
-}
-task.resume()
-

Don’t forget to resume your data task or the request won’t fire at all. 🔥

Data tasks and the Combine framework

Now as you can see the traditional “block-based” approach is nice, but can we do maybe something better here? You know, like describing the whole thing as a chain, like we used to do this with Promises? Beginning from iOS13 with the help of the amazing Combine framework you actually can go far beyond! 😃

My favorite part of Combine is memory management & cancellation.

Data task with Combine

So the most common example is usually the following one:

private var cancellable: AnyCancellable?
-//...
-self.cancellable = URLSession.shared.dataTaskPublisher(for: url)
-.map { $0.data }
-.decode(type: [Post].self, decoder: JSONDecoder())
-.replaceError(with: [])
-.eraseToAnyPublisher()
-.sink(receiveValue: { posts in
-    print(posts.count)
-})
-//...
-self.cancellable?.cancel()
-

I love how the code “explains itself”:

  • First we make a cancellable storage for your Publisher
  • Then we create a brand new data task publisher object
  • Map the response, we only care about the data part (ignore errors)
  • Decode the content of the data using a JSONDecoder
  • If anything goes wrong, just go with an empty array
  • Erase the underlying complexity to a simple AnyPublisher
  • Use sink to display some info about the final value
  • Optional: you can cancel your network request any time

Error handling

Let’s introduce some error handling, because I don’t like the idea of hiding errors. It’s so much better to present an alert with the actual error message, isn’t it? 🤔

enum HTTPError: LocalizedError {
-    case statusCode
-}
-
-self.cancellable = URLSession.shared.dataTaskPublisher(for: url)
-.tryMap { output in
-    guard let response = output.response as? HTTPURLResponse, response.statusCode == 200 else {
-        throw HTTPError.statusCode
-    }
-    return output.data
-}
-.decode(type: [Post].self, decoder: JSONDecoder())
-.eraseToAnyPublisher()
-.sink(receiveCompletion: { completion in
-    switch completion {
-    case .finished:
-        break
-    case .failure(let error):
-        fatalError(error.localizedDescription)
-    }
-}, receiveValue: { posts in
-    print(posts.count)
-})
-

In a nutshell, this time we check the response code and if something goes wrong we throw an error. Now because the publisher can result in an error state, sink has another variant, where you can check the outcome of the entire operation so you can do your own error thingy there, like displaying an alert. 🚨

Assign result to property

Another common pattern is to store the response in an internal variable somewhere in the view controller. You can simply do this by using the assign function.

class ViewController: UIViewController {
-
-    private var cancellable: AnyCancellable?
-
-    private var posts: [Post] = [] {
-        didSet {
-            print("posts --> \(self.posts.count)")
-        }
-    }
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        let url = URL(string: "https://jsonplaceholder.typicode.com/posts")!
-
-        self.cancellable = URLSession.shared.dataTaskPublisher(for: url)
-        .map { $0.data }
-        .decode(type: [Post].self, decoder: JSONDecoder())
-        .replaceError(with: [])
-        .eraseToAnyPublisher()
-        .assign(to: \.posts, on: self)
-    }
-}
-

Very easy, you can also use the didSet property observer to get notified about changes.

Group multiple requests

Sending multiple requests was a painful process in the past. Now we have Compose and this task is just ridiculously easy with Publishers.Zip. You can literally combine multiple requests togeter and wait until both of them are finished. 🤐

let url1 = URL(string: "https://jsonplaceholder.typicode.com/posts")!
-let url2 = URL(string: "https://jsonplaceholder.typicode.com/todos")!
-
-let publisher1 = URLSession.shared.dataTaskPublisher(for: url1)
-.map { $0.data }
-.decode(type: [Post].self, decoder: JSONDecoder())
-
-let publisher2 = URLSession.shared.dataTaskPublisher(for: url2)
-.map { $0.data }
-.decode(type: [Todo].self, decoder: JSONDecoder())
-
-self.cancellable = Publishers.Zip(publisher1, publisher2)
-.eraseToAnyPublisher()
-.catch { _ in
-    Just(([], []))
-}
-.sink(receiveValue: { posts, todos in
-    print(posts.count)
-    print(todos.count)
-})
-

Same pattern as before, we’re just zipping together two publishers.

Request dependency

Sometimes you have to load a resource from a given URL, and then use another one to extend the object with something else. I’m talking about request dependency, which was quite problematic without Combine, but now you can chain two HTTP calls together with just a few lines of Swift code. Let me show you:

override func viewDidLoad() {
-    super.viewDidLoad()
-
-    let url1 = URL(string: "https://jsonplaceholder.typicode.com/posts")!
-
-    self.cancellable = URLSession.shared.dataTaskPublisher(for: url1)
-    .map { $0.data }
-    .decode(type: [Post].self, decoder: JSONDecoder())
-    .tryMap { posts in
-        guard let id = posts.first?.id else {
-            throw HTTPError.post
-        }
-        return id
-    }
-    .flatMap { id in
-        return self.details(for: id)
-    }
-    .sink(receiveCompletion: { completion in
-
-    }) { post in
-        print(post.title)
-    }
-}
-
-func details(for id: Int) -> AnyPublisher<Post, Error> {
-    let url = URL(string: "https://jsonplaceholder.typicode.com/posts/\(id)")!
-    return URLSession.shared.dataTaskPublisher(for: url)
-        .mapError { $0 as Error }
-        .map { $0.data }
-        .decode(type: Post.self, decoder: JSONDecoder())
-        .eraseToAnyPublisher()
-}
-

The trick here is that you can flatMap a publisher into another.

Conclusion

Combine is an amazing framework, it can do a lot, but it definitely has some learning curve. Sadly you can only use it if you are targeting iOS13 or above (this means that you have one whole year to learn every single bit of the framework) so think twice before adopting this new technology.

You should also note that currently there is no upload and download task publisher, but you can make your very own solution until Apple officially releases something. Fingers crossed. 🤞

I really love how Apple implemented some concepts of reactive programming, I can’t wait for Combine to arrive as an open source package with Linux support as well. ❤️

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

How to use iCloud drive documents?

-
-

Learn how to sync files and data through a shared iCloud drive folder using the latest version of Swift programming language.

- -
- iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

iOS Auto Layout tutorial programmatically

-
-

In this great iOS Auto Layout tutorial I'll teach you how to support rotation, use constraints, work with layers, animate corner radius.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

iOS custom transition tutorial in Swift

-
-

In this tutorial, you'll learn how to replace the push, pop and modal animations with custom transitions & percent driven interactions.

- -
- UIKit - iOS -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/utilizing-makefiles-for-swift-projects/index.html b/docs/utilizing-makefiles-for-swift-projects/index.html deleted file mode 100644 index 718c747..0000000 --- a/docs/utilizing-makefiles-for-swift-projects/index.html +++ /dev/null @@ -1,391 +0,0 @@ - - - - - - - - - - - - Utilizing Makefiles for Swift projects - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 2 min read - -
-

Utilizing Makefiles for Swift projects

-
-

In this tutorial I'll show you how to use Makefiles for server-side Swift projects to help running utility tasks in a more simple way.

-
- -

Make is a build automation software that you can use to automatically run various commands. If you want to run something, you have to specify your commands (more precisely: build targets) through Makefiles. In this quick tutorial I’ll show you some of my best practices for Swift projects. 😉

Usually I create a Makefile for my server-side Swift projects and place some of the most used Swift Package Manager commands there.

# My Makefile - for server side Swift projects
-
-build:
-    swift build
-
-update: 
-    swift package update
-
-release:
-    swift build -c release
-    
-test:
-    swift test --parallel
-
-clean:
-    rm -rf .build
-

This way, for example, I can simply run the make release command to create a release version of my Swift package. I usually end-up adding even more complex commands to the Makefile, another common scenario is, when the package has an executable target. I usually create an install and uninstall command to quickly setup or remove the binary product locally. 🏗️

install: release
-    install ./.build/release/my-app /usr/local/bin/my-app
-
-uninstall:
-    rm /usr/local/bin/my-app
-

As you might know, nowadays I mostly create Vapor-based apps (or Hummingbird, but that deserves a separate post), so it’s really convenient to have a dedicated set of commands inside my Makefile to manage the state of the server application. 💧

start:
-    my-app serve --port 8080 &
-    
-stop:
-    @lsof -i :8080 -sTCP:LISTEN | awk 'NR > 1 {print $$2}' | xargs kill -15
-
-restart: stop start
-
-reset: stop
-    rm -f ./Resources/db.sqlite
-

By using the & at the end of the start command the server will run in the background, and using the @ character before the lsof command will silence the output of the make command (By default the make command will echo out your commands as well).

Since everything should work under Linux as well I often use Docker to run the app in a container. I have a Docker cheat-sheet, but I’m also a lazy developer, so I made a few helpers in the Makefile.

#
-# Dockerfile:
-# ----
-#
-# FROM swift:5.7-amazonlinux2
-# 
-# WORKDIR /my-app
-#
-# ----
-#
-
-docker-build-image:
-    docker build -t my-app-image .
-
-docker-run:
-    docker run --name my-app-instance \
-        -v $(PWD):/my-app \
-        -w /my-app \
-        -e "PS1=\u@\w: " \
-        -it my-app-image \
-        --rm
-

First you have to build the image for the Swift application, for this purpose you also have to create a Dockerfile next to the Makefile, but afterwards you can create a disposable docker instance from it by using the make docker-run command. 🐳

There are two more topics I’d like to talk about. The first one is related to code coverage generation for Swift package manager based apps. Here is what I have in my Makefile to support this:

test-with-coverage:
-    swift test --parallel --enable-code-coverage
-
-# 
-# Install dependencies (on macOS):
-# ----
-# brew install llvm
-# echo 'export PATH="/usr/local/opt/llvm/bin:$PATH"' >> ~/.zshrc
-# ----
-# 
-code-coverage: test-with-coverage
-    llvm-cov report \
-        .build/x86_64-apple-macosx/debug/myAppPackageTests.xctest/Contents/MacOS/myAppPackageTests \
-        -instr-profile=.build/x86_64-apple-macosx/debug/codecov/default.profdata \
-        -ignore-filename-regex=".build|Tests" \
-        -use-color
-

You can easily generate code coverage data by running the make code-coverage command. If you want to know more about the underlying details, please refer to the linked article.

The very last thing is going to be about documentation. Apple released DocC for Swift quite a long time ago and now it seems like a lot of people are using it. Initially I was not a huge fan of DocC, but now I am for sure. It is possible to simplify the doc generation process through Makefiles and I tend to run the make docs-preview command quite often to have a quick sneak peak of the API. 🔨

docs-preview:
-    swift package --disable-sandbox preview-documentation --target MyLibrary
-
-docs-generate:
-    swift package generate-documentation \
-        --target MyLibrary
-
-docs-generate-static:
-    swift package --disable-sandbox \
-        generate-documentation \
-        --transform-for-static-hosting \
-        --hosting-base-path "MyLibrary" \
-        --target MyLibrary \
-        --output-path ./docs
-

Of course you can add more targets to your Makefile to automate your workflow as needed. These are just a few common practices that I’m currently using for my server-side Swift projects. iOS developers can also take advantage of Makefiles, there are some quite lenghty xcodebuild related commands that you can simplify a lot by using a Makefile. 💪

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Conventions for Xcode

-
-

Learn how to organize your codebase. If you are struggling with Xcode project structure, files, naming conventions, read this.

- -
- Tooling - Xcode -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

Custom working directory in Xcode

-
-

Learn how to set a custom working directory in Xcode to solve one of the most common beginner issue when using Vapor.

- -
- Tooling - Xcode -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

How to launch a macOS app at login?

-
-

In this tutorial I'll show you how to launch a completely sandboxed macOS application on system startup written in Swift.

- -
- Tooling - macOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 2 min read -
- -

How to store keys in env files?

-
-

In this tutorial I'll show you how to save and load secret keys as base64 encoded strings using dotenv files in Vapor 4.

- -
- Server - Tooling - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - -
-
- -
- - - -
- - - - diff --git a/docs/viper-best-practices-for-ios-developers/index.html b/docs/viper-best-practices-for-ios-developers/index.html deleted file mode 100644 index 537672c..0000000 --- a/docs/viper-best-practices-for-ios-developers/index.html +++ /dev/null @@ -1,690 +0,0 @@ - - - - - - - - - - - - VIPER best practices for iOS developers - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 8 min read - -
-

VIPER best practices for iOS developers

-
-

In this tutorial I'm going to show you a complete guide about how to build a VIPER based iOS application, written entirely in Swift.

-
- -

Getting started with VIPER

First of all, you should read my previous (more theoretical) article about the VIPER architecture itself. It’s a pretty decent writing explaining all the VIPER components and memory management. I’ve also polished it a little bit, last week. ⭐️

The problem with that article however was that I haven’t show you the real deal, aka. the Swift code for implementing VIPER. Now after a full year of projects using this architecture I can finally share all my best practices with you.

So, let’s start by creating a brand new Xcode project, use the single view app template, name the project (VIPER best practices), use Swift and now you’re ready to take the next step of making an awesome “enterprise grade” iOS app.

Generating VIPER modules

Lesson 1: never create a module by hand, always use a code generator, because it’s a repetative task, it’s fuckin’ boring plus you should focus on more important things than making boilerplate code. You can use my lightweight module generator called:

This section is outdated, you should use the swift template repository.

Just download or clone the repository from GitHub. You can install the binary tool by running swift run install –with-templates. This will install the vipera app under /usr/local/bin/ and the basic templates under the ~/.vipera directory. You can use your own templates too, but for now I’ll work with the default one. 🔨

I usually start with a module called Main that’s the root view of the application. You can generate it by calling vipera Main in the project directory, so the generator can use the proper project name for the header comments inside the template files.

Clean up the project structure a little bit, by applying my conventions for Xcode, this means that resources goes to an Assets folder, and all the Swift files into the Sources directory. Nowadays I also change the AppDelegate.swift file, and I make a separate extension for the UIApplicationDelegate protocol.

Create a Modules group (with a physical folder too) under the Sources directory and move the newly generated Main module under that group. Now fix the project issues, by selecting the Info.plist file from the Assets folder for the current target. Also do remove the Main Interface, and after that you can safely delete the Main.storyboard and the ViewController.swift files, because we’re not going to need them at all.

Inside the AppDelegate.swift file, you have to set the Main module’s view controller as the root view controller, so it should look somewhat like this:

import UIKit
-
-@UIApplicationMain
-class AppDelegate: UIResponder {
-
-    var window: UIWindow?
-}
-
-extension AppDelegate: UIApplicationDelegate {
-
-    func application(_ application: UIApplication,
-                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
-
-        self.window = UIWindow(frame: UIScreen.main.bounds)
-        self.window?.rootViewController = MainModule().buildDefault()
-        self.window?.makeKeyAndVisible()
-
-        return true
-    }
-}
-

Congratulations, you’ve created your very first VIPER module! 🎉

UITabBarController & VIPER

I have a super simple solution for using a tab bar controller in a VIPER module. First let’s generate a few new modules, those are going to be the tabs. I’m going to use the JSONPlaceholder service, so let’s imagine a separate tab for each of these resources: posts, albums, photos, todos (with the same module name). Generate them all, and move them into the modules folder.

Now, let’s generate one more module called Home. This will implement our tab bar controller view. If you want you can use the Main module for this purpose, but I like to keep that for animation purposes, to have a neat transition between the loading screen and my Home module (it all depends on your needs).

So the main logic that we’re going to implement is this: the main view will notify the presenter about the viewDidAppear event, and the presenter will ask the router to display the Home module. The Home module’s view will be a subclass of a UITabBarController, it’ll also notify it’s presenter about viewDidLoad, and the presenter will ask for the proper tabs, by using its router.

Here is the code, without the interfaces:

class MainDefaultView: UIViewController {
-
-    var presenter: MainPresenter?
-
-    override func viewDidAppear(_ animated: Bool) {
-        super.viewDidAppear(animated)
-
-        self.presenter?.viewDidAppear()
-    }
-}
-
-extension MainDefaultPresenter: MainPresenter {
-
-    func viewDidAppear() {
-        self.router?.showHome()
-    }
-}
-
-extension MainDefaultRouter: MainRouter {
-
-    func showHome() {
-        let viewController = HomeModule().buildDefault()
-        self.viewController?.present(viewController, animated: true, completion: nil)
-    }
-}
-
-extension HomeDefaultView: HomeView {
-
-    func display(_ viewControllers: [UIViewController]) {
-        self.viewControllers = viewControllers
-    }
-}
-
-// MARK: - Home module
-
-extension HomeDefaultPresenter: HomePresenter {
-
-    func setupViewControllers() {
-        guard let controllers = self.router?.getViewControllers() else {
-            return
-        }
-        self.view?.display(controllers)
-    }
-
-}
-
-extension HomeDefaultRouter: HomeRouter {
-
-    func getViewControllers() -> [UIViewController] {
-        return [
-            PostsModule().buildDefault(),
-            AlbumsModule().buildDefault(),
-            PhotosModule().buildDefault(),
-            TodosModule().buildDefault(),
-        ].map { UINavigationController(rootViewController: $0) }
-    }
-}
-
-class HomeModule {
-
-    func buildDefault() -> UIViewController {
-        /* ... */
-
-        presenter.setupViewControllers()
-
-        return view
-    }
-}
-

There is one additional line inside the Home module builder function that triggers the presenter to setup proper view controllers. That’s just because the UITabBarController viewDidLoad method gets called before the init process finishes. This behaviour is quite undocumented but I assume it’s an UIKit hack in order to maintain the view references (or just a simple bug… is anyone from Apple here?). 😊

Anyway, now you have a proper tab bar inside the project integrated as a VIPER module. It’s time to get some data from the server and here comes another important lesson: not everything is a VIPER module.

Services and entities
As you might noticed there is no such thing as an Entity inside my modules. I usually wrap APIs, CoreData and many more data providers as a service. This way, all the related entities can be abstracted away, so the service can be easily replaced (with a mock for example) and all my interactors can use the service through the protocol definition without knowing the underlying implementation.

Another thing is that I always use my promise library if I have to deal with async code. The reason behind it is pretty simple: it’s way more elegant than using callbacks and optional result elements. You should learn promises too. So here is some part of my service implementation around the JSONPlaceholder API:

protocol Api {
-
-    func posts() -> Promise<[Post]>
-    func comments(for post: Post) -> Promise<[Comment]>
-    func albums() -> Promise<[Album]>
-    func photos(for album: Album) -> Promise<[Photo]>
-    func todos() -> Promise<[Todo]>
-}
-
-// MARK: - entities
-
-struct Post: Codable {
-
-    let id: Int
-    let title: String
-    let body: String
-}
-
-// MARK: - API implementation
-
-class JSONPlaceholderService {
-
-    var baseUrl = URL(string: "https://jsonplaceholder.typicode.com/")!
-
-    enum Error: LocalizedError {
-        case invalidStatusCode
-        case emptyData
-    }
-
-    private func request<T>(path: String) -> Promise<T> where T: Decodable {
-        let promise = Promise<T>()
-        let url = baseUrl.appendingPathComponent(path)
-        print(url)
-        URLSession.shared.dataTask(with: url) { data, response, error in
-            if let error = error {
-                promise.reject(error)
-                return
-            }
-            guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
-                promise.reject(Error.invalidStatusCode)
-                return
-            }
-            guard let data = data else {
-                promise.reject(Error.emptyData)
-                return
-            }
-            do {
-                let model = try JSONDecoder().decode(T.self, from: data)
-                promise.fulfill(model)
-            }
-            catch {
-                promise.reject(error)
-            }
-        }.resume()
-        return promise
-    }
-}
-
-extension JSONPlaceholderService: Api {
-
-    func posts() -> Promise<[Post]> {
-        return self.request(path: "posts")
-    }
-
-    /* ... */
-}
-

Usually I have a mock service implementation next to this one, so I can easily test out everything I want. How do I switch between these services? Well, there is a shared (singleton - don’t hate me it’s completely fine 🤪) App class that I use mostly for styling purposes, but I also put the dependency injection (DI) related code there too. This way I can pass around proper service objects for the VIPER modules.

class App {
-
-    static let shared = App()
-
-    private init() {
-
-    }
-
-    var apiService: Api {
-        return JSONPlaceholderService()
-    }
-}
-
-// MARK: - module
-
-class PostsModule {
-
-    func buildDefault() -> UIViewController {
-        let view = PostsDefaultView()
-        let interactor = PostsDefaultInteractor(apiService: App.shared.apiService)
-
-        /* ... */
-
-        return view
-    }
-}
-
-// MARK: - interactor
-
-class PostsDefaultInteractor {
-
-    weak var presenter: PostsPresenter?
-
-    var apiService: Api
-
-    init(apiService: Api) {
-        self.apiService = apiService
-    }
-}
-
-extension PostsDefaultInteractor: PostsInteractor {
-
-    func posts() -> Promise<[Post]> {
-        return self.apiService.posts()
-    }
-
-}
-

You can do this in a 100 other ways, but I currently prefer this approach. This way interactors can directly call the service with some extra details, like filters, order, sort, etc. Basically the service is just a high concept wrapper around the endpoint, and the interactor is creating the fine-tuned (better) API for the presenter.

Making promises

Implementing the business logic is the task of the presenter. I always use promises so a basic presenter implementation that only loads some content asynchronously and displays the results or the error (plus a loading indicator) is just a few lines long. I’m always trying to implement the three basic UI stack elements (loading, data, error) by using the same protocol naming conventions on the view. 😉

On the view side I’m using my good old collection view logic, which significantly reduces the amount of code I have to write. You can go with the traditional way, implementing a few data source & delegate method for a table or collection view is not so much code after all. Here is my view example:

extension PostsDefaultPresenter: PostsPresenter {
-
-    func viewDidLoad() {
-        self.view?.displayLoading()
-        self.interactor?.posts()
-        .onSuccess(queue: .main) { posts  in
-            self.view?.display(posts)
-        }
-        .onFailure(queue: .main) { error in
-            self.view?.display(error)
-        }
-    }
-}
-
-// MARK: - view
-
-class PostsDefaultView: CollectionViewController {
-
-    var presenter: PostsPresenter?
-
-    init() {
-        super.init(nibName: nil, bundle: nil)
-
-        self.title = "Posts"
-    }
-
-    required init?(coder aDecoder: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        self.presenter?.viewDidLoad()
-    }
-}
-
-extension PostsDefaultView: PostsView {
-
-    func displayLoading() {
-        print("loading...")
-    }
-
-    func display(_ posts: [Post]) {
-        let grid = Grid(columns: 1, margin: UIEdgeInsets(all: 8))
-
-        self.source = CollectionViewSource(grid: grid, sections: [
-            CollectionViewSection(items: posts.map { PostViewModel($0) })
-        ])
-        self.collectionView.reloadData()
-    }
-
-    func display(_ error: Error) {
-        print(error.localizedDescription)
-    }
-}
-

The cell and the ViewModel is outside the VIPER module, I tend to dedicate an App folder for the custom application specific views, extensions, view models, etc.

class PostCell: CollectionViewCell {
-
-    @IBOutlet weak var textLabel: UILabel!
-}
-
-class PostViewModel: CollectionViewViewModel<PostCell, Post> {
-
-    override func config(cell: PostCell, data: Post, indexPath: IndexPath, grid: Grid) {
-        cell.textLabel.text = data.title
-    }
-
-    override func size(data: Post, indexPath: IndexPath, grid: Grid, view: UIView) -> CGSize {
-        let width = grid.width(for: view, items: grid.columns)
-        return CGSize(width: width, height: 64)
-    }
-}
-

Nothing special, if you’d like to know more about this collection view architecture, you should read my other tutorial about mastering collection views.

Module communication

Another important lesson is to learn how to communicate between two VIPER modules. Normally I go with simple variables - and delegates if I have to send back some sort of info to the original module - that I pass around inside the build methods. I’m going to show you a really simple example for this too.

class PostsDefaultRouter {
-
-    weak var presenter: PostsPresenter?
-    weak var viewController: UIViewController?
-}
-
-extension PostsDefaultRouter: PostsRouter {
-
-    func showComments(for post: Post) {
-        let viewController = PostDetailsModule().buildDefault(with: post, delegate: self)
-        self.viewController?.show(viewController, sender: nil)
-    }
-}
-
-extension PostsDefaultRouter: PostDetailsModuleDelegate {
-
-    func toggleBookmark(for post: Post) {
-        self.presenter?.toggleBookmark(for: post)
-    }
-}
-
-// MARK: - details
-
-
-protocol PostDetailsModuleDelegate: class {
-    func toggleBookmark(for post: Post)
-}
-
-class PostDetailsModule {
-
-    func buildDefault(with post: Post, delegate: PostDetailsModuleDelegate? = nil) -> UIViewController {
-        let view = PostDetailsDefaultView()
-        let interactor = PostDetailsDefaultInteractor(apiService: App.shared.apiService,
-                                                      bookmarkService: App.shared.bookmarkService)
-        let presenter = PostDetailsDefaultPresenter(post: post)
-
-        /* ... */
-
-        return view
-    }
-}
-
-class PostDetailsDefaultRouter {
-
-    weak var presenter: PostDetailsPresenter?
-    weak var viewController: UIViewController?
-    weak var delegate: PostDetailsModuleDelegate?
-}
-
-extension PostDetailsDefaultRouter: PostDetailsRouter {
-
-    func toggleBookmark(for post: Post) {
-        self.delegate?.toggleBookmark(for: post)
-    }
-}
-
-
-class PostDetailsDefaultPresenter {
-
-    var router: PostDetailsRouter?
-    var interactor: PostDetailsInteractor?
-    weak var view: PostDetailsView?
-
-    let post: Post
-
-    init(post: Post) {
-        self.post = post
-    }
-}
-
-extension PostDetailsDefaultPresenter: PostDetailsPresenter {
-
-    func reload() {
-        self.view?.setup(with: self.interactor!.bookmark(for: self.post))
-
-        //display loading...
-        self.interactor?.comments(for: self.post)
-        .onSuccess(queue: .main) { comments in
-            self.view?.display(comments)
-        }
-        .onFailure(queue: .main) { error in
-            //display error...
-        }
-    }
-
-    func toggleBookmark() {
-        self.router?.toggleBookmark(for: self.post)
-        self.view?.setup(with: self.interactor!.bookmark(for: self.post))
-    }
-}
-

In the builder method I can access every component of the VIPER module so I can simply pass around the variable to the designated place (same applies for the delegate parameter). I usually set input variables on the presenter and delegates on the router.

It’s usually a presenter who needs data from the original module, and I like to store the delegate on the router, because if the navigation pattern changes I don’t have to change the presenter at all. This is just a personal preference, but I like the way it looks like in code. It’s really hard to write down these things in a single article, so I’d recommend to download my finished sample code from GitHub.

Summary

As you can see I’m using various design patterns in this VIPER architecture tutorial. Some say that there is no silver bullet, but I believe that I’ve found a really amazing methodology that I can turn on my advantage to build quality apps in a short time.

Combining Promises, MVVM with collection views on top of a VIPER structure simply puts every single piece into the right place. Over-engineered? Maybe. For me it’s worth the overhead. What do you think about it? Feel free to message me through twitter. You can also subscribe to my monthly newsletter below.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

How to build SwiftUI apps using VIPER?

-
-

In this tutorial I'll show you how to combine SwiftUI with the VIPER architecture in a real world iOS application example.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 7 min read -
- -

How to write services for VIPER?

-
-

Not everything is a VIPER module. In this article I'll show you how do I separate the service layer from the modules, using Swift.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Mastering the VIPER architecture

-
-

Learn how to master the VIPER architectural design pattern, with some protocol oriented programming techniques using Swift.

- -
- VIPER -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

The ultimate VIPER architecture tutorial

-
-

Learn how to write scalable iOS code using the VIPER architecture with some MVVM and MVC tricks and coordinators in mind.

- -
- VIPER -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/websockets-for-beginners-using-vapor-4-and-vanilla-javascript/index.html b/docs/websockets-for-beginners-using-vapor-4-and-vanilla-javascript/index.html deleted file mode 100644 index 5b84367..0000000 --- a/docs/websockets-for-beginners-using-vapor-4-and-vanilla-javascript/index.html +++ /dev/null @@ -1,771 +0,0 @@ - - - - - - - - - - - - Websockets for beginners using Vapor 4 and Vanilla JavaScript - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 13 min read - -
-

Websockets for beginners using Vapor 4 and Vanilla JavaScript

-
-

Learn how to create a websocket server using Swift & Vapor. Multiplayer game development using JavaScript in the browser.

-
- -

What the heck is a websocket?

The HTTP protocol is a fundamental building block of the internet, you can use a browser to request a website using a request-response based communication model. The web browser submits a HTTP request to the server, then the server responds with a response. The response contains status information, content related headers and the message body. In most cases after you receive some kind of response the connection will be closed. End of story.

The communication model described above can be ideal for most of the websites, but what happens when you would like to constantly transmit data over the network? Just think about real-time web applications or games, they need a constant data flow between the server and the client. Initiating a connection is quite an expensive task, you could keep the connection alive with some hacky tricks, but fortunately there is a better approach. 🍀

The Websocket communication model allows us to continuously send and receive messages in both direction (full-duplex) over a single TCP connection. A socket can be used to communicate between two different processes on different machines using standard file descriptors. This way we can have a dedicated channel to a given server through a socket and use that channel any time to deliver or receive messages instead of using requests & responses.

Websockets can be used to notify the client if something happens on the server, this comes handy in many cases. If you want to build a communication heavy application such as a messenger or a multiplayer game you should definitely consider using this kind of technology.

Websockets in Vapor 4

Vapor 4 comes with built-in websockets support without additional dependencies. The underlying SwiftNIO framework provides the functionality, so we can hook up a websocket service into our backend app with just a few lines of Swift code. You can check the official documentation for the available websocket API methods, it is pretty straightforward. 💧

In this tutorial we are going to build a massively multiplayer online tag game using websockets. Start a new project using the vapor new myProject command, we don’t need a database driver this time. Delete the routes.swift file and the Controllers folder. Feel free to clean up the configuration method, we don’t need to have anything there just yet.

The very first thing that we want to achieve is an identification system for the websocket clients. We have to uniquely identify each client so we can send messages back to them. You should create a Websocket folder and add a new WebsocketClient.swift file inside of it.

import Vapor
-
-open class WebSocketClient {
-    open var id: UUID
-    open var socket: WebSocket
-
-    public init(id: UUID, socket: WebSocket) {
-        self.id = id
-        self.socket = socket
-    }
-}
-

We are going to store all the connected websocket clients and associate every single one with a unique identifier. The unique identifier will come from the client, but of course in a real world server you might want to ensure the uniqueness on the server side by using some kind of generator.

The next step is to provide a storage for all the connected clients. We are going to build a new WebsocketClients class for this purpose. This will allow us to add, remove or quickly find a given client based on the unique identifier. 🔍

import Vapor
-
-open class WebsocketClients {
-    var eventLoop: EventLoop
-    var storage: [UUID: WebSocketClient]
-    
-    var active: [WebSocketClient] {
-        self.storage.values.filter { !$0.socket.isClosed }
-    }
-
-    init(eventLoop: EventLoop, clients: [UUID: WebSocketClient] = [:]) {
-        self.eventLoop = eventLoop
-        self.storage = clients
-    }
-    
-    func add(_ client: WebSocketClient) {
-        self.storage[client.id] = client
-    }
-
-    func remove(_ client: WebSocketClient) {
-        self.storage[client.id] = nil
-    }
-    
-    func find(_ uuid: UUID) -> WebSocketClient? {
-        self.storage[uuid]
-    }
-
-    deinit {
-        let futures = self.storage.values.map { $0.socket.close() }
-        try! self.eventLoop.flatten(futures).wait()
-    }
-}
-

We are using the EventLoop object to close every socket connection when we don’t need them anymore. Closing a socket is an async operation that’s why we have to flatten the futures and wait before all of them are closed.

Clients can send any kind of data (ByteBuffer) or text to the server, but it would be real nice to work with JSON objects, plus if they could provide the associated unique identifier right next to the incoming message that would have other benefits.

To make this happen we will create a generic WebsocketMessage object. There is a hacky solution to decode incoming messages from JSON data. Bastian Inuk showed me this one, but I believe it is pretty simple & works like a charm. Thanks for letting me borrow your idea. 😉

import Vapor
-
-struct WebsocketMessage<T: Codable>: Codable {
-    let client: UUID
-    let data: T
-}
-
-extension ByteBuffer {
-    func decodeWebsocketMessage<T: Codable>(_ type: T.Type) -> WebsocketMessage<T>? {
-        try? JSONDecoder().decode(WebsocketMessage<T>.self, from: self)
-    }
-}
-

That’s about the helpers, now we should figure out what kind of messages do we need, right?

First of all, we’d like to store a client after a successful connection event happens. We are going to use a Connect message for this purpose. The client will send a simple connect boolean flag, right after the connection was established so the server can save the client.

import Foundation
-
-struct Connect: Codable {
-    let connect: Bool
-}
-

We are building a game, so we need players as clients, let’s subclass the WebSocketClient class, so we can store additional properties on it later on.

import Vapor
-
-final class PlayerClient: WebSocketClient {
-    
-    public init(id: UUID, socket: WebSocket, status: Status) {
-        super.init(id: id, socket: socket)
-    }
-}
-

Now we have to make a GameSystem object that will be responsible for storing clients with associated identifiers and decoding & handling incoming websocket messages.

import Vapor
-
-class GameSystem {
-    var clients: WebsocketClients
-
-    init(eventLoop: EventLoop) {
-        self.clients = WebsocketClients(eventLoop: eventLoop)
-    }
-
-    func connect(_ ws: WebSocket) {
-        ws.onBinary { [unowned self] ws, buffer in
-            if let msg = buffer.decodeWebsocketMessage(Connect.self) {
-                let player = PlayerClient(id: msg.client, socket: ws)
-                self.clients.add(player)
-            }
-        }
-    }
-}
-

We can hook up the GameSystem class inside the config method to a websocket channel using the built-in .webSocket method, that’s part of the Vapor 4 framework by default.

import Vapor
-
-public func configure(_ app: Application) throws {
-    app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
-    
-    let gameSystem = GameSystem(eventLoop: app.eventLoopGroup.next())
-
-    app.webSocket("channel") { req, ws in
-        gameSystem.connect(ws)
-    }
-    
-    app.get { req in
-        req.view.render("index.html")
-    }
-}
-

We are also going to render a new view called index.html, the plaintext renderer is the default in Vapor so we don’t have to set up Leaf if we want to display with basic HTML files.

<html>
-<head>
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <title>Sockets</title>
-</head>
-
-<body>
-    <div style="float: left; margin-right: 16px;">
-        <canvas id="canvas" width="640" height="480" style="width: 640px; height: 480px; border: 1px dashed #000;"></canvas>
-        <div>
-            <a href="javascript:WebSocketStart()">Start</a>
-            <a href="javascript:WebSocketStop()">Stop</a>
-        </div>
-    </div>
-
-    <script src="js/main.js"></script>
-</body>
-</html>
-

We can save the snippet from above under the Resources/Views/index.html file. The canvas will be used to render our 2d game, plus will need some additional JavaScript magic to start and stop the websocket connection using the control buttons. ⭐️

A websocket client using JavaScript

Create a new Public/js/main.js file with the following contents, I’ll explain everything below.

function blobToJson(blob) {
-    return new Promise((resolve, reject) => {
-        let fr = new FileReader();
-        fr.onload = () => {
-            resolve(JSON.parse(fr.result));
-        };
-        fr.readAsText(blob);
-    });
-}
-
-function uuidv4() {
-    return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16));
-}
-
-WebSocket.prototype.sendJsonBlob = function(data) {
-    const string = JSON.stringify({ client: uuid, data: data })
-    const blob = new Blob([string], {type: "application/json"});
-    this.send(blob)
-};
-
-const uuid = uuidv4()
-let ws = undefined
-
-function WebSocketStart() {
-    ws = new WebSocket("wss://" + window.location.host + "/channel")
-    ws.onopen = () => {
-        console.log("Socket is opened.");
-        ws.sendJsonBlob({ connect: true })
-    }
-
-    ws.onmessage = (event) => {
-        blobToJson(event.data).then((obj) => {
-            console.log("Message received.");
-        })
-    };
-
-    ws.onclose = () => {
-        console.log("Socket is closed.");
-    };
-}
-
-function WebSocketStop() {
-    if ( ws !== undefined ) {
-        ws.close()
-    }
-}
-

We need some helper methods to convert JSON to blob and vica versa. The blobToJson function is an asynchronous method that returns a new Promise with the parsed JSON value of the original binary data. In JavaScript can use the .then method to chain promises. 🔗

The uuidv4 method is a unique identifier generator, it’s far from perfect, but we can use it to create a somewhat unique client identifier. We will call this in a few lines below.

In JavaScript you can extend a built-in functions, just like we extend structs, classes or protocols in Swift. We are extending the WebSocket object with a helper method to send JSON messages with the client UUID encoded as blob data (sendJsonBlob).

When the main.js file is loaded all the top level code gets executed. The uuid constant will be available for later use with a unique value, plus we construct a new ws variable to store the opened websocket connection locally. If you take a quick look at the HTML file you can see that there are two onClick listeners on the links, the WebSocketStart and WebSocketStop methods will be called when you click those buttons. ✅

Inside the start method we are initiating a new WebSocket connection using a URL string, we can use the window.location.host property to get the domain with the port. The schema should be wss for secure (HTTPS) connections, but you can also use the ws for insecure (HTTP) ones.

There are three event listeners that you can subscribe to. They work like delegates in the iOS world, once the connection is established the onopen handler will be called. In the callback function we send the connect message as a blob value using our previously defined helper method on the WebSocket object.

If there is an incoming message (onmessage) we can simply log it using the console.log method, if you bring up the inspector panel in a browser there is a Console tab where you will be able to see these kind of logs. If the connection is closed (onclose) we do the same. When the user clicks the stop button we can use the close method to manually terminate the websocket connection.

Now you can try to build & run what we have so far, but don’t expect more than raw logs. 😅

Building a websocket game

We will build a 2d catcher game, all the players are going to be represented as little colorful circles. A white dot will mark your own player and the catcher is going to be tagged with a black circle. Players need positions, colors and we have to send the movement controls from the client to the server side. The client will take care of the rendering, so we need to push the position of every connected player through the websocket channel. We will use a fixed size canvas for the sake of simplicity, but I’ll show you how to add support for HiDPI displays. 🎮

Let’s start by updating the server, so we can store everything inside the PlayerClient.

import Vapor
-
-final class PlayerClient: WebSocketClient {
-
-    struct Status: Codable {
-        var id: UUID!
-        var position: Point
-        var color: String
-        var catcher: Bool = false
-        var speed = 4
-    }
-    
-    var status: Status
-    var upPressed: Bool = false
-    var downPressed: Bool = false
-    var leftPressed: Bool = false
-    var rightPressed: Bool = false
-    
-    
-    public init(id: UUID, socket: WebSocket, status: Status) {
-        self.status = status
-        self.status.id = id
-
-        super.init(id: id, socket: socket)
-    }
-
-    func update(_ input: Input) {
-        switch input.key {
-        case .up:
-            self.upPressed = input.isPressed
-        case .down:
-            self.downPressed = input.isPressed
-        case .left:
-            self.leftPressed = input.isPressed
-        case .right:
-            self.rightPressed = input.isPressed
-        }
-    }
-
-    func updateStatus() {
-        if self.upPressed {
-            self.status.position.y = max(0, self.status.position.y - self.status.speed)
-        }
-        if self.downPressed {
-            self.status.position.y = min(480, self.status.position.y + self.status.speed)
-        }
-        if self.leftPressed {
-            self.status.position.x = max(0, self.status.position.x - self.status.speed)
-        }
-        if self.rightPressed {
-            self.status.position.x = min(640, self.status.position.x + self.status.speed)
-        }
-    }
-}
-

We are going to share the status of each player in every x millisecond with the clients, so they can re-render the canvas based on the fresh data. We also need a new Input struct, so clients can send key change events to the server and we can update players based on that.

import Foundation
-
-struct Input: Codable {
-
-    enum Key: String, Codable {
-        case up
-        case down
-        case left
-        case right
-    }
-
-    let key: Key
-    let isPressed: Bool
-}
-

Position values are stored as points with x and y coordinates, we can build a struct for this purpose with an additional function to calculate the distance between two players. If they get too close to each other, we can pass the tag to the catched player. 🎯

import Foundation
-
-struct Point: Codable {
-    var x: Int = 0
-    var y: Int = 0
-    
-    func distance(_ to: Point) -> Float {
-        let xDist = Float(self.x - to.x)
-        let yDist = Float(self.y - to.y)
-        return sqrt(xDist * xDist + yDist * yDist)
-    }
-}
-

Now the tricky part. The game system should be able to notify all the clients in every x milliseconds to provide a smooth 60fps experience. We can use the Dispatch framework to schedule a timer for this purpose. The other thing is that we want to avoid “tagbacks”, so after one player catched another we are going to put a 2 second timeout, this way users will have some time to run away.

import Vapor
-import Dispatch
-
-class GameSystem {
-    var clients: WebsocketClients
-
-    var timer: DispatchSourceTimer
-    var timeout: DispatchTime?
-        
-    init(eventLoop: EventLoop) {
-        self.clients = WebsocketClients(eventLoop: eventLoop)
-
-        self.timer = DispatchSource.makeTimerSource()
-        self.timer.setEventHandler { [unowned self] in
-            self.notify()
-        }
-        self.timer.schedule(deadline: .now() + .milliseconds(20), repeating: .milliseconds(20))
-        self.timer.activate()
-    }
-
-    func randomRGBAColor() -> String {
-        let range = (0..<255)
-        let r = range.randomElement()!
-        let g = range.randomElement()!
-        let b = range.randomElement()!
-        return "rgba(\(r), \(g), \(b), 1)"
-    }
-
-    func connect(_ ws: WebSocket) {
-        ws.onBinary { [unowned self] ws, buffer in
-            if let msg = buffer.decodeWebsocketMessage(Connect.self) {
-                let catcher = self.clients.storage.values
-                    .compactMap { $0 as? PlayerClient }
-                    .filter { $0.status.catcher }
-                    .isEmpty
-
-                let player = PlayerClient(id: msg.client,
-                                          socket: ws,
-                                          status: .init(position: .init(x: 0, y: 0),
-                                                        color: self.randomRGBAColor(),
-                                                        catcher: catcher))
-                self.clients.add(player)
-            }
-
-            if
-                let msg = buffer.decodeWebsocketMessage(Input.self),
-                let player = self.clients.find(msg.client) as? PlayerClient
-            {
-                player.update(msg.data)
-            }
-        }
-    }
-
-    func notify() {
-        if let timeout = self.timeout {
-            let future = timeout + .seconds(2)
-            if future < DispatchTime.now() {
-                self.timeout = nil
-            }
-        }
-
-        let players = self.clients.active.compactMap { $0 as? PlayerClient }
-        guard !players.isEmpty else {
-            return
-        }
-
-        let gameUpdate = players.map { player -> PlayerClient.Status in
-            player.updateStatus()
-            
-            players.forEach { otherPlayer in
-                guard
-                    self.timeout == nil,
-                    otherPlayer.id != player.id,
-                    (player.status.catcher || otherPlayer.status.catcher),
-                    otherPlayer.status.position.distance(player.status.position) < 18
-                else {
-                    return
-                }
-                self.timeout = DispatchTime.now()
-                otherPlayer.status.catcher = !otherPlayer.status.catcher
-                player.status.catcher = !player.status.catcher
-            }
-            return player.status
-        }
-        let data = try! JSONEncoder().encode(gameUpdate)
-        players.forEach { player in
-            player.socket.send([UInt8](data))
-        }
-    }
-    
-    deinit {
-        self.timer.setEventHandler {}
-        self.timer.cancel()
-    }
-}
-

Inside the notify method we’re using the built-in .send method on the WebSocket object to send binary data to the clients. In a chat application we would not require the whole timer logic, but we could simply notify everyone inside the onBinary block after a new incoming chat message.

The server is now ready to use, but we still have to alter the WebSocketStart method on the client side to detect key presses and releases and to render the incoming data on the canvas element.

function WebSocketStart() {
-
-    function getScaled2DContext(canvas) {
-        const ctx = canvas.getContext('2d')
-        const devicePixelRatio = window.devicePixelRatio || 1
-        const backingStorePixelRatio = [
-            ctx.webkitBackingStorePixelRatio,
-            ctx.mozBackingStorePixelRatio,
-            ctx.msBackingStorePixelRatio,
-            ctx.oBackingStorePixelRatio,
-            ctx.backingStorePixelRatio,
-            1
-        ].reduce((a, b) => a || b)
-
-        const pixelRatio = devicePixelRatio / backingStorePixelRatio
-        const rect = canvas.getBoundingClientRect();
-        canvas.width = rect.width * pixelRatio;
-        canvas.height = rect.height * pixelRatio;
-        ctx.scale(pixelRatio, pixelRatio);
-        return ctx;
-    }
-
-    function drawOnCanvas(ctx, x, y, color, isCatcher, isLocalPlayer) {
-        ctx.beginPath();
-        ctx.arc(x, y, 9, 0, 2 * Math.PI, false);
-        ctx.fillStyle = color;
-        ctx.fill();
-
-        if ( isCatcher ) {
-            ctx.beginPath();
-            ctx.arc(x, y, 6, 0, 2 * Math.PI, false);
-            ctx.fillStyle = 'black';
-            ctx.fill();
-        }
-
-        if ( isLocalPlayer ) {
-            ctx.beginPath();
-            ctx.arc(x, y, 3, 0, 2 * Math.PI, false);
-            ctx.fillStyle = 'white';
-            ctx.fill();
-        }
-    }
-
-
-    const canvas = document.getElementById('canvas')
-    const ctx = getScaled2DContext(canvas);
-
-    ws = new WebSocket("wss://" + window.location.host + "/channel")
-    ws.onopen = () => {
-        console.log("Socket is opened.");
-        ws.sendJsonBlob({ connect: true })
-    }
-
-    ws.onmessage = (event) => {
-        blobToJson(event.data).then((obj) => {
-            ctx.clearRect(0, 0, canvas.width, canvas.height)
-            for (var i in obj) {
-                var p = obj[i]
-                const isLocalPlayer = p.id.toLowerCase() == uuid
-                drawOnCanvas(ctx, p.position.x, p.position.y, p.color, p.catcher, isLocalPlayer)
-            }
-        })
-    };
-
-    ws.onclose = () => {
-        console.log("Socket is closed.");
-        ctx.clearRect(0, 0, canvas.width, canvas.height)
-    };
-
-    document.onkeydown = () => {
-        switch (event.keyCode) {
-            case 38: ws.sendJsonBlob({ key: 'up', isPressed: true }); break;
-            case 40: ws.sendJsonBlob({ key: 'down', isPressed: true }); break;
-            case 37: ws.sendJsonBlob({ key: 'left', isPressed: true }); break;
-            case 39: ws.sendJsonBlob({ key: 'right', isPressed: true }); break;
-        }
-    }
-
-    document.onkeyup = () => {
-        switch (event.keyCode) {
-            case 38: ws.sendJsonBlob({ key: 'up', isPressed: false }); break;
-            case 40: ws.sendJsonBlob({ key: 'down', isPressed: false }); break;
-            case 37: ws.sendJsonBlob({ key: 'left', isPressed: false }); break;
-            case 39: ws.sendJsonBlob({ key: 'right', isPressed: false }); break;
-        }
-    }
-}
-

The getScaled2DContext method will scale the canvas based on the pixel ratio, so we can draw smooth circles both on retina and standard displays. The drawOnCanvas method draws a player using the context at a given point. You can also draw the player with a tag and the white marker if the unique player id matches the local client identifier.

Before we connect to the socket we create a new reference using the canvas element and create a draw context. When a new message arrives we can decode it and draw the players based on the incoming status data. We clear the canvas before the render and after the connection is closed.

The last thing we have to do is to send the key press and release events to the server. We can add two listeners using the document variable, key codes are stored as integers, but we can map them and send right the JSON message as a blob value for the arrow keys.

Closing thoughts

As you can see it is relatively easy to add websocket support to an existing Vapor 4 application. Most of the time you will have to think about the architecture and the message structure instead of the Swift code. On by the way if you are setting up the backend behind an nginx proxy you might have to add the Upgrade and Connection headers to the location section.

server {
-    location @proxy {
-        proxy_pass http://127.0.0.1:8080;
-        proxy_pass_header Server;
-        proxy_set_header Host $http_host;
-        proxy_set_header X-Real-IP $remote_addr;
-        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
-        proxy_set_header Upgrade $http_upgrade;
-        proxy_set_header Connection "Upgrade";
-        proxy_connect_timeout 3s;
-        proxy_read_timeout 10s;
-        http2_push_preload on;
-    }
-}
-

This tutorial was mostly about building a proof of concept websocket game, this was the first time I’ve worked with websockets using Vapor 4, but I had a lot of fun while I made this little demo. In a real-time multiplayer game you have to think about a more intelligent lag handler, you can search for the interpolation, extrapolation or lockstep keywords, but IMHO this is a good starting point.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/what-are-the-best-practices-to-learn-ios-swift-in-2020/index.html b/docs/what-are-the-best-practices-to-learn-ios-swift-in-2020/index.html deleted file mode 100644 index 370b9fd..0000000 --- a/docs/what-are-the-best-practices-to-learn-ios-swift-in-2020/index.html +++ /dev/null @@ -1,418 +0,0 @@ - - - - - - - - - - - - What are the best practices to learn iOS / Swift in 2020? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 13 min read - -
-

What are the best practices to learn iOS / Swift in 2020?

-
-

Are you learning iOS development? Looking for Swift best practices? This is the right place to start your journey as a mobile application developer.

-
- -

Hello Swift!

Learning a programming language is hard. Even after more than a decade of software developer experience I feel like that I’m only scratching the surface. I don’t know much about low level assembly code, I don’t know how to create 3D games using shaders and many more. Still we all learn something new every single day. It’s a life-long journey and the path is full with obstacles, but if you keep going forward you’ll find that there’s gold at the end of the road. I still love to create new stuff from literally nothing but plain code. 😍

Everyone can code

In the beginning it’ll feel like that you have to learn a million things, but you shouldn’t be afraid because time is your friend. Day-by-day you’ll learn something new that’ll help you moving forward to achieve your next goal. I believe that the most important thing here is to have fun. If you feel frustrated because you can’t understand something just ask for help or take a break. The Swift community a group of amazing people, everybody is really helpful, so if you choose this programming language to bring your ideas to life you’ll meet some great people.

Now let me show you what you’ll need to start your career as a Swift application developer. 👨‍💻

Hardware

The first thing you’ll need to start your Apple developer career is a Mac. Unfortunately Mac’s are quite expensive machines nowadays, also the current series of MacBooks (both Air’s and Pro’s) have the completely broken butterfly keyboard mechanism. Hopefully this will change real soon.

I’d say that you should go with pre-butterfly models, you can look for the secondary market if you want to have a cheap deal. Otherwise you can go with a Mac mini, but if you buy one you should think about the extra expenses such as the monitor, keyboard & mouse.

If you have unlimited money, you should go with some high-end device like the new MacBook Pro 16”, iMac Pro or simply buy a Mac Pro. Nevertheless you should always choose a machine with an SSD. It’s kind of a shame that in 2020 a base iMac comes with a ridiculously slow HDD.

Another option is to build a hackintosh, but IMHO that’s the worst that you can do. You won’t get the same experience, plus you will struggle a lot fighting the system.

You might heard rumors that you’re going to be just fine with an iPad & Swift playgrounds. Honestly that’s just not the case. You can play around and learn the Swift programming language even with a Linux machine, but if you want to make your own iOS / iPadOS / macOS / watchOS apps, you’ll need a Mac for sure.

Software

So you’ve got a decent Mac. Let’s see what kind of software will you need if you want to start developing iOS apps. You might heard that Apple’s operating system is macOS. First of all, if you never used a mac before, you should get familiar with the system itself. When I bought my very first MacBook Pro, it took me about a week to get used to all the apps, system shortcuts and everything else.

If you don’t want to figure out everything for yourself, you came to the right place. Let me walk you through of every single app, tool that I’m using to my work as a professional mobile / backend developer.

Terminal

The most important thing that you should get used to is the Terminal (console) application. If you never heard about terminals before you should simply look for a beginner’s guide tutorial, but I highly recommend to learn at least the really basic commands.

Brew & cask

The very first thing that you should install on your new Mac is Homebrew. If you have used Linux before, might find this tool familiar (it’s working on Linux too). According to Max Howell (the creator):

The Missing Package Manager for macOS (or Linux)

You can also install regular applications with brew, by using the cask subcommand. e.g:

brew cask install firefox
-

I prefer to create a list of tools that I’m always using, so when I reinstall my machine I simply copy & paste those commands into terminal and I’m more or less ready with the restoration process. This is extremely convenient if you have to reinstall macOS from scratch.

MAS

You might noticed that I’ve got a thing for Terminal. If you don’t like the interface of the App Store, you can install MAS, a little helper tool. With the help of it you can install everything available in the store by using Terminal commands.

The readme on GitHub is really good, you should read it carefully. Anyway you don’t necessary need the mas-cli to do iOS development, but since it’s really convenient, I’d recommend to check it out.

Xcode

The number one tool that you’ll definitely need is Xcode. There is an alternative IDE tool called AppCode, but it’s always lagging behind and the vast majority of the community prefers Xcode. 🔨

A new Xcode version is released every single year with brand new features and functionalities. I’d recommend to go with the most recent one (also you should always upgrade your existing projects to support the latest version). You can get Xcode from the App Store, it’s completely free.

It’ll take a lot of time to install Xcode on your machine, but don’t worry after a few hours it’ll be ready to help you writing your very first iOS application. So be patient. :)

Git

Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.

Every single developer should use a proper version control system (aka. Git). Git is the de facto standard tool for version control and you can learn the basics in just about 15 minutes. Of course mastering it will take much longer, but it’s totally worth to start playing around with it.

GitHub

GitHub is a web-based hosting service for version control using git.

To be honest, GitHub it’s not just a repository hosting service anymore, it’s a complete platform with tools for issue management, project planning, continuous integration support and many more.

GitHub offers you a free tier both for public and private Git repositories for individuals. In 2019 it was acquired by Microsoft (everyone was afraid of this change, since MS has some history about ruining good services), but until for now they introduced lots of amazing new features. Go and get your free account now!

iOS app development using Swift

I believe that Swift has evolved to a stable & mature language during the past 5 years. If you think about it, it’s the only good option to write future proof iOS apps. You should clearly forget Objective-C, since Apple already made his choice. Eventually Swift will be the only programming language that Apple supports, there are already some frameworks that are Swift “only”. Just take a look at on SwiftUI. I mean you can’t write SwiftUI views in Objective-C, although the framework is 100% compatible with Objective-C based projects.

Dependency management

At some point in time you don’t want to write everything by yourself, because you’d like to make progress fast. This is where external packages are coming into the picture. Take my advice:

Never connect 3rd-party libraries by hand

The Swift Package Manager is natively integrated into Xcode. In the past CocoaPods was the ultimate dependency manager (some people preferred Carthage) for iOS projects, but nowadays it’s way better to use SPM. If you need to integrate external libraries SwiftPM is the right choice in 2020. If you don’t know how it works, you should read my comprehensive tutorial about how to use the Swift Package Manager.

I also made a quite popular article about the best / most popular iOS libraries written in Swift last year. It was featured on Sean Allen’s (Swift News) YouTube channel. Unfortunately he stopped that show, but he is still making some really good videos, so you should definitely check his channel if you have some time.

Anyway, if you don’t know where to start and what to integrate into your Swift project you should go and read my blog post, since it’s still up-to-date. Fortunately these things are not changing that often.

Application architecture

Picking the right architecture for your upcoming iOS project is one of the hardest things. The other one is building up your user interface, but let’s keep that topic for another day. You should never be afraid of architectures. Even if you choose MVC, MVP, MVVM or VIPER you can have a really well-written application strucutre. This is really important, because you don’t want to make your future self angry with some 2000+ lines sphagetti coded view controller with some nasty side effects.

So how you should pick an architecture? Since there a lots of them, you can even come up with a random one. Well, this is a real debate amongst iOS developers. My favorite one is VIPER, though I get a lot of criticism because of this. Honestly I really don’t give a damn about this, because it works for me (and my teams). Whether you go with plain old MVC it really doesn’t matters until it can solve your issue.

If you are a completely beginner please don’t start with VIPER, unless you can have someone by your side who can answer all your questions. My advice here is just to simply sit down and think through what do you want to achieve and make a basic plan. Of course it helps a lot if you are familiar with the patterns, but in the end of the day, you’ll realize that all of them are made by humans and none of them is perfect. 🤔

Conventions for Xcode

I made some really basic conventions for Xcode that you should check if you don’t know how to organize your projects. If you are not familiar with Xcode formats, targets, schemes, you should search the internet for such a tutorial, there are some well-explained examples about the entire thing.

Use folders to represent groups in Xcode

You know just like in real life, you don’t throw all your clothes into the shelf, right?. Being well organized is the only way to create a good project. Fortunately Apple realized this as well, and now groups are represented as physical folders on your hard drive by default.

Always try to fix every single warning

There is a reason that warnings exists, but you should never leave any of them in your production code. If you want to be more radical, there is a build flag in Xcode to treat warnings as errors. TURN IT ON! Kill all warnings! Haha, don’t do that, but you should always try to fix all your warnings.

Don’t let your code to grow on you

You know that awkward situation, when you open a source file, and you start scrolling, scrolling and you have to do even more scrolling. Yep, usually that’s a massive view controller problem and you already know that you are lost forever. If you get to this point, you can try to refactor your code by introducing a new object that can take over some of the functionality from your controller class.

Do not reinvent the wheel

If there is a best practice, use that. You should always look up the problem before you start coding. You should also think through the problem carefully before you start coding. Remember: you are not alone with your coding issues, I can almost guarantee that someone already had the exact same issue that you are working on. StackOverflow is the right place to look for solutions. Use the power of the community, don’t be afraid to ask questions on the internet, or from your co-workers, but don’t expect that others will solve your problem, that’s your job.

Swift advices for beginners

In this section I’m going to give you some real quick advices about how to write proper Swift code. I know I can’t have everything in this list, but in my opinion these are some really important ones.

Learn how to write proper async code

Look, if you know what is the “Great” Pyramid of Doom, you’ll know what I’m talking about. You’re going to write async code eventually, most of the API’s have async methods. Even a simple networking task is asynchrounous. It’s a smart move to learn how to write proper async code from the beginning.

There are a few approaches that you can choose from.

Of course you can go old-school by using completion blocks. That’s a good way of learning the concept and you can practice a lot, but there are way better options to write good async code in 2020.

Promises are high level abstractions over async tasks, they’ll make your life SO MUCH BETTER. You the real power comes from the fact that you can chain & transform them using functional methods. Promises are amazing, but they don’t really have built-in support for cancellation.

You can go with NSOperation tasks as well, but if you’d like to have some syntax sugar I’d recommend Promises. My problem is that if you have to pass data between operations you’ll have to create a new operation to do it, but in exchange of this little inconvenience they can run on queues, they can have priorities and dependencies.

I believe that the brand new Combine framework is the best way to deal with async code in 2020.

Only use singletons if necessary

They are the root of all evil. Honestly, avoid singletons as much as you can. If you want to deal with mixed-up states & untestable code, just go with them, but your life will be better if you take my advice. If you don’t know how to avoid the singleton pattern please do some research. There are lots of great articles on the net about this topic.

One exception: you can use a singleton if you are especially looking for shared states, like cache mechanisms or a local storage object like UserDefaults. Otherwise don’t use a singleton.

Do not create helpers (or managers)

If you need a helper class, you are doing things wrong! Every single object has it’s own place in your codebase, helpers are useless & not good for anything. Rethink, redefine, refactor if you need, but avoid helper classes at all cost. Learn about Swift design patterns or draw a chart about your models, but trust me there is no place for helpers in your code.

Avoid side effects & global state

Using globals is a really bad practice. Eventually some part of your code will override the global property and things are going to be pretty messed up. You can avoid side effects by simply eliminating those global variables. Also going functional is a neat way to improve your code.

Write some tests

You should always write tests, I’m not saying that you should go with TDD, but unit tests are good practice. They’ll help you to think through the possible mistakes and they validate your codebase. Also UI tests are good for validating the user interface, plus you can save countless hours if you don’t have to run manual tests.

Non-technical skills

I know it’s quite a list. Don’t be afraid, you don’t have to learn everything at once. Beginning your iOS career is not just all about learning new stuff, but you should have fun on the road. 😊

Time

Be patient & consistent. Always dedicate a fixed amount of time per day to code. It really doesn’t matters if it’s just half an hour, if you do it every day you’ll form a habit and the numbers will sum up as well. In this busy world, it’s really hard to find some time to really focus on something, but if you really want to learn how to write Swift code, this is the most important step that you need to take.

Motivation

Being motivated is easy if you have a “dream”. Do you want to build an app? Do you want to learn how to write something in Swift? Do you want to have a better job? Do you want to make a game for your kids? All of these things can be great motivators. The problem starts when you constantly hit the obstacles.

Don’t be afraid! Being a programmer means that sometimes you just try & fail. If you want to be a real good developer you should learn from those mistakes and do better on the second time. Of course you’ll learn a lot from other people as well, but sometimes you have to solve your own problems.

Goals

Don’t try to aim for one really big goal. Celebrate the little success stories and achievements. You should also be proud of what you’ve done “today”. It’s easy to forget to remember these little things, but making an app or learning a new programming language is a long-term project. If you don’t have your small moments that you can celebrate eventually you will lose motivation and interest for the “project”.

I think these three things are the most important non-technical skills if you want to learn Swift. Have your very own dedicated time to code every single day. Gain motivation from your dream (follow the big picture), but also celebrate every little success story that you achieved. Go step-by-step and you’ll find that there is nothing that you can’t learn. Anyway, technical skills are just secondary… 🤷‍♂️

The Swift community is amazing

It’s really good to see that there are still plenty of dedicated people who are keeping up writing about the good and bad parts of iOS / Swift development. Here are the best resources that you should know in 2020.

Best iOS / Swift tutorial sites

Best iOS / Swift blogs

Best iOS / Swift newsletters

Best iOS / Swift podcasts

Twitter accounts to follow

Where to go next?

In the past year I’ve interviewed lots of iOS developer candidates. Absolute beginners are always asking me the same question again and again: where should I go next, what should I learn next?

There is no definite answer, but this year I’m trying help you a lot. This is the very first year when I’ll dedicate more time on my blog than on anything else. No more new client projects, no more excuses.

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 3 min read -
- -

How to use iCloud drive documents?

-
-

Learn how to sync files and data through a shared iCloud drive folder using the latest version of Swift programming language.

- -
- iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

iOS Auto Layout tutorial programmatically

-
-

In this great iOS Auto Layout tutorial I'll teach you how to support rotation, use constraints, work with layers, animate corner radius.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

iOS custom transition tutorial in Swift

-
-

In this tutorial, you'll learn how to replace the push, pop and modal animations with custom transitions & percent driven interactions.

- -
- UIKit - iOS -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/whats-new-in-swift-5-3/index.html b/docs/whats-new-in-swift-5-3/index.html deleted file mode 100644 index c00878c..0000000 --- a/docs/whats-new-in-swift-5-3/index.html +++ /dev/null @@ -1,488 +0,0 @@ - - - - - - - - - - - - What's new in Swift 5.3? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 5 min read - -
-

What's new in Swift 5.3?

-
-

Swift 5.3 is going to be an exciting new release. This post is a showcase of the latest Swift programming language features.

-
- -

The Swift 5.3 release process started in late March, there are lots of new features that are already implemented on the 5.3 branch. If you are curious what are those you can try it out by installing the latest snapshot using swiftenv for example, you can grab them from swift.org.

Package Manager updates

Swift Package tools version 5.3 will feature some really great additions.

Resources

With the implementation of SE-0271 the Swift Package Manager can finally bundle resource files alongside code. I believe that this was quite a popular request, since there are some libraries that embed asset files, they were not able to add SPM support, until now.

Localized resources

SE-0278 extends the resource support, with this implementation you can declare localized resources for your Swift packages. The description explains well the proposed structure, you should take a look if you are interested in shipping localized files with your package.

Binary dependencies

The other great thing is that SPM will finally be able to use binary dependencies. SE-0272 adds this capability so people who want to ship closed source code can now take advantage of this feature. This will make it possible to have a binaryTarget dependency at a given path or location and you can use the binary as a product in a library or executable.

Conditional Target Dependencies

SE-0273 gives us a nice little addition so we can use dependencies based on given platforms. This means that you can use a product for a target when you build for a specific platform.

These features are great additions to the SPM, hopefully Xcode will benefit from these things as well, and we will see some great new enhancements in the upcoming version of the IDE too.

Language features

There are many new interesting proposals that got into the 5.3 version.

Multiple Trailing Closures

SE-0279 is one of the most debated new proposal. When I first saw it I was not sure about the need of it, why would someone put so much effort to eliminate a few brackets? 🤔

import UIKit
-
-class ViewController: UIViewController {
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        // old
-        UIView.animate(withDuration: 0.3, animations: {
-          self.view.alpha = 0
-        }, completion: { _ in
-          self.view.removeFromSuperview()
-        })
-        // still old
-        UIView.animate(withDuration: 0.3, animations: {
-          self.view.alpha = 0
-        }) { _ in
-          self.view.removeFromSuperview()
-        }
-
-        // new
-        UIView.animate(withDuration: 0.3) {
-          self.view.alpha = 0
-        }
-        
-        UIView.animate(withDuration: 0.3) {
-            self.view.alpha = 0
-        } completion: { _ in
-            self.view.removeFromSuperview()
-        }
-    }
-}
-

As you can see this is mostly a syntactic sugar, but I convinced myself that it is nice to have.

Synthesized Comparable conformance for enum types

Enum types don’t have to explicitly implement the Comparable protocol thanks to SE-0266.

enum Membership: Comparable {
-    case premium(Int)
-    case preferred
-    case general
-}
-([.preferred, .premium(1), .general, .premium(0)] as [Membership]).sorted()
-

The Comparable protocol is automatically synthesized, just like the Equatable and Hashable conformances for eligible types. Of course you can provide your own implementation if needed.

Enum cases as protocol witnesses

Swift enums are crazy powerful building blocks and now they just got better. 💪

protocol DecodingError {
-  static var fileCorrupted: Self { get }
-  static func keyNotFound(_ key: String) -> Self
-}
-
-enum JSONDecodingError: DecodingError {
-  case fileCorrupted
-  case keyNotFound(_ key: String)
-}
-

The main goal of SE-0280 to lift an existing restriction, this way enum cases can be protocol witnesses if they provide the same case names and arguments as the protocol requires.

Type-Based Program Entry Points

SE-0281 gives us a new @main attribute that you can use to define entry points for your apps. This is a great addition, you don’t have to write the MyApp.main() method anymore, but simply mark the MyApp object with the main attribute instead.

@main
-class AppDelegate: UIResponder, UIApplicationDelegate {
-
-    static func main() {
-        print("App will launch & exit right away.")
-    }
-}
-

The UIApplicationMain and NSApplicationMain attributes will be deprecated in favor of @main, I’d bet this is coming with the next major release…

Multi-Pattern Catch Clauses

SE-0276 is another syntactic sugar, it’s really handy to catch multiple cases at once.

do {
-    try performTask()
-}
-catch TaskError.someRecoverableError {
-    recover()
-}
-catch TaskError.someFailure(let msg), TaskError.anotherFailure(let msg) {
-    showMessage(msg)
-}
-

This eliminates the need of using a switch case in the catch block. ✅

Float16

Nothing much to say here, SE-0277 adds Float16 to the standard library.

let f16: Float16 = 3.14
-

Generic math functions are also coming soon…

Self changes

SE-0269 aka. Increase availability of implicit self in @escaping closures when reference cycles are unlikely to occur is a nice addition for those who don’t like to write self. 🧐

//old
-execute {
-    let foo = self.doFirstThing()
-    performWork(with: self.bar)
-    self.doSecondThing(with: foo)
-    self.cleanup()
-}
-
-//new
-execute { [self] in
-    let foo = doFirstThing()
-    performWork(with: bar)
-    doSecondThing(with: foo)
-    cleanup()
-}
-

This will allow us to write self in the capture list only and omit it later on inside the block.

Refine didSet Semantics

SE-0268 is an under the hood improvement to make didSet behavior better & more reliable. 😇

class Foo {
-    var bar = 0 {
-        didSet { print("didSet called") }
-    }
-
-    var baz = 0 {
-        didSet { print(oldValue) }
-    }
-}
-
-let foo = Foo()
-// This will not call the getter to fetch the oldValue
-foo.bar = 1
-// This will call the getter to fetch the oldValue
-foo.baz = 2
-

In a nutshell previously the getter of a property was always called, but from now on it’ll be only invoked if we use to the oldValue parameter in our didSet block.

Add Collection Operations on Noncontiguous Elements

SE-0270 adds a RangeSet type for representing multiple, noncontiguous ranges, as well as a variety of collection operations for creating and working with range sets.

var numbers = Array(1...15)
-
-// Find the indices of all the even numbers
-let indicesOfEvens = numbers.subranges(where: { $0.isMultiple(of: 2) })
-
-// Perform an operation with just the even numbers
-let sumOfEvens = numbers[indicesOfEvens].reduce(0, +)
-// sumOfEvens == 56
-
-// You can gather the even numbers at the beginning
-let rangeOfEvens = numbers.moveSubranges(indicesOfEvens, to: numbers.startIndex)
-// numbers == [2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15]
-// numbers[rangeOfEvens] == [2, 4, 6, 8, 10, 12, 14]
-

This proposal also extends the Collection type with some API methods using the RangeSet type, you should take a look if you are working a lot with ranges. 🤓

Where clauses on contextually generic declarations

With SE-0267 you’ll be able to implement functions and put a where constraint on them if you are only referencing generic parameters. Consider the following snippet:

protocol P {
-    func foo()
-}
-
-extension P {
-    func foo() where Self: Equatable {
-        print("lol")
-    }
-}
-

This won’t compile on older versions, but it’ll work like magic after Swift 5.3.

Add a String Initializer with Access to Uninitialized Storage

SE-0263 adds a new String initializer that allows you to work with an uninitialized buffer.

let myCocoaString = NSString("The quick brown fox jumps over the lazy dog") as CFString
-var myString = String(unsafeUninitializedCapacity: CFStringGetMaximumSizeForEncoding(myCocoaString, ...)) { buffer in
-    var initializedCount = 0
-    CFStringGetBytes(
-        myCocoaString,
-        buffer,
-        ...,
-        &initializedCount
-    )
-    return initializedCount
-}
-// myString == "The quick brown fox jumps over the lazy dog"
-

By using this new init method you don’t have to mess around with unsafe pointers anymore.

Future evolution of Swift

Currently there are 6 more accepted proposals on the Swift evolution dashboard and one is under review. Swift 5.3 is going to contain some amazing new features that were long awaited by the community. I’m really happy that the language is evolving in the right direction. 👍

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 6 min read -
- -

All about the Bool type in Swift

-
-

Learn everything about logical types and the Boolean algebra using the Swift programming language and some basic math.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Async HTTP API clients in Swift

-
-

Learn how to communicate with API endpoints using the brand new SwiftHttp library, including async / await support.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 9 min read -
- -

Beginners guide to functional Swift

-
-

The one and only tutorial that you'll ever need to learn higher order functions like: map, flatMap, compactMap, reduce, filter and more.

- -
- Swift -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Beginner's guide to modern generic programming in Swift

-
-

Learn the very basics about protocols, existentials, opaque types and how they are related to generic programming in Swift.

- -
- Swift -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/whats-new-in-vapor-4/index.html b/docs/whats-new-in-vapor-4/index.html deleted file mode 100644 index 434060b..0000000 --- a/docs/whats-new-in-vapor-4/index.html +++ /dev/null @@ -1,484 +0,0 @@ - - - - - - - - - - - - What's new in Vapor 4? - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 4 min read - -
-

What's new in Vapor 4?

-
-

Vapor is the most popular server side Swift web application framework. This time we'll cover what's new in Vapor 4.

-
- -

Swift 5.1

Vapor 3 was built on top of some great new features of Swift 4.1, that’s why it was only released shortly (2 months) after the new programming language arrived. This is the exact same situation with Vapor 4. Property wrappers are heavily used in the latest version of the Vapor framework, this feature is only going to be finalized in Swift 5.1 during the fall, which means that we can expect Vapor 4 shortly after. 🍁

SwiftNIO v2 and HTTP2 support

A HUGE step forward and a long awaited feature, because HTTP2 is amazing. Multiplexed streams, server push, header compression, binary data format instead of the good old textual one over a secure layer by default. These are just a few important changes that the new protocol brings to the table. The basic implementation is already there in Vapor 4 alpha 2, I tried to setup my own HTTP2 server, but I faced a constant crash, as soon as I can make it work, I’ll write a tutorial about it. 🤞

Fluent is amazing in Vapor 4!

Controllers now have an associated database object, this means you can query directly on this database, instead of the incoming request object. Note that the Future alias is now gone, it’s simply EventLoopFuture from SwiftNIO.

// Vapor 3
-
-import Vapor
-
-/// Controls basic CRUD operations on `Todo`s.
-final class TodoController {
-    /// Returns a list of all `Todo`s.
-    func index(_ req: Request) throws -> Future<[Todo]> {
-        return Todo.query(on: req).all()
-    }
-
-    /// Saves a decoded `Todo` to the database.
-    func create(_ req: Request) throws -> Future<Todo> {
-        return try req.content.decode(Todo.self).flatMap { todo in
-            return todo.save(on: req)
-        }
-    }
-
-    /// Deletes a parameterized `Todo`.
-    func delete(_ req: Request) throws -> Future<HTTPStatus> {
-        return try req.parameters.next(Todo.self).flatMap { todo in
-            return todo.delete(on: req)
-        }.transform(to: .ok)
-    }
-}
-
-// Vapor 4
-
-import Fluent
-import Vapor
-
-final class TodoController {
-    let db: Database
-
-    init(db: Database) {
-        self.db = db
-    }
-
-    func index(req: Request) throws -> EventLoopFuture<[Todo]> {
-        return Todo.query(on: self.db).all()
-    }
-
-    func create(req: Request) throws -> EventLoopFuture<Todo> {
-        let todo = try req.content.decode(Todo.self)
-        return todo.save(on: self.db).map { todo }
-    }
-
-    func delete(req: Request) throws -> EventLoopFuture<HTTPStatus> {
-        return Todo.find(req.parameters.get("todoID"), on: self.db)
-            .unwrap(or: Abort(.notFound))
-            .flatMap { $0.delete(on: self.db) }
-            .transform(to: .ok)
-    }
-}
-

Fluent has dynamic models, also the entire database layer is more sophisticated. You can define your own keys, schemas and many more which I personally love it, because it reminds me of my really old PHP based web framework. It’s really amazing that you don’t have to deal the underlying database provider anymore. It’s just Fluent so it really doesn’t matter if it’s pgsql or sqlite under the hood. ❤️

// Vapor 3
-
-import FluentSQLite
-import Vapor
-
-/// A single entry of a Todo list.
-final class Todo: SQLiteModel {
-    /// The unique identifier for this `Todo`.
-    var id: Int?
-
-    /// A title describing what this `Todo` entails.
-    var title: String
-
-    /// Creates a new `Todo`.
-    init(id: Int? = nil, title: String) {
-        self.id = id
-        self.title = title
-    }
-}
-
-/// Allows `Todo` to be used as a dynamic migration.
-extension Todo: Migration { }
-
-/// Allows `Todo` to be encoded to and decoded from HTTP messages.
-extension Todo: Content { }
-
-/// Allows `Todo` to be used as a dynamic parameter in route definitions.
-extension Todo: Parameter { }
-
-// Vapor 4
-
-import Fluent
-import Vapor
-
-final class Todo: Model, Content {
-    static let schema = "todos"
-
-    @ID(key: "id")
-    var id: Int?
-
-    @Field(key: "title")
-    var title: String
-
-    init() { }
-
-    init(id: Int? = nil, title: String) {
-        self.id = id
-        self.title = title
-    }
-}
-

There is a brand new migration layer with a ridiculously easy to learn API. 👍

import Fluent
-
-struct CreateTodo: Migration {
-    func prepare(on database: Database) -> EventLoopFuture<Void> {
-        return database.schema("todos")
-            .field("id", .int, .identifier(auto: true))
-            .field("title", .string, .required)
-            .create()
-    }
-
-    func revert(on database: Database) -> EventLoopFuture<Void> {
-        return database.schema("todos").delete()
-    }
-}
-

SwiftLog

A native logger library made by Apple is now the default logger in Vapor 4.

The entire logging system is bootstrapped during the boot process which I like quite a lot, because in the past I had some issues with the logger configuration in Vapor 3. 🤔

import Vapor
-
-func boot(_ app: Application) throws {
-    try LoggingSystem.bootstrap(from: &app.environment)
-    try app.boot()
-}
-

“Syntactic sugar”

Some little changes were introduced in the latest version of the framework.

For example the input parameter names in the config and the routes file are just one letter long (you don’t need to type that much). I personally don’t like this, because we have auto-complete. I know, it’s just a template and I can change it, but still… 🤐

Another small change is that the entire application launch / configuration process is way more simple than it was before, plus from now on you can shut down your app server gracefully. Overall it feels like all the API’s in Vapor were polished just the right amount, I really like the changes so far. 😉

… and many many more!

Tanner Nelson posted quite a list on Vapor’s discord server (it’s such an amazing community, you should join too). I’m going to shamelessly rip that off to show you most of the things that are going to be included in Vapor 4. Here is the list:

Vapor

  • services on controllers
  • synchronous content decoding
  • upload / download streaming
  • backpressure
  • http/2
  • extensible route builder (for openapi)
  • apple logging
  • improved session syntax
  • dotenv support
  • validation included
  • authentication included
  • XCTVapor testing module
  • swift server http client
  • simplified websocket endpoints
  • graceful shutdown
  • nio 2

ConsoleKit

  • type safe signatures

RoutingKit

  • performance improvements
  • performance testing bot

Fluent

  • dynamic models
  • simplified driver requirements
  • eager loading: join + subquery
  • partial selects
  • dirty updates

LeafKit

  • improved body syntax
  • separate lexer + parser

Toolbox

  • dynamic project init

How to set up a Vapor 4 project (on macOS)?

If you want to play around with Vapor 4, you can do it right now. You just have to install Xcode 11, the Vapor toolbox and run the following command from Terminal:

#optional: select Xcode 11
-sudo xcode-select --switch /Applications/Xcode-beta.app/Contents/Developer
-
-#create a brand new Vapor 4 project
-vapor new myproject --branch=4
-cd myproject
-vapor update -y
-

Personally I really love these new changes in Vapor, especially the HTTP2 support and the new Fluent abstraction. Vapor 3 was quite a big hit, I believe that this trend will continue with Vapor 4, because it’s going to be a really nice refinement update. 💧

I can’t wait to see some new benchmarks, because of the underlying changes in vapor, plus all the optimizations in Swift 5.1 will have such a nice impact on the overall performance. Vapor 3 was already crazy fast, but Vapor 4 will be on fire! 🔥

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

10 short advices that will make you a better Vapor developer right away

-
-

As a beginner server side Swift developer you'll face many obstackles. I'll show you how to avoid the most common ones.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 10 min read -
- -

A generic CRUD solution for Vapor 4

-
-

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

A simple HTTP/2 server using Vapor 4

-
-

Get started with server-side Swift using the Vapor 4 framework. Learn how to build a really simple HTTP/2 backend server.

- -
- Server - Vapor -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

AJAX calls using Vapor 4

-
-

Learn how to implement Asynchronous JavaScript and XML (AJAX) calls using Leaf templates and Vapor 4 as a server.

- -
- Server - Vapor -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - - diff --git a/docs/working-with-diffable-data-sources-and-table-views-using-uikit/index.html b/docs/working-with-diffable-data-sources-and-table-views-using-uikit/index.html deleted file mode 100644 index 792cdef..0000000 --- a/docs/working-with-diffable-data-sources-and-table-views-using-uikit/index.html +++ /dev/null @@ -1,747 +0,0 @@ - - - - - - - - - - - - Working with diffable data sources and table views using UIKit - The.Swift.Dev. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
-
-
-
- - · 7 min read - -
-

Working with diffable data sources and table views using UIKit

-
-

In this tutorial we're going to build a screen to allow single and multiple selections using diffable data source and a table view.

-
- -

Project setup

We’re going to use a regular storyboard-based Xcode project, since we’re working with UIKit.

We’re also going to need a table view, for this purpose we could go with a traditional setup, but since we’re using modern UIKit practices we’re going to do things just a bit different this time.

It’s quite unfortunate that we still have to provide our own type-safe reusable extensions for UITableView and UICollectionView classes. Anyway, here’s a quick snippet that we’ll use. ⬇️

import UIKit
-
-extension UITableViewCell {
-    
-    static var reuseIdentifier: String {
-        String(describing: self)
-    }
-
-    var reuseIdentifier: String {
-        type(of: self).reuseIdentifier
-    }
-}
-
-extension UITableView {
-        
-    func register<T: UITableViewCell>(_ type: T.Type) {
-        register(T.self, forCellReuseIdentifier: T.reuseIdentifier)
-    }
-
-    func reuse<T: UITableViewCell>(_ type: T.Type, _ indexPath: IndexPath) -> T {
-        dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as! T
-    }
-}
-

I’ve also created a subclass for UITableView, so I can configure everything inside the initialize function that we’re going to need in this tutorial.

import UIKit
-
-open class TableView: UITableView {
-
-    public init(style: UITableView.Style = .plain) {
-        super.init(frame: .zero, style: style)
-        
-        initialize()
-    }
-
-    @available(*, unavailable)
-    required public init?(coder: NSCoder) {
-        fatalError("init(coder:) has not been implemented")
-    }
-    
-    open func initialize() {
-        translatesAutoresizingMaskIntoConstraints = false
-        allowsMultipleSelection = true
-    }
-    
-    func layoutConstraints(in view: UIView) -> [NSLayoutConstraint] {
-        [
-            topAnchor.constraint(equalTo: view.topAnchor),
-            bottomAnchor.constraint(equalTo: view.bottomAnchor),
-            leadingAnchor.constraint(equalTo: view.leadingAnchor),
-            trailingAnchor.constraint(equalTo: view.trailingAnchor),
-        ]
-    }
-}
-

We are going to build a settings screen with a single selection and a multiple selection area, so it’s nice to have some extensions too that’ll help us to manage the selected table view cells. 💡

import UIKit
-
-public extension UITableView {
-    
-    func select(_ indexPaths: [IndexPath],
-                animated: Bool = true,
-                scrollPosition: UITableView.ScrollPosition = .none) {
-        for indexPath in indexPaths {
-            selectRow(at: indexPath, animated: animated, scrollPosition: scrollPosition)
-        }
-    }
-    
-
-    func deselect(_ indexPaths: [IndexPath], animated: Bool = true) {
-        for indexPath in indexPaths {
-            deselectRow(at: indexPath, animated: animated)
-        }
-    }
-    
-    func deselectAll(animated: Bool = true) {
-        deselect(indexPathsForSelectedRows ?? [], animated: animated)
-    }
-
-    func deselectAllInSection(except indexPath: IndexPath) {
-        let indexPathsToDeselect = (indexPathsForSelectedRows ?? []).filter {
-            $0.section == indexPath.section && $0.row != indexPath.row
-        }
-        deselect(indexPathsToDeselect)
-    }
-}
-

Now we can focus on creating a custom cell, we are going to use the new cell configuration API, but first we need a model for our custom cell class.

import Foundation
-
-protocol CustomCellModel {
-    var text: String { get }
-    var secondaryText: String? { get }
-}
-
-extension CustomCellModel {
-    var secondaryText: String? { nil }
-}
-

Now we can use this cell model and configure the CustomCell using the model properties. This cell will have two states, if the cell is selected we’re going to display a filled check mark icon, otherwise just an empty circle. We also update the labels using the abstract model values. ✅

import UIKit
-
-class CustomCell: UITableViewCell {
-
-    var model: CustomCellModel?
-
-    override func updateConfiguration(using state: UICellConfigurationState) {
-        super.updateConfiguration(using: state)
-        
-        var contentConfig = defaultContentConfiguration().updated(for: state)
-        contentConfig.text = model?.text
-        contentConfig.secondaryText = model?.secondaryText
-        
-        contentConfig.imageProperties.tintColor = .systemBlue
-        contentConfig.image = UIImage(systemName: "circle")
-
-        if state.isHighlighted || state.isSelected {
-            contentConfig.image = UIImage(systemName: "checkmark.circle.fill")
-        }
-        contentConfiguration = contentConfig
-    }
-}
-

Inside the ViewController class we can easily setup the newly created table view. Since we’re using a storyboard file we can override the init(coder:) method this time, but if you are instantiating the controller programmatically then you could simply create your own init method.

By the way I also wrapped this view controller inside a navigation controller so I’m display a custom title using the large style by default and there are some missing code pieces that we have to write.

import UIKit
-
-class ViewController: UIViewController {
-    
-    var tableView: TableView
-    
-    required init?(coder: NSCoder) {
-        self.tableView = TableView(style: .insetGrouped)
-
-        super.init(coder: coder)
-    }
-    
-    override func loadView() {
-        super.loadView()
-        
-        view.addSubview(tableView)
-
-        NSLayoutConstraint.activate(tableView.layoutConstraints(in: view))
-    }
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-        
-        title = "Table view"
-        navigationController?.navigationBar.prefersLargeTitles = true
-
-        tableView.register(CustomCell.self)
-        tableView.delegate = self
-
-    }
-    
-    override func viewDidAppear(_ animated: Bool) {
-        super.viewDidAppear(animated)
-        
-        reload()
-    }
-    
-    func reload() {
-        /// coming soon...
-    }
-
-}
-
-extension ViewController: UITableViewDelegate {
-
-    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
-        /// coming soon...
-    }
-
-    func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
-        /// coming soon...
-    }
-}
-

We don’t have to implement the table view data source methods, but we’re going to use a diffable data source for that purpose, let me show you how it works.

Diffable data source

I’ve already included one example containing a diffable data source, but that was a tutorial for creating modern collection views. A diffable data source is literally a data source tied to a view, in our case the UITableViewDiffableDataSource generic class is going to act as a data source object four our table view. The good think about these data sources is that you can easily manipulate the sections and rows inside the table view without the need of working with index paths.

So the main idea here is that we’d like to display two sections, one with a single selection option for selecting a number, and the second option group is going to contain a multi-selection group with some letters from the alphabet. Here are the data models for the section items.

enum NumberOption: String, CaseIterable {
-    case one
-    case two
-    case three
-}
-
-extension NumberOption: CustomCellModel {
- 
-    var text: String { rawValue }
-}
-
-enum LetterOption: String, CaseIterable {
-    case a
-    case b
-    case c
-    case d
-}
-
-extension LetterOption: CustomCellModel {
- 
-    var text: String { rawValue }
-}
-

Now we should be able to display these items inside the table view, if we implement the regular data source methods, but since we’re going to work with a diffable data source we need some additional models. To eliminate the need of index paths, we can use a Hashable enum to define our sections, we’re going to have two sections, one for the numbers and another for the letters. We’re going to wrap the corresponding type inside an enum with type-safe case values.

enum Section: Hashable {
-    case numbers
-    case letters
-}
-
-enum SectionItem: Hashable {
-    case number(NumberOption)
-    case letter(LetterOption)
-}
-
-struct SectionData {
-    var key: Section
-    var values: [SectionItem]
-}
-

We’re also going to introduce a SectionData helper, this way it’s going to be more easy to insert the necessary sections and section items using the data source.

final class DataSource: UITableViewDiffableDataSource<Section, SectionItem> {
-    
-    init(_ tableView: UITableView) {
-        super.init(tableView: tableView) { tableView, indexPath, itemIdentifier in
-            let cell = tableView.reuse(CustomCell.self, indexPath)
-            cell.selectionStyle = .none
-            switch itemIdentifier {
-            case .number(let model):
-                cell.model = model
-            case .letter(let model):
-                cell.model = model
-            }
-            return cell
-        }
-    }
-    
-    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
-        let id = sectionIdentifier(for: section)
-        switch id {
-        case .numbers:
-            return "Pick a number"
-        case .letters:
-            return "Pick some letters"
-        default:
-            return nil
-        }
-    }
-
-    func reload(_ data: [SectionData], animated: Bool = true) {
-        var snapshot = snapshot()
-        snapshot.deleteAllItems()
-        for item in data {
-            snapshot.appendSections([item.key])
-            snapshot.appendItems(item.values, toSection: item.key)
-        }
-        apply(snapshot, animatingDifferences: animated)
-    }
-}
-

We can provide a custom init method for the data source, where we can use the cell provider block to configure our cells with the given item identifier. As you can see the item identifier is actually the SectionItem enum that we created a few minutes ago. We can use a switch to get back the underlying model, and since these models conform to the CustomCellModel protocol we can set the cell.model property. It is also possible to implement the regular titleForHeaderInSection method and we can switch the section id and return a proper label for each section.

The final method is a helper, I’m using it to reload the data source with the given section items.

import UIKit
-
-class ViewController: UIViewController {
-    
-    var tableView: TableView
-    var dataSource: DataSource
-    
-    required init?(coder: NSCoder) {
-        self.tableView = TableView(style: .insetGrouped)
-        self.dataSource = DataSource(tableView)
-
-        super.init(coder: coder)
-    }
-    
-    override func loadView() {
-        super.loadView()
-        
-        view.addSubview(tableView)
-
-        NSLayoutConstraint.activate(tableView.layoutConstraints(in: view))
-    }
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-        
-        title = "Table view"
-        navigationController?.navigationBar.prefersLargeTitles = true
-
-        tableView.register(CustomCell.self)
-        tableView.delegate = self
-
-    }
-    
-    override func viewDidAppear(_ animated: Bool) {
-        super.viewDidAppear(animated)
-        
-        reload()
-    }
-    
-    func reload() {
-        dataSource.reload([
-            .init(key: .numbers, values: NumberOption.allCases.map { .number($0) }),
-            .init(key: .letters, values: LetterOption.allCases.map { .letter($0) }),
-        ])
-    }
-
-}
-
-extension ViewController: UITableViewDelegate {
-
-    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
-        // coming soon...
-    }
-
-    func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
-        // coming soon...
-    }
-}
-

So inside the view controller it is possible to render the table view and display both sections, even the cells are selectable by default, but I’d like to show you how to build a generic approach to store and return selected values, of course we could use the indexPathsForSelectedRows property, but I have a little helper tool which will allow single and multiple selection per section. 🤔

struct SelectionOptions<T: Hashable> {
-
-    var values: [T]
-    var selectedValues: [T]
-    var multipleSelection: Bool
-
-    init(_ values: [T], selected: [T] = [], multiple: Bool = false) {
-        self.values = values
-        self.selectedValues = selected
-        self.multipleSelection = multiple
-    }
-
-    mutating func toggle(_ value: T) {
-        guard multipleSelection else {
-            selectedValues = [value]
-            return
-        }
-        if selectedValues.contains(value) {
-            selectedValues = selectedValues.filter { $0 != value }
-        }
-        else {
-            selectedValues.append(value)
-        }
-    }
-}
-

By using a generic extension on the UITableViewDiffableDataSource class we can turn the selected item values into index paths, this will help us to make the cells selected when the view loads.

import UIKit
-
-extension UITableViewDiffableDataSource {
-
-    func selectedIndexPaths<T: Hashable>(_ selection: SelectionOptions<T>,
-                                         _ transform: (T) -> ItemIdentifierType) ->  [IndexPath] {
-        selection.values
-            .filter { selection.selectedValues.contains($0) }
-            .map { transform($0) }
-            .compactMap { indexPath(for: $0) }
-    }
-}
-

There is only one thing left to do, which is to handle the single and multiple selection using the didSelectRowAt and didDeselectRowAt delegate methods.

import UIKit
-
-class ViewController: UIViewController {
-    
-    var tableView: TableView
-    var dataSource: DataSource
-    
-    var singleOptions = SelectionOptions<NumberOption>(NumberOption.allCases, selected: [.two])
-    var multipleOptions = SelectionOptions<LetterOption>(LetterOption.allCases, selected: [.a, .c], multiple: true)
-
-    required init?(coder: NSCoder) {
-        self.tableView = TableView(style: .insetGrouped)
-        self.dataSource = DataSource(tableView)
-
-        super.init(coder: coder)
-    }
-    
-    override func loadView() {
-        super.loadView()
-        
-        view.addSubview(tableView)
-
-        NSLayoutConstraint.activate(tableView.layoutConstraints(in: view))
-    }
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-        
-        title = "Table view"
-        navigationController?.navigationBar.prefersLargeTitles = true
-
-        tableView.register(CustomCell.self)
-        tableView.delegate = self
-
-    }
-    
-    override func viewDidAppear(_ animated: Bool) {
-        super.viewDidAppear(animated)
-        
-        reload()
-    }
-    
-    func reload() {
-        dataSource.reload([
-            .init(key: .numbers, values: singleOptions.values.map { .number($0) }),
-            .init(key: .letters, values: multipleOptions.values.map { .letter($0) }),
-        ])
-
-        tableView.select(dataSource.selectedIndexPaths(singleOptions) { .number($0) })
-        tableView.select(dataSource.selectedIndexPaths(multipleOptions) { .letter($0) })
-    }
-
-}
-
-extension ViewController: UITableViewDelegate {
-
-    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
-        guard let sectionId = dataSource.sectionIdentifier(for: indexPath.section) else {
-            return
-        }
-
-        switch sectionId {
-        case .numbers:
-            guard case let .number(model) = dataSource.itemIdentifier(for: indexPath) else {
-                return
-            }
-            tableView.deselectAllInSection(except: indexPath)
-            singleOptions.toggle(model)
-            print(singleOptions.selectedValues)
-            
-        case .letters:
-            guard case let .letter(model) = dataSource.itemIdentifier(for: indexPath) else {
-                return
-            }
-            multipleOptions.toggle(model)
-            print(multipleOptions.selectedValues)
-        }
-    }
-
-    func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
-        guard let sectionId = dataSource.sectionIdentifier(for: indexPath.section) else {
-            return
-        }
-        switch sectionId {
-        case .numbers:
-            tableView.select([indexPath])
-        case .letters:
-            guard case let .letter(model) = dataSource.itemIdentifier(for: indexPath) else {
-                return
-            }
-            multipleOptions.toggle(model)
-            print(multipleOptions.selectedValues)
-        }
-    }
-}
-

This is why we’ve created the selection helper methods in the beginning of the article. It is relatively easy to implement a single and multi-selection section with this technique, but of course these things are even more simple if you can work with SwiftUI.

Anyway, I hope this tutorial helps for some of you, I still like UIKit a lot and I’m glad that Apple adds new features to it. Diffable data sources are excellent way of configuring table views and collection views, with these little helpers you can build your own settings or picker screens easily. 💪

- -
- - - -
-

Related posts

-
-
- - -
- - Tibor Bödecs - -
-
- - · 8 min read -
- -

10 little UIKit tips you should know

-
-

In this article I've gathered my top 10 favorite modern UIKit tips that I'd definitely want to know before I start my next project.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Building input forms for iOS apps

-
-

Learn how to build complex forms with my updated collection view view-model framework without the struggle using Swift.

- -
- UIKit -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 5 min read -
- -

Custom UIView subclass from a xib file

-
-

Do you want to learn how to load a xib file to create a custom view object? Well, this UIKit tutorial is just for you written in Swift.

- -
- UIKit - iOS -
-
-
- - -
- - Tibor Bödecs - -
-
- - · 4 min read -
- -

Custom views, input forms and mistakes

-
-

Just a little advice about creating custom view programmatically and the truth about why form building with collection views sucks.

- -
- UIKit -
-
-
-
- -
-
- -
-
- - Practical Server Side Swift cover image - -

- Get the Practical Server Side Swift book -

-

Swift on the server is an amazing new opportunity to build fast, safe and scalable backend apps. Write your very first web-based application by using your favorite programming language. Learn how to build a modular blog engine using the latest version of the Vapor 4 framework. This book will help you to design and create modern APIs that'll allow you to share code between the server side and iOS. Start becoming a full-stack Swift developer.

- Available on Gumroad -
- - - - - -
-
- -
- - - -
- - - -