Category Archives: Swift

Getting started with Vapor

Vapor is a web application framework written in Swift.

Installing on Linux

As I’m more likely to be using a Linux server for my web application, that’s what we’ll use for this post. So following the Install on Linux instructione, I went the route of installing the toolbox for the toolbox repo (instructions recreated below)

git clone https://github.com/vapor/toolbox.git
cd toolbox
git checkout <desired version>
make install

This will build and install vapor.

Generating an application

Vapor will generate an application for us using

vapor new hello-vapor -n

(obviously replace hello-vapor with your application name).

As we’re using Linux, we’ll cd into the hello-vapor application folder and then run

swift run

This will run, by default on localhost:8080 which is not great if you want to access remotely, so let’s instead start the application using

swift run Run --hostname 0.0.0.0 --port 8080

Now if you connect to the server’s ip address, port 8080 you should see the default return “It works!” if you use http://localhost:8080/hello you’ll see “Hello, world!” returned.

If you take a look at the source generated by Vapor and check the folder Sources/App/routes.swift you’ll see code like this

import Vapor

func routes(_ app: Application) throws {
    app.get { req in
        return "It works!"
    }

    app.get("hello") { req -> String in
        return "Hello, world!"
    }
}

As you can see this is routing the /hello GET method to return “Hello, world!”.

Using HTTP methods

As you can see from the previous code, app.get denotes the method is a GET method, so let’s simply write an example of each of the HTTP methods, so change your routes.swift file to add the following

app.get("method") { req -> String in
   return "\(req)"
}

app.post("method") { req -> String in
   return "\(req)"
}

app.put("method") { req -> String in
   return "\(req)"
}

app.patch("method") { req -> String in
   return "\(req)"
}

app.delete("method") { req -> String in
   return "\(req)"
}

This doesn’t cover all HTTP methods, but we can use the alternate syntax to the above, which looks like this

app.on(.GET, "method") { req -> String in
   return "\(req)"
}

and simply replace .GET with each of the HTTP methods, such as .CONNECT, .OPTIONS, etc.

Now let’s tests these – if we simply use CURL to execute each method, i.e.

curl -X GET localhost:8080/method
curl -X POST localhost:8080/method
curl -X PUT localhost:8080/method
curl -X PATCH localhost:8080/method
curl -X DELETE localhost:8080/method

Parameters and Query Parameters

We’re also likely to need to handle parameters, so for example /method/aparam

app.get("method", ":param") { req -> String in
   let param = req.parameters.get("param")!        
   return "\(param) --> \(req)"
}

The : prefixing the param token is indicates the parameter is dynamic and hence we need to use req.parameters.get(“param”)! to get the value from the param.

Finally for this post, what about if we want to pass query parameters, for example http://192.168.0.88:8080/method?firstName=Scooby&lastName=Doo

We can declare a struct to handle our expected query parameters like this

struct Person: Content {
    var firstName: String?
    var lastName: String?
}

and now change our GET method to decode the query parameters into this struct, for example

app.get("method") { req -> String in
   let person = try req.query.decode(Person.self)
   return "Firstname: \(person.firstName!), Lastname \(person.lastName!)"
}

There’s a whole lot more functionality, as you’d expect, including streaming, validation etc. take a look at the Vapor Docs.

Setting up swift on Ubuntu 20.04

This is an update to Setting up swift on Ubuntu 18.04 – installing Swift on Ubuntu 20.04.

Check out Downloads for the current info. from swift.org.

  • We need to install dependencies, so start with
    apt-get install binutils git gnupg2 libc6-dev libcurl4 libedit2 libgcc-9-dev libpython2.7 libsqlite3-0 libstdc++-9-dev libxml2 libz3-dev pkg-config tzdata uuid-dev zlib1g-dev
    
  • Next we need to download the tar for the version of Swift you’re targeting, mine’s Swift 5.5.2
    wget https://download.swift.org/swift-5.5.2-release/ubuntu2004/swift-5.5.2-RELEASE/swift-5.5.2-RELEASE-ubuntu20.04.tar.gz
    

Next up we should verify this download

  • First download the signature using
    wget https://swift.org/builds/swift-5.5.2-release/ubuntu2004/swift-5.5.2-RELEASE/swift-5.5.2-RELEASE-ubuntu20.04.tar.gz.sig
    
  • Now import the key
    wget -q -O - https://swift.org/keys/all-keys.asc | gpg --import -
    
  • Now run the following
    gpg --keyserver hkp://keyserver.ubuntu.com --refresh-keys Swift
    gpg --verify swift-5.5.2-RELEASE-ubuntu20.04.tar.gz.sig
    

Assuming everything’s verified we now want to extract the download into our preferred location – in my case I am just creating a folder name ~/Swift and extracting the tar to this location, running the following from this location

tar xzf swift-5.5.2-RELEASE-ubuntu20.04.tar.gz

Finally let’s update the path – I’ve added this to the last line of my .bashrc

export PATH=~/Swift/swift-5.5.2-RELEASE-ubuntu20.04/usr/bin:"${PATH}"

Swift enumerations

Swift enumerations are similar like Java enum types in that they can declare functionality as well as the usual values.

To declare a basic enumeration we use the following syntax

enum Color {
  case red
  case white
  case blue
  // ... etc.
}

We can also declare using associated values, i.e.

enum Color: Int {
  case red = 1
  case white
  case blue
  // ... etc.
}

We can also declare enums as strings, for example

enum Color: String {
  case red = "Red"
  case white = "White"
  case blue = "Blue"
  // ... etc.
}

Enums can also have methods, so for example (a rather contrived example)

enum Color {
  case red
  case white
  case blue
  // ... etc.

  func isRed() -> Bool {
    self == .red // .red shows abbreviated syntax
  }    
}

We can also declare enum cases with associated data/properties, so for example

enum Device {
  case phone(model: String)
  case tablet(model: String)
  case desktop(model: String)
}

let device = Device.phone("Samsung")

switch device {
  case .phone(let model)
    print("Phone: \(model)")
  case .tablet(let model)
    print("Tablet: \(model)")
  case .desktop(let model)
    print("Desktop: \(model)")
}

Recursive enum, where we might refer to the enum within the enum require the use of the keyword indirectcase. for example

enum Thing {
  case value(String)
   indirectcase thing(Thing)
}

Swift basics – let’s try this again

A while back I looked into the Swift programming language but had so many other things on at the time that I didn’t get far with it, so let’s try again but from the basics.

This post is the equivalent of a cheat sheet of the basics of Swift.

Note: ofcourse there’s no way I could include everything about a language in a single post, so this in non-exhaustive and primarily aimed at understanding key points of the language to get us up and running quickly.

What are the absolute basics I need to know about Swift?

  • Swift does not require semi-colons at the end of lines unless a single line has multiple statements
  • We declare variables using the var keyword and const values use let and we can either supply type annotation if we want to be explicit about the type used (which we need to do if declaring a float as double is implicitly used if decimal point numbers are used), for example
    var a: Int = 1  // variable, explicit type
    var b = 1       // variable, implicit type
    let s = "Hello" // constant, implicit type
    let dbl = 42.9  // constant double, implicit type
    let fl: Float = 42.9  // constant float, requires explicit type
    

    Interestingly constants in Swift (using the let keyword) are evaluated at runtime, unlike some other languages, hence they can be assigned at runtime.

  • Nulls exist within Swift, but they’re known as nil
  • Like other languages, We can declare numbers as hexadecimal , binary etc. For example
    let million = 1_000_000
    let binary = 0b1010
    let oct = 0o52
    let hex = 0x2a
    
  • We can also declare variables using the types initialization methods, for example
    let a = Double(42)
    let b = Int(1.2)
    let c = Int("123")
    

    In the above b is equivalent to Int(round(1.2)) and c attempts to convert the string to an Int.

  • Type aliasing can be used to declare an alias to a type, which is useful for more complex types or just to give a type some context to it’s usage, for example
    typealias counter = Int
    typealias Predicate = (s: String) -> Bool
    
  • Int, Bool, Double and other such types are value types, meaning they’re passed by value, however unlike some other languages, String, Array and Dictionary are also passed by value. Swift uses copy on write (COW) to ensure that when we pass around an array (for example) it’s actually a copy of the reference until the time that it’s mutated, then a copy is made.
  • The usual operators, -,+,/,*,% as well as += etc. exist but there is no ++ or — in the latest version of Swift. Hence we instead use += 1 and -= 1 in it’s place
  • The standard ==, !=, <, >, <=, >= comparison operators exist as do the logical operators || and &&
  • Ternary operators (as per C++/C# etc.) exists so we can write
    let a = b > 0 ? "+ve" : "-ve or zero"
    
  • Parenthesis are not used around control flow i.e.
    if a > 0 {
    }
    

Value and Reference Types

As mentioned above, many types are value types in Swift, these include structures, enumerations and tuples as well as the basic types, Int, Bool etc. which is probably expected, however Array, Dictionary and Set are also value types which might not be expected.

Classes are reference types, and just like many other languages – the big difference between value and reference types is how they’re passed between functions.

Value types are passed by passing a copy of the instance (in other words any changes made are not reflected in the calling code reference). Reference types are passed by reference and hence changes to the instance are reflected in the original instance.

Swift uses COW (copy on write) for assignment of value type instances, however not all value types get this.

Conventions

I always believe that when you write code in different languages you should try to write in the conventions and idioms of that languages.

  • Classes, structs etc. should use PascalCase (also known as CapitalizedCamelCase)
  • Variables, constants, function/method names should use camelCase (also known as uncapitalizedCamelCase)
  • The preference is for curly braces not be on a line of their own except the last brace, in other words
    if a > 0 {
    } else {
    }
    
    // is preferred over
    
    if a > 0
    {
    }
    else
    {
    }
    
  • Instead of naming functions like this addItem a preference seems to be, where appropriate, to instead use a more generic name which the parameter name giving more information, i.e. add(item: String) – we’ll look at functions soon.

Comments

Single line comments start with // and block comments /* */, one difference to some other languages is we can also nest block comments, for example /* /* */ */

nil and Optionals

As mentioned earlier nil is the Swift equivalent of NULL, null etc. Swift has optionals (or nullables) which also extend to the String type, hence the syntax String? means String is optional/nullable and we therefor also have syntax such as the following to ensure person is not nil

let fn = person?.firstName

To unwrap an optional we use !, i.e.

if person.firstName != nil {
  let fn = person.firstName! 
}

Swift also include the nil coalescing operator f

let a = b ?? c

Arrays

Arrays are declared using fairly standard [] syntax, i.e.

let a = [21, 22, 23] 
let c = a + [45, 46] // concat the two arrays

Dictionary

Dictionaries can be declare using a key/value array syntax, i.e.

let d = ["A": "1", "B", "2"]
let empty = [:] // empty dictionary

Set

Sets are simply unordered arrays in essence so can be declared using the same syntax but we need to supply an explicit type to Swift, for example

let s: Set = [1, 2]

Strings

A string is a struct, hence passed by value and uses standard string syntax as per this example “Hello” but also a character is a single string value, i.e. “a”.

String interpolation uses the \(…) syntax, for example

let s = "\(count) items"

Tuples

Most languages seem to include a Tuple type, Swift is no different, we declare a tuple like this

let t1 = (1, "One")

and as we’re not using named parameters, access to the values in the tuple are accessed via

let n = t1.0
let s = t1.1

Alternatively we can declare the tuple with named params, i.e.

let t1 = (idx: 1, name: "One")

Now we can access via the names, i.e.

let n = t1.idx
let s = t1.name

Switch statements

Switch statement are very similar to C# etc. except they do not (be default) fall-through to the next case statement, hence break is only needed where we need to exit a case block before the end. The default case is also not a requirement like it is for exhaustive switch statements.

Let’s look at some examples

switch color {
  case "red":
    print("it's red")  
  default:
    print("not red")
}

We can also use compound cases, i.e.

switch color {
  case "red", "white", "blue": 
    print("it's red, white or blue")  
  default:
    print("not red, white or blue")
}

For integers we can also use ranges do switch on, for example

case 0...<10:
  print("less than ten")
case 10...20:
  print("between 10 and 20")

We can extend this further using the where keyword, for example if a value is within a range of values and where it also matches another predicate

switch a {
  case 0...10 where a % 2 == 0
    print("Event num between 0 and 10")
}

Tuple matching exists where we can match all parameters or some, using a discard to ignore a parameter, for example

[code language="csharp"]
let t = (0, 2)
switch t {
  case (0, 0):
    // do something
  case (0, _):
    // only care about first item
  case (_, 0):
    // only care about second item
}

Next up, we can value bind a tuple within the case clause, basically meaning we have a way to get the actual value and assign it as part of the matching process, i.e.

let t = (0, 2)
switch t {
  case (0, 0):
    // do something
  case (let x, _):
    // get's the first item assigns as x
}

So in the above we actual switch on a (0, 0) tuple pattern but then for any other we just assign the first parameter to x and discard the second.

I mentioned that break is used in some case, for example

switch a {
  case (let a) where a % 2 == 0
    if(a == 4) {
      break
    }

    print("Even number between 0 and 10 but not 4")
}

On the other hand, if we actually do want our cases to fall-through then we can use the fallthrough keyword, i.e.

switch a {
  case (let a) where a % 2 == 0
    print("Is even")
    fallthrough
  case 4
    print("Is 4")
}

Loops

We’ve seen earlier that ++ and — do not exist in Swift, so the C#/Java/C++ way of looping is not going to exist, instead Swift generally uses a syntax more in line with enumerables using ranges (denoted by startend to denote the start and end of a loop)

for i in 1...10 {
  print(i)
}

So this works rather like C# foreach, hence as you’d expect, we can iterate over array items and dictionaries using pretty much the same syntax, i.e.

let a = [1,10,200]

for i in a {
  // do something with i
}

With dictionaries we simply deconstruct the tuples of key/value like this

for (a, b) in dictionary {
  // do something with a and/or b
}

While loops also exist, and in these we will need to use the += or -= , for example

while i < 10 {
  i += 1
}

repeat {
  i += 1
} while i < 10

Functions

Functions are written in the form

func funtionName(arg: argType) -> returnType {   
}

So this means we’d write an add function like this

func add(a: Int, b: Int) -> Int {
  return a + b
}

Calling our add function requires that we supply the names of the parameters, so to call this function we’d use the following

add(a: 1, b: 2)

In some cases we’d prefer to have anonymous arguments, i.e. not requiring the developer to supply the names, this would require the named parameter to be prefixed by a discard, i.e.

func add(_ a: Int, _ b: Int) -> Int {
  return a + b
}

and now we can just supply the arguments by index, i.e.

add(1, 2)

Interestingly we can also declare function parameters with two names, for example

func data(for request: URLRequest) -> Srring {
   // do something
}

In this case the first name for is the external name, i.e. the caller uses, whereas the second name request is the name used internally within the actual function.

Default values for parameters are also supported, for example

function fn(s: String = "Hello") -> Void {
}

Variadic arguments can be used, only one parameter can be variadic however, unlike some languages where this must be the last parameter of a function, due to variable names being used, the variadic argument can be in any position, for example

func addThenMultiply(values: Int..., multiplier: Int) -> Int
{
  for v in values {
     // do something
  }

  return somevalue
}

and we can call this using the following

addThenMultiply(args: 1, 2, 10, multiplier: 9)

Many of the other standard syntax exists, such as function overloading, passing functions (which are first class objects in Swift) as arguments etc.

One interesting addition is the requirement to mark functions which change state as mutating, for example, assuming the following code was part of a Container class which changes the internal storage when we add items

mutating func add(item: MyClass) -> Void {
}

Guards

The guard keyword can be used for preconditions on a functions, for example

func doSomething(i: Int) -> Void {
  guard i >= 0 else {
    return 
  }

  print("Positive")
}

Closures

Swift supports closures and we can assign closures to variables like this

var c = { print("Inside Closure") }  // () -> Void
var d = { return 123 } // () -> Int

Swift comes with a special syntax for trailing closures whereby a function is passed into another function without the need to supply the parenthesis, for example if we have a function like this

func fn(f: () -> Int) -> Void {
}

We can ofcourse supply one of our previously defined closures like this

fn(f: d)

but we can also supply the closures as a trailing closure like this

fn {
  return 987
}

There’s an alternative syntax that allows us to access closure parameters using the parameter index, so for example

let op = (Int, Int)  -> Int = { return $0 + $1 }
// or shorthand version
let op = (Int, Int)  -> Int = { $0 + $1 }

Properties

We can declare properties on our types like this

protocol SomeType {
  var error: String { get set } // read/write
  var instance: String { get }  // readonly
}

Operator overloading

Operator overloading is supported, here’s an example

extention Math {
  static func +(lhs: Math, rhs: Math) -> lhs.value + rhs.value // assuming value exists on Math somewhere
}

We can also define custom operators, for example

extension Math {
  // prefix style op
  static prefix func -><- (value: Int) -> Math
}

// in use
let x = -><- 100

Structs, Classes, Protocols & Enums

I’m not going to spend too much time on these four types as they would take up a lot of space in this post (which is already long) but suffice to say we can create a struct, class, protocol and enum (an enum is more like the enums in Java than C# and is a type that may also include functionality). The syntax for each of these is pretty much as expected

struct MyStruct {
}

class MyClass {
}

enum MyEnum {
}

procotol MyProtocol {
}

A struct is a value type (as discussed previously) and can have properties, methods etc. Structs are heavily uses in Swift. A class is a reference type and as one would expect, can also have properties, methods etc. A protocol is similar to a C# interface bordering on an abstract type as it can contain default functionality.

Enums are able to have values (options) as well as functionality, so here’s a simply example

enum Color {
    case red
    case white
    case blue
    // ... etc.

    func isRed() -> Bool {
        self == .red // .red shows abbreviated syntax
    }    
}

Extensions

Swift also has the ability to create extension methods (as per C#). The extension method can be used on protocols, structs classes etc.

If you’re used to C# extension methods then you’ll know that they allow us to extend frameworks or other libraries (as well as our own) which methods without the need to change the actual types themselves. The syntax for Swift extension is as follows

extension String {
  func appendOne() -> String {
     return self + "1"
  }
}

Namespaces/Modules

It appears that Swift does not (in this current release) have a concept of namespace or modules (or at least not as namespace like units). So what do we do?

The most obvious way of duplicating a namespace (well to a point) is to host code within classes, structs or enums. In the case of a classes and structs we’d want to hide and make private the init()

@available(*, unavailable) private init() {}

With enums, if they do not contain cases then they cannot be instantiated, hence we can now just include the functions or whatever we want into an enum.

Exception handling

Swift uses a slightly different syntax to many other languages, it still has a try and a catch but the try does not wrap all code that may throw an exception. Exceptions in Swift are of type Error. So instead of wrapping code in a try block we wrap it in a do with try for specific lines, i.e.

do {
   let s = try getString()

   // do something with s
} catch MyError.StringError {
   print('String Error occurred')   
} catch MyError.SomeOtherError(let errorCode) {
   print('Some Other Error occurred, error code: \(errorCode)')   
} catch {
   print('Unhandled exception occurred \(error)')
}

In the above we declare our do block and our method getString potentially throws an error. We therefore try to call this method and if all is well, we’ll do something with the returns string. Otherwise we try to catch the error if it’s a MyError.StringError (some error we’ve previously declared in this case) otherwise the catch all takes care of things.

What might our StringError look like? Well as Swift loves it’s enums, we’d potentially declare custom error conditions like this

enum MyError: Error {
  case StringError
  case SomeOtherError(errorCode: Int)
}

Swift still uses the concept of throwing errors, so our method getString might look like this

func getString() throws -> String {
   throw MyError.StringError   
}

One neat feature Swift has around errors is that we can convert them to an optional, so for example

let s = try? getString()

In this case if getString throws and error it’s converted to a nil. If, on the other hand we know that the method will not throw an error, for example maybe it throws an error if some member variable has not been set and we know it has been set, then we can disable error propogation using

let s = try! getString()

Generics

This “Swift basics” has a lot of topics, I’ve tried to reduce down to bare minimum of functionality most developers would look for or expect within a language. One last one feature we’ll cover is generics.

As somebody who started using generics/templates way back in my C++ days and was surprised at the lack of them in Java and C# in their early days and even Go today (if I recall). It’s nice to see Swift has generics and uses the fairly standard syntax of <T> (well in some ways fairly standard), i.e.

func get<T>(index: Int) -> T {
   return someValue;  
}

Swift doesn’t have the concept of default(T) like C#, so instead we would use nil (Optional.None).

We can type constrain our generic parameters, for example if had an add method which should only work on MyClass types we would declare the method like this

mutating func add<T: MyClass>(item: T) -> Void {
}

Now, I said that Swift uses fairly standard syntax “well in some ways fairly standard”. The difference is when we declare protocols with, what’s called Associated Types. In the case of protocols we declare an associatedtype within the protocol, for example

protocol Container {
  associatedtype Item
  mutating func add(item: Item)
  func get(index: Int) -> Item
}

When we come to implement the protocol we declare the type via the typealias, for example

struct MyContainer: Container {
  typealias Item = MyClass

  mutating func add(item: Item) {
    // mutate something
  }

  get(index: Int) -> Item {
     // return something
  }
}

Ofcourse MyContainer itself might take generic parameters and hence we might have

struct MyContainer<T>: Container {
  typealias Item = T

And I think that’s more than enough for this post, other posts will probably focus more on specific parts of the language that are of interest.

Setting up Swift on Ubuntu 18.04

Let’s setup a swift development environment on Ubuntu 18.04. “Why?”, you might ask, as swift was written by Apple for Mac and iOS development and I do happen to have a Apple Mac with everything installed there, my answer is “why not”, let’s give it a try.

  • Go to https://swift.org/download/#releases and locate the version of Swift you want to download, I picked Swift 5.3.2, Ubuntu 18.04. Download this to your machine.
  • From ~./Downloads run
    tar -xvzf swift-5.3.2-RELEASE-ubuntu18.04.tar.gz 
    

    Obviously replace the .tar.gz with whatever version you download.

  • Now would probably be a good time to move the resultant decompressed folder to where you want it to be kept, mine’s in a ~/Home/swift directory.
  • Open .bashrc and add the following line (or just export the path if you want it temporary without adding to .bashrc)
    export PATH=$PATH:$HOME/swift/swift-5.3.2-RELEASE-ubuntu18.04/usr/bin
    

    Don’t forget to save the .bashrc file if you’ve gone that route and ensure the path to swift usr/bin matches where you moved your files to

  • I’m going to use VSCode, as my editor, and there’s several Swift extensions, I installed Swift Language 0.2.0. This has the largest number of downloads, I’ve no idea how good this is compared to others, but that’s the one I’ve installed for now.
  • If all went well, open a terminal window in VSCode or just use the a bash terminal and type
    swift --version
    

    If all went well you’ll see the Swift version and Target listed

Getting Started

Now we have swift installed (hopefully), let’s look at the sort of “Getting Started” type of things you’ll want to try.

Let’s use the swift command to create a simple executable application. So run the following for your terminal

swift package init --type executable

This will use swift’s package manager to create a new executable application with good old “Hello World”.

To build this, simple execute the following command

swift build

and to run this we simply execute the command

swift run

When making changes to your code you can actually just save the file(s) and use the run command which will build and run the application.

The command which generated this source created a Package.swift file which is where we add dependencies etc. Source code is stored in the Sources folder, and here the file is named main.swift which simply contains

print("Hello World!")

In the Tests folder we have the tests for the application. We’re not going to go into those now except to say, you can run the following command to run the tests

swift test

Creating a Swift Package

In my previous post I looked at creating a CircleImage, basically creating a reusable SwiftUI control. Now let’s look at how we might start creating packages to allow us to share such library code.

Creating a Swift Package

From Xcode, select File | New | Swift Package. A very basic package is created (mine’s name MyLibrary) with a Sources/YouPackageName/YourPackageName.swift file containing the following code

struct MyLibrary {
    var text = "Hello, World!"
}

We’ll stick with this simple bit of code but make the struct and the text var public. Also we’ll need to add a public init method or you’ll see an error such as this

initializer is inaccessible due to ‘internal’ protection level

So the new code looks like this

public struct MyLibrary {
    public var text = "Hello, World!"
    
    public init() {
    }
}

Now use Command + B to build the package.

Local Package

To use the package locally, i.e. not having it deployed to a remote repos or the likes.

Within the application that you wish to use the package, simply (using Finder) select the package folder (i..e mine’s MyLibrary) and drag and drop this into your application under the top level project node in the Project Navigator.

Now we can use the code by simply importing it, for example

import MyLibrary

and now within your code

let text = MyLibrary().text

Making our package more useful

Let’s now move the code for our CircleImage into the package, we’ll leave the package name and therefore the component will be renamed, as MyLibrary

Here’s the code

import SwiftUI

@available(iOS 13, *)
public struct MyLibrary: View {
    var image: Image
    var borderColor: Color
    var shadowRadius: CGFloat

    public var body: some View {
        image
            .clipShape(Circle())
            .overlay(Circle().stroke(borderColor, lineWidth: 4))
            .shadow(radius: shadowRadius)
    }
    
    public init(image: Image,
                borderColor: Color = .white,
                shadowRadius: CGFloat = 10) {
        self.image = image
        self.borderColor = borderColor
        self.shadowRadius = shadowRadius
    }
}

In the above we’ve removed the public keyword on the fields and instead relied on the init method to set up any defaults. We also need to mark the code with the @available attribute to state what versions of OSX or iOS is supported, for example Image is available in 13.0 of iOS.

Within the package file Package.swift we also add the platforms value, here’s a snippet of my code, showing where to expect the platforms value

...
    name: "MyLibrary",
    platforms: [
        .macOS(.v10_15),
        .iOS(.v13),
    ],
    products: [
...

and in our application we now simply use something like the following

MyLibrary(image: landmark.image)
   .offset(x: 0, y: -130)
   .padding(.bottom, -130)

We’ll look at using GitHub or the likes to host our package in a subsequent post, but this gives an idea on how to get something up and running.

Swift UI Circle Image

Here’s a simple circle image using SwiftUI. This is pretty much taken from the SwiftUI Tutorial with a few additions.

struct CircleImage: View {
    var image: Image
    var borderColor: Color = .white
    var shadowRadius: CGFloat = 10

    var body: some View {
        image
            .clipShape(Circle())
            .overlay(Circle().stroke(borderColor, lineWidth: 4))
            .shadow(radius: shadowRadius)
    }
}

In this case we pass and image to the CircleImage, clipping it into a circle shape. Then we display a line (in white by default) around it with a shadow (with radius 10 by default).

We simply clip the supplied image, i.e.

CircleImage(image: Image("imageFrom.Assets.xcassets"), 
   borderColor: .red, 
   shadowRadius: 5)

The image should be stored within the Assets.xcassets editor.